nx 23.1.0 → 23.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/nx.js +2 -1
- package/dist/release/changelog-renderer/index.js +4 -5
- package/dist/release/index.d.ts +1 -1
- package/dist/src/ai/configure-ai-agents-disclaimer.d.ts +5 -0
- package/dist/src/ai/configure-ai-agents-disclaimer.js +31 -0
- package/dist/src/analytics/analytics.d.ts +6 -0
- package/dist/src/analytics/analytics.js +16 -8
- package/dist/src/analytics/index.d.ts +1 -1
- package/dist/src/analytics/index.js +2 -1
- package/dist/src/command-line/add/add.js +13 -1
- package/dist/src/command-line/affected/affected.js +1 -0
- package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +2 -2
- package/dist/src/command-line/graph/graph.d.ts +1 -0
- package/dist/src/command-line/graph/graph.js +15 -6
- package/dist/src/command-line/import/import.js +2 -3
- package/dist/src/command-line/init/ai-agent-prompts.d.ts +1 -1
- package/dist/src/command-line/init/ai-agent-prompts.js +7 -6
- package/dist/src/command-line/init/command-object.js +10 -2
- package/dist/src/command-line/init/configure-plugins.js +3 -2
- package/dist/src/command-line/init/implementation/add-nx-to-monorepo.js +4 -2
- package/dist/src/command-line/init/implementation/add-nx-to-nest.js +4 -3
- package/dist/src/command-line/init/implementation/add-nx-to-npm-repo.js +4 -3
- package/dist/src/command-line/init/implementation/add-nx-to-turborepo.js +4 -3
- package/dist/src/command-line/init/implementation/angular/index.js +4 -2
- package/dist/src/command-line/init/implementation/angular/integrated-workspace.js +2 -5
- package/dist/src/command-line/init/implementation/angular/legacy-angular-versions.js +3 -0
- package/dist/src/command-line/init/implementation/dot-nx/add-nx-scripts.js +4 -1
- package/dist/src/command-line/init/implementation/utils.d.ts +3 -3
- package/dist/src/command-line/init/implementation/utils.js +22 -3
- package/dist/src/command-line/init/init-v2.js +4 -5
- package/dist/src/command-line/migrate/execute-migration.d.ts +72 -0
- package/dist/src/command-line/migrate/execute-migration.js +389 -0
- package/dist/src/command-line/migrate/migrate-ui-api.js +17 -12
- package/dist/src/command-line/migrate/migrate.d.ts +5 -45
- package/dist/src/command-line/migrate/migrate.js +122 -402
- package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +1 -1
- package/dist/src/command-line/nx-cloud/connect/view-logs.js +2 -0
- package/dist/src/command-line/release/changelog.js +5 -0
- package/dist/src/command-line/release/index.d.ts +1 -1
- package/dist/src/command-line/release/utils/git.js +7 -3
- package/dist/src/command-line/release/utils/release-graph.d.ts +19 -2
- package/dist/src/command-line/release/utils/release-graph.js +71 -19
- package/dist/src/command-line/release/utils/remote-release-clients/github.js +7 -1
- package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +1 -1
- package/dist/src/command-line/release/utils/shared.d.ts +2 -2
- package/dist/src/command-line/release/version/release-group-processor.d.ts +24 -0
- package/dist/src/command-line/release/version/release-group-processor.js +80 -6
- package/dist/src/command-line/release/version/version-actions.d.ts +27 -2
- package/dist/src/command-line/release/version/version-actions.js +9 -1
- package/dist/src/command-line/release/version.js +9 -5
- package/dist/src/command-line/reset/reset.js +6 -1
- package/dist/src/command-line/run/run-one.js +1 -0
- package/dist/src/command-line/run-many/run-many.js +1 -0
- package/dist/src/command-line/show/show-target/info.js +11 -2
- package/dist/src/command-line/show/show-target/inputs.js +50 -153
- package/dist/src/command-line/show/show-target/outputs.js +48 -173
- package/dist/src/command-line/show/show-target/utils.d.ts +28 -0
- package/dist/src/command-line/show/show-target/utils.js +83 -3
- package/dist/src/command-line/watch/watch.js +13 -0
- package/dist/src/command-line/yargs-utils/shared-options.d.ts +1 -1
- package/dist/src/commands-runner/create-command-graph.js +5 -0
- package/dist/src/config/nx-json.d.ts +5 -3
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/core/graph/styles.js +1 -1
- package/dist/src/daemon/client/client.d.ts +52 -3
- package/dist/src/daemon/client/client.js +177 -55
- package/dist/src/daemon/client/daemon-environment.d.ts +17 -0
- package/dist/src/daemon/client/daemon-environment.js +110 -2
- package/dist/src/daemon/client/daemon-socket-messenger.js +4 -0
- package/dist/src/daemon/message-types/daemon-message.d.ts +32 -0
- package/dist/src/daemon/message-types/daemon-message.js +25 -0
- package/dist/src/daemon/server/file-watching/file-watcher-sockets.d.ts +5 -0
- package/dist/src/daemon/server/file-watching/file-watcher-sockets.js +16 -0
- package/dist/src/daemon/server/handle-tasks-execution-hooks.d.ts +1 -1
- package/dist/src/daemon/server/latest-nx.js +5 -2
- package/dist/src/daemon/server/server.js +31 -4
- package/dist/src/daemon/server/shutdown-utils.d.ts +5 -0
- package/dist/src/daemon/server/shutdown-utils.js +11 -4
- package/dist/src/daemon/socket-utils.d.ts +1 -0
- package/dist/src/daemon/socket-utils.js +24 -3
- package/dist/src/daemon/tmp-dir.d.ts +43 -3
- package/dist/src/daemon/tmp-dir.js +437 -15
- package/dist/src/devkit-internals.d.ts +96 -3
- package/dist/src/devkit-internals.js +200 -4
- package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.d.ts +5 -0
- package/dist/src/generators/internal-utils/format-changed-files-with-prettier-if-available.js +9 -2
- package/dist/src/generators/tree.d.ts +4 -2
- package/dist/src/generators/utils/nx-json.js +13 -3
- package/dist/src/hasher/check-task-files.d.ts +86 -0
- package/dist/src/hasher/check-task-files.js +378 -0
- package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.d.ts +12 -0
- package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.js +250 -0
- package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.md +50 -0
- package/dist/src/native/index.d.ts +31 -2
- package/dist/src/native/index.js +71 -28
- package/dist/src/native/native-bindings.js +3 -0
- package/dist/src/native/native-file-cache-location.d.ts +17 -1
- package/dist/src/native/native-file-cache-location.js +86 -12
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/nx-cloud/utilities/get-cloud-options.js +5 -2
- package/dist/src/nx-cloud/utilities/onboarding.js +5 -2
- package/dist/src/plugins/js/lock-file/bun-parser.js +43 -10
- package/dist/src/plugins/js/lock-file/npm-parser.js +71 -27
- package/dist/src/plugins/js/lock-file/pnpm-parser.js +110 -17
- package/dist/src/plugins/js/lock-file/project-graph-pruning.js +15 -9
- package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.js +3 -0
- package/dist/src/plugins/js/lock-file/yarn-parser.js +32 -5
- package/dist/src/plugins/js/project-graph/affected/npm-packages.js +51 -20
- package/dist/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +14 -4
- package/dist/src/plugins/js/utils/register.d.ts +18 -0
- package/dist/src/plugins/js/utils/register.js +81 -0
- package/dist/src/plugins/js/utils/typescript.d.ts +9 -0
- package/dist/src/plugins/js/utils/typescript.js +15 -0
- package/dist/src/project-graph/affected/affected-project-graph-models.d.ts +1 -1
- package/dist/src/project-graph/affected/affected-project-graph.d.ts +1 -1
- package/dist/src/project-graph/affected/affected-project-graph.js +2 -2
- package/dist/src/project-graph/affected/locators/project-glob-changes.js +5 -2
- package/dist/src/project-graph/build-project-graph.js +7 -1
- package/dist/src/project-graph/error-types.d.ts +12 -1
- package/dist/src/project-graph/error-types.js +68 -3
- package/dist/src/project-graph/file-utils.js +6 -1
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.d.ts +1 -0
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +22 -1
- package/dist/src/project-graph/plugins/isolation/messaging.d.ts +16 -3
- package/dist/src/project-graph/plugins/isolation/messaging.js +3 -0
- package/dist/src/project-graph/plugins/isolation/plugin-worker.js +58 -8
- package/dist/src/project-graph/plugins/loaded-nx-plugin.js +1 -0
- package/dist/src/project-graph/plugins/transpiler.js +4 -0
- package/dist/src/project-graph/project-graph.js +23 -0
- package/dist/src/project-graph/utils/project-configuration/name-substitution-manager.d.ts +10 -12
- package/dist/src/project-graph/utils/project-configuration/name-substitution-manager.js +93 -63
- package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.d.ts +5 -8
- package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.js +16 -15
- package/dist/src/project-graph/utils/project-configuration/source-maps.d.ts +12 -5
- package/dist/src/project-graph/utils/project-configuration/source-maps.js +21 -7
- package/dist/src/project-graph/utils/project-configuration/target-defaults.d.ts +1 -1
- package/dist/src/project-graph/utils/project-configuration/target-defaults.js +26 -27
- package/dist/src/project-graph/utils/project-configuration/target-merging.js +50 -8
- package/dist/src/project-graph/utils/project-configuration/target-normalization.js +149 -6
- package/dist/src/project-graph/utils/project-configuration/utils.d.ts +15 -0
- package/dist/src/project-graph/utils/project-configuration/utils.js +42 -6
- package/dist/src/project-graph/utils/project-configuration-utils.d.ts +15 -12
- package/dist/src/project-graph/utils/project-configuration-utils.js +79 -101
- package/dist/src/tasks-runner/life-cycles/performance-analysis.d.ts +11 -4
- package/dist/src/tasks-runner/life-cycles/performance-analysis.js +15 -6
- package/dist/src/tasks-runner/life-cycles/performance-life-cycle.js +5 -4
- package/dist/src/tasks-runner/life-cycles/performance-report.d.ts +18 -8
- package/dist/src/tasks-runner/life-cycles/performance-report.js +66 -29
- package/dist/src/tasks-runner/run-command.js +4 -2
- package/dist/src/tasks-runner/task-env.d.ts +9 -0
- package/dist/src/tasks-runner/task-env.js +31 -4
- package/dist/src/tasks-runner/task-orchestrator.d.ts +4 -1
- package/dist/src/tasks-runner/task-orchestrator.js +23 -10
- package/dist/src/tasks-runner/utils.js +2 -6
- package/dist/src/utils/acknowledge-build-scripts.d.ts +13 -0
- package/dist/src/utils/acknowledge-build-scripts.js +102 -0
- package/dist/src/utils/analytics-prompt.d.ts +0 -6
- package/dist/src/utils/analytics-prompt.js +0 -51
- package/dist/src/utils/assert-workspace-validity.js +7 -2
- package/dist/src/utils/catalog/manager-utils.d.ts +1 -2
- package/dist/src/utils/catalog/manager-utils.js +22 -5
- package/dist/src/utils/catalog/pnpm-manager.d.ts +1 -0
- package/dist/src/utils/catalog/pnpm-manager.js +3 -15
- package/dist/src/utils/catalog/yarn-manager.d.ts +1 -0
- package/dist/src/utils/catalog/yarn-manager.js +3 -15
- package/dist/src/utils/command-line-utils.js +30 -20
- package/dist/src/utils/fileutils.d.ts +5 -0
- package/dist/src/utils/fileutils.js +7 -2
- package/dist/src/utils/git-revision.d.ts +12 -0
- package/dist/src/utils/git-revision.js +25 -0
- package/dist/src/utils/git-utils.d.ts +16 -3
- package/dist/src/utils/git-utils.js +146 -52
- package/dist/src/utils/git-worktrees.d.ts +30 -0
- package/dist/src/utils/git-worktrees.js +212 -0
- package/dist/src/utils/ignore.d.ts +128 -1
- package/dist/src/utils/ignore.js +182 -12
- package/dist/src/utils/long-running-target.d.ts +10 -0
- package/dist/src/utils/long-running-target.js +19 -0
- package/dist/src/utils/min-release-age/behavior/bun.js +7 -11
- package/dist/src/utils/min-release-age/behavior/npm.js +5 -2
- package/dist/src/utils/min-release-age/behavior/pnpm.js +9 -7
- package/dist/src/utils/min-release-age/packument.js +1 -1
- package/dist/src/utils/nx-tmp-dir.d.ts +18 -0
- package/dist/src/utils/nx-tmp-dir.js +40 -0
- package/dist/src/utils/owned-private-dir.d.ts +132 -0
- package/dist/src/utils/owned-private-dir.js +323 -0
- package/dist/src/utils/package-json.js +11 -7
- package/dist/src/utils/package-manager-config/bunfig.d.ts +14 -0
- package/dist/src/utils/package-manager-config/bunfig.js +49 -0
- package/dist/src/utils/package-manager-config/npmrc.d.ts +23 -0
- package/dist/src/utils/package-manager-config/npmrc.js +124 -0
- package/dist/src/utils/package-manager-config/pnpm-config.d.ts +17 -0
- package/dist/src/utils/package-manager-config/pnpm-config.js +60 -0
- package/dist/src/utils/package-manager.d.ts +23 -2
- package/dist/src/utils/package-manager.js +264 -43
- package/dist/src/utils/provenance.js +18 -16
- package/dist/src/utils/registry-config/bun.d.ts +2 -0
- package/dist/src/utils/registry-config/bun.js +257 -0
- package/dist/src/utils/registry-config/index.d.ts +13 -0
- package/dist/src/utils/registry-config/index.js +102 -0
- package/dist/src/utils/registry-config/pnpm.d.ts +2 -0
- package/dist/src/utils/registry-config/pnpm.js +1551 -0
- package/dist/src/utils/registry-config/utils.d.ts +195 -0
- package/dist/src/utils/registry-config/utils.js +496 -0
- package/dist/src/utils/registry-config/yarn-berry.d.ts +2 -0
- package/dist/src/utils/registry-config/yarn-berry.js +612 -0
- package/dist/src/utils/registry-config/yarn-classic.d.ts +2 -0
- package/dist/src/utils/registry-config/yarn-classic.js +1001 -0
- package/dist/src/utils/safe-spawn.d.ts +24 -0
- package/dist/src/utils/safe-spawn.js +104 -0
- package/dist/src/utils/shell-quoting.d.ts +15 -0
- package/dist/src/utils/shell-quoting.js +35 -0
- package/dist/src/utils/wait-for-socket-connection.d.ts +11 -0
- package/dist/src/utils/wait-for-socket-connection.js +14 -8
- package/dist/src/utils/workspace-id.d.ts +20 -0
- package/dist/src/utils/workspace-id.js +54 -0
- package/migrations.json +13 -1
- package/package.json +22 -17
- package/dist/src/migrations/update-17-3-0/nx-release-path.d.ts +0 -3
- package/dist/src/migrations/update-17-3-0/nx-release-path.js +0 -47
- package/dist/src/utils/min-release-age/npmrc.d.ts +0 -15
- package/dist/src/utils/min-release-age/npmrc.js +0 -45
|
@@ -133,6 +133,22 @@ function findV3Version(snapshot, packageName) {
|
|
|
133
133
|
}
|
|
134
134
|
function getDependencies(data, keyMap, ctx) {
|
|
135
135
|
const dependencies = [];
|
|
136
|
+
// Memoizes semver `satisfies(version, range)` for this dependency walk. The
|
|
137
|
+
// same (version, range) pairs recur across many edges, so the range parse
|
|
138
|
+
// happens once per distinct pair instead of once per edge. Scoped to the walk
|
|
139
|
+
// (not module-global) so V8 collects it when dependency creation finishes
|
|
140
|
+
// rather than retaining it for the daemon's lifetime.
|
|
141
|
+
const versionSatisfiesCache = new Map();
|
|
142
|
+
const cachedSatisfies = (version, range) => {
|
|
143
|
+
const key = `${version}\n${range}`;
|
|
144
|
+
const cached = versionSatisfiesCache.get(key);
|
|
145
|
+
if (cached !== undefined) {
|
|
146
|
+
return cached;
|
|
147
|
+
}
|
|
148
|
+
const result = (0, semver_1.satisfies)(version, range);
|
|
149
|
+
versionSatisfiesCache.set(key, result);
|
|
150
|
+
return result;
|
|
151
|
+
};
|
|
136
152
|
if (data.lockfileVersion > 1) {
|
|
137
153
|
Object.entries(data.packages).forEach(([path, snapshot]) => {
|
|
138
154
|
// we are skipping workspaces packages
|
|
@@ -147,7 +163,7 @@ function getDependencies(data, keyMap, ctx) {
|
|
|
147
163
|
].forEach((section) => {
|
|
148
164
|
if (section) {
|
|
149
165
|
Object.entries(section).forEach(([name, versionRange]) => {
|
|
150
|
-
const target = findTarget(path, keyMap, name, versionRange);
|
|
166
|
+
const target = findTarget(path, keyMap, name, versionRange, cachedSatisfies);
|
|
151
167
|
if (target) {
|
|
152
168
|
const dep = {
|
|
153
169
|
source: sourceName,
|
|
@@ -164,12 +180,12 @@ function getDependencies(data, keyMap, ctx) {
|
|
|
164
180
|
}
|
|
165
181
|
else {
|
|
166
182
|
Object.entries(data.dependencies).forEach(([packageName, snapshot]) => {
|
|
167
|
-
addV1NodeDependencies(`node_modules/${packageName}`, snapshot, dependencies, keyMap, ctx);
|
|
183
|
+
addV1NodeDependencies(`node_modules/${packageName}`, snapshot, dependencies, keyMap, ctx, cachedSatisfies);
|
|
168
184
|
});
|
|
169
185
|
}
|
|
170
186
|
return dependencies;
|
|
171
187
|
}
|
|
172
|
-
function findTarget(sourcePath, keyMap, targetName, versionRange,
|
|
188
|
+
function findTarget(sourcePath, keyMap, targetName, versionRange, cachedSatisfies,
|
|
173
189
|
// When a package is found at a path but its version doesn't satisfy the
|
|
174
190
|
// range (e.g. due to npm overrides), we keep it as a fallback. npm already
|
|
175
191
|
// resolved this dependency to that location, so it is the correct target
|
|
@@ -186,12 +202,13 @@ fallback) {
|
|
|
186
202
|
versionRange.startsWith('npm:')) {
|
|
187
203
|
const nodeVersion = child.data.version.slice(child.data.version.indexOf('@', 5) + 1);
|
|
188
204
|
const depVersion = versionRange.slice(versionRange.indexOf('@', 5) + 1);
|
|
189
|
-
if (nodeVersion === depVersion ||
|
|
205
|
+
if (nodeVersion === depVersion ||
|
|
206
|
+
cachedSatisfies(nodeVersion, depVersion)) {
|
|
190
207
|
return child;
|
|
191
208
|
}
|
|
192
209
|
}
|
|
193
210
|
else if (child.data.version === versionRange ||
|
|
194
|
-
(
|
|
211
|
+
cachedSatisfies(child.data.version, versionRange)) {
|
|
195
212
|
return child;
|
|
196
213
|
}
|
|
197
214
|
// Version mismatch — save as fallback (could be an npm override)
|
|
@@ -203,13 +220,17 @@ fallback) {
|
|
|
203
220
|
if (!sourcePath) {
|
|
204
221
|
return fallback;
|
|
205
222
|
}
|
|
206
|
-
|
|
223
|
+
// Walk one level up the nesting chain by dropping the trailing
|
|
224
|
+
// `node_modules/<pkg>` segment. Slash-index arithmetic avoids the
|
|
225
|
+
// split/slice/join array allocation on every hop.
|
|
226
|
+
const lastNodeModules = sourcePath.lastIndexOf('node_modules/');
|
|
227
|
+
return findTarget(lastNodeModules === -1 ? '' : sourcePath.substring(0, lastNodeModules), keyMap, targetName, versionRange, cachedSatisfies, fallback);
|
|
207
228
|
}
|
|
208
|
-
function addV1NodeDependencies(path, snapshot, dependencies, keyMap, ctx) {
|
|
229
|
+
function addV1NodeDependencies(path, snapshot, dependencies, keyMap, ctx, cachedSatisfies) {
|
|
209
230
|
if (keyMap.has(path) && snapshot.requires) {
|
|
210
231
|
const source = keyMap.get(path).name;
|
|
211
232
|
Object.entries(snapshot.requires).forEach(([name, versionRange]) => {
|
|
212
|
-
const target = findTarget(path, keyMap, name, versionRange);
|
|
233
|
+
const target = findTarget(path, keyMap, name, versionRange, cachedSatisfies);
|
|
213
234
|
if (target) {
|
|
214
235
|
const dep = {
|
|
215
236
|
source: source,
|
|
@@ -223,14 +244,14 @@ function addV1NodeDependencies(path, snapshot, dependencies, keyMap, ctx) {
|
|
|
223
244
|
}
|
|
224
245
|
if (snapshot.dependencies) {
|
|
225
246
|
Object.entries(snapshot.dependencies).forEach(([depName, depSnapshot]) => {
|
|
226
|
-
addV1NodeDependencies(`${path}/node_modules/${depName}`, depSnapshot, dependencies, keyMap, ctx);
|
|
247
|
+
addV1NodeDependencies(`${path}/node_modules/${depName}`, depSnapshot, dependencies, keyMap, ctx, cachedSatisfies);
|
|
227
248
|
});
|
|
228
249
|
}
|
|
229
250
|
const { peerDependencies } = getPeerDependencies(path);
|
|
230
251
|
if (peerDependencies) {
|
|
231
252
|
const node = keyMap.get(path);
|
|
232
253
|
Object.entries(peerDependencies).forEach(([depName, depSpec]) => {
|
|
233
|
-
const target = findTarget(path, keyMap, depName, depSpec);
|
|
254
|
+
const target = findTarget(path, keyMap, depName, depSpec, cachedSatisfies);
|
|
234
255
|
if (target) {
|
|
235
256
|
const dep = {
|
|
236
257
|
source: node.name,
|
|
@@ -358,10 +379,11 @@ function mapSnapshots(rootLockFile, graph) {
|
|
|
358
379
|
const nestedNodes = new Set();
|
|
359
380
|
const visitedNodes = new Map();
|
|
360
381
|
const remappedPackages = new Map();
|
|
382
|
+
const packageIndex = buildV3Index(rootLockFile.packages);
|
|
361
383
|
// add first level children
|
|
362
384
|
Object.values(graph.externalNodes).forEach((node) => {
|
|
363
385
|
if (node.name === `npm:${node.data.packageName}`) {
|
|
364
|
-
const mappedPackage = mapPackage(rootLockFile, node.data.packageName, node.data.version);
|
|
386
|
+
const mappedPackage = mapPackage(rootLockFile, packageIndex, node.data.packageName, node.data.version);
|
|
365
387
|
remappedPackages.set(mappedPackage.path, mappedPackage);
|
|
366
388
|
visitedNodes.set(node, {
|
|
367
389
|
packagePaths: new Set([mappedPackage.path]),
|
|
@@ -375,7 +397,7 @@ function mapSnapshots(rootLockFile, graph) {
|
|
|
375
397
|
let remappedPackagesArray;
|
|
376
398
|
if (nestedNodes.size) {
|
|
377
399
|
const invertedGraph = (0, operators_1.reverse)(graph);
|
|
378
|
-
nestMappedPackages(invertedGraph, remappedPackages, nestedNodes, visitedNodes, rootLockFile);
|
|
400
|
+
nestMappedPackages(invertedGraph, remappedPackages, nestedNodes, visitedNodes, rootLockFile, packageIndex);
|
|
379
401
|
// initially we naively map package paths to topParent/../parent/child
|
|
380
402
|
// but some of those should be nested higher up the tree
|
|
381
403
|
remappedPackagesArray = elevateNestedPaths(remappedPackages);
|
|
@@ -385,14 +407,14 @@ function mapSnapshots(rootLockFile, graph) {
|
|
|
385
407
|
}
|
|
386
408
|
return remappedPackagesArray.sort((a, b) => a.path.localeCompare(b.path));
|
|
387
409
|
}
|
|
388
|
-
function mapPackage(rootLockFile, packageName, version, parentPath = '') {
|
|
410
|
+
function mapPackage(rootLockFile, packageIndex, packageName, version, parentPath = '') {
|
|
389
411
|
const lockfileVersion = rootLockFile.lockfileVersion;
|
|
390
412
|
let valueV3, valueV1;
|
|
391
413
|
if (lockfileVersion < 3) {
|
|
392
414
|
valueV1 = findMatchingPackageV1(rootLockFile.dependencies, packageName, version);
|
|
393
415
|
}
|
|
394
416
|
if (lockfileVersion > 1) {
|
|
395
|
-
valueV3 = findMatchingPackageV3(
|
|
417
|
+
valueV3 = findMatchingPackageV3(packageIndex, packageName, version);
|
|
396
418
|
}
|
|
397
419
|
return {
|
|
398
420
|
path: parentPath + `node_modules/${packageName}`,
|
|
@@ -401,7 +423,7 @@ function mapPackage(rootLockFile, packageName, version, parentPath = '') {
|
|
|
401
423
|
valueV3,
|
|
402
424
|
};
|
|
403
425
|
}
|
|
404
|
-
function nestMappedPackages(invertedGraph, result, nestedNodes, visitedNodes, rootLockFile) {
|
|
426
|
+
function nestMappedPackages(invertedGraph, result, nestedNodes, visitedNodes, rootLockFile, packageIndex) {
|
|
405
427
|
const initialSize = nestedNodes.size;
|
|
406
428
|
if (!initialSize) {
|
|
407
429
|
return;
|
|
@@ -421,7 +443,7 @@ function nestMappedPackages(invertedGraph, result, nestedNodes, visitedNodes, ro
|
|
|
421
443
|
if (visitedNodes.has(targetNode) &&
|
|
422
444
|
!visitedNodes.get(targetNode).unresolvedParents.size) {
|
|
423
445
|
visitedNodes.get(targetNode).packagePaths.forEach((path) => {
|
|
424
|
-
const mappedPackage = mapPackage(rootLockFile, node.data.packageName, node.data.version, path + '/');
|
|
446
|
+
const mappedPackage = mapPackage(rootLockFile, packageIndex, node.data.packageName, node.data.version, path + '/');
|
|
425
447
|
result.set(mappedPackage.path, mappedPackage);
|
|
426
448
|
visitedNodes.get(node).packagePaths.add(mappedPackage.path);
|
|
427
449
|
visitedNodes.get(node).unresolvedParents.delete(target);
|
|
@@ -439,7 +461,7 @@ function nestMappedPackages(invertedGraph, result, nestedNodes, visitedNodes, ro
|
|
|
439
461
|
].join('\n'));
|
|
440
462
|
}
|
|
441
463
|
else {
|
|
442
|
-
nestMappedPackages(invertedGraph, result, nestedNodes, visitedNodes, rootLockFile);
|
|
464
|
+
nestMappedPackages(invertedGraph, result, nestedNodes, visitedNodes, rootLockFile, packageIndex);
|
|
443
465
|
}
|
|
444
466
|
}
|
|
445
467
|
// sort paths by number of segments and then alphabetically
|
|
@@ -495,16 +517,38 @@ function elevateNestedPaths(remappedPackages) {
|
|
|
495
517
|
});
|
|
496
518
|
return Array.from(result.values());
|
|
497
519
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
520
|
+
// Bucket packages by their trailing "node_modules/<name>" segment so a lookup
|
|
521
|
+
// scans only that name's copies instead of every package (was O(nodes *
|
|
522
|
+
// allPackages)). Mirrors the old `key.endsWith(node_modules/<name>)` match:
|
|
523
|
+
// the name is whatever follows the last "node_modules/" in the key.
|
|
524
|
+
function buildV3Index(packages) {
|
|
525
|
+
const index = new Map();
|
|
526
|
+
if (!packages)
|
|
527
|
+
return index;
|
|
528
|
+
const marker = 'node_modules/';
|
|
529
|
+
for (const key of Object.keys(packages)) {
|
|
530
|
+
const i = key.lastIndexOf(marker);
|
|
531
|
+
if (i === -1)
|
|
532
|
+
continue; // root "" / workspace paths never matched endsWith
|
|
533
|
+
const name = key.slice(i + marker.length);
|
|
534
|
+
let bucket = index.get(name);
|
|
535
|
+
if (!bucket)
|
|
536
|
+
index.set(name, (bucket = []));
|
|
537
|
+
bucket.push(packages[key]);
|
|
538
|
+
}
|
|
539
|
+
return index;
|
|
540
|
+
}
|
|
541
|
+
function findMatchingPackageV3(packageIndex, name, version) {
|
|
542
|
+
const bucket = packageIndex.get(name);
|
|
543
|
+
if (!bucket)
|
|
544
|
+
return undefined;
|
|
545
|
+
for (const { dev, peer, ...snapshot } of bucket) {
|
|
546
|
+
if ([
|
|
547
|
+
snapshot.version,
|
|
548
|
+
snapshot.resolved,
|
|
549
|
+
`npm:${snapshot.name}@${snapshot.version}`,
|
|
550
|
+
].includes(version)) {
|
|
551
|
+
return snapshot;
|
|
508
552
|
}
|
|
509
553
|
}
|
|
510
554
|
}
|
|
@@ -12,6 +12,7 @@ const file_hasher_1 = require("../../../hasher/file-hasher");
|
|
|
12
12
|
const catalog_1 = require("../../../utils/catalog");
|
|
13
13
|
const project_graph_pruning_1 = require("./project-graph-pruning");
|
|
14
14
|
const path_1 = require("path");
|
|
15
|
+
const workspace_root_1 = require("../../../utils/workspace-root");
|
|
15
16
|
const get_workspace_packages_from_graph_1 = require("../utils/get-workspace-packages-from-graph");
|
|
16
17
|
const semver_1 = require("semver");
|
|
17
18
|
const WORKSPACE_DEP_TYPES = [
|
|
@@ -133,6 +134,17 @@ function findPatchHash(patchEntriesByPackage, packageName, version) {
|
|
|
133
134
|
const nameOnlyMatch = entries.find((entry) => entry.versionSpecifier === null);
|
|
134
135
|
return nameOnlyMatch?.hash;
|
|
135
136
|
}
|
|
137
|
+
// Segment-aware: `..` and `../x` escape, a directory literally named `..cache`
|
|
138
|
+
// does not. Absolute targets are resolved against the workspace root.
|
|
139
|
+
function linkTargetEscapesWorkspace(importerPath, depVersion) {
|
|
140
|
+
const rawTarget = depVersion.slice('link:'.length);
|
|
141
|
+
if (path_1.posix.isAbsolute(rawTarget) || (0, path_1.isAbsolute)(rawTarget)) {
|
|
142
|
+
const rel = (0, path_1.relative)(workspace_root_1.workspaceRoot, rawTarget);
|
|
143
|
+
return rel === '..' || rel.startsWith(`..${path_1.sep}`) || (0, path_1.isAbsolute)(rel);
|
|
144
|
+
}
|
|
145
|
+
const combined = path_1.posix.normalize(path_1.posix.join(importerPath === '.' ? '' : importerPath, rawTarget));
|
|
146
|
+
return combined === '..' || combined.startsWith('../');
|
|
147
|
+
}
|
|
136
148
|
function getNodes(data, isV5) {
|
|
137
149
|
cachedInvertedRecords.clear();
|
|
138
150
|
const keyMap = new Map();
|
|
@@ -142,7 +154,12 @@ function getNodes(data, isV5) {
|
|
|
142
154
|
if (data.patchedDependencies) {
|
|
143
155
|
for (const specifier of Object.keys(data.patchedDependencies)) {
|
|
144
156
|
const patchInfo = data.patchedDependencies[specifier];
|
|
145
|
-
|
|
157
|
+
const patchHash = typeof patchInfo === 'string'
|
|
158
|
+
? patchInfo
|
|
159
|
+
: patchInfo && typeof patchInfo === 'object' && 'hash' in patchInfo
|
|
160
|
+
? patchInfo.hash
|
|
161
|
+
: undefined;
|
|
162
|
+
if (patchHash) {
|
|
146
163
|
const packageName = extractNameFromKey(specifier, false);
|
|
147
164
|
const versionSpecifier = getVersion(specifier, packageName) || null;
|
|
148
165
|
if (!patchEntriesByPackage.has(packageName)) {
|
|
@@ -150,7 +167,7 @@ function getNodes(data, isV5) {
|
|
|
150
167
|
}
|
|
151
168
|
patchEntriesByPackage.get(packageName).push({
|
|
152
169
|
versionSpecifier,
|
|
153
|
-
hash:
|
|
170
|
+
hash: patchHash,
|
|
154
171
|
});
|
|
155
172
|
}
|
|
156
173
|
}
|
|
@@ -178,7 +195,8 @@ function getNodes(data, isV5) {
|
|
|
178
195
|
}
|
|
179
196
|
}
|
|
180
197
|
const packageNames = new Set();
|
|
181
|
-
for
|
|
198
|
+
// pnpm omits the packages block for workspace-only lockfiles (no external deps)
|
|
199
|
+
for (const [key, snapshot] of Object.entries(data.packages ?? {})) {
|
|
182
200
|
let packageNameObj;
|
|
183
201
|
const originalPackageName = extractNameFromKey(key, isV5);
|
|
184
202
|
if (!originalPackageName) {
|
|
@@ -339,6 +357,54 @@ function getNodes(data, isV5) {
|
|
|
339
357
|
results[node.name] = node;
|
|
340
358
|
});
|
|
341
359
|
}
|
|
360
|
+
// `link:` dependencies pointing outside the workspace never appear in the
|
|
361
|
+
// packages section, so nothing above minted a node for them — and a task
|
|
362
|
+
// input naming one via `externalDependencies` fails hashing with "could not
|
|
363
|
+
// be found". Mint a node under the bare `npm:<name>`, hashed on the link
|
|
364
|
+
// path: the hash changes only when the path does, not when the linked
|
|
365
|
+
// content does, which is how linked dependencies behave everywhere else in
|
|
366
|
+
// Nx. Links that resolve inside the workspace are workspace projects, not
|
|
367
|
+
// externals, and are skipped.
|
|
368
|
+
//
|
|
369
|
+
// One node per package name, chosen deterministically by visiting importers
|
|
370
|
+
// root-first, then lexicographically. An `externalDependencies` input names
|
|
371
|
+
// a package, not a link target, so it could not distinguish two linked
|
|
372
|
+
// targets anyway.
|
|
373
|
+
const importerPaths = Object.keys(data.importers ?? {}).sort((a, b) => a === '.' ? -1 : b === '.' ? 1 : a.localeCompare(b));
|
|
374
|
+
for (const importerPath of importerPaths) {
|
|
375
|
+
const importer = data.importers[importerPath];
|
|
376
|
+
for (const depType of [
|
|
377
|
+
'dependencies',
|
|
378
|
+
'devDependencies',
|
|
379
|
+
'optionalDependencies',
|
|
380
|
+
]) {
|
|
381
|
+
const deps = importer[depType];
|
|
382
|
+
if (!deps) {
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
for (const [depName, depVersion] of Object.entries(deps)) {
|
|
386
|
+
if (typeof depVersion !== 'string' || !depVersion.startsWith('link:')) {
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
if (!linkTargetEscapesWorkspace(importerPath, depVersion)) {
|
|
390
|
+
continue; // inside the workspace -> a workspace project
|
|
391
|
+
}
|
|
392
|
+
const bareName = `npm:${depName}`;
|
|
393
|
+
if (results[bareName]) {
|
|
394
|
+
continue; // a hoisted registry version or an earlier link wins
|
|
395
|
+
}
|
|
396
|
+
results[bareName] = {
|
|
397
|
+
type: 'npm',
|
|
398
|
+
name: bareName,
|
|
399
|
+
data: {
|
|
400
|
+
version: depVersion,
|
|
401
|
+
packageName: depName,
|
|
402
|
+
hash: (0, file_hasher_1.hashArray)([depName, depVersion]),
|
|
403
|
+
},
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
342
408
|
return { nodes: results, keyMap };
|
|
343
409
|
}
|
|
344
410
|
function getHoistedVersion(packageName, isV5, hoistedKeysByPackage) {
|
|
@@ -359,7 +425,8 @@ function getHoistedVersion(packageName, isV5, hoistedKeysByPackage) {
|
|
|
359
425
|
}
|
|
360
426
|
function getDependencies(data, keyMap, isV5, ctx) {
|
|
361
427
|
const results = [];
|
|
362
|
-
|
|
428
|
+
// pnpm omits the packages block for workspace-only lockfiles (no external deps)
|
|
429
|
+
Object.keys(data.packages ?? {}).forEach((key) => {
|
|
363
430
|
const snapshot = data.packages[key];
|
|
364
431
|
const nodes = keyMap.get(key);
|
|
365
432
|
nodes.forEach((node) => {
|
|
@@ -391,9 +458,12 @@ function parseBaseVersion(rawVersion, isV5) {
|
|
|
391
458
|
}
|
|
392
459
|
function stringifyPnpmLockfile(graph, rootLockFileContent, packageJson, workspaceRoot) {
|
|
393
460
|
const data = (0, pnpm_normalizer_1.parseAndNormalizePnpmLockfile)(rootLockFileContent);
|
|
394
|
-
const { lockfileVersion,
|
|
395
|
-
|
|
396
|
-
const
|
|
461
|
+
const { lockfileVersion, importers } = data;
|
|
462
|
+
// pnpm omits the packages block for workspace-only lockfiles (no external deps)
|
|
463
|
+
const packages = data.packages ?? {};
|
|
464
|
+
const packageIndex = indexPackagesByName(packages, +lockfileVersion);
|
|
465
|
+
const { snapshot: rootSnapshot, importers: requiredImporters } = mapRootSnapshot(packageJson, importers, packages, packageIndex, graph, +lockfileVersion, workspaceRoot);
|
|
466
|
+
const snapshots = mapSnapshots(packages, packageIndex, graph.externalNodes, +lockfileVersion);
|
|
397
467
|
const workspaceModules = (0, get_workspace_packages_from_graph_1.getWorkspacePackagesFromGraph)(graph);
|
|
398
468
|
// Walk transitive workspace deps so every package copy-workspace-modules
|
|
399
469
|
// writes to disk has a matching importer block. Without this, pnpm errors
|
|
@@ -455,12 +525,10 @@ function stringifyPnpmLockfile(graph, rootLockFileContent, packageJson, workspac
|
|
|
455
525
|
};
|
|
456
526
|
return (0, pnpm_normalizer_1.stringifyToPnpmYaml)(output);
|
|
457
527
|
}
|
|
458
|
-
function mapSnapshots(packages, nodes, lockfileVersion) {
|
|
528
|
+
function mapSnapshots(packages, packageIndex, nodes, lockfileVersion) {
|
|
459
529
|
const result = {};
|
|
460
530
|
Object.values(nodes).forEach((node) => {
|
|
461
|
-
const matchedKeys = findOriginalKeys(packages, node, lockfileVersion, {
|
|
462
|
-
returnFullKey: true,
|
|
463
|
-
});
|
|
531
|
+
const matchedKeys = findOriginalKeys(packages, packageIndex, node, lockfileVersion, { returnFullKey: true });
|
|
464
532
|
// the package manager doesn't check for types of dependencies
|
|
465
533
|
// so we can safely set all to prod
|
|
466
534
|
matchedKeys.forEach(([key, snapshot]) => {
|
|
@@ -500,10 +568,35 @@ function remapDependencies(snapshot) {
|
|
|
500
568
|
}
|
|
501
569
|
});
|
|
502
570
|
}
|
|
503
|
-
|
|
504
|
-
|
|
571
|
+
// Bucket package keys by their package name so a node only scans its own name's
|
|
572
|
+
// versions instead of every key (was O(nodes * allPackages)). v5 is excluded
|
|
573
|
+
// below and keeps the full scan: its standard keys use a "/" separator while
|
|
574
|
+
// tarball keys use "@", so a single name index would misfile v5 tarballs.
|
|
575
|
+
function indexPackagesByName(packages, lockfileVersion) {
|
|
576
|
+
const isV5 = lockfileVersion < 6;
|
|
577
|
+
const index = new Map();
|
|
505
578
|
for (const key of Object.keys(packages)) {
|
|
506
|
-
const
|
|
579
|
+
const name = extractNameFromKey(key, isV5);
|
|
580
|
+
let bucket = index.get(name);
|
|
581
|
+
if (!bucket)
|
|
582
|
+
index.set(name, (bucket = []));
|
|
583
|
+
bucket.push([key, packages[key]]);
|
|
584
|
+
}
|
|
585
|
+
return index;
|
|
586
|
+
}
|
|
587
|
+
// npm alias version is "npm:<name>@<ver>"; extract <name> the same way
|
|
588
|
+
// versionIsAlias does so the index lookup matches the alias branch below.
|
|
589
|
+
function aliasTargetName(version) {
|
|
590
|
+
return version.slice('npm:'.length, version.indexOf('@', 'npm:'.length + 1));
|
|
591
|
+
}
|
|
592
|
+
const NO_CANDIDATES = [];
|
|
593
|
+
function findOriginalKeys(packages, packageIndex, node, lockfileVersion, { returnFullKey } = {}) {
|
|
594
|
+
const { data: { packageName, version }, } = node;
|
|
595
|
+
const candidates = lockfileVersion >= 6
|
|
596
|
+
? (packageIndex.get(version.startsWith('npm:') ? aliasTargetName(version) : packageName) ?? NO_CANDIDATES)
|
|
597
|
+
: Object.entries(packages);
|
|
598
|
+
const matchedKeys = [];
|
|
599
|
+
for (const [key, snapshot] of candidates) {
|
|
507
600
|
// tarball package
|
|
508
601
|
if (key.startsWith(`${packageName}@${version}`) &&
|
|
509
602
|
snapshot.resolution?.['tarball']) {
|
|
@@ -562,10 +655,11 @@ function versionIsAlias(key, versionExpr, lockfileVersion) {
|
|
|
562
655
|
? key.startsWith(`${packageName}/${version}`)
|
|
563
656
|
: key.startsWith(`${packageName}@${version}`);
|
|
564
657
|
}
|
|
565
|
-
function mapRootSnapshot(packageJson, rootImporters, packages, graph, lockfileVersion, workspaceRoot) {
|
|
658
|
+
function mapRootSnapshot(packageJson, rootImporters, packages, packageIndex, graph, lockfileVersion, workspaceRoot) {
|
|
566
659
|
const workspaceModules = (0, get_workspace_packages_from_graph_1.getWorkspacePackagesFromGraph)(graph);
|
|
567
660
|
const snapshot = { specifiers: {} };
|
|
568
661
|
const importers = {};
|
|
662
|
+
const manager = (0, catalog_1.getCatalogManager)(workspaceRoot);
|
|
569
663
|
[
|
|
570
664
|
'dependencies',
|
|
571
665
|
'optionalDependencies',
|
|
@@ -575,7 +669,6 @@ function mapRootSnapshot(packageJson, rootImporters, packages, graph, lockfileVe
|
|
|
575
669
|
if (packageJson[depType]) {
|
|
576
670
|
Object.keys(packageJson[depType]).forEach((packageName) => {
|
|
577
671
|
let version = packageJson[depType][packageName];
|
|
578
|
-
const manager = (0, catalog_1.getCatalogManager)(workspaceRoot);
|
|
579
672
|
if (manager?.isCatalogReference(version)) {
|
|
580
673
|
version = manager.resolveCatalogReference(workspaceRoot, packageName, version);
|
|
581
674
|
if (!version) {
|
|
@@ -612,7 +705,7 @@ function mapRootSnapshot(packageJson, rootImporters, packages, graph, lockfileVe
|
|
|
612
705
|
// peer dependencies are mapped to dependencies
|
|
613
706
|
let section = depType === 'peerDependencies' ? 'dependencies' : depType;
|
|
614
707
|
snapshot[section] = snapshot[section] || {};
|
|
615
|
-
snapshot[section][packageName] = findOriginalKeys(packages, node, lockfileVersion)[0][0];
|
|
708
|
+
snapshot[section][packageName] = findOriginalKeys(packages, packageIndex, node, lockfileVersion)[0][0];
|
|
616
709
|
}
|
|
617
710
|
});
|
|
618
711
|
}
|
|
@@ -38,9 +38,9 @@ function normalizeDependencies(packageJson, graph, workspacePackages, workspaceR
|
|
|
38
38
|
...optionalDependencies,
|
|
39
39
|
...peerDependencies,
|
|
40
40
|
};
|
|
41
|
+
const manager = (0, catalog_1.getCatalogManager)(workspaceRootPath);
|
|
41
42
|
Object.entries(combinedDependencies).forEach(([packageName, versionRange]) => {
|
|
42
43
|
let resolvedVersionRange = versionRange;
|
|
43
|
-
const manager = (0, catalog_1.getCatalogManager)(workspaceRootPath);
|
|
44
44
|
if (manager?.isCatalogReference(versionRange)) {
|
|
45
45
|
resolvedVersionRange = manager.resolveCatalogReference(workspaceRootPath, packageName, versionRange);
|
|
46
46
|
if (!resolvedVersionRange) {
|
|
@@ -177,17 +177,23 @@ function switchNodeToHoisted(node, builder, invBuilder) {
|
|
|
177
177
|
const sources = Object.keys(builder.graph.dependencies).filter((name) => builder.graph.dependencies[name].some((d) => d.target === previousName));
|
|
178
178
|
builder.removeNode(node.name);
|
|
179
179
|
invBuilder.removeNode(node.name);
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
180
|
+
// Re-add under the hoisted name as a new object. The node is shared by
|
|
181
|
+
// reference with the caller's graph, so renaming it in place would leave that
|
|
182
|
+
// graph with a node keyed `npm:<pkg>@<version>` but named `npm:<pkg>`, and
|
|
183
|
+
// every later prune of the same graph would fail to resolve the edge.
|
|
184
|
+
const hoistedNode = {
|
|
185
|
+
...node,
|
|
186
|
+
name: `npm:${node.data.packageName}`,
|
|
187
|
+
};
|
|
188
|
+
builder.addExternalNode(hoistedNode);
|
|
189
|
+
invBuilder.addExternalNode(hoistedNode);
|
|
184
190
|
targets.forEach((target) => {
|
|
185
|
-
builder.addStaticDependency(
|
|
186
|
-
invBuilder.addStaticDependency(target,
|
|
191
|
+
builder.addStaticDependency(hoistedNode.name, target);
|
|
192
|
+
invBuilder.addStaticDependency(target, hoistedNode.name);
|
|
187
193
|
});
|
|
188
194
|
sources.forEach((source) => {
|
|
189
|
-
builder.addStaticDependency(source,
|
|
190
|
-
invBuilder.addStaticDependency(
|
|
195
|
+
builder.addStaticDependency(source, hoistedNode.name);
|
|
196
|
+
invBuilder.addStaticDependency(hoistedNode.name, source);
|
|
191
197
|
});
|
|
192
198
|
}
|
|
193
199
|
// BFS to find the shortest path to a dependency specified in package.json
|
|
@@ -54,6 +54,9 @@ const YAML_DOCUMENT_SEPARATOR = '\n---\n';
|
|
|
54
54
|
// always read the workspace document.
|
|
55
55
|
// https://github.com/pnpm/pnpm/blob/main/lockfile/fs/src/yamlDocuments.ts
|
|
56
56
|
function extractMainLockfileDocument(content) {
|
|
57
|
+
// Lockfiles written on Windows may use CRLF line endings, which would never
|
|
58
|
+
// match the LF-only document markers.
|
|
59
|
+
content = content.replace(/\r\n/g, '\n');
|
|
57
60
|
if (!content.startsWith(YAML_DOCUMENT_START)) {
|
|
58
61
|
return content;
|
|
59
62
|
}
|
|
@@ -314,9 +314,10 @@ function mapSnapshots(rootDependencies, nodes, packageJson, workspaceModules, is
|
|
|
314
314
|
};
|
|
315
315
|
// yarn classic splits keys when parsing so we need to stich them back together
|
|
316
316
|
const groupedDependencies = groupDependencies(rootDependencies, isBerry);
|
|
317
|
+
const keyIndex = buildYarnKeyIndex(groupedDependencies);
|
|
317
318
|
// collect snapshots and their matching keys
|
|
318
319
|
Object.values(nodes).forEach((node) => {
|
|
319
|
-
const foundOriginalKeys = findOriginalKeys(groupedDependencies, node, workspaceModules);
|
|
320
|
+
const foundOriginalKeys = findOriginalKeys(groupedDependencies, keyIndex, node, workspaceModules);
|
|
320
321
|
if (!foundOriginalKeys) {
|
|
321
322
|
throw new Error(`Original key(s) not found for "${node.data.packageName}@${node.data.version}" while pruning yarn.lock.`);
|
|
322
323
|
}
|
|
@@ -349,7 +350,7 @@ function mapSnapshots(rootDependencies, nodes, packageJson, workspaceModules, is
|
|
|
349
350
|
}
|
|
350
351
|
if (isBerry) {
|
|
351
352
|
// look for patched versions
|
|
352
|
-
const patch = findPatchedKeys(groupedDependencies, node, resolutions[node.data.packageName]);
|
|
353
|
+
const patch = findPatchedKeys(groupedDependencies, keyIndex, node, resolutions[node.data.packageName]);
|
|
353
354
|
if (patch) {
|
|
354
355
|
const [matchedKeys, snapshot] = patch;
|
|
355
356
|
snapshotMap.set(snapshot, new Set(matchedKeys));
|
|
@@ -424,8 +425,34 @@ function isClassicAlias(node, keys) {
|
|
|
424
425
|
return (node.data.version.startsWith('npm:') &&
|
|
425
426
|
keys.some((k) => k === `${node.data.packageName}@${node.data.version}`));
|
|
426
427
|
}
|
|
427
|
-
|
|
428
|
+
// Bucket grouped-dependency key expressions by the package names they contain
|
|
429
|
+
// so a node scans only its own name's entries (was O(nodes * allEntries)). A
|
|
430
|
+
// key like "foo@npm:1.0" indexes under "foo"; the inner match checks below are
|
|
431
|
+
// unchanged, so candidates are exactly the entries the old full scan would not
|
|
432
|
+
// have skipped and the result is identical.
|
|
433
|
+
function extractYarnKeyName(key) {
|
|
434
|
+
const at = key.indexOf('@', 1);
|
|
435
|
+
return at === -1 ? key : key.slice(0, at);
|
|
436
|
+
}
|
|
437
|
+
function buildYarnKeyIndex(dependencies) {
|
|
438
|
+
const index = new Map();
|
|
428
439
|
for (const keyExpr of Object.keys(dependencies)) {
|
|
440
|
+
const seen = new Set();
|
|
441
|
+
for (const k of keyExpr.split(', ')) {
|
|
442
|
+
const name = extractYarnKeyName(k);
|
|
443
|
+
if (seen.has(name))
|
|
444
|
+
continue;
|
|
445
|
+
seen.add(name);
|
|
446
|
+
let bucket = index.get(name);
|
|
447
|
+
if (!bucket)
|
|
448
|
+
index.set(name, (bucket = []));
|
|
449
|
+
bucket.push(keyExpr);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return index;
|
|
453
|
+
}
|
|
454
|
+
function findOriginalKeys(dependencies, keyIndex, node, workspaceModules) {
|
|
455
|
+
for (const keyExpr of keyIndex.get(node.data.packageName) ?? []) {
|
|
429
456
|
const snapshot = dependencies[keyExpr];
|
|
430
457
|
const keys = keyExpr.split(', ');
|
|
431
458
|
if (!keys.some((k) => k.startsWith(`${node.data.packageName}@`))) {
|
|
@@ -450,8 +477,8 @@ function findOriginalKeys(dependencies, node, workspaceModules) {
|
|
|
450
477
|
}
|
|
451
478
|
}
|
|
452
479
|
}
|
|
453
|
-
function findPatchedKeys(dependencies, node, resolutionVersion) {
|
|
454
|
-
for (const keyExpr of
|
|
480
|
+
function findPatchedKeys(dependencies, keyIndex, node, resolutionVersion) {
|
|
481
|
+
for (const keyExpr of keyIndex.get(node.data.packageName) ?? []) {
|
|
455
482
|
const snapshot = dependencies[keyExpr];
|
|
456
483
|
const keys = keyExpr.split(', ');
|
|
457
484
|
if (!keys[0].startsWith(`${node.data.packageName}@patch:`)) {
|
|
@@ -13,6 +13,7 @@ const getTouchedNpmPackages = (touchedFiles, _, nxJson, packageJson, projectGrap
|
|
|
13
13
|
let touched = [];
|
|
14
14
|
const changes = packageJsonChange.getChanges();
|
|
15
15
|
const npmPackages = Object.values(projectGraph.externalNodes);
|
|
16
|
+
let packagesByName;
|
|
16
17
|
const missingTouchedNpmPackages = [];
|
|
17
18
|
for (const c of changes) {
|
|
18
19
|
if ((0, json_diff_1.isJsonChange)(c) &&
|
|
@@ -53,26 +54,20 @@ const getTouchedNpmPackages = (touchedFiles, _, nxJson, packageJson, projectGrap
|
|
|
53
54
|
(c.path[0] === 'overrides' ||
|
|
54
55
|
c.path[0] === 'resolutions' ||
|
|
55
56
|
(c.path[0] === 'pnpm' && c.path[1] === 'overrides'))) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if ('packageName' in npmPackage.data &&
|
|
66
|
-
globalPackages.has(npmPackage.data.packageName)) {
|
|
67
|
-
return Object.keys(projectGraph.nodes);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
// If the package isn't found in external nodes, it might affect all projects
|
|
72
|
-
// since overrides can affect transitive dependencies
|
|
73
|
-
return Object.keys(projectGraph.nodes);
|
|
74
|
-
}
|
|
57
|
+
const packageSelector = getPackageSelector(c);
|
|
58
|
+
if (!packageSelector)
|
|
59
|
+
continue;
|
|
60
|
+
packagesByName ??= groupPackagesByName(npmPackages);
|
|
61
|
+
const matchingNpmPackages = findPackagesForSelector(packageSelector, packagesByName, c.path[0] === 'pnpm');
|
|
62
|
+
// An unresolved selector can still target a transitive dependency,
|
|
63
|
+
// so fall back to marking every project affected.
|
|
64
|
+
if (!matchingNpmPackages.length) {
|
|
65
|
+
return Object.keys(projectGraph.nodes);
|
|
75
66
|
}
|
|
67
|
+
if (matchingNpmPackages.some((pkg) => globalPackages.has(pkg.data.packageName))) {
|
|
68
|
+
return Object.keys(projectGraph.nodes);
|
|
69
|
+
}
|
|
70
|
+
touched.push(...matchingNpmPackages.map((pkg) => pkg.name));
|
|
76
71
|
}
|
|
77
72
|
else if ((0, file_utils_1.isWholeFileChange)(c)) {
|
|
78
73
|
// Whole file was touched, so all npm packages are touched.
|
|
@@ -83,9 +78,45 @@ const getTouchedNpmPackages = (touchedFiles, _, nxJson, packageJson, projectGrap
|
|
|
83
78
|
if (missingTouchedNpmPackages.length) {
|
|
84
79
|
logger_1.logger.warn(`The affected projects might have not been identified properly. The package(s) ${missingTouchedNpmPackages.join(', ')} were not found. Please open an issue in GitHub including the package.json file.`);
|
|
85
80
|
}
|
|
86
|
-
return touched;
|
|
81
|
+
return [...new Set(touched)];
|
|
87
82
|
};
|
|
88
83
|
exports.getTouchedNpmPackages = getTouchedNpmPackages;
|
|
84
|
+
function getPackageSelector(change) {
|
|
85
|
+
if (typeof change.value.lhs !== 'string' &&
|
|
86
|
+
typeof change.value.rhs !== 'string') {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const selectorIndex = change.path[0] === 'pnpm' ? 2 : change.path.length - 1;
|
|
90
|
+
const selector = change.path[selectorIndex];
|
|
91
|
+
return selector === '.' ? change.path[selectorIndex - 1] : selector;
|
|
92
|
+
}
|
|
93
|
+
function groupPackagesByName(npmPackages) {
|
|
94
|
+
const packagesByName = new Map();
|
|
95
|
+
for (const pkg of npmPackages) {
|
|
96
|
+
const packageName = pkg.data.packageName;
|
|
97
|
+
if (!packageName)
|
|
98
|
+
continue;
|
|
99
|
+
const packages = packagesByName.get(packageName);
|
|
100
|
+
if (packages) {
|
|
101
|
+
packages.push(pkg);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
packagesByName.set(packageName, [pkg]);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return packagesByName;
|
|
108
|
+
}
|
|
109
|
+
function findPackagesForSelector(selector, packagesByName, isPnpmOverride) {
|
|
110
|
+
if (isPnpmOverride) {
|
|
111
|
+
// Pnpm does not treat `>` as a parent delimiter when it starts a range.
|
|
112
|
+
const parentDelimiterIndex = selector.search(/[^ |@]>/);
|
|
113
|
+
if (parentDelimiterIndex !== -1) {
|
|
114
|
+
selector = selector.slice(parentDelimiterIndex + 2);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const packageName = selector.match(/(?:^|\/)(@[^/@>\s]+\/[^/@>\s]+|[^/@>\s]+)(?:@[^/]*)?$/)?.[1];
|
|
118
|
+
return packageName ? (packagesByName.get(packageName) ?? []) : [];
|
|
119
|
+
}
|
|
89
120
|
function getGlobalPackages(plugins) {
|
|
90
121
|
return (plugins ?? [])
|
|
91
122
|
.map((p) => (0, get_package_name_from_import_path_1.getPackageNameFromImportPath)(typeof p === 'string' ? p : p.plugin))
|