nx 23.1.0 → 23.1.1

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.
Files changed (126) hide show
  1. package/dist/bin/nx.js +2 -1
  2. package/dist/src/ai/configure-ai-agents-disclaimer.d.ts +5 -0
  3. package/dist/src/ai/configure-ai-agents-disclaimer.js +31 -0
  4. package/dist/src/analytics/analytics.d.ts +6 -0
  5. package/dist/src/analytics/analytics.js +16 -8
  6. package/dist/src/analytics/index.d.ts +1 -1
  7. package/dist/src/analytics/index.js +2 -1
  8. package/dist/src/command-line/add/add.js +8 -1
  9. package/dist/src/command-line/graph/graph.d.ts +1 -0
  10. package/dist/src/command-line/graph/graph.js +15 -6
  11. package/dist/src/command-line/import/import.js +1 -1
  12. package/dist/src/command-line/init/ai-agent-prompts.d.ts +1 -1
  13. package/dist/src/command-line/init/ai-agent-prompts.js +7 -6
  14. package/dist/src/command-line/init/command-object.js +10 -2
  15. package/dist/src/command-line/init/configure-plugins.js +3 -2
  16. package/dist/src/command-line/init/implementation/add-nx-to-monorepo.js +4 -2
  17. package/dist/src/command-line/init/implementation/add-nx-to-nest.js +4 -3
  18. package/dist/src/command-line/init/implementation/add-nx-to-npm-repo.js +4 -3
  19. package/dist/src/command-line/init/implementation/add-nx-to-turborepo.js +4 -3
  20. package/dist/src/command-line/init/implementation/angular/index.js +4 -2
  21. package/dist/src/command-line/init/implementation/angular/integrated-workspace.js +2 -5
  22. package/dist/src/command-line/init/implementation/angular/legacy-angular-versions.js +3 -0
  23. package/dist/src/command-line/init/implementation/dot-nx/add-nx-scripts.js +4 -1
  24. package/dist/src/command-line/init/implementation/utils.d.ts +3 -3
  25. package/dist/src/command-line/init/implementation/utils.js +22 -3
  26. package/dist/src/command-line/migrate/execute-migration.d.ts +72 -0
  27. package/dist/src/command-line/migrate/execute-migration.js +389 -0
  28. package/dist/src/command-line/migrate/migrate-ui-api.js +17 -12
  29. package/dist/src/command-line/migrate/migrate.d.ts +1 -45
  30. package/dist/src/command-line/migrate/migrate.js +33 -376
  31. package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +1 -1
  32. package/dist/src/command-line/nx-cloud/connect/view-logs.js +2 -0
  33. package/dist/src/command-line/release/utils/git.js +6 -2
  34. package/dist/src/command-line/release/utils/release-graph.d.ts +2 -2
  35. package/dist/src/command-line/release/utils/remote-release-clients/github.js +1 -1
  36. package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +1 -1
  37. package/dist/src/command-line/release/utils/shared.d.ts +2 -2
  38. package/dist/src/command-line/show/show-target/info.js +11 -2
  39. package/dist/src/command-line/show/show-target/inputs.js +50 -153
  40. package/dist/src/command-line/show/show-target/outputs.js +48 -173
  41. package/dist/src/command-line/show/show-target/utils.d.ts +28 -0
  42. package/dist/src/command-line/show/show-target/utils.js +83 -3
  43. package/dist/src/command-line/yargs-utils/shared-options.d.ts +1 -1
  44. package/dist/src/commands-runner/create-command-graph.js +5 -0
  45. package/dist/src/config/nx-json.d.ts +5 -3
  46. package/dist/src/core/graph/main.js +1 -1
  47. package/dist/src/daemon/client/client.d.ts +0 -2
  48. package/dist/src/daemon/client/client.js +17 -19
  49. package/dist/src/daemon/server/handle-tasks-execution-hooks.d.ts +1 -1
  50. package/dist/src/daemon/server/server.js +7 -4
  51. package/dist/src/devkit-internals.d.ts +3 -0
  52. package/dist/src/devkit-internals.js +10 -1
  53. package/dist/src/generators/utils/nx-json.js +13 -3
  54. package/dist/src/hasher/check-task-files.d.ts +86 -0
  55. package/dist/src/hasher/check-task-files.js +378 -0
  56. package/dist/src/native/index.d.ts +21 -2
  57. package/dist/src/native/native-bindings.js +2 -0
  58. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  59. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  60. package/dist/src/plugins/js/lock-file/npm-parser.js +71 -27
  61. package/dist/src/plugins/js/lock-file/pnpm-parser.js +50 -17
  62. package/dist/src/plugins/js/lock-file/project-graph-pruning.js +1 -1
  63. package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.js +3 -0
  64. package/dist/src/plugins/js/lock-file/yarn-parser.js +32 -5
  65. package/dist/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +12 -4
  66. package/dist/src/plugins/js/utils/register.d.ts +18 -0
  67. package/dist/src/plugins/js/utils/register.js +81 -0
  68. package/dist/src/plugins/js/utils/typescript.d.ts +9 -0
  69. package/dist/src/plugins/js/utils/typescript.js +15 -0
  70. package/dist/src/project-graph/build-project-graph.js +7 -1
  71. package/dist/src/project-graph/error-types.js +41 -1
  72. package/dist/src/project-graph/file-utils.js +6 -1
  73. package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +4 -0
  74. package/dist/src/project-graph/plugins/loaded-nx-plugin.js +1 -0
  75. package/dist/src/project-graph/plugins/transpiler.js +4 -0
  76. package/dist/src/project-graph/project-graph.js +3 -0
  77. package/dist/src/project-graph/utils/project-configuration/name-substitution-manager.d.ts +10 -12
  78. package/dist/src/project-graph/utils/project-configuration/name-substitution-manager.js +93 -63
  79. package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.d.ts +5 -8
  80. package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.js +11 -14
  81. package/dist/src/project-graph/utils/project-configuration/source-maps.d.ts +12 -5
  82. package/dist/src/project-graph/utils/project-configuration/source-maps.js +21 -7
  83. package/dist/src/project-graph/utils/project-configuration/target-defaults.d.ts +1 -1
  84. package/dist/src/project-graph/utils/project-configuration/target-defaults.js +26 -27
  85. package/dist/src/project-graph/utils/project-configuration/target-merging.js +50 -8
  86. package/dist/src/project-graph/utils/project-configuration/target-normalization.js +16 -3
  87. package/dist/src/project-graph/utils/project-configuration/utils.d.ts +15 -0
  88. package/dist/src/project-graph/utils/project-configuration/utils.js +42 -6
  89. package/dist/src/project-graph/utils/project-configuration-utils.d.ts +15 -12
  90. package/dist/src/project-graph/utils/project-configuration-utils.js +79 -101
  91. package/dist/src/tasks-runner/life-cycles/performance-analysis.d.ts +11 -4
  92. package/dist/src/tasks-runner/life-cycles/performance-analysis.js +15 -6
  93. package/dist/src/tasks-runner/life-cycles/performance-life-cycle.js +5 -4
  94. package/dist/src/tasks-runner/life-cycles/performance-report.d.ts +18 -8
  95. package/dist/src/tasks-runner/life-cycles/performance-report.js +66 -29
  96. package/dist/src/tasks-runner/run-command.js +4 -2
  97. package/dist/src/tasks-runner/task-env.d.ts +9 -0
  98. package/dist/src/tasks-runner/task-env.js +26 -2
  99. package/dist/src/tasks-runner/task-orchestrator.d.ts +4 -1
  100. package/dist/src/tasks-runner/task-orchestrator.js +23 -10
  101. package/dist/src/utils/acknowledge-build-scripts.d.ts +13 -0
  102. package/dist/src/utils/acknowledge-build-scripts.js +102 -0
  103. package/dist/src/utils/analytics-prompt.d.ts +0 -6
  104. package/dist/src/utils/analytics-prompt.js +0 -51
  105. package/dist/src/utils/catalog/manager-utils.d.ts +1 -2
  106. package/dist/src/utils/catalog/manager-utils.js +22 -5
  107. package/dist/src/utils/catalog/pnpm-manager.d.ts +1 -0
  108. package/dist/src/utils/catalog/pnpm-manager.js +3 -15
  109. package/dist/src/utils/catalog/yarn-manager.d.ts +1 -0
  110. package/dist/src/utils/catalog/yarn-manager.js +3 -15
  111. package/dist/src/utils/command-line-utils.js +30 -20
  112. package/dist/src/utils/git-revision.d.ts +12 -0
  113. package/dist/src/utils/git-revision.js +25 -0
  114. package/dist/src/utils/git-utils.d.ts +16 -3
  115. package/dist/src/utils/git-utils.js +146 -52
  116. package/dist/src/utils/min-release-age/behavior/pnpm.js +9 -7
  117. package/dist/src/utils/package-json.js +11 -7
  118. package/dist/src/utils/package-manager.d.ts +3 -1
  119. package/dist/src/utils/package-manager.js +28 -4
  120. package/dist/src/utils/provenance.js +12 -2
  121. package/dist/src/utils/workspace-id.d.ts +20 -0
  122. package/dist/src/utils/workspace-id.js +54 -0
  123. package/migrations.json +7 -1
  124. package/package.json +22 -17
  125. package/dist/src/migrations/update-17-3-0/nx-release-path.d.ts +0 -3
  126. package/dist/src/migrations/update-17-3-0/nx-release-path.js +0 -47
@@ -20,4 +20,13 @@ export declare function getRootTsConfigCustomConditions(root?: string): string[]
20
20
  * `migrate-development-custom-condition` (21.5).
21
21
  */
22
22
  export declare function getRootTsConfigResolveExportsConditions(root?: string): string[];
23
+ /**
24
+ * Node `--conditions <name>` CLI args for spawning a plugin worker or the daemon
25
+ * with the plugin-resolution conditions active at startup. Mirrors the set Nx
26
+ * uses to resolve the plugin entry (`getRootTsConfigResolveExportsConditions`)
27
+ * so the entry and the plugin's transitive workspace imports resolve the same
28
+ * way; Node's own resolver otherwise ignores TypeScript `customConditions` and a
29
+ * source-loaded plugin's imports land on their unbuilt `dist`.
30
+ */
31
+ export declare function getPluginResolveConditionNodeArgs(root?: string): string[];
23
32
  export declare function findNodes(node: Node, kind: SyntaxKind | SyntaxKind[], max?: number): Node[];
@@ -8,6 +8,7 @@ exports.getRootTsConfigFileName = getRootTsConfigFileName;
8
8
  exports.getRootTsConfigPath = getRootTsConfigPath;
9
9
  exports.getRootTsConfigCustomConditions = getRootTsConfigCustomConditions;
10
10
  exports.getRootTsConfigResolveExportsConditions = getRootTsConfigResolveExportsConditions;
11
+ exports.getPluginResolveConditionNodeArgs = getPluginResolveConditionNodeArgs;
11
12
  exports.findNodes = findNodes;
12
13
  const workspace_root_1 = require("../../../utils/workspace-root");
13
14
  const fs_1 = require("fs");
@@ -113,6 +114,20 @@ function getRootTsConfigResolveExportsConditions(root = workspace_root_1.workspa
113
114
  ? conditions
114
115
  : [...conditions, 'development'];
115
116
  }
117
+ /**
118
+ * Node `--conditions <name>` CLI args for spawning a plugin worker or the daemon
119
+ * with the plugin-resolution conditions active at startup. Mirrors the set Nx
120
+ * uses to resolve the plugin entry (`getRootTsConfigResolveExportsConditions`)
121
+ * so the entry and the plugin's transitive workspace imports resolve the same
122
+ * way; Node's own resolver otherwise ignores TypeScript `customConditions` and a
123
+ * source-loaded plugin's imports land on their unbuilt `dist`.
124
+ */
125
+ function getPluginResolveConditionNodeArgs(root = workspace_root_1.workspaceRoot) {
126
+ return getRootTsConfigResolveExportsConditions(root).flatMap((c) => [
127
+ '--conditions',
128
+ c,
129
+ ]);
130
+ }
116
131
  function findNodes(node, kind, max = Infinity) {
117
132
  if (!node || max == 0) {
118
133
  return [];
@@ -7,6 +7,7 @@ exports.applyProjectMetadata = applyProjectMetadata;
7
7
  const fs_1 = require("fs");
8
8
  const path_1 = require("path");
9
9
  const perf_hooks_1 = require("perf_hooks");
10
+ const analytics_1 = require("../analytics");
10
11
  const configuration_1 = require("../config/configuration");
11
12
  const file_hasher_1 = require("../hasher/file-hasher");
12
13
  const typescript_1 = require("../plugins/js/utils/typescript");
@@ -222,7 +223,12 @@ async function updateProjectGraphWithPlugins(context, initProjectGraph, plugins,
222
223
  perf_hooks_1.performance.measure(`${plugin.name}:createDependencies`, {
223
224
  start: `${plugin.name}:createDependencies - start`,
224
225
  end: `${plugin.name}:createDependencies - end`,
225
- detail: { track: true },
226
+ detail: {
227
+ track: true,
228
+ ...(analytics_1.customDimensions && {
229
+ [analytics_1.customDimensions.sampleRate]: analytics_1.PERF_SPAN_SAMPLE_RATE,
230
+ }),
231
+ },
226
232
  });
227
233
  }));
228
234
  perf_hooks_1.performance.mark('createDependencies:end');
@@ -242,9 +242,48 @@ class AggregateCreateNodesError extends Error {
242
242
  errorTuple.length === 2)) {
243
243
  throw new Error('AggregateCreateNodesError must be constructed with an array of tuples where the first element is a filename or undefined and the second element is the underlying error.');
244
244
  }
245
+ // Plugins pass through whatever value they caught, which is not
246
+ // guaranteed to be an Error. Coerce so formatting can rely on
247
+ // message and stack being present.
248
+ for (const errorTuple of errors) {
249
+ errorTuple[1] = coerceToError(errorTuple[1]);
250
+ }
245
251
  }
246
252
  }
247
253
  exports.AggregateCreateNodesError = AggregateCreateNodesError;
254
+ function coerceToError(value) {
255
+ if (value instanceof Error) {
256
+ return value;
257
+ }
258
+ let message;
259
+ let stack;
260
+ if (typeof value === 'object' && value !== null) {
261
+ const candidate = value;
262
+ if (typeof candidate.message === 'string') {
263
+ message = candidate.message;
264
+ if (typeof candidate.stack === 'string') {
265
+ stack = candidate.stack;
266
+ }
267
+ }
268
+ else {
269
+ try {
270
+ message = JSON.stringify(value);
271
+ }
272
+ catch {
273
+ // Circular structures cannot be stringified.
274
+ message = String(value);
275
+ }
276
+ }
277
+ }
278
+ else {
279
+ message = String(value);
280
+ }
281
+ const error = new Error(message);
282
+ // A synthesized stack would point at this coercion site rather than
283
+ // the original failure, so prefer the original stack or just the message.
284
+ error.stack = stack ?? message;
285
+ return error;
286
+ }
248
287
  function formatAggregateCreateNodesError(error, pluginName) {
249
288
  const errorCount = error.errors.length > 1 ? `${error.errors.length} errors` : 'An error';
250
289
  const pluginLocation = error.pluginIndex
@@ -270,7 +309,8 @@ function formatAggregateCreateNodesError(error, pluginName) {
270
309
  }
271
310
  for (const e of errors) {
272
311
  const messageLines = e.message.split('\n');
273
- const stackLines = e.stack.split('\n');
312
+ // Errors deserialized from a plugin worker may arrive without a stack.
313
+ const stackLines = (e.stack ?? e.message).split('\n');
274
314
  if (file) {
275
315
  errorBodyLines.push(...messageLines.map((line) => ` ${line}`));
276
316
  errorStackLines.push(...stackLines.map((line) => ` ${line}`));
@@ -12,6 +12,7 @@ const fs_1 = require("fs");
12
12
  const os_1 = require("os");
13
13
  const path_1 = require("path");
14
14
  const fileutils_1 = require("../utils/fileutils");
15
+ const git_revision_1 = require("../utils/git-revision");
15
16
  const ignore_1 = require("../utils/ignore");
16
17
  const json_diff_1 = require("../utils/json-diff");
17
18
  const workspace_root_1 = require("../utils/workspace-root");
@@ -116,11 +117,14 @@ function readLockFileAtRevision(file, lockFileName, revision, readFileAtRevision
116
117
  }
117
118
  exports.TEN_MEGABYTES = 1024 * 10000;
118
119
  function defaultReadFileAtRevision(file, revision) {
120
+ if (revision) {
121
+ (0, git_revision_1.assertValidGitRevision)(revision);
122
+ }
119
123
  try {
120
124
  const filePathInGitRepository = getFilePathInGitRepository(file);
121
125
  return !revision
122
126
  ? (0, fs_1.readFileSync)(file, 'utf-8')
123
- : (0, child_process_1.execSync)(`git show ${revision}:${filePathInGitRepository}`, {
127
+ : (0, child_process_1.execFileSync)('git', ['show', `${revision}:${filePathInGitRepository}`], {
124
128
  maxBuffer: exports.TEN_MEGABYTES,
125
129
  stdio: ['pipe', 'pipe', 'ignore'],
126
130
  windowsHide: true,
@@ -140,6 +144,7 @@ function defaultReadBunLockFileAtRevision(file, revision) {
140
144
  windowsHide: true,
141
145
  }).trim();
142
146
  }
147
+ (0, git_revision_1.assertValidGitRevision)(revision);
143
148
  const filePathInGitRepository = getFilePathInGitRepository(file);
144
149
  const tempDirectory = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'nx-bun-lock-'));
145
150
  const tempLockfilePath = (0, path_1.join)(tempDirectory, 'bun.lockb');
@@ -7,6 +7,7 @@ const path = require("path");
7
7
  const logger_1 = require("../../../daemon/logger");
8
8
  const socket_utils_1 = require("../../../daemon/socket-utils");
9
9
  const consume_messages_from_socket_1 = require("../../../utils/consume-messages-from-socket");
10
+ const typescript_1 = require("../../../plugins/js/utils/typescript");
10
11
  const installation_directory_1 = require("../../../utils/installation-directory");
11
12
  const logger_2 = require("../../../utils/logger");
12
13
  const progress_topics_1 = require("../../../utils/progress-topics");
@@ -353,6 +354,9 @@ async function startPluginWorker(name) {
353
354
  };
354
355
  const ipcPath = (0, socket_utils_1.getPluginOsSocketPath)([process.pid, global.nxPluginWorkerCount++, performance.now()].join('-'));
355
356
  const worker = (0, child_process_1.spawn)(process.execPath, [
357
+ // Spawn the worker with the same resolve conditions Nx uses for plugin
358
+ // entries so the plugin's transitive workspace imports resolve to source.
359
+ ...(0, typescript_1.getPluginResolveConditionNodeArgs)(),
356
360
  ...(isWorkerTypescript ? ['--require', 'ts-node/register'] : []),
357
361
  workerPath,
358
362
  ipcPath,
@@ -63,6 +63,7 @@ class LoadedNxPlugin {
63
63
  track: true,
64
64
  ...(analytics_1.customDimensions && {
65
65
  [analytics_1.customDimensions.projectCount]: projectCount,
66
+ [analytics_1.customDimensions.sampleRate]: analytics_1.PERF_SPAN_SAMPLE_RATE,
66
67
  }),
67
68
  },
68
69
  });
@@ -25,6 +25,10 @@ function registerPluginTSTranspiler() {
25
25
  if (exports.unregisterPluginTSTranspiler !== null) {
26
26
  return;
27
27
  }
28
+ // Align Node's runtime resolution with the source-first plugin entry so the
29
+ // plugin's transitive workspace imports don't fall through to unbuilt `dist`.
30
+ // A no-op inside a plugin worker/daemon already spawned with `--conditions`.
31
+ (0, register_1.ensureResolveConditionsInjected)((0, typescript_1.getRootTsConfigResolveExportsConditions)(workspace_root_1.workspaceRoot));
28
32
  if ((0, register_1.isNativeStripPreferred)()) {
29
33
  // Native strip handles `.ts` syntax but doesn't rewrite NodeNext-style
30
34
  // `.js` relative specifiers to their `.ts` sources. Patch the CJS resolver
@@ -244,6 +244,7 @@ async function createProjectGraphAndSourceMapsAsync(opts = {
244
244
  track: true,
245
245
  ...(analytics_1.customDimensions && {
246
246
  [analytics_1.customDimensions.projectCount]: Object.keys(currentProjectGraph.nodes).length,
247
+ [analytics_1.customDimensions.sampleRate]: analytics_1.PERF_SPAN_SAMPLE_RATE,
247
248
  }),
248
249
  },
249
250
  });
@@ -310,6 +311,7 @@ async function createProjectGraphAndSourceMapsAsync(opts = {
310
311
  ...(analytics_1.customDimensions && {
311
312
  [analytics_1.customDimensions.projectCount]: Object.keys(res.projectGraph.nodes)
312
313
  .length,
314
+ [analytics_1.customDimensions.sampleRate]: analytics_1.PERF_SPAN_SAMPLE_RATE,
313
315
  }),
314
316
  },
315
317
  });
@@ -333,6 +335,7 @@ async function createProjectGraphAndSourceMapsAsync(opts = {
333
335
  track: true,
334
336
  ...(analytics_1.customDimensions && {
335
337
  [analytics_1.customDimensions.projectCount]: Object.keys(projectGraphAndSourceMaps.projectGraph.nodes).length,
338
+ [analytics_1.customDimensions.sampleRate]: analytics_1.PERF_SPAN_SAMPLE_RATE,
336
339
  }),
337
340
  },
338
341
  });
@@ -4,16 +4,13 @@ import { ProjectConfiguration } from '../../../config/workspace-json-project-jso
4
4
  * reference. `RootRef` carries the referenced project's root (resolved
5
5
  * via nameMap lookup); `UsageRef` carries the raw written name (for
6
6
  * forward refs, promoted to `RootRef` in place when the name is
7
- * identified). `parent` + `key` let the final pass write the resolved
8
- * name back; `targetPart` preserves the `:target` suffix from
7
+ * identified). `targetPart` preserves the `:target` suffix from
9
8
  * `dependsOn` strings.
10
9
  */
11
10
  export declare abstract class NameRef {
12
11
  value: string;
13
- parent: unknown;
14
- key: string | undefined;
15
12
  targetPart: string | undefined;
16
- constructor(value: string, parent: unknown, key: string | undefined, targetPart: string | undefined);
13
+ constructor(value: string, targetPart: string | undefined);
17
14
  }
18
15
  export declare class RootRef extends NameRef {
19
16
  }
@@ -27,16 +24,17 @@ export declare function isUsageRef(value: unknown): value is UsageRef;
27
24
  * then resolves them after all merging is done.
28
25
  *
29
26
  * Tracking by array position breaks once `'...'` spreads shuffle indices,
30
- * so each ref becomes a sentinel object. Arrays spread-merge by pushing
31
- * element references, so sentinel identity survives any downstream
32
- * merges — the final pass walks a flat registry and writes the resolved
33
- * name back through each sentinel's `parent` back-reference. Orphaned
34
- * sentinels (from arrays dropped by a full-replace) write harmlessly.
27
+ * so each ref becomes a sentinel object. Merges copy sentinels by
28
+ * reference — one sentinel can end up in many arrays (e.g. a pattern
29
+ * target's dependsOn applied to every matching target) — so the final
30
+ * pass sweeps the merged rootMap and resolves every sentinel where it
31
+ * actually sits. Sentinels in arrays dropped by a full-replace are never
32
+ * visited and vanish with their array.
35
33
  */
36
34
  export declare class ProjectNameInNodePropsManager {
37
35
  private getNameMap;
38
- private allRefs;
39
36
  private pendingByName;
37
+ private nameHistory;
40
38
  constructor(getNameMap?: () => Record<string, ProjectConfiguration>);
41
39
  registerNameRefs(pluginResultProjects?: Record<string, Omit<ProjectConfiguration, 'root'> & Partial<ProjectConfiguration>>): void;
42
40
  private processInputs;
@@ -45,6 +43,6 @@ export declare class ProjectNameInNodePropsManager {
45
43
  private createRef;
46
44
  identifyProjectWithRoot(root: string, name: string): void;
47
45
  applySubstitutions(rootMap: Record<string, ProjectConfiguration>): void;
46
+ private substituteInArray;
48
47
  private resolveFinalName;
49
- private writeReplacement;
50
48
  }
@@ -11,15 +11,12 @@ const split_target_1 = require("../../../utils/split-target");
11
11
  * reference. `RootRef` carries the referenced project's root (resolved
12
12
  * via nameMap lookup); `UsageRef` carries the raw written name (for
13
13
  * forward refs, promoted to `RootRef` in place when the name is
14
- * identified). `parent` + `key` let the final pass write the resolved
15
- * name back; `targetPart` preserves the `:target` suffix from
14
+ * identified). `targetPart` preserves the `:target` suffix from
16
15
  * `dependsOn` strings.
17
16
  */
18
17
  class NameRef {
19
- constructor(value, parent, key, targetPart) {
18
+ constructor(value, targetPart) {
20
19
  this.value = value;
21
- this.parent = parent;
22
- this.key = key;
23
20
  this.targetPart = targetPart;
24
21
  }
25
22
  }
@@ -44,16 +41,19 @@ function isUsageRef(value) {
44
41
  * then resolves them after all merging is done.
45
42
  *
46
43
  * Tracking by array position breaks once `'...'` spreads shuffle indices,
47
- * so each ref becomes a sentinel object. Arrays spread-merge by pushing
48
- * element references, so sentinel identity survives any downstream
49
- * merges — the final pass walks a flat registry and writes the resolved
50
- * name back through each sentinel's `parent` back-reference. Orphaned
51
- * sentinels (from arrays dropped by a full-replace) write harmlessly.
44
+ * so each ref becomes a sentinel object. Merges copy sentinels by
45
+ * reference — one sentinel can end up in many arrays (e.g. a pattern
46
+ * target's dependsOn applied to every matching target) — so the final
47
+ * pass sweeps the merged rootMap and resolves every sentinel where it
48
+ * actually sits. Sentinels in arrays dropped by a full-replace are never
49
+ * visited and vanish with their array.
52
50
  */
53
51
  class ProjectNameInNodePropsManager {
54
52
  constructor(getNameMap) {
55
- this.allRefs = new Set();
56
53
  this.pendingByName = new Map();
54
+ // name → root for every name a project has ever been identified by, so refs
55
+ // to a since-renamed name still bind to the right root (see createRef).
56
+ this.nameHistory = new Map();
57
57
  this.getNameMap = getNameMap ?? (() => ({}));
58
58
  }
59
59
  // Replaces each project-name ref in `inputs`/`dependsOn` with a sentinel.
@@ -82,26 +82,20 @@ class ProjectNameInNodePropsManager {
82
82
  processInputs(inputs) {
83
83
  for (let i = 0; i < inputs.length; i++) {
84
84
  const entry = inputs[i];
85
- // Existing sentinel: spread merges may have copied it out of its
86
- // original array, so rebind parent to this one.
87
- if (isNameRef(entry)) {
88
- entry.parent = inputs;
85
+ if (isNameRef(entry))
89
86
  continue;
90
- }
91
87
  if (!entry || typeof entry !== 'object')
92
88
  continue;
93
89
  if (!('projects' in entry))
94
90
  continue;
95
91
  const element = entry;
96
92
  const projects = element.projects;
97
- if (isNameRef(projects)) {
98
- // Object-parent sentinel — element identity is stable across spread.
93
+ if (isNameRef(projects))
99
94
  continue;
100
- }
101
95
  if (typeof projects === 'string') {
102
96
  if (projects === 'self' || projects === 'dependencies')
103
97
  continue;
104
- element.projects = this.createRef(projects, element, 'projects');
98
+ element.projects = this.createRef(projects);
105
99
  }
106
100
  else if (Array.isArray(projects)) {
107
101
  this.processProjectsArray(projects);
@@ -111,12 +105,8 @@ class ProjectNameInNodePropsManager {
111
105
  processDependsOn(dependsOn, ownerTargets, ownerName) {
112
106
  for (let i = 0; i < dependsOn.length; i++) {
113
107
  const dep = dependsOn[i];
114
- // Existing sentinel: rebind parent to this array in case a spread
115
- // merge copied it out of its original.
116
- if (isNameRef(dep)) {
117
- dep.parent = dependsOn;
108
+ if (isNameRef(dep))
118
109
  continue;
119
- }
120
110
  if (typeof dep === 'string') {
121
111
  // `^target` and same-project targets aren't cross-project refs.
122
112
  if (dep.startsWith('^') || (ownerTargets && dep in ownerTargets)) {
@@ -126,7 +116,7 @@ class ProjectNameInNodePropsManager {
126
116
  if (rest.length === 0)
127
117
  continue;
128
118
  const targetPart = rest.join(':');
129
- dependsOn[i] = this.createRef(maybeProject, dependsOn, undefined, targetPart);
119
+ dependsOn[i] = this.createRef(maybeProject, targetPart);
130
120
  continue;
131
121
  }
132
122
  if (!dep || typeof dep !== 'object' || !('projects' in dep))
@@ -142,7 +132,7 @@ class ProjectNameInNodePropsManager {
142
132
  projects === 'dependencies') {
143
133
  continue;
144
134
  }
145
- element.projects = this.createRef(projects, element, 'projects');
135
+ element.projects = this.createRef(projects);
146
136
  }
147
137
  else if (Array.isArray(projects)) {
148
138
  this.processProjectsArray(projects);
@@ -152,24 +142,26 @@ class ProjectNameInNodePropsManager {
152
142
  processProjectsArray(projects) {
153
143
  for (let j = 0; j < projects.length; j++) {
154
144
  const name = projects[j];
155
- if (isNameRef(name)) {
156
- name.parent = projects;
145
+ if (isNameRef(name))
157
146
  continue;
158
- }
159
147
  if (typeof name !== 'string')
160
148
  continue;
161
149
  if ((0, globs_1.isGlobPattern)(name))
162
150
  continue;
163
- projects[j] = this.createRef(name, projects, undefined);
151
+ projects[j] = this.createRef(name);
164
152
  }
165
153
  }
166
- // Builds a sentinel and registers it.
167
- createRef(referencedName, parent, key, targetPart) {
168
- const referencedRoot = this.getNameMap()[referencedName]?.root;
154
+ // Builds a sentinel and registers it. When `referencedName` isn't in the
155
+ // current name map, fall back to the rename history: a batch may reference a
156
+ // project by a name that a project earlier in the same batch already renamed
157
+ // (refs register after the whole batch merges), and the old name still
158
+ // identifies the same root.
159
+ createRef(referencedName, targetPart) {
160
+ const referencedRoot = this.getNameMap()[referencedName]?.root ??
161
+ this.nameHistory.get(referencedName);
169
162
  const ref = referencedRoot !== undefined
170
- ? new RootRef(referencedRoot, parent, key, targetPart)
171
- : new UsageRef(referencedName, parent, key, targetPart);
172
- this.allRefs.add(ref);
163
+ ? new RootRef(referencedRoot, targetPart)
164
+ : new UsageRef(referencedName, targetPart);
173
165
  if (ref instanceof UsageRef) {
174
166
  let set = this.pendingByName.get(referencedName);
175
167
  if (!set) {
@@ -184,6 +176,11 @@ class ProjectNameInNodePropsManager {
184
176
  // RootRef by prototype swap. Sentinel identity across spread copies means
185
177
  // one promotion updates every array the sentinel reached.
186
178
  identifyProjectWithRoot(root, name) {
179
+ // Every name a root has ever gone by, including pre-rename names. A later
180
+ // ref to a stale name resolves to the root it identified at the time.
181
+ // (If two roots use the same name over a graph construction, the later
182
+ // one wins — matching how the live name map would have resolved it.)
183
+ this.nameHistory.set(name, root);
187
184
  const pending = this.pendingByName.get(name);
188
185
  if (!pending)
189
186
  return;
@@ -195,24 +192,72 @@ class ProjectNameInNodePropsManager {
195
192
  ref.value = root;
196
193
  }
197
194
  }
198
- // Writes each sentinel's current resolved name back into its owning slot.
199
- // Called once after all plugin results have been merged.
195
+ // Resolves every sentinel in the merged rootMap in place. Sweeping the
196
+ // final config (rather than writing through back-references held by the
197
+ // sentinels) covers sentinels that merges copied into arrays other than
198
+ // the one they were created in, e.g. a pattern target's dependsOn applied
199
+ // to every matching target. Called once after all plugin results have
200
+ // been merged.
200
201
  applySubstitutions(rootMap) {
201
202
  const nameByRoot = {};
202
203
  for (const root in rootMap) {
203
204
  nameByRoot[root] = rootMap[root]?.name;
204
205
  }
205
- for (const ref of this.allRefs) {
206
- const finalName = this.resolveFinalName(ref, nameByRoot);
207
- if (finalName === undefined)
208
- continue;
209
- const replacement = ref.targetPart !== undefined
210
- ? `${finalName}:${ref.targetPart}`
211
- : finalName;
212
- this.writeReplacement(ref, replacement);
206
+ for (const root in rootMap) {
207
+ const targets = rootMap[root]?.targets;
208
+ if (!targets)
209
+ continue;
210
+ for (const targetName in targets) {
211
+ const targetConfig = targets[targetName];
212
+ if (!targetConfig || typeof targetConfig !== 'object')
213
+ continue;
214
+ if (Array.isArray(targetConfig.inputs)) {
215
+ this.substituteInArray(targetConfig.inputs, nameByRoot);
216
+ }
217
+ if (Array.isArray(targetConfig.dependsOn)) {
218
+ this.substituteInArray(targetConfig.dependsOn, nameByRoot);
219
+ }
220
+ }
213
221
  }
214
- this.allRefs.clear();
215
222
  this.pendingByName.clear();
223
+ this.nameHistory.clear();
224
+ }
225
+ substituteInArray(entries, nameByRoot) {
226
+ for (let i = 0; i < entries.length; i++) {
227
+ const entry = entries[i];
228
+ if (isNameRef(entry)) {
229
+ const finalName = this.resolveFinalName(entry, nameByRoot);
230
+ if (finalName !== undefined) {
231
+ entries[i] =
232
+ entry.targetPart !== undefined
233
+ ? `${finalName}:${entry.targetPart}`
234
+ : finalName;
235
+ }
236
+ continue;
237
+ }
238
+ if (!entry || typeof entry !== 'object' || !('projects' in entry)) {
239
+ continue;
240
+ }
241
+ const element = entry;
242
+ if (isNameRef(element.projects)) {
243
+ const finalName = this.resolveFinalName(element.projects, nameByRoot);
244
+ if (finalName !== undefined) {
245
+ element.projects = finalName;
246
+ }
247
+ }
248
+ else if (Array.isArray(element.projects)) {
249
+ const projects = element.projects;
250
+ for (let j = 0; j < projects.length; j++) {
251
+ const name = projects[j];
252
+ if (!isNameRef(name))
253
+ continue;
254
+ const finalName = this.resolveFinalName(name, nameByRoot);
255
+ if (finalName !== undefined) {
256
+ projects[j] = finalName;
257
+ }
258
+ }
259
+ }
260
+ }
216
261
  }
217
262
  resolveFinalName(ref, nameByRoot) {
218
263
  if (ref instanceof RootRef) {
@@ -221,20 +266,5 @@ class ProjectNameInNodePropsManager {
221
266
  // Unpromoted forward ref — best effort, fall back to the written name.
222
267
  return this.getNameMap()[ref.value]?.name ?? ref.value;
223
268
  }
224
- writeReplacement(ref, replacement) {
225
- const parent = ref.parent;
226
- if (Array.isArray(parent)) {
227
- // One sentinel may appear at multiple indices (e.g. `[..., ...]`
228
- // pushed the same reference twice via spread), so replace all.
229
- for (let i = 0; i < parent.length; i++) {
230
- if (parent[i] === ref)
231
- parent[i] = replacement;
232
- }
233
- return;
234
- }
235
- if (parent && typeof parent === 'object' && ref.key !== undefined) {
236
- parent[ref.key] = replacement;
237
- }
238
- }
239
269
  }
240
270
  exports.ProjectNameInNodePropsManager = ProjectNameInNodePropsManager;
@@ -28,15 +28,12 @@ export declare class ProjectNodesManager {
28
28
  mergeProjectNode(project: ProjectConfiguration, configurationSourceMaps?: ConfigurationSourceMaps, sourceInformation?: SourceInformation): void;
29
29
  /**
30
30
  * Inserts project-name sentinels into `inputs` and `dependsOn` on the
31
- * merged objects from `mergedRootMap` (defaulting to this manager's
32
- * rootMap). Walking the merged entries matters because a spread-produced
33
- * array is a fresh instance.
34
- *
35
- * Pass a different `mergedRootMap` for the default-plugin intermediate
36
- * pass, then call again with `this.rootMap` after it's applied so
37
- * sentinel parents rebind onto the final arrays.
31
+ * merged objects in this manager's rootMap. Walking the merged entries —
32
+ * not the plugin results — matters because a spread-produced array is a
33
+ * fresh instance, and because sentinels written into plugin-result arrays
34
+ * would corrupt them for any later merge that re-reads the results.
38
35
  */
39
- registerNameRefs(pluginResultProjects?: Record<string, Omit<ProjectConfiguration, 'root'> & Partial<ProjectConfiguration>>, mergedRootMap?: Record<string, ProjectConfiguration>): void;
36
+ registerNameRefs(pluginResultProjects?: Record<string, Omit<ProjectConfiguration, 'root'> & Partial<ProjectConfiguration>>): void;
40
37
  /**
41
38
  * Applies all pending name substitutions. Call once after all plugin
42
39
  * results have been merged.
@@ -129,10 +129,10 @@ deferSpreadsWithoutBase = true) {
129
129
  // if augmenting an existing target.
130
130
  const target = project.targets?.[targetName];
131
131
  if (sourceMap) {
132
- // A target default stamping fields onto an existing target must not
133
- // steal provenance for the target's existence; real plugins still win
134
- // last. Field-level attribution is handled inside
135
- // `mergeTargetConfigurations`.
132
+ // Claims the node key for a newly-created target (or reclaims it from
133
+ // a weak target-defaults stamp). Whether an *existing* target changes
134
+ // owners is decided inside `mergeTargetConfigurations`, which knows
135
+ // whether this merge changed the target's identity.
136
136
  (0, source_maps_1.recordTargetIdentitySourceMapInfo)(sourceMap, (0, source_maps_1.targetSourceMapKey)(targetName), sourceInformation);
137
137
  }
138
138
  const normalizedTarget = skipTargetNormalization
@@ -257,21 +257,18 @@ class ProjectNodesManager {
257
257
  }
258
258
  /**
259
259
  * Inserts project-name sentinels into `inputs` and `dependsOn` on the
260
- * merged objects from `mergedRootMap` (defaulting to this manager's
261
- * rootMap). Walking the merged entries matters because a spread-produced
262
- * array is a fresh instance.
263
- *
264
- * Pass a different `mergedRootMap` for the default-plugin intermediate
265
- * pass, then call again with `this.rootMap` after it's applied so
266
- * sentinel parents rebind onto the final arrays.
260
+ * merged objects in this manager's rootMap. Walking the merged entries —
261
+ * not the plugin results — matters because a spread-produced array is a
262
+ * fresh instance, and because sentinels written into plugin-result arrays
263
+ * would corrupt them for any later merge that re-reads the results.
267
264
  */
268
- registerNameRefs(pluginResultProjects, mergedRootMap = this.rootMap) {
265
+ registerNameRefs(pluginResultProjects) {
269
266
  if (!pluginResultProjects)
270
267
  return;
271
268
  const scoped = {};
272
269
  for (const root in pluginResultProjects) {
273
- if (mergedRootMap[root]) {
274
- scoped[root] = mergedRootMap[root];
270
+ if (this.rootMap[root]) {
271
+ scoped[root] = this.rootMap[root];
275
272
  }
276
273
  }
277
274
  this.nameSubstitutionManager.registerNameRefs(scoped);
@@ -9,12 +9,19 @@ export type SourceInformation = [file: string | null, plugin: string];
9
9
  */
10
10
  export declare const TARGET_DEFAULTS_PLUGIN_NAME = "nx/target-defaults";
11
11
  /**
12
- * Write the source for a target's "identity" key (the target node itself, or
13
- * its executor/command). Real plugins win last; a target-defaults stamp only
14
- * claims the key when no real plugin already recorded it — target defaults
15
- * never bring a target into existence, they only layer fields onto one.
12
+ * Write the source for the target node key (`targets.<name>`). Ownership of a
13
+ * target follows its identity, not the last writer:
14
+ *
15
+ * - An unowned key goes to whoever writes it first (the creator).
16
+ * - A target-defaults stamp is weak — it never authors a target's existence,
17
+ * so any real plugin reclaims the key from it, and it can never take the
18
+ * key from a real plugin.
19
+ * - Between real plugins, the key only changes hands when the merge changed
20
+ * the target's identity (executor/command) — a plugin that merely layers
21
+ * fields (dependsOn, options, …) onto an existing target does not become
22
+ * its owner.
16
23
  */
17
- export declare function recordTargetIdentitySourceMapInfo(sourceMap: Record<string, SourceInformation>, key: string, sourceInfo: SourceInformation): void;
24
+ export declare function recordTargetIdentitySourceMapInfo(sourceMap: Record<string, SourceInformation>, key: string, sourceInfo: SourceInformation, identityChanged?: boolean): void;
18
25
  /** Source map per project root. */
19
26
  export type ConfigurationSourceMaps = Record<string, Record<string, SourceInformation>>;
20
27
  export declare function forEachSourceMapKeyForArray(prefixKey: string, array: unknown[], callback: (key: string, index: number) => void, startIndex?: number): void;