feat(core): support npm v1 lock file pruning with disclaimer (#13218)

This commit is contained in:
Miroslav Jonaš 2022-11-17 14:04:50 +01:00 committed by GitHub
parent c0deca8da3
commit 2bc9e84edd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 160 additions and 94 deletions

View File

@ -135,9 +135,13 @@ describe('Vite Plugin', () => {
afterEach(() => killPorts()); afterEach(() => killPorts());
it('should serve applications in dev mode', async () => { it('should serve applications in dev mode', async () => {
const p = await runCommandUntil(`run ${myApp}:serve`, (output) => { const port = 4212;
const p = await runCommandUntil(
`run ${myApp}:serve --port=${port}`,
(output) => {
return output.includes('Local:'); return output.includes('Local:');
}); }
);
p.kill(); p.kill();
}, 200000); }, 200000);
}); });

View File

@ -21565,34 +21565,13 @@ export const lockFileV1YargsAndDevkitOnly = `{
"peer": true "peer": true
}, },
"@yarnpkg/parsers": { "@yarnpkg/parsers": {
"version": "3.0.0-rc.28", "version": "3.0.0-rc.27",
"resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.28.tgz", "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.27.tgz",
"integrity": "sha512-OdBYBaACPjFnqek4jtyR5VH7wX5i7BwfS0AP8m6hTqgULRVOLEc6TKxUBxMCTISzZPGdo5wWAB7OcMmU6G2UnA==", "integrity": "sha512-qs2wZulOYVjaOS6tYOs3SsR7m/qeHwjPrB5i4JtBJELsgWrEkyL+rJH21RA+fVwttJobAYQqw5Xj5SYLaDK/bQ==",
"peer": true, "peer": true,
"requires": { "requires": {
"js-yaml": "^3.10.0", "js-yaml": "^3.10.0",
"tslib": "^2.4.0" "tslib": "^2.4.0"
},
"dependencies": {
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"peer": true,
"requires": {
"sprintf-js": "~1.0.2"
}
},
"js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"peer": true,
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
}
} }
}, },
"@zkochan/js-yaml": { "@zkochan/js-yaml": {
@ -21602,6 +21581,14 @@ export const lockFileV1YargsAndDevkitOnly = `{
"peer": true, "peer": true,
"requires": { "requires": {
"argparse": "^2.0.1" "argparse": "^2.0.1"
},
"dependencies": {
"argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"peer": true
}
} }
}, },
"ansi-colors": { "ansi-colors": {
@ -21634,10 +21621,13 @@ export const lockFileV1YargsAndDevkitOnly = `{
} }
}, },
"argparse": { "argparse": {
"version": "2.0.1", "version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"peer": true "peer": true,
"requires": {
"sprintf-js": "~1.0.2"
}
}, },
"async": { "async": {
"version": "3.2.4", "version": "3.2.4",
@ -21718,9 +21708,9 @@ export const lockFileV1YargsAndDevkitOnly = `{
} }
}, },
"chalk": { "chalk": {
"version": "4.1.2", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"requires": { "requires": {
"ansi-styles": "^4.1.0", "ansi-styles": "^4.1.0",
"supports-color": "^7.1.0" "supports-color": "^7.1.0"
@ -22127,21 +22117,13 @@ export const lockFileV1YargsAndDevkitOnly = `{
} }
}, },
"js-yaml": { "js-yaml": {
"version": "4.1.0", "version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"peer": true, "peer": true,
"requires": { "requires": {
"argparse": "^2.0.1" "argparse": "^1.0.7",
} "esprima": "^4.0.0"
},
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"peer": true,
"requires": {
"minimist": "^1.2.0"
} }
}, },
"jsonc-parser": { "jsonc-parser": {
@ -22289,14 +22271,19 @@ export const lockFileV1YargsAndDevkitOnly = `{
"yargs-parser": "21.1.1" "yargs-parser": "21.1.1"
}, },
"dependencies": { "dependencies": {
"chalk": { "argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"peer": true
},
"js-yaml": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"peer": true, "peer": true,
"requires": { "requires": {
"ansi-styles": "^4.1.0", "argparse": "^2.0.1"
"supports-color": "^7.1.0"
} }
}, },
"minimatch": { "minimatch": {
@ -22481,12 +22468,6 @@ export const lockFileV1YargsAndDevkitOnly = `{
"ansi-regex": "^5.0.1" "ansi-regex": "^5.0.1"
} }
}, },
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
"peer": true
},
"strong-log-transformer": { "strong-log-transformer": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz", "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz",
@ -22553,6 +22534,23 @@ export const lockFileV1YargsAndDevkitOnly = `{
"json5": "^1.0.1", "json5": "^1.0.1",
"minimist": "^1.2.6", "minimist": "^1.2.6",
"strip-bom": "^3.0.0" "strip-bom": "^3.0.0"
},
"dependencies": {
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"peer": true,
"requires": {
"minimist": "^1.2.0"
}
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
"peer": true
}
} }
}, },
"tslib": { "tslib": {

View File

@ -14,6 +14,19 @@ import {
lockFileV1YargsAndDevkitOnly, lockFileV1YargsAndDevkitOnly,
lockFileV2YargsAndDevkitOnly, lockFileV2YargsAndDevkitOnly,
} from './__fixtures__/npm.lock'; } from './__fixtures__/npm.lock';
import { vol } from 'memfs';
import { readJsonFile } from '../fileutils';
jest.mock('fs', () => require('memfs').fs);
jest.mock('@nrwl/devkit', () => ({
...jest.requireActual<any>('@nrwl/devkit'),
workspaceRoot: '/root',
}));
jest.mock('nx/src/utils/workspace-root', () => ({
workspaceRoot: '/root',
}));
describe('npm LockFile utility', () => { describe('npm LockFile utility', () => {
describe('v3', () => { describe('v3', () => {
@ -316,7 +329,22 @@ describe('npm LockFile utility', () => {
expect(stringifyNpmLockFile(parsedLockFile)).toEqual(lockFileV1); expect(stringifyNpmLockFile(parsedLockFile)).toEqual(lockFileV1);
}); });
xit('should prune the lock file', () => { describe('pruning', () => {
beforeAll(() => {
const v2packages = JSON.parse(lockFileV2).packages;
const fileSys = {};
// map all v2 packages to the file system
Object.keys(v2packages).forEach((key) => {
if (key) {
fileSys[`/root/${key}/package.json`] = JSON.stringify(
v2packages[key]
);
}
});
vol.fromJSON(fileSys, '/root');
});
it('should prune the lock file', () => {
expect( expect(
Object.keys( Object.keys(
pruneNpmLockFile(parsedLockFile, ['typescript']).dependencies pruneNpmLockFile(parsedLockFile, ['typescript']).dependencies
@ -330,13 +358,13 @@ describe('npm LockFile utility', () => {
).toEqual(136); ).toEqual(136);
}); });
xit('should correctly prune lockfile with single package', () => { it('should correctly prune lockfile with single package', () => {
expect( expect(
stringifyNpmLockFile(pruneNpmLockFile(parsedLockFile, ['typescript'])) stringifyNpmLockFile(pruneNpmLockFile(parsedLockFile, ['typescript']))
).toEqual(lockFileV1JustTypescript); ).toEqual(lockFileV1JustTypescript);
}); });
xit('should correctly prune lockfile with multiple packages', () => { it('should correctly prune lockfile with multiple packages', () => {
expect( expect(
stringifyNpmLockFile( stringifyNpmLockFile(
pruneNpmLockFile(parsedLockFile, ['yargs', '@nrwl/devkit']) pruneNpmLockFile(parsedLockFile, ['yargs', '@nrwl/devkit'])
@ -344,4 +372,5 @@ describe('npm LockFile utility', () => {
).toEqual(lockFileV1YargsAndDevkitOnly); ).toEqual(lockFileV1YargsAndDevkitOnly);
}); });
}); });
});
}); });

View File

@ -1,4 +1,9 @@
import { existsSync } from 'fs';
import { satisfies } from 'semver'; import { satisfies } from 'semver';
import { readJsonFile } from '../fileutils';
import { output } from '../output';
import { joinPathFragments } from '../path';
import { workspaceRoot } from '../workspace-root';
import { LockFileData, PackageDependency } from './lock-file-type'; import { LockFileData, PackageDependency } from './lock-file-type';
import { import {
sortObject, sortObject,
@ -450,16 +455,26 @@ export function pruneNpmLockFile(
packages: string[], packages: string[],
projectName?: string projectName?: string
): LockFileData { ): LockFileData {
let isV1;
// NPM V1 does not track full dependency list in the lock file, // NPM V1 does not track full dependency list in the lock file,
// so we can't reuse the lock file to generate a new one // so we can't reuse the lock file to generate a new one
if (lockFileData.lockFileMetadata.metadata.lockfileVersion === 1) { if (lockFileData.lockFileMetadata.metadata.lockfileVersion === 1) {
console.warn( output.warn({
`npm v7 is required to prune lockfile. Please upgrade to npm v7 or run "npm i --package-lock-only" to generate pruned lockfile. title: 'Pruning v1 lock file',
Returning entire lock file.` bodyLines: [
); `If your "node_modules" are not in sync with the lock file, you might get inaccurate results.`,
return lockFileData; `Run "npm ci" to ensure your installed packages are synchronized or upgrade to NPM v7+ to benefit from the new lock file format`,
],
});
isV1 = true;
} }
const dependencies = pruneDependencies(lockFileData.dependencies, packages);
const dependencies = pruneDependencies(
lockFileData.dependencies,
packages,
isV1
);
const lockFileMetadata = { const lockFileMetadata = {
...lockFileData.lockFileMetadata, ...lockFileData.lockFileMetadata,
...pruneRootPackage(lockFileData, packages, projectName), ...pruneRootPackage(lockFileData, packages, projectName),
@ -503,7 +518,8 @@ function pruneRootPackage(
// iterate over packages to collect the affected tree of dependencies // iterate over packages to collect the affected tree of dependencies
function pruneDependencies( function pruneDependencies(
dependencies: LockFileData['dependencies'], dependencies: LockFileData['dependencies'],
packages: string[] packages: string[],
isV1?: boolean
): LockFileData['dependencies'] { ): LockFileData['dependencies'] {
const result: LockFileData['dependencies'] = {}; const result: LockFileData['dependencies'] = {};
@ -523,7 +539,8 @@ function pruneDependencies(
[packageName], [packageName],
dependencies, dependencies,
result, result,
result[packageName][key] result[packageName][key],
isV1
); );
} else { } else {
console.warn( console.warn(
@ -543,17 +560,34 @@ function pruneTransitiveDependencies(
dependencies: LockFileData['dependencies'], dependencies: LockFileData['dependencies'],
prunedDeps: LockFileData['dependencies'], prunedDeps: LockFileData['dependencies'],
value: PackageDependency, value: PackageDependency,
isV1?: boolean,
modifier?: 'dev' | 'optional' | 'peer' modifier?: 'dev' | 'optional' | 'peer'
): void { ): void {
if (!value.dependencies && !value.peerDependencies) { let packageJSON: PackageDependency;
if (isV1) {
const pathToPackageJSON = joinPathFragments(
workspaceRoot,
value.packageMeta[0].path,
'package.json'
);
// if node_modules are our of sync with lock file, we might not have the package.json
if (existsSync(pathToPackageJSON)) {
packageJSON = readJsonFile(pathToPackageJSON);
}
}
if (
!value.dependencies &&
!value.peerDependencies &&
!packageJSON?.peerDependencies
) {
return; return;
} }
Object.entries({ Object.entries({
...value.dependencies, ...value.dependencies,
...value.devDependencies,
...value.peerDependencies, ...value.peerDependencies,
...value.optionalDependencies, ...value.optionalDependencies,
...packageJSON?.peerDependencies,
}).forEach(([packageName, version]: [string, string]) => { }).forEach(([packageName, version]: [string, string]) => {
const versions = dependencies[packageName]; const versions = dependencies[packageName];
if (versions) { if (versions) {
@ -579,7 +613,7 @@ function pruneTransitiveDependencies(
const packageMeta = setPackageMetaModifiers( const packageMeta = setPackageMetaModifiers(
packageName, packageName,
dependency, dependency,
value, packageJSON || value,
modifier modifier
); );
currentMeta.push(packageMeta); currentMeta.push(packageMeta);
@ -589,7 +623,7 @@ function pruneTransitiveDependencies(
const packageMeta = setPackageMetaModifiers( const packageMeta = setPackageMetaModifiers(
packageName, packageName,
dependency, dependency,
value, packageJSON || value,
modifier modifier
); );
@ -601,6 +635,7 @@ function pruneTransitiveDependencies(
dependencies, dependencies,
prunedDeps, prunedDeps,
prunedDeps[packageName][key], prunedDeps[packageName][key],
isV1,
getModifier(packageMeta) getModifier(packageMeta)
); );
} }