From d2e663b4cc02efe8fac8376cf3d130680f03e238 Mon Sep 17 00:00:00 2001 From: Miroslav Jonas Date: Fri, 9 Dec 2022 16:24:40 +0100 Subject: [PATCH] fix(core): handle npm workspaces --- .../nx/src/lock-file/__fixtures__/npm.lock.ts | 38 ------------- packages/nx/src/lock-file/npm.spec.ts | 56 ++++++++++++------- packages/nx/src/lock-file/npm.ts | 48 +++++++++++++--- 3 files changed, 74 insertions(+), 68 deletions(-) diff --git a/packages/nx/src/lock-file/__fixtures__/npm.lock.ts b/packages/nx/src/lock-file/__fixtures__/npm.lock.ts index 79990d7facb251..4e58f980f18500 100644 --- a/packages/nx/src/lock-file/__fixtures__/npm.lock.ts +++ b/packages/nx/src/lock-file/__fixtures__/npm.lock.ts @@ -22642,41 +22642,3 @@ export const lockFileV1YargsAndDevkitOnly = `{ } } `; - -/** - * V1 - * dependencies: - * "workspace-a": { - "version": "file:workspace-a", - "requires": { - "@nrwl/devkit": "15.0.13", - "postgres": "charsleysa/postgres#fix-errors-compiled", - "yargs": "17.6.2" - } - }, -*/ - -/** - * V1 - * packages: - * "node_modules/workspace-a": { - "resolved": "workspace-a", - "link": true - }, - "workspace-a": { - "dependencies": { - "@nrwl/devkit": "15.0.13", - "postgres": "charsleysa/postgres#fix-errors-compiled", - "yargs": "17.6.2" - } - }, -* dependencies: - "workspace-a": { - "version": "file:workspace-a", - "requires": { - "@nrwl/devkit": "15.0.13", - "postgres": "charsleysa/postgres#fix-errors-compiled", - "yargs": "17.6.2" - } - }, -*/ diff --git a/packages/nx/src/lock-file/npm.spec.ts b/packages/nx/src/lock-file/npm.spec.ts index 474c94902d1669..24af232e912164 100644 --- a/packages/nx/src/lock-file/npm.spec.ts +++ b/packages/nx/src/lock-file/npm.spec.ts @@ -121,7 +121,9 @@ describe('npm LockFile utility', () => { }); it('should match the original file on stringification', () => { - expect(stringifyNpmLockFile(parsedLockFile)).toEqual(lockFileV3); + expect(JSON.parse(stringifyNpmLockFile(parsedLockFile))).toEqual( + JSON.parse(lockFileV3) + ); }); it('should prune the lock file', () => { @@ -139,18 +141,22 @@ describe('npm LockFile utility', () => { it('should correctly prune lockfile with single package', () => { expect( - stringifyNpmLockFile( - pruneNpmLockFile(parsedLockFile, TypeScriptOnlyPackage) + JSON.parse( + stringifyNpmLockFile( + pruneNpmLockFile(parsedLockFile, TypeScriptOnlyPackage) + ) ) - ).toEqual(lockFileV3JustTypescript); + ).toEqual(JSON.parse(lockFileV3JustTypescript)); }); it('should correctly prune lockfile with multiple packages', () => { expect( - stringifyNpmLockFile( - pruneNpmLockFile(parsedLockFile, YargsAndDevkitPackage) + JSON.parse( + stringifyNpmLockFile( + pruneNpmLockFile(parsedLockFile, YargsAndDevkitPackage) + ) ) - ).toEqual(lockFileV3YargsAndDevkitOnly); + ).toEqual(JSON.parse(lockFileV3YargsAndDevkitOnly)); }); }); @@ -189,8 +195,8 @@ describe('npm LockFile utility', () => { const parsedWorkspaceLockFile = parseNpmLockFile( npmLockFileWithWorkspaces ); - expect(stringifyNpmLockFile(parsedWorkspaceLockFile)).toEqual( - npmLockFileWithWorkspaces + expect(JSON.parse(stringifyNpmLockFile(parsedWorkspaceLockFile))).toEqual( + JSON.parse(npmLockFileWithWorkspaces) ); }); @@ -244,7 +250,9 @@ describe('npm LockFile utility', () => { }); it('should match the original file on stringification', () => { - expect(stringifyNpmLockFile(parsedLockFile)).toEqual(lockFileV2); + expect(JSON.parse(stringifyNpmLockFile(parsedLockFile))).toEqual( + JSON.parse(lockFileV2) + ); }); it('should prune the lock file', () => { @@ -262,16 +270,18 @@ describe('npm LockFile utility', () => { it('should correctly prune lockfile with single package', () => { expect( - stringifyNpmLockFile( - pruneNpmLockFile(parsedLockFile, TypeScriptOnlyPackage) + JSON.parse( + stringifyNpmLockFile( + pruneNpmLockFile(parsedLockFile, TypeScriptOnlyPackage) + ) ) - ).toEqual(lockFileV2JustTypescript); + ).toEqual(JSON.parse(lockFileV2JustTypescript)); }); it('should correctly prune lockfile with multiple packages', () => { const pruned = pruneNpmLockFile(parsedLockFile, YargsAndDevkitPackage); - expect(stringifyNpmLockFile(pruned)).toEqual( - lockFileV2YargsAndDevkitOnly + expect(JSON.parse(stringifyNpmLockFile(pruned))).toEqual( + JSON.parse(lockFileV2YargsAndDevkitOnly) ); }); }); @@ -345,7 +355,9 @@ describe('npm LockFile utility', () => { }); it('should match the original file on stringification', () => { - expect(stringifyNpmLockFile(parsedLockFile)).toEqual(lockFileV1); + expect(JSON.parse(stringifyNpmLockFile(parsedLockFile))).toEqual( + JSON.parse(lockFileV1) + ); }); describe('pruning', () => { @@ -378,16 +390,18 @@ describe('npm LockFile utility', () => { it('should correctly prune lockfile with single package', () => { expect( - stringifyNpmLockFile( - pruneNpmLockFile(parsedLockFile, TypeScriptOnlyPackage) + JSON.parse( + stringifyNpmLockFile( + pruneNpmLockFile(parsedLockFile, TypeScriptOnlyPackage) + ) ) - ).toEqual(lockFileV1JustTypescript); + ).toEqual(JSON.parse(lockFileV1JustTypescript)); }); it('should correctly prune lockfile with multiple packages', () => { const pruned = pruneNpmLockFile(parsedLockFile, YargsAndDevkitPackage); - expect(stringifyNpmLockFile(pruned)).toEqual( - lockFileV1YargsAndDevkitOnly + expect(JSON.parse(stringifyNpmLockFile(pruned))).toEqual( + JSON.parse(lockFileV1YargsAndDevkitOnly) ); }); }); diff --git a/packages/nx/src/lock-file/npm.ts b/packages/nx/src/lock-file/npm.ts index c8b28e5f461e5b..8a1011c65583a0 100644 --- a/packages/nx/src/lock-file/npm.ts +++ b/packages/nx/src/lock-file/npm.ts @@ -7,7 +7,7 @@ import { workspaceRoot } from '../utils/workspace-root'; import { LockFileData, PackageDependency } from './utils/lock-file-type'; import { TransitiveLookupFunctionInput } from './utils/mapping'; import { hashString, generatePrunnedHash } from './utils/hashing'; -import { PackageJsonDeps } from './utils/pruning'; +import type { PackageJsonDeps } from './utils/pruning'; type PackageMeta = { path: string; @@ -110,13 +110,32 @@ function mapPackages( packagePath ); + let dependency; + if (lockfileVersion === 2) { + const path = packagePath.split(/\/?node_modules\//).slice(1); + + path.forEach((proj) => { + if (!dependency) { + dependency = dependencies[proj]; + } else { + dependency = dependency.dependencies[proj]; + } + }); + // if versions are same, no need to track it further + if (dependency && value.version === dependency.version) { + dependency = undefined; + } + } + mapPackageDependency( mappedPackages, packageName, newKey, packagePath, value, - lockfileVersion + lockfileVersion, + undefined, + dependency ); } }); @@ -151,7 +170,8 @@ function mapPackageDependency( packagePath: string, value: NpmDependency | Omit, lockfileVersion: number, - isRootVersion?: boolean + isRootVersion?: boolean, + dependencyValue?: NpmDependency ) { const { dev, peer, optional } = value; const packageMeta = { @@ -177,10 +197,17 @@ function mapPackageDependency( mappedPackages[packageName][key] = { ...(value as Omit), - ...(!value.integrity && { - actualVersion: value.version, - version: value.resolved, - }), + ...(!value.integrity && + value.version && { + actualVersion: value.version, + version: value.resolved, + }), + ...(value.integrity && + dependencyValue && { + actualVersion: value.version, + version: dependencyValue.version, + }), + ...(dependencyValue && { dependencyValue }), packageMeta: [], rootVersion, }; @@ -297,6 +324,7 @@ function unmapPackage(packages: Dependencies, dependency: PackageDependency) { dev, peer, optional, + dependencyValue, ...value } = dependency; // we need to decompose value, to achieve particular field ordering @@ -321,7 +349,8 @@ function unmapDependencies( packageName: string, { packageMeta, ...value }: PackageDependency & { packageMeta: PackageMeta[] } ): void { - const { version, resolved, integrity, devOptional } = value; + const { version, resolved, integrity, devOptional, dependencyValue, from } = + value; for (let i = 0; i < packageMeta.length; i++) { const { path, dev, optional, peer } = packageMeta[i]; @@ -334,10 +363,11 @@ function unmapDependencies( ); // sorting fields to match package-lock structure - innerDeps[packageName] = { + innerDeps[packageName] = dependencyValue || { version, resolved, integrity, + from, dev, devOptional, optional,