From 43b802d1ccb6f2ebe6117360644db9b5d56658e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Jona=C5=A1?= Date: Thu, 17 Aug 2023 16:22:48 +0200 Subject: [PATCH] fix(core): fix ghost alias when parsing yarn lockfile (#18646) --- .../plugins/js/lock-file/yarn-parser.spec.ts | 144 ++++++++++++++++++ .../src/plugins/js/lock-file/yarn-parser.ts | 33 ++-- 2 files changed, 162 insertions(+), 15 deletions(-) diff --git a/packages/nx/src/plugins/js/lock-file/yarn-parser.spec.ts b/packages/nx/src/plugins/js/lock-file/yarn-parser.spec.ts index 630af38a61eb9..c932c1f3fe76d 100644 --- a/packages/nx/src/plugins/js/lock-file/yarn-parser.spec.ts +++ b/packages/nx/src/plugins/js/lock-file/yarn-parser.spec.ts @@ -365,6 +365,11 @@ describe('yarn LockFile utility', () => { 'node_modules/yargs/package.json': '{"version": "17.6.2"}', 'node_modules/yargs-parser/package.json': '{"version": "21.1.1"}', 'node_modules/yocto-queue/package.json': '{"version": "0.1.0"}', + 'node_modules/@types/prop-types/package.json': '{"version": "15.7.5"}', + 'node_modules/@docusaurus/core/package.json': '{"version": "2.4.1"}', + 'node_modules/@docusaurus/react-loadable/package.json': + '{"version": "5.5.2"}', + 'node_modules/react-loadable/package.json': '{"version": "5.5.2"}', }; vol.fromJSON(fileSys, '/root'); }); @@ -620,6 +625,145 @@ describe('yarn LockFile utility', () => { prunedLockFile.split('\n').slice(2) ); }); + + describe('alias duplicate', () => { + it('should parse yarn berry', async () => { + const lockFile = `# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 6 + cacheKey: 8 + +"@docusaurus/core@npm:2.4.1": + version: 2.4.1 + resolution: "@docusaurus/core@npm:2.4.1" + dependencies: + "@docusaurus/react-loadable": 5.5.2 + react-loadable: "npm:@docusaurus/react-loadable@5.5.2" + bin: + docusaurus: bin/docusaurus.mjs + checksum: 40c887ef662f7679d803695d4193268c2c177c6d4e13b43b56cc519322522a1608b4bfc4999f6355be778ca7a0256f0d27ab18a19b352a9da1aed66e2644dc82 + languageName: node + linkType: hard + +"@docusaurus/react-loadable@npm:5.5.2, react-loadable@npm:@docusaurus/react-loadable@5.5.2": + version: 5.5.2 + resolution: "@docusaurus/react-loadable@npm:5.5.2" + checksum: 930fb9e2936412a12461f210acdc154a433283921ca43ac3fc3b84cb6c12eb738b3a3719373022bf68004efeb1a928dbe36c467d7a1f86454ed6241576d936e7 + languageName: node + linkType: hard +`; + + const packageJson: PackageJson = { + name: '@my-ns/example', + version: '0.0.1', + type: 'commonjs', + dependencies: { + '@docusaurus/core': '2.4.1', + }, + }; + + const builder = new ProjectGraphBuilder(); + parseYarnLockfile(lockFile, packageJson, builder); + const graph = builder.getUpdatedProjectGraph(); + expect(graph.externalNodes).toMatchInlineSnapshot(` + { + "npm:@docusaurus/core": { + "data": { + "hash": "40c887ef662f7679d803695d4193268c2c177c6d4e13b43b56cc519322522a1608b4bfc4999f6355be778ca7a0256f0d27ab18a19b352a9da1aed66e2644dc82", + "packageName": "@docusaurus/core", + "version": "2.4.1", + }, + "name": "npm:@docusaurus/core", + "type": "npm", + }, + "npm:@docusaurus/react-loadable": { + "data": { + "hash": "930fb9e2936412a12461f210acdc154a433283921ca43ac3fc3b84cb6c12eb738b3a3719373022bf68004efeb1a928dbe36c467d7a1f86454ed6241576d936e7", + "packageName": "@docusaurus/react-loadable", + "version": "5.5.2", + }, + "name": "npm:@docusaurus/react-loadable", + "type": "npm", + }, + "npm:react-loadable": { + "data": { + "hash": "930fb9e2936412a12461f210acdc154a433283921ca43ac3fc3b84cb6c12eb738b3a3719373022bf68004efeb1a928dbe36c467d7a1f86454ed6241576d936e7", + "packageName": "react-loadable", + "version": "npm:@docusaurus/react-loadable@5.5.2", + }, + "name": "npm:react-loadable", + "type": "npm", + }, + } + `); + }); + + it('should parse yarn classic', async () => { + const lockFile = `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@docusaurus/core@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-2.4.1.tgz#4b8ff5766131ce3fbccaad0b1daf2ad4dc76f62d" + integrity sha512-SNsY7PshK3Ri7vtsLXVeAJGS50nJN3RgF836zkyUfAD01Fq+sAk5EwWgLw+nnm5KVNGDu7PRR2kRGDsWvqpo0g== + dependencies: + "@docusaurus/react-loadable" "5.5.2" + react-loadable "npm:@docusaurus/react-loadable@5.5.2" + +"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2": + version "5.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce" + integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ== +`; + + const packageJson: PackageJson = { + name: '@my-ns/example', + version: '0.0.1', + type: 'commonjs', + dependencies: { + '@docusaurus/core': '2.4.1', + }, + }; + + const builder = new ProjectGraphBuilder(); + parseYarnLockfile(lockFile, packageJson, builder); + const graph = builder.getUpdatedProjectGraph(); + expect(graph.externalNodes).toMatchInlineSnapshot(` + { + "npm:@docusaurus/core": { + "data": { + "hash": "sha512-SNsY7PshK3Ri7vtsLXVeAJGS50nJN3RgF836zkyUfAD01Fq+sAk5EwWgLw+nnm5KVNGDu7PRR2kRGDsWvqpo0g==", + "packageName": "@docusaurus/core", + "version": "2.4.1", + }, + "name": "npm:@docusaurus/core", + "type": "npm", + }, + "npm:@docusaurus/react-loadable": { + "data": { + "hash": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", + "packageName": "@docusaurus/react-loadable", + "version": "5.5.2", + }, + "name": "npm:@docusaurus/react-loadable", + "type": "npm", + }, + "npm:react-loadable": { + "data": { + "hash": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", + "packageName": "react-loadable", + "version": "npm:@docusaurus/react-loadable@5.5.2", + }, + "name": "npm:react-loadable", + "type": "npm", + }, + } + `); + }); + }); }); describe('auxiliary tagged version ranges', () => { diff --git a/packages/nx/src/plugins/js/lock-file/yarn-parser.ts b/packages/nx/src/plugins/js/lock-file/yarn-parser.ts index 15212527e8004..eb49cf1d19de5 100644 --- a/packages/nx/src/plugins/js/lock-file/yarn-parser.ts +++ b/packages/nx/src/plugins/js/lock-file/yarn-parser.ts @@ -50,13 +50,17 @@ export function parseYarnLockfile( addDependencies(groupedDependencies, builder, keyMap); } -function getPackageNames(keys: string): string[] { - const packageNames = new Set(); +function getPackageNameKeyPairs(keys: string): Map> { + const result = new Map>(); keys.split(', ').forEach((key) => { const packageName = key.slice(0, key.indexOf('@', 1)); - packageNames.add(packageName); + if (result.has(packageName)) { + result.get(packageName).add(key); + } else { + result.set(packageName, new Set([key])); + } }); - return Array.from(packageNames); + return result; } function addNodes( @@ -79,16 +83,14 @@ function addNodes( if (snapshot.linkType === 'soft' || keys.includes('@patch:')) { return; } - const packageNames = getPackageNames(keys); - packageNames.forEach((packageName) => { - const version = findVersion( - packageName, - keys.split(', ')[0], - snapshot, - isBerry - ); - - keys.split(', ').forEach((key) => { + const nameKeyPairs = getPackageNameKeyPairs(keys); + nameKeyPairs.forEach((keySet, packageName) => { + const keysArray = Array.from(keySet); + // use key relevant to the package name + const version = findVersion(packageName, keysArray[0], snapshot, isBerry); + + // use keys linked to the extracted package name + keysArray.forEach((key) => { // we don't need to keep duplicates, we can just track the keys const existingNode = nodes.get(packageName)?.get(version); if (existingNode) { @@ -182,11 +184,12 @@ function findVersion( snapshot: YarnDependency, isBerry: boolean ): string { - const versionRange = key.slice(packageName.length + 1); + const versionRange = key.slice(key.indexOf('@', 1) + 1); // check for alias packages const isAlias = isBerry ? snapshot.resolution && !snapshot.resolution.startsWith(`${packageName}@`) : versionRange.startsWith('npm:'); + if (isAlias) { return versionRange; }