diff --git a/packages/nx/src/hasher/hasher.ts b/packages/nx/src/hasher/hasher.ts index 7c5173a944283..c5c69b2000dee 100644 --- a/packages/nx/src/hasher/hasher.ts +++ b/packages/nx/src/hasher/hasher.ts @@ -345,12 +345,6 @@ class TaskHasher { if (n?.data?.hash) { // we already know the hash of this dependency hash = n.data.hash; - } else if (version) { - const deps = [`${projectName}@${version}`]; - this.traverseExternalNodesDependencies(projectName, deps); - hash = this.hashing.hashArray(deps.sort()); - // store hash of given dependency for later use - n.data.hash = hash; } else { // unknown dependency // this may occur dependency is not an npm package @@ -366,19 +360,6 @@ class TaskHasher { }; } - private traverseExternalNodesDependencies( - projectName: string, - visited: string[] - ) { - const dependencies = this.projectGraph.dependencies[projectName] ?? []; - dependencies.forEach((d) => { - if (visited.indexOf(d.target) === -1) { - visited.push(d.target); - this.traverseExternalNodesDependencies(d.target, visited); - } - }); - } - private hashTarget(projectName: string, targetName: string): PartialHash { const projectNode = this.projectGraph.nodes[projectName]; const target = projectNode.data.targets[targetName]; diff --git a/packages/nx/src/utils/lock-file/__snapshots__/lock-file.spec.ts.snap b/packages/nx/src/utils/lock-file/__snapshots__/lock-file.spec.ts.snap index ae522790fc803..cb088eb046b68 100644 --- a/packages/nx/src/utils/lock-file/__snapshots__/lock-file.spec.ts.snap +++ b/packages/nx/src/utils/lock-file/__snapshots__/lock-file.spec.ts.snap @@ -3,6 +3,7 @@ exports[`lock-file mapLockFileDataToExternalNodes should map npm lock file data to external nodes 1`] = ` Object { "data": Object { + "hash": "54342be9f4609650eba8e995881c5eb98c29b9d7bb95b9a699d9e9df68d6428e", "packageName": "yargs", "version": "17.5.1", }, @@ -15,37 +16,37 @@ exports[`lock-file mapLockFileDataToExternalNodes should map npm lock file data Array [ Object { "source": "npm:yargs", - "target": "npm:cliui@7.0.4", + "target": "npm:cliui", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:escalade@3.1.1", + "target": "npm:escalade", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:get-caller-file@2.0.5", + "target": "npm:get-caller-file", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:require-directory@2.1.1", + "target": "npm:require-directory", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:string-width@4.2.3", + "target": "npm:string-width", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:y18n@5.0.8", + "target": "npm:y18n", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:yargs-parser@21.0.1", + "target": "npm:yargs-parser", "type": "static", }, ] @@ -54,6 +55,7 @@ Array [ exports[`lock-file mapLockFileDataToExternalNodes should map pnpm lock file data to external nodes 1`] = ` Object { "data": Object { + "hash": "c22918aa52c9fdce78bf82451d8114e00b6d4375a8e5b1d1d96c6cf4755dbe1a", "packageName": "yargs", "version": "17.5.1", }, @@ -66,37 +68,37 @@ exports[`lock-file mapLockFileDataToExternalNodes should map pnpm lock file data Array [ Object { "source": "npm:yargs", - "target": "npm:cliui@7.0.4", + "target": "npm:cliui", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:escalade@3.1.1", + "target": "npm:escalade", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:get-caller-file@2.0.5", + "target": "npm:get-caller-file", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:require-directory@2.1.1", + "target": "npm:require-directory", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:string-width@4.2.3", + "target": "npm:string-width", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:y18n@5.0.8", + "target": "npm:y18n", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:yargs-parser@21.1.1", + "target": "npm:yargs-parser", "type": "static", }, ] @@ -105,6 +107,7 @@ Array [ exports[`lock-file mapLockFileDataToExternalNodes should map successfully complex npm lock file 1`] = ` Object { "data": Object { + "hash": "1e61b9db38c17534ce427d40ce84f9c03b4c439253f659e9a48513a10a52b465", "packageName": "nx", "version": "14.7.5", }, @@ -117,82 +120,82 @@ exports[`lock-file mapLockFileDataToExternalNodes should map successfully comple Array [ Object { "source": "npm:nx", - "target": "npm:@nrwl/cli@14.7.5", + "target": "npm:@nrwl/cli", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:@nrwl/tao@14.7.5", + "target": "npm:@nrwl/tao", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:@parcel/watcher@2.0.4", + "target": "npm:@parcel/watcher", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:chalk@4.1.0", + "target": "npm:chalk", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:chokidar@3.5.3", + "target": "npm:chokidar", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:cli-cursor@3.1.0", + "target": "npm:cli-cursor", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:cli-spinners@2.6.1", + "target": "npm:cli-spinners", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:cliui@7.0.4", + "target": "npm:cliui", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:dotenv@10.0.0", + "target": "npm:dotenv", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:enquirer@2.3.6", + "target": "npm:enquirer", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:fast-glob@3.2.7", + "target": "npm:fast-glob", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:figures@3.2.0", + "target": "npm:figures", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:flat@5.0.2", + "target": "npm:flat", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:fs-extra@10.1.0", + "target": "npm:fs-extra", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:glob@7.1.4", + "target": "npm:glob", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:ignore@5.2.0", + "target": "npm:ignore", "type": "static", }, Object { @@ -202,67 +205,67 @@ Array [ }, Object { "source": "npm:nx", - "target": "npm:jsonc-parser@3.0.0", + "target": "npm:jsonc-parser", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:minimatch@3.0.5", + "target": "npm:minimatch", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:npm-run-path@4.0.1", + "target": "npm:npm-run-path", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:open@8.4.0", + "target": "npm:open", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:semver@7.3.4", + "target": "npm:semver", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:string-width@4.2.3", + "target": "npm:string-width", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:tar-stream@2.2.0", + "target": "npm:tar-stream", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:tmp@0.2.1", + "target": "npm:tmp", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:tsconfig-paths@3.14.1", + "target": "npm:tsconfig-paths", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:tslib@2.4.0", + "target": "npm:tslib", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:v8-compile-cache@2.3.0", + "target": "npm:v8-compile-cache", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:yargs@17.5.1", + "target": "npm:yargs", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:yargs-parser@21.0.1", + "target": "npm:yargs-parser", "type": "static", }, ] @@ -271,6 +274,7 @@ Array [ exports[`lock-file mapLockFileDataToExternalNodes should map successfully complex pnpm lock file 1`] = ` Object { "data": Object { + "hash": "1e61b9db38c17534ce427d40ce84f9c03b4c439253f659e9a48513a10a52b465", "packageName": "nx", "version": "14.7.5", }, @@ -283,152 +287,152 @@ exports[`lock-file mapLockFileDataToExternalNodes should map successfully comple Array [ Object { "source": "npm:nx", - "target": "npm:@nrwl/cli@14.7.5", + "target": "npm:@nrwl/cli", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:@nrwl/tao@14.7.5", + "target": "npm:@nrwl/tao", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:@parcel/watcher@2.0.4", + "target": "npm:@parcel/watcher", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:chalk@4.1.0", + "target": "npm:chalk", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:chokidar@3.5.3", + "target": "npm:chokidar", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:cli-cursor@3.1.0", + "target": "npm:cli-cursor", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:cli-spinners@2.6.1", + "target": "npm:cli-spinners", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:cliui@7.0.4", + "target": "npm:cliui", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:dotenv@10.0.0", + "target": "npm:dotenv", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:enquirer@2.3.6", + "target": "npm:enquirer", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:fast-glob@3.2.7", + "target": "npm:fast-glob", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:figures@3.2.0", + "target": "npm:figures", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:flat@5.0.2", + "target": "npm:flat", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:fs-extra@10.1.0", + "target": "npm:fs-extra", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:glob@7.1.4", + "target": "npm:glob", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:ignore@5.2.0", + "target": "npm:ignore", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:js-yaml@4.1.0", + "target": "npm:js-yaml", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:jsonc-parser@3.0.0", + "target": "npm:jsonc-parser", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:minimatch@3.0.5", + "target": "npm:minimatch", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:npm-run-path@4.0.1", + "target": "npm:npm-run-path", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:open@8.4.0", + "target": "npm:open", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:semver@7.3.4", + "target": "npm:semver", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:string-width@4.2.3", + "target": "npm:string-width", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:tar-stream@2.2.0", + "target": "npm:tar-stream", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:tmp@0.2.1", + "target": "npm:tmp", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:tsconfig-paths@3.14.1", + "target": "npm:tsconfig-paths", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:tslib@2.4.0", + "target": "npm:tslib", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:v8-compile-cache@2.3.0", + "target": "npm:v8-compile-cache", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:yargs@17.5.1", + "target": "npm:yargs", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:yargs-parser@21.0.1", + "target": "npm:yargs-parser", "type": "static", }, ] @@ -437,6 +441,7 @@ Array [ exports[`lock-file mapLockFileDataToExternalNodes should map successfully complex pnpm lock file 3`] = ` Object { "data": Object { + "hash": "6c5961e1a6ab89174727587860e480508f945407ce4c2cc7c9f1e46f181e6468", "packageName": "@phenomnomnominal/tsquery", "version": "4.1.1", }, @@ -449,12 +454,12 @@ exports[`lock-file mapLockFileDataToExternalNodes should map successfully comple Array [ Object { "source": "npm:@phenomnomnominal/tsquery", - "target": "npm:esquery@1.4.0", + "target": "npm:esquery", "type": "static", }, Object { "source": "npm:@phenomnomnominal/tsquery", - "target": "npm:typescript@4.8.3", + "target": "npm:typescript", "type": "static", }, ] @@ -463,6 +468,7 @@ Array [ exports[`lock-file mapLockFileDataToExternalNodes should map successfully complex yarn lock file 1`] = ` Object { "data": Object { + "hash": "1e61b9db38c17534ce427d40ce84f9c03b4c439253f659e9a48513a10a52b465", "packageName": "nx", "version": "14.7.5", }, @@ -475,152 +481,152 @@ exports[`lock-file mapLockFileDataToExternalNodes should map successfully comple Array [ Object { "source": "npm:nx", - "target": "npm:@nrwl/cli@14.7.5", + "target": "npm:@nrwl/cli", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:@nrwl/tao@14.7.5", + "target": "npm:@nrwl/tao", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:@parcel/watcher@2.0.4", + "target": "npm:@parcel/watcher", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:chalk@4.1.0", + "target": "npm:chalk", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:chokidar@3.5.3", + "target": "npm:chokidar", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:cli-cursor@3.1.0", + "target": "npm:cli-cursor", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:cli-spinners@2.6.1", + "target": "npm:cli-spinners", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:cliui@7.0.4", + "target": "npm:cliui", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:dotenv@10.0.0", + "target": "npm:dotenv", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:enquirer@2.3.6", + "target": "npm:enquirer", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:fast-glob@3.2.7", + "target": "npm:fast-glob", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:figures@3.2.0", + "target": "npm:figures", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:flat@5.0.2", + "target": "npm:flat", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:fs-extra@10.1.0", + "target": "npm:fs-extra", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:glob@7.1.4", + "target": "npm:glob", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:ignore@5.2.0", + "target": "npm:ignore", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:js-yaml@4.1.0", + "target": "npm:js-yaml", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:jsonc-parser@3.0.0", + "target": "npm:jsonc-parser", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:minimatch@3.0.5", + "target": "npm:minimatch", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:npm-run-path@4.0.1", + "target": "npm:npm-run-path", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:open@8.4.0", + "target": "npm:open", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:semver@7.3.4", + "target": "npm:semver", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:string-width@4.2.3", + "target": "npm:string-width", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:tar-stream@2.2.0", + "target": "npm:tar-stream", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:tmp@0.2.1", + "target": "npm:tmp", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:tsconfig-paths@3.14.1", + "target": "npm:tsconfig-paths", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:tslib@2.4.0", + "target": "npm:tslib", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:v8-compile-cache@2.3.0", + "target": "npm:v8-compile-cache", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:yargs@17.5.1", + "target": "npm:yargs", "type": "static", }, Object { "source": "npm:nx", - "target": "npm:yargs-parser@21.0.1", + "target": "npm:yargs-parser", "type": "static", }, ] @@ -629,6 +635,7 @@ Array [ exports[`lock-file mapLockFileDataToExternalNodes should map yarn lock file data to external nodes 1`] = ` Object { "data": Object { + "hash": "c22918aa52c9fdce78bf82451d8114e00b6d4375a8e5b1d1d96c6cf4755dbe1a", "packageName": "yargs", "version": "17.5.1", }, @@ -641,37 +648,37 @@ exports[`lock-file mapLockFileDataToExternalNodes should map yarn lock file data Array [ Object { "source": "npm:yargs", - "target": "npm:cliui@7.0.4", + "target": "npm:cliui", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:escalade@3.1.1", + "target": "npm:escalade", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:get-caller-file@2.0.5", + "target": "npm:get-caller-file", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:require-directory@2.1.1", + "target": "npm:require-directory", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:string-width@4.2.3", + "target": "npm:string-width", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:y18n@5.0.8", + "target": "npm:y18n", "type": "static", }, Object { "source": "npm:yargs", - "target": "npm:yargs-parser@21.1.1", + "target": "npm:yargs-parser", "type": "static", }, ] diff --git a/packages/nx/src/utils/lock-file/lock-file.ts b/packages/nx/src/utils/lock-file/lock-file.ts index e1b359b02198c..8d9702aa6b23e 100644 --- a/packages/nx/src/utils/lock-file/lock-file.ts +++ b/packages/nx/src/utils/lock-file/lock-file.ts @@ -15,12 +15,12 @@ import { prunePnpmLockFile, stringifyPnpmLockFile, } from './pnpm'; -import { LockFileData, PackageVersions } from './lock-file-type'; +import { LockFileData } from './lock-file-type'; import { workspaceRoot } from '../workspace-root'; import { join } from 'path'; import { - findMatchingVersion, getNodeName, + hashExternalNodes, hashString, mapExternalNodeDependencies, } from './utils'; @@ -144,6 +144,7 @@ export function mapLockFileDataToPartialGraph( } }); }); + hashExternalNodes(result); return result; } diff --git a/packages/nx/src/utils/lock-file/utils.ts b/packages/nx/src/utils/lock-file/utils.ts index 89dfe0ae0cf09..7b8e76d336659 100644 --- a/packages/nx/src/utils/lock-file/utils.ts +++ b/packages/nx/src/utils/lock-file/utils.ts @@ -3,6 +3,11 @@ import { defaultHashing } from '../../hasher/hashing-impl'; import { PackageDependency, PackageVersions } from './lock-file-type'; import { workspaceRoot } from '../workspace-root'; import { existsSync, readFileSync } from 'fs'; +import { + ProjectGraph, + ProjectGraphDependency, + ProjectGraphExternalNode, +} from '../../config/project-graph'; /** * Simple sort function to ensure keys are ordered alphabetically @@ -61,7 +66,6 @@ export function isRootVersion(packageName: string, version: string): boolean { } else { return false; } - return true; } /** @@ -120,7 +124,8 @@ function mapTransitiveDependencies( const result: string[] = []; Object.keys(dependencies).forEach((packageName) => { - const key = `${packageName}@${dependencies[packageName]}`; + const cleanVersion = dependencies[packageName].split('_')[0]; + const key = `${packageName}@${cleanVersion}`; // some of the peer dependencies might not be installed, // we don't have them as nodes in externalNodes @@ -134,18 +139,56 @@ function mapTransitiveDependencies( result.push(versionCache[key]); } else { const versions = packages[packageName]; - const version = - findMatchingVersion(packageName, versions, dependencies[packageName]) || - dependencies[packageName]; - const nodeName = getNodeName( - packageName, - version, - versions[version]?.rootVersion - ); - result.push(nodeName); - versionCache[key] = nodeName; + const version = findMatchingVersion(packageName, versions, cleanVersion); + // for some peer dependencies, we won't find installed version so we'll just ignore those + if (version) { + const nodeName = getNodeName( + packageName, + version, + versions[`${packageName}@${version}`]?.rootVersion + ); + result.push(nodeName); + versionCache[key] = nodeName; + } } }); return result; } + +export function hashExternalNodes(projectGraph: ProjectGraph) { + Object.keys(projectGraph.externalNodes).forEach((key) => { + if (!projectGraph.externalNodes[key].data.hash) { + // hash it using it's dependencies + hashExternalNode(projectGraph.externalNodes[key], projectGraph); + } + }); +} + +function hashExternalNode(node: ProjectGraphExternalNode, graph: ProjectGraph) { + const hashKey = `${node.data.packageName}@${node.data.version}`; + if (!graph.dependencies[node.name]) { + node.data.hash = hashString(hashKey); + } else { + const hashingInput = [hashKey]; + traverseExternalNodesDependencies(node.name, graph, hashingInput); + node.data.hash = defaultHashing.hashArray(hashingInput.sort()); + } +} + +function traverseExternalNodesDependencies( + projectName: string, + graph: ProjectGraph, + visited: string[] +) { + graph.dependencies[projectName].forEach((d) => { + const target = graph.externalNodes[d.target]; + const targetKey = `${target.data.packageName}@${target.data.version}`; + if (visited.indexOf(targetKey) === -1) { + visited.push(targetKey); + if (graph.dependencies[d.target]) { + traverseExternalNodesDependencies(d.target, graph, visited); + } + } + }); +}