diff --git a/packages/nx/src/lock-file/__fixtures__/npm.lock.ts b/packages/nx/src/lock-file/__fixtures__/npm.lock.ts index bdf2da5d3ea80..25df1e9529dda 100644 --- a/packages/nx/src/lock-file/__fixtures__/npm.lock.ts +++ b/packages/nx/src/lock-file/__fixtures__/npm.lock.ts @@ -22952,3 +22952,104 @@ export const ssh2LockFileV3 = `{ } } `; + +export const rxjsTslibLockFileV1 = `{ + "name": "test", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "requires": { + "tslib": "^2.1.0" + } + }, + "tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + } + } +} +`; +export const rxjsTslibLockFileV2 = `{ + "name": "test", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "test", + "version": "0.0.0", + "license": "MIT", + "dependencies": { + "rxjs": "^7.8.0", + "tslib": "^2.4.1" + } + }, + "node_modules/rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + } + }, + "dependencies": { + "rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "requires": { + "tslib": "^2.1.0" + } + }, + "tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + } + } +} +`; + +export const rxjsTslibLockFileV3 = `{ + "name": "test", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "test", + "version": "0.0.0", + "license": "MIT", + "dependencies": { + "rxjs": "^7.8.0", + "tslib": "^2.4.1" + } + }, + "node_modules/rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + } + } +} +`; diff --git a/packages/nx/src/lock-file/__fixtures__/pnpm.lock.ts b/packages/nx/src/lock-file/__fixtures__/pnpm.lock.ts index cd1f5e19c2f96..310eef0acce8b 100644 --- a/packages/nx/src/lock-file/__fixtures__/pnpm.lock.ts +++ b/packages/nx/src/lock-file/__fixtures__/pnpm.lock.ts @@ -5776,7 +5776,7 @@ packages: export const lockFileYargsAndDevkit = `lockfileVersion: 5.4 specifiers: - '@nrwl/devkit': 15.1.0 + '@nrwl/devkit': 15.0.13 yargs: 17.6.2 dependencies: @@ -6803,3 +6803,26 @@ packages: resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} dev: false `; + +export const rxjsTslibLockFile = `lockfileVersion: 5.4 + +specifiers: + rxjs: ^7.8.0 + tslib: ^2.4.1 + +dependencies: + rxjs: 7.8.0 + tslib: 2.4.1 + +packages: + + /rxjs/7.8.0: + resolution: {integrity: sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==} + dependencies: + tslib: 2.4.1 + dev: false + + /tslib/2.4.1: + resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} + dev: false +`; diff --git a/packages/nx/src/lock-file/__fixtures__/yarn.lock.ts b/packages/nx/src/lock-file/__fixtures__/yarn.lock.ts index f075ac083a8f9..e048c9d36eb81 100644 --- a/packages/nx/src/lock-file/__fixtures__/yarn.lock.ts +++ b/packages/nx/src/lock-file/__fixtures__/yarn.lock.ts @@ -8848,3 +8848,63 @@ __metadata: languageName: node linkType: hard `; + +/* +{ + name: 'test', + version: '0.0.0', + dependencies: { + rxjs: '^7.8.0', + tslib: '^2.4.1', + }, +} +*/ + +export const rxjsTslibLockFile = `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +rxjs@^7.8.0: + version "7.8.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" + integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== + dependencies: + tslib "^2.1.0" + +tslib@^2.1.0, tslib@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BO +`; + +export const berryRxjsTslibLockFile = `# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 6 + cacheKey: 8 + +"rxjs@npm:^7.8.0": + version: 7.8.0 + resolution: "rxjs@npm:7.8.0" + dependencies: + tslib: ^2.1.0 + checksum: 61b4d4fd323c1043d8d6ceb91f24183b28bcf5def4f01ca111511d5c6b66755bc5578587fe714ef5d67cf4c9f2e26f4490d4e1d8cabf9bd5967687835e9866a2 + languageName: node + linkType: hard + +"test@workspace:.": + version: 0.0.0-use.local + resolution: "test@workspace:." + dependencies: + rxjs: ^7.8.0 + tslib: ^2.4.1 + languageName: unknown + linkType: soft + +"tslib@npm:^2.1.0, tslib@npm:^2.4.1": + version: 2.4.1 + resolution: "tslib@npm:2.4.1" + checksum: 19480d6e0313292bd6505d4efe096a6b31c70e21cf08b5febf4da62e95c265c8f571f7b36fcc3d1a17e068032f59c269fab3459d6cd3ed6949eafecf64315fca + languageName: node +`; diff --git a/packages/nx/src/lock-file/npm.spec.ts b/packages/nx/src/lock-file/npm.spec.ts index d54419a4f4cd1..d0a4a64c2af90 100644 --- a/packages/nx/src/lock-file/npm.spec.ts +++ b/packages/nx/src/lock-file/npm.spec.ts @@ -4,18 +4,21 @@ import { stringifyNpmLockFile, } from './npm'; import { - lockFileV2, lockFileV1, - lockFileV3, - lockFileV3JustTypescript, - lockFileV3YargsAndDevkitOnly, - lockFileV2JustTypescript, lockFileV1JustTypescript, lockFileV1YargsAndDevkitOnly, + lockFileV2, + lockFileV2JustTypescript, lockFileV2YargsAndDevkitOnly, + lockFileV3, + lockFileV3JustTypescript, + lockFileV3YargsAndDevkitOnly, + rxjsTslibLockFileV1, + rxjsTslibLockFileV2, + rxjsTslibLockFileV3, + ssh2LockFileV1, ssh2LockFileV2, ssh2LockFileV3, - ssh2LockFileV1, } from './__fixtures__/npm.lock'; import { vol } from 'memfs'; import { npmLockFileWithWorkspaces } from './__fixtures__/workspaces.lock'; @@ -48,6 +51,14 @@ const Ssh2Package = { ssh2: '1.11.0', }, }; +const RxjsTslibPackage = { + name: 'test', + version: '0.0.0', + dependencies: { + rxjs: '^7.8.0', + tslib: '^2.4.1', + }, +}; describe('npm LockFile utility', () => { describe('v3', () => { @@ -176,6 +187,17 @@ describe('npm LockFile utility', () => { ) ).toEqual(ssh2LockFileV3); }); + + it('should correctly prune lockfile with packages in multiple versions', () => { + expect( + stringifyNpmLockFile( + pruneNpmLockFile( + parseNpmLockFile(rxjsTslibLockFileV3), + RxjsTslibPackage + ) + ) + ).toEqual(rxjsTslibLockFileV3); + }); }); describe('v2', () => { @@ -310,6 +332,17 @@ describe('npm LockFile utility', () => { ) ).toEqual(ssh2LockFileV2); }); + + it('should correctly prune lockfile with packages in multiple versions', () => { + expect( + stringifyNpmLockFile( + pruneNpmLockFile( + parseNpmLockFile(rxjsTslibLockFileV2), + RxjsTslibPackage + ) + ) + ).toEqual(rxjsTslibLockFileV2); + }); }); describe('v1', () => { @@ -438,6 +471,17 @@ describe('npm LockFile utility', () => { ) ).toEqual(ssh2LockFileV1); }); + + it('should correctly prune lockfile with packages in multiple versions', () => { + expect( + stringifyNpmLockFile( + pruneNpmLockFile( + parseNpmLockFile(rxjsTslibLockFileV1), + RxjsTslibPackage + ) + ) + ).toEqual(rxjsTslibLockFileV1); + }); }); }); }); diff --git a/packages/nx/src/lock-file/pnpm.spec.ts b/packages/nx/src/lock-file/pnpm.spec.ts index 9b11f1112da43..412a998b3b2a0 100644 --- a/packages/nx/src/lock-file/pnpm.spec.ts +++ b/packages/nx/src/lock-file/pnpm.spec.ts @@ -8,6 +8,7 @@ import { lockFileJustTypescript, lockFileWithInlineSpecifiers, lockFileYargsAndDevkit, + rxjsTslibLockFile, ssh2LockFile, } from './__fixtures__/pnpm.lock'; import { @@ -32,6 +33,14 @@ const Ssh2Package = { ssh2: '1.11.0', }, }; +const RxjsTslibPackage = { + name: 'test', + version: '0.0.0', + dependencies: { + rxjs: '^7.8.0', + tslib: '^2.4.1', + }, +}; describe('pnpm LockFile utility', () => { describe('standard lock file', () => { @@ -169,6 +178,17 @@ describe('pnpm LockFile utility', () => { ) ).toEqual(ssh2LockFile); }); + + it('should correctly prune lockfile with packages in multiple versions', () => { + expect( + stringifyPnpmLockFile( + prunePnpmLockFile( + parsePnpmLockFile(rxjsTslibLockFile), + RxjsTslibPackage + ) + ) + ).toEqual(rxjsTslibLockFile); + }); }); it('should parse lockfile with time-based resolution and workspaces', () => { diff --git a/packages/nx/src/lock-file/pnpm.ts b/packages/nx/src/lock-file/pnpm.ts index 95267be7d504d..162d19aedda34 100644 --- a/packages/nx/src/lock-file/pnpm.ts +++ b/packages/nx/src/lock-file/pnpm.ts @@ -3,9 +3,9 @@ import { PackageDependency, PackageVersions, } from './utils/lock-file-type'; -import { load, dump } from '@zkochan/js-yaml'; -import { TransitiveLookupFunctionInput, isRootVersion } from './utils/mapping'; -import { hashString, generatePrunnedHash } from './utils/hashing'; +import { dump, load } from '@zkochan/js-yaml'; +import { isRootVersion, TransitiveLookupFunctionInput } from './utils/mapping'; +import { generatePrunnedHash, hashString } from './utils/hashing'; import { satisfies } from 'semver'; import { PackageJsonDeps } from './utils/pruning'; import { sortObjectByKeys } from '../utils/object-sort'; @@ -422,11 +422,11 @@ function pruneDependencies( ): LockFileData['dependencies'] { const result: LockFileData['dependencies'] = {}; - Object.keys({ + Object.entries({ ...normalizedPackageJson.dependencies, ...normalizedPackageJson.devDependencies, ...normalizedPackageJson.peerDependencies, - }).forEach((packageName) => { + }).forEach(([packageName, packageVersion]) => { if (dependencies[packageName]) { const [key, { packageMeta, ...value }] = Object.entries( dependencies[packageName] @@ -446,7 +446,7 @@ function pruneDependencies( isDevDependency: !!normalizedPackageJson.devDependencies?.[packageName], key: meta.key, - specifier: value.version, + specifier: packageVersion, dependencyDetails: meta.dependencyDetails, }, ], diff --git a/packages/nx/src/lock-file/yarn.spec.ts b/packages/nx/src/lock-file/yarn.spec.ts index 7ea9ced1dbc83..3dbd4d13d1b2c 100644 --- a/packages/nx/src/lock-file/yarn.spec.ts +++ b/packages/nx/src/lock-file/yarn.spec.ts @@ -6,10 +6,12 @@ import { import { berryLockFile, berryLockFileDevkitAndYargs, + berryRxjsTslibLockFile, berrySsh2LockFile, lockFile, lockFileDevkitAndYargs, lockFileJustTypescript, + rxjsTslibLockFile, ssh2LockFile, } from './__fixtures__/yarn.lock'; @@ -43,6 +45,14 @@ const Ssh2Package = { ssh2: '1.11.0', }, }; +const RxjsTslibPackage = { + name: 'test', + version: '0.0.0', + dependencies: { + rxjs: '^7.8.0', + tslib: '^2.4.1', + }, +}; describe('yarn LockFile utility', () => { describe('classic', () => { @@ -134,6 +144,17 @@ describe('yarn LockFile utility', () => { ) ).toEqual(ssh2LockFile); }); + + it('should correctly prune lockfile with packages in multiple versions', () => { + expect( + stringifyYarnLockFile( + pruneYarnLockFile( + parseYarnLockFile(rxjsTslibLockFile), + RxjsTslibPackage + ) + ) + ).toEqual(rxjsTslibLockFile); + }); }); describe('berry', () => { @@ -249,6 +270,19 @@ describe('yarn LockFile utility', () => { ) ).toEqual(removeComment(berrySsh2LockFile)); }); + + it('should correctly prune lockfile with packages in multiple versions', () => { + expect( + removeComment( + stringifyYarnLockFile( + pruneYarnLockFile( + parseYarnLockFile(berryRxjsTslibLockFile), + RxjsTslibPackage + ) + ) + ) + ).toEqual(removeComment(berryRxjsTslibLockFile)); + }); }); }); diff --git a/packages/nx/src/lock-file/yarn.ts b/packages/nx/src/lock-file/yarn.ts index c91306e026da9..fc3f801d094e6 100644 --- a/packages/nx/src/lock-file/yarn.ts +++ b/packages/nx/src/lock-file/yarn.ts @@ -5,8 +5,8 @@ import { PackageDependency, PackageVersions, } from './utils/lock-file-type'; -import { TransitiveLookupFunctionInput, isRootVersion } from './utils/mapping'; -import { hashString, generatePrunnedHash } from './utils/hashing'; +import { isRootVersion, TransitiveLookupFunctionInput } from './utils/mapping'; +import { generatePrunnedHash, hashString } from './utils/hashing'; import { PackageJsonDeps } from './utils/pruning'; import { sortObjectByKeys } from '../utils/object-sort'; @@ -308,26 +308,36 @@ function pruneDependency( ([, v]) => v.rootVersion ); - result[packageName] = result[packageName] || {}; + result[packageName] ??= {}; + result[packageName][key] ??= { ...value, packageMeta: [] }; + + let metaVersion: string; + if (isPatch) { const originalPackageName = packageName.split('@patch:').pop(); const patchSuffix = value.packageMeta[0].split('#').pop(); - value.packageMeta = [ - `${packageName}@${getVersionFromPackageJson( - normalizedPackageJson, - originalPackageName - )}#${patchSuffix}`, - ]; + + metaVersion = `${packageName}@${getVersionFromPackageJson( + normalizedPackageJson, + originalPackageName + )}#${patchSuffix}`; + } else if (value.resolution) { + metaVersion = `${value.resolution.replace( + value.version, + '' + )}${getVersionFromPackageJson(normalizedPackageJson, packageName)}`; } else { - value.packageMeta = [ - value.resolution || - `${packageName}@${getVersionFromPackageJson( - normalizedPackageJson, - packageName - )}`, - ]; + metaVersion = `${packageName}@${getVersionFromPackageJson( + normalizedPackageJson, + packageName + )}`; } - result[packageName][key] = value; + + result[packageName][key].packageMeta = ensureMetaVersion( + result[packageName][key].packageMeta, + metaVersion + ); + pruneTransitiveDependencies(dependencies, result, value); } @@ -370,17 +380,16 @@ function pruneTransitiveDependencies( } if (prunedDeps[packageName][key]) { - const packageMeta = prunedDeps[packageName][key].packageMeta; - if (packageMeta.indexOf(metaVersion) === -1) { - packageMeta.push(metaVersion); - packageMeta.sort(); - } + prunedDeps[packageName][key].packageMeta = ensureMetaVersion( + prunedDeps[packageName][key].packageMeta, + metaVersion + ); } else { prunedDeps[packageName][key] = { ...depValue, packageMeta: [metaVersion], }; - // recurively collect dependencies + // recursively collect dependencies pruneTransitiveDependencies( dependencies, prunedDeps, @@ -511,3 +520,14 @@ function findDependencyTriplet( } return; } + +function ensureMetaVersion( + packageMeta: string[], + metaVersion: string +): string[] { + if (packageMeta.indexOf(metaVersion) === -1) { + return [...packageMeta, metaVersion].sort(); + } + + return packageMeta; +}