From efa5ba2bb68662f52f4248fd1e25a883adb929cd Mon Sep 17 00:00:00 2001 From: Zach Tindall Date: Thu, 19 Dec 2024 23:07:06 -0500 Subject: [PATCH] fix(core): update yarn-parser to handle yarn v4 syntax (#29067) Update the yarn-parser so that yarn.lock file generation works with yarn v4 ## Current Behavior yarn.lock is not correctly created when using yarn v4 ``` "": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin::version=1.22.8&hash=c3c19d" dependencies: is-core-module: "npm:^2.13.0" path-parse: "npm:^1.0.7" supports-preserve-symlinks-flag: "npm:^1.0.0" bin: resolve: bin/resolve checksum: 0446f024439cd2e50c6c8fa8ba77eaa8370b4180f401a96abf3d1ebc770ac51c1955e12764cde449fde3fff480a61f84388e3505ecdbab778f4bef5f8212c729 languageName: node linkType: hard ``` ## Expected Behavior yarn.lock should be created correctly with no missing keys ## Related Issue(s) Fixes #19881 --- .../plugins/js/lock-file/yarn-parser.spec.ts | 125 ++++++++++++++++++ .../src/plugins/js/lock-file/yarn-parser.ts | 3 +- 2 files changed, 127 insertions(+), 1 deletion(-) 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 2e920bb5c1..2854648a33 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 @@ -2678,6 +2678,131 @@ __metadata: " `); }); + + it('should keep the builtin patch for yarn 4 patch syntax', () => { + 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 + +"resolve@npm:1.22.8, resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.12.0, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.4, resolve@npm:^1.22.8, resolve@npm:^1.9.0": + version: 1.22.8 + resolution: "resolve@npm:1.22.8" + dependencies: + is-core-module: "npm:^2.13.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10/c473506ee01eb45cbcfefb68652ae5759e092e6b0fb64547feadf9736a6394f258fbc6f88e00c5ca36d5477fbb65388b272432a3600fa223062e54333c156753 + languageName: node + linkType: hard + +"resolve@patch:resolve@npm%3A1.22.8#optional!builtin, resolve@patch:resolve@npm%3A^1.1.7#optional!builtin, resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.12.0#optional!builtin, resolve@patch:resolve@npm%3A^1.14.2#optional!builtin, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin, resolve@patch:resolve@npm%3A^1.9.0#optional!builtin": + version: 1.22.8 + resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin::version=1.22.8&hash=c3c19d" + dependencies: + is-core-module: "npm:^2.13.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10/f345cd37f56a2c0275e3fe062517c650bb673815d885e7507566df589375d165bbbf4bdb6aa95600a9bc55f4744b81f452b5a63f95b9f10a72787dba3c90890a + languageName: node + linkType: hard + `; + + const packageJson: PackageJson = { + name: '@my-ns/example', + version: '0.0.1', + type: 'commonjs', + dependencies: { + resolve: '^1.12.0', + }, + }; + + const hash = uniq('mock-hash'); + const externalNodes = getYarnLockfileNodes(lockFile, hash, packageJson); + const pg = { + nodes: {}, + dependencies: {}, + externalNodes, + }; + const ctx: CreateDependenciesContext = { + projects: {}, + externalNodes, + fileMap: { + nonProjectFiles: [], + projectFileMap: {}, + }, + filesToProcess: { + nonProjectFiles: [], + projectFileMap: {}, + }, + nxJsonConfiguration: null, + workspaceRoot: '/virtual', + }; + const dependencies = getYarnLockfileDependencies(lockFile, hash, ctx); + + const builder = new ProjectGraphBuilder(pg); + for (const dep of dependencies) { + builder.addDependency( + dep.source, + dep.target, + dep.type, + 'sourceFile' in dep ? dep.sourceFile : null + ); + } + const graph = builder.getUpdatedProjectGraph(); + + const prunedGraph = pruneProjectGraph(graph, packageJson); + const result = stringifyYarnLockfile(prunedGraph, lockFile, packageJson); + expect(result).toMatchInlineSnapshot(` + "# This file is generated by running "yarn install" inside your project. + # Manual changes might be lost - proceed with caution! + + __metadata: + version: 6 + cacheKey: 8 + + "@my-ns/example@workspace:.": + version: 0.0.0-use.local + resolution: "@my-ns/example@workspace:." + dependencies: + resolve: ^1.12.0 + languageName: unknown + linkType: soft + + "resolve@npm:^1.12.0": + version: 1.22.8 + resolution: "resolve@npm:1.22.8" + dependencies: + is-core-module: "npm:^2.13.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10/c473506ee01eb45cbcfefb68652ae5759e092e6b0fb64547feadf9736a6394f258fbc6f88e00c5ca36d5477fbb65388b272432a3600fa223062e54333c156753 + languageName: node + linkType: hard + + "resolve@patch:resolve@npm%3A^1.12.0#optional!builtin": + version: 1.22.8 + resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin::version=1.22.8&hash=c3c19d" + dependencies: + is-core-module: "npm:^2.13.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10/f345cd37f56a2c0275e3fe062517c650bb673815d885e7507566df589375d165bbbf4bdb6aa95600a9bc55f4744b81f452b5a63f95b9f10a72787dba3c90890a + languageName: node + linkType: hard + " + `); + }); }); }); 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 d96f3e5125..bdddd4a39b 100644 --- a/packages/nx/src/plugins/js/lock-file/yarn-parser.ts +++ b/packages/nx/src/plugins/js/lock-file/yarn-parser.ts @@ -474,9 +474,10 @@ function mapSnapshots( const packageName = key.slice(0, key.indexOf('@', 1)); let normalizedKey = key; if (isBerry && key.includes('@patch:') && key.includes('#')) { + const regEx = new RegExp(`@patch:${packageName}@(npm%3A)?(.*)$`); normalizedKey = key .slice(0, key.indexOf('#')) - .replace(`@patch:${packageName}@`, '@npm:'); + .replace(regEx, '@npm:$2'); } if ( !existingKeys.get(packageName) ||