fix(core): use package.json version in yarn parser when node_modules are unavailable (#18121)

This commit is contained in:
Miroslav Jonaš 2023-07-19 17:51:49 +02:00 committed by GitHub
parent 48d4927068
commit d9a95b64de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 452 additions and 81 deletions

View File

@ -14,7 +14,7 @@ import { workspaceRoot } from '../../../utils/workspace-root';
import { ProjectGraph } from '../../../config/project-graph';
import { ProjectGraphBuilder } from '../../../project-graph/project-graph-builder';
import { PackageJson } from '../../../utils/package-json';
import { fileHasher, hashArray } from '../../../hasher/file-hasher';
import { hashArray } from '../../../hasher/file-hasher';
import { output } from '../../../utils/output';
import { parseNpmLockfile, stringifyNpmLockfile } from './npm-parser';
@ -22,6 +22,7 @@ import { parsePnpmLockfile, stringifyPnpmLockfile } from './pnpm-parser';
import { parseYarnLockfile, stringifyYarnLockfile } from './yarn-parser';
import { pruneProjectGraph } from './project-graph-pruning';
import { normalizePackageJson } from './utils/package-json';
import { readJsonFile } from '../../../utils/fileutils';
const YARN_LOCK_FILE = 'yarn.lock';
const NPM_LOCK_FILE = 'package-lock.json';
@ -86,7 +87,8 @@ export function parseLockFile(
try {
if (packageManager === 'yarn') {
const content = readFileSync(YARN_LOCK_PATH, 'utf8');
parseYarnLockfile(content, builder);
const packageJson = readJsonFile('package.json');
parseYarnLockfile(content, packageJson, builder);
return builder.getUpdatedProjectGraph();
}
if (packageManager === 'pnpm') {
@ -145,19 +147,19 @@ export function createLockFile(
): string {
const normalizedPackageJson = normalizePackageJson(packageJson);
const content = readFileSync(getLockFileName(packageManager), 'utf8');
const rootPackageJson = readJsonFile('package.json');
const builder = new ProjectGraphBuilder();
try {
if (packageManager === 'yarn') {
parseYarnLockfile(content, builder);
parseYarnLockfile(content, rootPackageJson, builder);
const graph = builder.getUpdatedProjectGraph();
const prunedGraph = pruneProjectGraph(graph, packageJson);
return stringifyYarnLockfile(prunedGraph, content, normalizedPackageJson);
}
if (packageManager === 'pnpm') {
parsePnpmLockfile(content, builder);
const graph = builder.getUpdatedProjectGraph();
const prunedGraph = pruneProjectGraph(graph, packageJson);
return stringifyPnpmLockfile(prunedGraph, content, normalizedPackageJson);

View File

@ -157,6 +157,7 @@ describe('yarn LockFile utility', () => {
});
let lockFile;
let packageJson;
let graph: ProjectGraph;
@ -166,7 +167,11 @@ describe('yarn LockFile utility', () => {
__dirname,
'__fixtures__/nextjs/yarn.lock'
)).default;
parseYarnLockfile(lockFile, builder);
packageJson = require(joinPathFragments(
__dirname,
'__fixtures__/nextjs/package.json'
));
parseYarnLockfile(lockFile, packageJson, builder);
graph = builder.getUpdatedProjectGraph();
});
@ -219,10 +224,141 @@ describe('yarn LockFile utility', () => {
describe('auxiliary packages', () => {
beforeEach(() => {
const fileSys = {
'node_modules/@eslint/eslintrc/package.json': '{"version": "1.3.3"}',
'node_modules/@humanwhocodes/config-array/package.json':
'{"version": "0.11.7"}',
'node_modules/@humanwhocodes/module-importer/package.json':
'{"version": "1.0.1"}',
'node_modules/@humanwhocodes/object-schema/package.json':
'{"version": "1.2.1"}',
'node_modules/@nodelib/fs.scandir/package.json': '{"version": "2.1.5"}',
'node_modules/@nodelib/fs.stat/package.json': '{"version": "2.0.5"}',
'node_modules/@nodelib/fs.walk/package.json': '{"version": "1.2.8"}',
'node_modules/@nrwl/devkit/package.json': '{"version": "15.0.13"}',
'node_modules/@phenomnomnominal/tsquery/package.json':
'{"version": "4.1.1"}',
'node_modules/acorn/package.json': '{"version": "8.8.1"}',
'node_modules/acorn-jsx/package.json': '{"version": "5.3.2"}',
'node_modules/ajv/package.json': '{"version": "6.12.6"}',
'node_modules/ansi-regex/package.json': '{"version": "5.0.1"}',
'node_modules/ansi-styles/package.json': '{"version": "4.3.0"}',
'node_modules/app-root-path/package.json': '{"version": "3.1.0"}',
'node_modules/argparse/package.json': '{"version": "2.0.1"}',
'node_modules/async/package.json': '{"version": "3.2.4"}',
'node_modules/balanced-match/package.json': '{"version": "1.0.2"}',
'node_modules/brace-expansion/package.json': '{"version": "1.1.11"}',
'node_modules/callsites/package.json': '{"version": "3.1.0"}',
'node_modules/chalk/package.json': '{"version": "4.1.2"}',
'node_modules/cliui/package.json': '{"version": "8.0.1"}',
'node_modules/color-convert/package.json': '{"version": "2.0.1"}',
'node_modules/color-name/package.json': '{"version": "1.1.4"}',
'node_modules/concat-map/package.json': '{"version": "0.0.1"}',
'node_modules/cross-spawn/package.json': '{"version": "7.0.3"}',
'node_modules/debug/package.json': '{"version": "4.3.4"}',
'node_modules/deep-is/package.json': '{"version": "0.1.4"}',
'node_modules/doctrine/package.json': '{"version": "3.0.0"}',
'node_modules/ejs/package.json': '{"version": "3.1.8"}',
'node_modules/emoji-regex/package.json': '{"version": "8.0.0"}',
'node_modules/escalade/package.json': '{"version": "3.1.1"}',
'node_modules/escape-string-regexp/package.json':
'{"version": "4.0.0"}',
'node_modules/eslint/package.json': '{"version": "8.29.0"}',
'node_modules/eslint-plugin-disable-autofix/package.json':
'{"version": "3.0.0"}',
'node_modules/eslint-rule-composer/package.json':
'{"version": "0.3.0"}',
'node_modules/eslint-scope/package.json': '{"version": "7.1.1"}',
'node_modules/eslint-utils/package.json': '{"version": "3.0.0"}',
'node_modules/eslint-visitor-keys/package.json': '{"version": "3.3.0"}',
'node_modules/ignore/package.json': '{"version": "5.2.4"}',
'node_modules/espree/package.json': '{"version": "9.4.1"}',
'node_modules/esquery/package.json': '{"version": "1.4.0"}',
'node_modules/esrecurse/package.json': '{"version": "4.3.0"}',
'node_modules/estraverse/package.json': '{"version": "5.3.0"}',
'node_modules/esutils/package.json': '{"version": "2.0.3"}',
'node_modules/fast-deep-equal/package.json': '{"version": "3.1.3"}',
'node_modules/fast-json-stable-stringify/package.json':
'{"version": "2.1.0"}',
'node_modules/fast-levenshtein/package.json': '{"version": "2.0.6"}',
'node_modules/fastq/package.json': '{"version": "1.14.0"}',
'node_modules/file-entry-cache/package.json': '{"version": "6.0.1"}',
'node_modules/filelist/package.json': '{"version": "1.0.4"}',
'node_modules/find-up/package.json': '{"version": "5.0.0"}',
'node_modules/flat-cache/package.json': '{"version": "3.0.4"}',
'node_modules/flatted/package.json': '{"version": "3.2.7"}',
'node_modules/fs.realpath/package.json': '{"version": "1.0.0"}',
'node_modules/get-caller-file/package.json': '{"version": "2.0.5"}',
'node_modules/glob/package.json': '{"version": "7.2.3"}',
'node_modules/glob-parent/package.json': '{"version": "6.0.2"}',
'node_modules/globals/package.json': '{"version": "13.18.0"}',
'node_modules/grapheme-splitter/package.json': '{"version": "1.0.4"}',
'node_modules/has-flag/package.json': '{"version": "4.0.0"}',
'node_modules/ignore/package.json': '{"version": "5.2.1"}',
'node_modules/import-fresh/package.json': '{"version": "3.3.0"}',
'node_modules/imurmurhash/package.json': '{"version": "0.1.4"}',
'node_modules/inflight/package.json': '{"version": "1.0.6"}',
'node_modules/inherits/package.json': '{"version": "2.0.4"}',
'node_modules/is-extglob/package.json': '{"version": "2.1.1"}',
'node_modules/is-fullwidth-code-point/package.json':
'{"version": "3.0.0"}',
'node_modules/is-glob/package.json': '{"version": "4.0.3"}',
'node_modules/is-path-inside/package.json': '{"version": "3.0.3"}',
'node_modules/isexe/package.json': '{"version": "2.0.0"}',
'node_modules/jake/package.json': '{"version": "10.8.5"}',
'node_modules/js-sdsl/package.json': '{"version": "4.2.0"}',
'node_modules/js-tokens/package.json': '{"version": "4.0.0"}',
'node_modules/js-yaml/package.json': '{"version": "4.1.0"}',
'node_modules/json-schema-traverse/package.json':
'{"version": "0.4.1"}',
'node_modules/json-stable-stringify-without-jsonify/package.json':
'{"version": "1.0.1"}',
'node_modules/levn/package.json': '{"version": "0.4.1"}',
'node_modules/locate-path/package.json': '{"version": "6.0.0"}',
'node_modules/lodash.merge/package.json': '{"version": "4.6.2"}',
'node_modules/loose-envify/package.json': '{"version": "1.4.0"}',
'node_modules/lru-cache/package.json': '{"version": "6.0.0"}',
'node_modules/minimatch/package.json': '{"version": "3.1.2"}',
'node_modules/ms/package.json': '{"version": "2.1.2"}',
'node_modules/natural-compare/package.json': '{"version": "1.4.0"}',
'node_modules/once/package.json': '{"version": "1.4.0"}',
'node_modules/optionator/package.json': '{"version": "0.9.1"}',
'node_modules/p-limit/package.json': '{"version": "3.1.0"}',
'node_modules/p-locate/package.json': '{"version": "5.0.0"}',
'node_modules/parent-module/package.json': '{"version": "1.0.1"}',
'node_modules/path-exists/package.json': '{"version": "4.0.0"}',
'node_modules/path-is-absolute/package.json': '{"version": "1.0.1"}',
'node_modules/path-key/package.json': '{"version": "3.1.1"}',
'node_modules/postgres/package.json': '{"version": "3.2.4"}',
'node_modules/prelude-ls/package.json': '{"version": "1.2.1"}',
'node_modules/punycode/package.json': '{"version": "2.1.1"}',
'node_modules/queue-microtask/package.json': '{"version": "1.2.3"}',
'node_modules/react/package.json': '{"version": "18.2.0"}',
'node_modules/regexpp/package.json': '{"version": "3.2.0"}',
'node_modules/require-directory/package.json': '{"version": "2.1.1"}',
'node_modules/resolve-from/package.json': '{"version": "4.0.0"}',
'node_modules/reusify/package.json': '{"version": "1.0.4"}',
'node_modules/rimraf/package.json': '{"version": "3.0.2"}',
'node_modules/run-parallel/package.json': '{"version": "1.2.0"}',
'node_modules/semver/package.json': '{"version": "7.3.4"}',
'node_modules/shebang-command/package.json': '{"version": "2.0.0"}',
'node_modules/shebang-regex/package.json': '{"version": "3.0.0"}',
'node_modules/string-width/package.json': '{"version": "4.2.3"}',
'node_modules/strip-ansi/package.json': '{"version": "6.0.1"}',
'node_modules/strip-json-comments/package.json': '{"version": "3.1.1"}',
'node_modules/supports-color/package.json': '{"version": "7.2.0"}',
'node_modules/text-table/package.json': '{"version": "0.2.0"}',
'node_modules/tslib/package.json': '{"version": "2.4.1"}',
'node_modules/type-check/package.json': '{"version": "0.4.0"}',
'node_modules/type-fest/package.json': '{"version": "0.20.2"}',
'node_modules/uri-js/package.json': '{"version": "4.4.1"}',
'node_modules/which/package.json': '{"version": "2.0.2"}',
'node_modules/word-wrap/package.json': '{"version": "1.2.3"}',
'node_modules/wrap-ansi/package.json': '{"version": "7.0.0"}',
'node_modules/wrappy/package.json': '{"version": "1.0.2"}',
'node_modules/y18n/package.json': '{"version": "5.0.8"}',
'node_modules/yallist/package.json': '{"version": "4.0.0"}',
'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"}',
};
vol.fromJSON(fileSys, '/root');
});
@ -232,8 +368,12 @@ describe('yarn LockFile utility', () => {
__dirname,
'__fixtures__/auxiliary-packages/yarn.lock'
)).default;
const packageJson = require(joinPathFragments(
__dirname,
'__fixtures__/auxiliary-packages/package.json'
));
const builder = new ProjectGraphBuilder();
parseYarnLockfile(classicLockFile, builder);
parseYarnLockfile(classicLockFile, packageJson, builder);
const graph = builder.getUpdatedProjectGraph();
expect(Object.keys(graph.externalNodes).length).toEqual(127);
@ -289,6 +429,10 @@ describe('yarn LockFile utility', () => {
__dirname,
'__fixtures__/auxiliary-packages/yarn.lock'
)).default;
const packageJson = require(joinPathFragments(
__dirname,
'__fixtures__/auxiliary-packages/package.json'
));
const normalizedPackageJson = {
name: 'test',
version: '0.0.0',
@ -311,7 +455,7 @@ describe('yarn LockFile utility', () => {
)).default;
const builder = new ProjectGraphBuilder();
parseYarnLockfile(lockFile, builder);
parseYarnLockfile(lockFile, packageJson, builder);
const graph = builder.getUpdatedProjectGraph();
const prunedGraph = pruneProjectGraph(graph, normalizedPackageJson);
const result = stringifyYarnLockfile(
@ -349,7 +493,7 @@ describe('yarn LockFile utility', () => {
)).default;
const builder = new ProjectGraphBuilder();
parseYarnLockfile(lockFile, builder);
parseYarnLockfile(lockFile, normalizedPackageJson, builder);
const graph = builder.getUpdatedProjectGraph();
const prunedGraph = pruneProjectGraph(graph, normalizedPackageJson);
const result = stringifyYarnLockfile(
@ -370,8 +514,12 @@ describe('yarn LockFile utility', () => {
__dirname,
'__fixtures__/auxiliary-packages/yarn-berry.lock'
)).default;
const packageJson = require(joinPathFragments(
__dirname,
'__fixtures__/auxiliary-packages/package.json'
));
const builder = new ProjectGraphBuilder();
parseYarnLockfile(berryLockFile, builder);
parseYarnLockfile(berryLockFile, packageJson, builder);
const graph = builder.getUpdatedProjectGraph();
expect(Object.keys(graph.externalNodes).length).toEqual(129);
@ -448,9 +596,13 @@ describe('yarn LockFile utility', () => {
__dirname,
'__fixtures__/auxiliary-packages/yarn-berry.lock.pruned'
)).default;
const packageJson = require(joinPathFragments(
__dirname,
'__fixtures__/auxiliary-packages/package.json'
));
const builder = new ProjectGraphBuilder();
parseYarnLockfile(lockFile, builder);
parseYarnLockfile(lockFile, packageJson, builder);
const graph = builder.getUpdatedProjectGraph();
const prunedGraph = pruneProjectGraph(graph, normalizedPackageJson);
const result = stringifyYarnLockfile(
@ -464,6 +616,81 @@ describe('yarn LockFile utility', () => {
});
});
describe('auxiliary packages PnP', () => {
it('should parse yarn berry pnp', () => {
const berryLockFile = require(joinPathFragments(
__dirname,
'__fixtures__/auxiliary-packages/yarn-berry.lock'
)).default;
const packageJson = require(joinPathFragments(
__dirname,
'__fixtures__/auxiliary-packages/package.json'
));
const builder = new ProjectGraphBuilder();
parseYarnLockfile(berryLockFile, packageJson, builder);
const graph = builder.getUpdatedProjectGraph();
expect(Object.keys(graph.externalNodes).length).toEqual(129);
expect(graph.externalNodes['npm:react']).toMatchInlineSnapshot(`
{
"data": {
"hash": "88e38092da8839b830cda6feef2e8505dec8ace60579e46aa5490fc3dc9bba0bd50336507dc166f43e3afc1c42939c09fe33b25fae889d6f402721dcd78fca1b",
"packageName": "react",
"version": "18.2.0",
},
"name": "npm:react",
"type": "npm",
}
`);
expect(graph.externalNodes['npm:typescript']).toMatchInlineSnapshot(`
{
"data": {
"hash": "ee000bc26848147ad423b581bd250075662a354d84f0e06eb76d3b892328d8d4440b7487b5a83e851b12b255f55d71835b008a66cbf8f255a11e4400159237db",
"packageName": "typescript",
"version": "4.8.4",
},
"name": "npm:typescript",
"type": "npm",
}
`);
expect(graph.externalNodes['npm:@nrwl/devkit']).toMatchInlineSnapshot(`
{
"data": {
"hash": "7dcc3600998448c496228e062d7edd8ecf959fa1ddb9721e91bb1f60f1a2284fd0e12e09edc022170988e2fb54acf101c79dc09fe9c54a21c9941e682eb73b92",
"packageName": "@nrwl/devkit",
"version": "15.0.13",
},
"name": "npm:@nrwl/devkit",
"type": "npm",
}
`);
expect(graph.externalNodes['npm:postgres']).toMatchInlineSnapshot(`
{
"data": {
"hash": "521660853e0c9f1c604cf43d32c75e2b4675e2d912eaec7bb6749716539dd53f1dfaf575a422087f6a53362f5162f9a4b8a88cc1dadf9d7580423fc05137767a",
"packageName": "postgres",
"version": "https://github.com/charsleysa/postgres.git#commit=3b1a01b2da3e2fafb1a79006f838eff11a8de3cb",
},
"name": "npm:postgres",
"type": "npm",
}
`);
expect(graph.externalNodes['npm:eslint-plugin-disable-autofix'])
.toMatchInlineSnapshot(`
{
"data": {
"hash": "fb7272c37e5701df14a79d0f8a9d6a0cb521972011ba91d70290eefc33fca589307908a6fb63e2985257b1c7cc3839c076d1c8def0caabddf21a91f13d7c8fc1",
"packageName": "eslint-plugin-disable-autofix",
"version": "npm:@mattlewis92/eslint-plugin-disable-autofix@3.0.0",
},
"name": "npm:eslint-plugin-disable-autofix",
"type": "npm",
}
`);
});
});
describe('duplicate packages', () => {
beforeEach(() => {
const fileSys = {
@ -507,8 +734,12 @@ describe('yarn LockFile utility', () => {
__dirname,
'__fixtures__/duplicate-package/yarn.lock'
)).default;
const packageJson = require(joinPathFragments(
__dirname,
'__fixtures__/duplicate-package/package.json'
));
const builder = new ProjectGraphBuilder();
parseYarnLockfile(classicLockFile, builder);
parseYarnLockfile(classicLockFile, packageJson, builder);
const graph = builder.getUpdatedProjectGraph();
expect(Object.keys(graph.externalNodes).length).toEqual(371);
});
@ -538,7 +769,7 @@ describe('yarn LockFile utility', () => {
'__fixtures__/optional/package.json'
));
const builder = new ProjectGraphBuilder();
parseYarnLockfile(lockFile, builder);
parseYarnLockfile(lockFile, packageJson, builder);
const graph = builder.getUpdatedProjectGraph();
expect(Object.keys(graph.externalNodes).length).toEqual(103);
@ -716,8 +947,12 @@ describe('yarn LockFile utility', () => {
__dirname,
'__fixtures__/pruning/typescript/package.json'
));
const packageJson = require(joinPathFragments(
__dirname,
'__fixtures__/pruning/package.json'
));
const builder = new ProjectGraphBuilder();
parseYarnLockfile(lockFile, builder);
parseYarnLockfile(lockFile, packageJson, builder);
const graph = builder.getUpdatedProjectGraph();
const prunedGraph = pruneProjectGraph(graph, typescriptPackageJson);
const result = stringifyYarnLockfile(
@ -743,8 +978,12 @@ describe('yarn LockFile utility', () => {
__dirname,
'__fixtures__/pruning/devkit-yargs/package.json'
));
const packageJson = require(joinPathFragments(
__dirname,
'__fixtures__/pruning/package.json'
));
const builder = new ProjectGraphBuilder();
parseYarnLockfile(lockFile, builder);
parseYarnLockfile(lockFile, packageJson, builder);
const graph = builder.getUpdatedProjectGraph();
const prunedGraph = pruneProjectGraph(graph, multiPackageJson);
const result = stringifyYarnLockfile(
@ -776,8 +1015,12 @@ describe('yarn LockFile utility', () => {
__dirname,
'__fixtures__/workspaces/yarn.lock'
)).default;
const packageJson = require(joinPathFragments(
__dirname,
'__fixtures__/workspaces/package.json'
));
const builder = new ProjectGraphBuilder();
parseYarnLockfile(lockFile, builder);
parseYarnLockfile(lockFile, packageJson, builder);
const graph = builder.getUpdatedProjectGraph();
expect(Object.keys(graph.externalNodes).length).toEqual(5);
});
@ -787,8 +1030,12 @@ describe('yarn LockFile utility', () => {
__dirname,
'__fixtures__/workspaces/yarn.lock.berry'
)).default;
const packageJson = require(joinPathFragments(
__dirname,
'__fixtures__/workspaces/package.json'
));
const builder = new ProjectGraphBuilder();
parseYarnLockfile(lockFile, builder);
parseYarnLockfile(lockFile, packageJson, builder);
const graph = builder.getUpdatedProjectGraph();
expect(Object.keys(graph.externalNodes).length).toEqual(5);
});
@ -809,6 +1056,17 @@ describe('yarn LockFile utility', () => {
types: './src/index.d.ts',
};
beforeEach(() => {
const fileSys = {
'node_modules/@gitlab-examples/semantic-release-npm/package.json':
'{"version": "2.0.1"}',
'node_modules/tslib/package.json': '{"version": "2.5.0"}',
'node_modules/type-fest/package.json': '{"version": "0.20.2"}',
'node_modules/@gar/promisify/package.json': '{"version": "1.1.3"}',
};
vol.fromJSON(fileSys, '/root');
});
it('should parse and prune classic', () => {
const lockFile = `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
@ -830,8 +1088,21 @@ type-fest@^0.20.2:
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
`;
const packageJson: PackageJson = {
name: '@my-ns/example',
version: '0.0.1',
type: 'commonjs',
dependencies: {
'@gitlab-examples/semantic-release-npm': '^2.0.1',
'type-fest': '^0.20.2',
},
peerDependencies: {
tslib: '^2.4.0',
},
};
const builder = new ProjectGraphBuilder();
parseYarnLockfile(lockFile, builder);
parseYarnLockfile(lockFile, packageJson, builder);
const graph = builder.getUpdatedProjectGraph();
expect(graph.externalNodes['npm:tslib']).toMatchInlineSnapshot(`
{
@ -905,8 +1176,22 @@ __metadata:
languageName: node
linkType: hard
`;
const packageJson: PackageJson = {
name: '@my-ns/example',
version: '0.0.1',
type: 'commonjs',
dependencies: {
'@gitlab-examples/semantic-release-npm': '^2.0.1',
'@gar/promisify': '^1.1.3',
},
peerDependencies: {
tslib: '^2.4.0',
},
};
const builder = new ProjectGraphBuilder();
parseYarnLockfile(lockFile, builder);
parseYarnLockfile(lockFile, packageJson, builder);
const graph = builder.getUpdatedProjectGraph();
expect(graph.externalNodes['npm:tslib']).toMatchInlineSnapshot(`
{
@ -973,7 +1258,21 @@ __metadata:
describe('mixed keys', () => {
beforeEach(() => {
const fileSys = {
'node_modules/wrap-ansi/package.json': '{"version": "7.0.0"}',
'node_modules/@isaacs/cliui/package.json': '{"version": "8.0.2"}',
'node_modules/ansi-regex/package.json': '{"version": "5.0.1"}',
'node_modules/ansi-styles/package.json': '{"version": "4.3.0"}',
'node_modules/cliui/package.json': '{"version": "8.0.1"}',
'node_modules/color-convert/package.json': '{"version": "2.0.1"}',
'node_modules/color-name/package.json': '{"version": "1.1.4"}',
'node_modules/eastasianwidth/package.json': '{"version": "0.2.0"}',
'node_modules/emoji-regex/package.json': '{"version": "8.0.0"}',
'node_modules/is-fullwidth-code-point/package.json':
'{"version": "3.0.0"}',
'node_modules/string-width/package.json': '{"version": "5.1.2"}',
'node_modules/string-width-cjs/package.json': '{"version": "4.2.3"}',
'node_modules/strip-ansi/package.json': '{"version": "7.0.1"}',
'node_modules/strip-ansi-cjs/package.json': '{"version": "6.0.1"}',
'node_modules/wrap-ansi/package.json': '{"version": "8.1.0"}',
'node_modules/wrap-ansi-cjs/package.json': '{"version": "7.0.0"}',
};
vol.fromJSON(fileSys, '/root');
@ -990,7 +1289,7 @@ __metadata:
));
const builder = new ProjectGraphBuilder();
parseYarnLockfile(lockFile, builder);
parseYarnLockfile(lockFile, packageJson, builder);
const graph = builder.getUpdatedProjectGraph();
expect(graph.externalNodes).toMatchInlineSnapshot(`
{
@ -1003,13 +1302,13 @@ __metadata:
"name": "npm:@isaacs/cliui",
"type": "npm",
},
"npm:ansi-regex@5.0.1": {
"npm:ansi-regex": {
"data": {
"hash": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"packageName": "ansi-regex",
"version": "5.0.1",
},
"name": "npm:ansi-regex@5.0.1",
"name": "npm:ansi-regex",
"type": "npm",
},
"npm:ansi-regex@6.0.1": {
@ -1021,13 +1320,13 @@ __metadata:
"name": "npm:ansi-regex@6.0.1",
"type": "npm",
},
"npm:ansi-styles@4.3.0": {
"npm:ansi-styles": {
"data": {
"hash": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"packageName": "ansi-styles",
"version": "4.3.0",
},
"name": "npm:ansi-styles@4.3.0",
"name": "npm:ansi-styles",
"type": "npm",
},
"npm:ansi-styles@6.2.1": {
@ -1075,13 +1374,13 @@ __metadata:
"name": "npm:eastasianwidth",
"type": "npm",
},
"npm:emoji-regex@8.0.0": {
"npm:emoji-regex": {
"data": {
"hash": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"packageName": "emoji-regex",
"version": "8.0.0",
},
"name": "npm:emoji-regex@8.0.0",
"name": "npm:emoji-regex",
"type": "npm",
},
"npm:emoji-regex@9.2.2": {
@ -1102,6 +1401,15 @@ __metadata:
"name": "npm:is-fullwidth-code-point",
"type": "npm",
},
"npm:string-width": {
"data": {
"hash": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"packageName": "string-width",
"version": "5.1.2",
},
"name": "npm:string-width",
"type": "npm",
},
"npm:string-width-cjs": {
"data": {
"hash": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
@ -1120,13 +1428,13 @@ __metadata:
"name": "npm:string-width@4.2.3",
"type": "npm",
},
"npm:string-width@5.1.2": {
"npm:strip-ansi": {
"data": {
"hash": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"packageName": "string-width",
"version": "5.1.2",
"hash": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==",
"packageName": "strip-ansi",
"version": "7.0.1",
},
"name": "npm:string-width@5.1.2",
"name": "npm:strip-ansi",
"type": "npm",
},
"npm:strip-ansi-cjs": {
@ -1147,20 +1455,11 @@ __metadata:
"name": "npm:strip-ansi@6.0.1",
"type": "npm",
},
"npm:strip-ansi@7.0.1": {
"data": {
"hash": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==",
"packageName": "strip-ansi",
"version": "7.0.1",
},
"name": "npm:strip-ansi@7.0.1",
"type": "npm",
},
"npm:wrap-ansi": {
"data": {
"hash": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"hash": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
"packageName": "wrap-ansi",
"version": "7.0.0",
"version": "8.1.0",
},
"name": "npm:wrap-ansi",
"type": "npm",
@ -1174,13 +1473,13 @@ __metadata:
"name": "npm:wrap-ansi-cjs",
"type": "npm",
},
"npm:wrap-ansi@8.1.0": {
"npm:wrap-ansi@7.0.0": {
"data": {
"hash": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
"hash": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"packageName": "wrap-ansi",
"version": "8.1.0",
"version": "7.0.0",
},
"name": "npm:wrap-ansi@8.1.0",
"name": "npm:wrap-ansi@7.0.0",
"type": "npm",
},
}
@ -1202,7 +1501,7 @@ __metadata:
));
const builder = new ProjectGraphBuilder();
parseYarnLockfile(lockFile, builder);
parseYarnLockfile(lockFile, packageJson, builder);
const graph = builder.getUpdatedProjectGraph();
expect(graph.externalNodes).toMatchInlineSnapshot(`
{
@ -1215,13 +1514,13 @@ __metadata:
"name": "npm:@isaacs/cliui",
"type": "npm",
},
"npm:ansi-regex@5.0.1": {
"npm:ansi-regex": {
"data": {
"hash": "2aa4bb54caf2d622f1afdad09441695af2a83aa3fe8b8afa581d205e57ed4261c183c4d3877cee25794443fde5876417d859c108078ab788d6af7e4fe52eb66b",
"packageName": "ansi-regex",
"version": "5.0.1",
},
"name": "npm:ansi-regex@5.0.1",
"name": "npm:ansi-regex",
"type": "npm",
},
"npm:ansi-regex@6.0.1": {
@ -1233,13 +1532,13 @@ __metadata:
"name": "npm:ansi-regex@6.0.1",
"type": "npm",
},
"npm:ansi-styles@4.3.0": {
"npm:ansi-styles": {
"data": {
"hash": "513b44c3b2105dd14cc42a19271e80f386466c4be574bccf60b627432f9198571ebf4ab1e4c3ba17347658f4ee1711c163d574248c0c1cdc2d5917a0ad582ec4",
"packageName": "ansi-styles",
"version": "4.3.0",
},
"name": "npm:ansi-styles@4.3.0",
"name": "npm:ansi-styles",
"type": "npm",
},
"npm:ansi-styles@6.2.1": {
@ -1287,13 +1586,13 @@ __metadata:
"name": "npm:eastasianwidth",
"type": "npm",
},
"npm:emoji-regex@8.0.0": {
"npm:emoji-regex": {
"data": {
"hash": "d4c5c39d5a9868b5fa152f00cada8a936868fd3367f33f71be515ecee4c803132d11b31a6222b2571b1e5f7e13890156a94880345594d0ce7e3c9895f560f192",
"packageName": "emoji-regex",
"version": "8.0.0",
},
"name": "npm:emoji-regex@8.0.0",
"name": "npm:emoji-regex",
"type": "npm",
},
"npm:emoji-regex@9.2.2": {
@ -1314,6 +1613,15 @@ __metadata:
"name": "npm:is-fullwidth-code-point",
"type": "npm",
},
"npm:string-width": {
"data": {
"hash": "7369deaa29f21dda9a438686154b62c2c5f661f8dda60449088f9f980196f7908fc39fdd1803e3e01541970287cf5deae336798337e9319a7055af89dafa7193",
"packageName": "string-width",
"version": "5.1.2",
},
"name": "npm:string-width",
"type": "npm",
},
"npm:string-width-cjs": {
"data": {
"hash": "e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb",
@ -1332,15 +1640,6 @@ __metadata:
"name": "npm:string-width@4.2.3",
"type": "npm",
},
"npm:string-width@5.1.2": {
"data": {
"hash": "7369deaa29f21dda9a438686154b62c2c5f661f8dda60449088f9f980196f7908fc39fdd1803e3e01541970287cf5deae336798337e9319a7055af89dafa7193",
"packageName": "string-width",
"version": "5.1.2",
},
"name": "npm:string-width@5.1.2",
"type": "npm",
},
"npm:strip-ansi-cjs": {
"data": {
"hash": "f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c",
@ -1370,9 +1669,9 @@ __metadata:
},
"npm:wrap-ansi": {
"data": {
"hash": "a790b846fd4505de962ba728a21aaeda189b8ee1c7568ca5e817d85930e06ef8d1689d49dbf0e881e8ef84436af3a88bc49115c2e2788d841ff1b8b5b51a608b",
"hash": "371733296dc2d616900ce15a0049dca0ef67597d6394c57347ba334393599e800bab03c41d4d45221b6bc967b8c453ec3ae4749eff3894202d16800fdfe0e238",
"packageName": "wrap-ansi",
"version": "7.0.0",
"version": "8.1.0",
},
"name": "npm:wrap-ansi",
"type": "npm",
@ -1386,13 +1685,13 @@ __metadata:
"name": "npm:wrap-ansi-cjs",
"type": "npm",
},
"npm:wrap-ansi@8.1.0": {
"npm:wrap-ansi@7.0.0": {
"data": {
"hash": "371733296dc2d616900ce15a0049dca0ef67597d6394c57347ba334393599e800bab03c41d4d45221b6bc967b8c453ec3ae4749eff3894202d16800fdfe0e238",
"hash": "a790b846fd4505de962ba728a21aaeda189b8ee1c7568ca5e817d85930e06ef8d1689d49dbf0e881e8ef84436af3a88bc49115c2e2788d841ff1b8b5b51a608b",
"packageName": "wrap-ansi",
"version": "8.1.0",
"version": "7.0.0",
},
"name": "npm:wrap-ansi@8.1.0",
"name": "npm:wrap-ansi@7.0.0",
"type": "npm",
},
}
@ -1405,6 +1704,18 @@ __metadata:
});
describe('invalid resolved', () => {
beforeEach(() => {
const fileSys = {
'node_modules/@octokit/request-error/package.json':
'{"version": "3.0.3"}',
'node_modules/@octokit/types/package.json': '{"version": "9.2.0"}',
'node_modules/@octokit/webhooks-types/package.json':
'{"version": "5.8.0"}',
'node_modules/@octokit/webhooks/package.json': '{"version": "9.26.0"}',
};
vol.fromJSON(fileSys, '/root');
});
it('should parse yarn.lock with invalid resolved field', () => {
const lockFile = `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
@ -1440,8 +1751,21 @@ __metadata:
"@octokit/webhooks-types" "5.8.0"
aggregate-error "^3.1.0"
`;
const packageJson: PackageJson = {
name: '@my-ns/example',
version: '0.0.1',
type: 'commonjs',
dependencies: {
'@octokit/request-error': '^3',
'@octokit/types': '^9',
'@octokit/webhooks-types': '5.8.0',
'@octokit/webhooks': '^9.8.4',
},
};
const builder = new ProjectGraphBuilder();
parseYarnLockfile(lockFile, builder);
parseYarnLockfile(lockFile, packageJson, builder);
const graph = builder.getUpdatedProjectGraph();
expect(graph.externalNodes).toMatchInlineSnapshot(`
{

View File

@ -1,6 +1,6 @@
import { getHoistedPackageVersion } from './utils/package-json';
import { ProjectGraphBuilder } from '../../../project-graph/project-graph-builder';
import { satisfies, Range } from 'semver';
import { satisfies, Range, gt } from 'semver';
import { NormalizedPackageJson } from './utils/package-json';
import {
ProjectGraph,
@ -37,6 +37,7 @@ type YarnDependency = {
export function parseYarnLockfile(
lockFileContent: string,
packageJson: NormalizedPackageJson,
builder: ProjectGraphBuilder
) {
const { parseSyml } = require('@yarnpkg/parsers');
@ -44,7 +45,7 @@ export function parseYarnLockfile(
// we use key => node map to avoid duplicate work when parsing keys
const keyMap = new Map<string, ProjectGraphExternalNode>();
addNodes(data, builder, keyMap);
addNodes(data, packageJson, builder, keyMap);
addDependencies(data, builder, keyMap);
}
@ -59,11 +60,18 @@ function getPackageNames(keys: string): string[] {
function addNodes(
{ __metadata, ...dependencies }: YarnLockFile,
packageJson: NormalizedPackageJson,
builder: ProjectGraphBuilder,
keyMap: Map<string, ProjectGraphExternalNode>
) {
const isBerry = !!__metadata;
const nodes: Map<string, Map<string, ProjectGraphExternalNode>> = new Map();
const combinedDeps = {
...packageJson.dependencies,
...packageJson.devDependencies,
...packageJson.peerDependencies,
...packageJson.optionalDependencies,
};
Object.entries(dependencies).forEach(([keys, snapshot]) => {
// ignore workspace projects & patches
@ -103,23 +111,22 @@ function addNodes(
};
keyMap.set(key, node);
// use actual version so we can detect it later based on npm package's version
const mapKey =
snapshot.version && version !== snapshot.version
? snapshot.version
: version;
if (!nodes.has(packageName)) {
nodes.set(packageName, new Map([[version, node]]));
nodes.set(packageName, new Map([[mapKey, node]]));
} else {
nodes.get(packageName).set(version, node);
nodes.get(packageName).set(mapKey, node);
}
});
});
});
for (const [packageName, versionMap] of nodes.entries()) {
let hoistedNode: ProjectGraphExternalNode;
if (versionMap.size === 1) {
hoistedNode = versionMap.values().next().value;
} else {
const hoistedVersion = getHoistedVersion(packageName);
hoistedNode = versionMap.get(hoistedVersion);
}
const hoistedNode = findHoistedNode(packageName, versionMap, combinedDeps);
if (hoistedNode) {
hoistedNode.name = `npm:${packageName}`;
}
@ -130,6 +137,44 @@ function addNodes(
}
}
function findHoistedNode(
packageName: string,
versionMap: Map<string, ProjectGraphExternalNode>,
combinedDeps: Record<string, string>
): ProjectGraphExternalNode {
const hoistedVersion = getHoistedVersion(packageName);
if (hoistedVersion) {
return versionMap.get(hoistedVersion);
}
const rootVersionSpecifier = combinedDeps[packageName];
if (!rootVersionSpecifier) {
return;
}
const versions = Array.from(versionMap.keys()).sort((a, b) =>
gt(a, b) ? -1 : 1
);
// take the highest version found
if (rootVersionSpecifier === '*') {
return versionMap.get(versions[0]);
}
// take version that satisfies the root version specifier
let version = versions.find((v) => satisfies(v, rootVersionSpecifier));
if (!version) {
// try to find alias version
version = versions.find(
(v) =>
versionMap.get(v).name === `npm:${packageName}@${rootVersionSpecifier}`
);
}
if (!version) {
// try to find tarball package
version = versions.find((v) => versionMap.get(v).data.version !== v);
}
if (version) {
return versionMap.get(version);
}
}
function findVersion(
packageName: string,
key: string,