import { cleanupProject, newProject, runCLI, runCommand, uniq, updateFile, updateJson, } from '@nx/e2e/utils'; expect.addSnapshotSerializer({ serialize(str: string) { return ( str // Remove all output unique to specific projects to ensure deterministic snapshots .replaceAll(/my-pkg-\d+/g, '{project-name}') .replaceAll( /integrity:\s*.*/g, 'integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' ) .replaceAll(/\b[0-9a-f]{40}\b/g, '{SHASUM}') .replaceAll(/\d*B index\.js/g, 'XXB index.js') .replaceAll(/\d*B project\.json/g, 'XXB project.json') .replaceAll(/\d*B package\.json/g, 'XXXB package.json') .replaceAll(/size:\s*\d*\s?B/g, 'size: XXXB') .replaceAll(/\d*\.\d*\s?kB/g, 'XXX.XXX kb') .replaceAll(/[a-fA-F0-9]{7}/g, '{COMMIT_SHA}') .replaceAll(/Test @[\w\d]+/g, 'Test @{COMMIT_AUTHOR}') // Normalize the version title date. .replaceAll(/\(\d{4}-\d{2}-\d{2}\)/g, '(YYYY-MM-DD)') // We trim each line to reduce the chances of snapshot flakiness .split('\n') .map((r) => r.trim()) .join('\n') ); }, test(val: string) { return val != null && typeof val === 'string'; }, }); describe('nx release lock file updates', () => { let pkg1: string; let pkg2: string; let pkg3: string; let previousPackageManager: string; let previousYarnEnableImmutableInstalls: string; beforeAll(() => { previousPackageManager = process.env.SELECTED_PM; previousYarnEnableImmutableInstalls = process.env.YARN_ENABLE_IMMUTABLE_INSTALLS; }); // project will be created by each test individually // in order to test different package managers const initializeProject = (packageManager: 'npm' | 'yarn' | 'pnpm') => { process.env.SELECTED_PM = packageManager; newProject({ packages: ['@nx/js'], packageManager, }); pkg1 = uniq('my-pkg-1'); runCLI(`generate @nx/workspace:npm-package ${pkg1}`); pkg2 = uniq('my-pkg-2'); runCLI(`generate @nx/workspace:npm-package ${pkg2}`); pkg3 = uniq('my-pkg-3'); runCLI(`generate @nx/workspace:npm-package ${pkg3}`); // Update pkg2 to depend on pkg1 updateJson(`${pkg2}/package.json`, (json) => { json.dependencies ??= {}; json.dependencies[`@proj/${pkg1}`] = packageManager === 'pnpm' ? 'workspace:' : '0.0.0'; return json; }); }; afterEach(() => { cleanupProject(); }); afterAll(() => { process.env.SELECTED_PM = previousPackageManager; process.env.YARN_ENABLE_IMMUTABLE_INSTALLS = previousYarnEnableImmutableInstalls; }); it('should update package-lock.json when package manager is npm', async () => { initializeProject('npm'); updateJson('package.json', (json) => { json.workspaces = [pkg1, pkg2, pkg3]; return json; }); runCommand(`npm install`); // workaround for NXC-143 runCLI('reset'); runCommand(`git add .`); runCommand(`git commit -m "chore: initial commit"`); const versionOutput = runCLI(`release version 999.9.9`); expect(versionOutput.match(/NX Updating npm lock file/g).length).toBe(1); const filesChanges = runCommand('git diff --name-only HEAD'); expect(filesChanges).toMatchInlineSnapshot(` {project-name}/package.json {project-name}/package.json {project-name}/package.json package-lock.json `); }); it('should not update package-lock.json when package manager is npm and workspaces are not enabled', async () => { initializeProject('npm'); updateJson('package.json', (json) => { delete json.workspaces; return json; }); runCommand(`npm install`); // workaround for NXC-143 runCLI('reset'); runCommand(`git add .`); runCommand(`git commit -m "chore: initial commit"`); const versionOutput = runCLI(`release version 999.9.9 --verbose`); expect( versionOutput.match( /Skipped lock file update because npm workspaces are not enabled./g ).length ).toBe(1); const filesChanges = runCommand('git diff --name-only HEAD'); expect(filesChanges).toMatchInlineSnapshot(` {project-name}/package.json {project-name}/package.json {project-name}/package.json `); }); it('should not update lock file when package manager is yarn classic', async () => { initializeProject('yarn'); updateJson('package.json', (json) => { json.workspaces = [pkg1, pkg2, pkg3]; return json; }); runCommand(`corepack prepare yarn@1.22.19 --activate`); runCommand(`yarn set version 1.22.19`); runCommand(`yarn install`); // workaround for NXC-143 runCLI('reset'); runCommand(`git add .`); runCommand(`git commit -m "chore: initial commit"`); const versionOutput = runCLI(`release version 999.9.9 --verbose`); expect( versionOutput.match( /Skipped lock file update because it is not necessary for Yarn Classic./g ).length ).toBe(1); const filesChanges = runCommand('git diff --name-only HEAD'); expect(filesChanges).toMatchInlineSnapshot(` {project-name}/package.json {project-name}/package.json {project-name}/package.json `); }); it('should update yarn.lock when package manager is yarn', async () => { process.env.YARN_ENABLE_IMMUTABLE_INSTALLS = 'false'; initializeProject('yarn'); updateJson('package.json', (json) => { json.workspaces = [pkg1, pkg2, pkg3]; return json; }); runCommand(`corepack prepare yarn@4.0.2 --activate`); runCommand(`yarn set version 4.0.2`); runCommand(`yarn install`); // workaround for NXC-143 runCLI('reset'); runCommand(`git add .`); runCommand(`git commit -m "chore: initial commit"`); const versionOutput = runCLI(`release version 999.9.9`); expect(versionOutput.match(/NX Updating yarn lock file/g).length).toBe(1); const filesChanges = runCommand('git diff --name-only HEAD'); expect(filesChanges).toMatchInlineSnapshot(` .yarn/install-state.gz {project-name}/package.json {project-name}/package.json {project-name}/package.json yarn.lock `); }); it('should not update pnpm-lock.yaml when package manager is pnpm (>= 9)', async () => { initializeProject('pnpm'); updateFile( 'pnpm-workspace.yaml', `packages:\n - ${pkg1}\n - ${pkg2}\n - ${pkg3}\n` ); // workaround for NXC-143 runCLI('reset'); runCommand(`pnpm install`); runCommand(`git add .`); runCommand(`git commit -m "chore: initial commit"`); const versionOutput = runCLI(`release version 999.9.9`); expect(versionOutput.match(/NX Updating pnpm lock file/g).length).toBe(1); const filesChanges = runCommand('git diff --name-only HEAD'); expect(filesChanges).toMatchInlineSnapshot(` {project-name}/package.json {project-name}/package.json {project-name}/package.json `); }); });