nx 23.1.0 → 23.2.0-beta.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.
- package/dist/bin/nx.js +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/command-line/add/add.js +8 -1
- package/dist/src/command-line/import/import.js +1 -1
- 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/utils.d.ts +3 -3
- package/dist/src/command-line/init/implementation/utils.js +22 -3
- 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 +14 -45
- package/dist/src/command-line/migrate/migrate.js +58 -375
- package/dist/src/command-line/release/utils/git.js +6 -2
- package/dist/src/command-line/release/utils/release-graph.d.ts +2 -2
- package/dist/src/command-line/release/utils/shared.d.ts +2 -2
- package/dist/src/command-line/show/show-target/info.js +11 -2
- package/dist/src/commands-runner/create-command-graph.js +5 -0
- package/dist/src/config/nx-json.d.ts +1 -1
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/daemon/client/client.d.ts +0 -2
- package/dist/src/daemon/client/client.js +17 -19
- package/dist/src/devkit-internals.d.ts +2 -0
- package/dist/src/devkit-internals.js +7 -1
- package/dist/src/generators/utils/nx-json.js +13 -3
- package/dist/src/native/index.d.ts +1 -1
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/plugins/js/lock-file/npm-parser.js +71 -27
- package/dist/src/plugins/js/lock-file/pnpm-parser.js +46 -15
- package/dist/src/plugins/js/lock-file/project-graph-pruning.js +1 -1
- package/dist/src/plugins/js/lock-file/yarn-parser.js +32 -5
- 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/error-types.js +41 -1
- package/dist/src/project-graph/file-utils.js +6 -1
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +4 -0
- package/dist/src/project-graph/plugins/transpiler.js +4 -0
- package/dist/src/project-graph/utils/project-configuration/name-substitution-manager.d.ts +9 -12
- package/dist/src/project-graph/utils/project-configuration/name-substitution-manager.js +77 -61
- package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.d.ts +1 -1
- package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.js +1 -1
- package/dist/src/project-graph/utils/project-configuration-utils.js +4 -6
- 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 +5 -3
- package/dist/src/utils/acknowledge-build-scripts.d.ts +13 -0
- package/dist/src/utils/acknowledge-build-scripts.js +100 -0
- 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/git-revision.d.ts +12 -0
- package/dist/src/utils/git-revision.js +25 -0
- package/dist/src/utils/git-utils.d.ts +2 -3
- package/dist/src/utils/git-utils.js +101 -47
- package/dist/src/utils/min-release-age/behavior/pnpm.js +9 -7
- package/dist/src/utils/package-manager.js +6 -2
- package/dist/src/utils/provenance.js +12 -2
- package/migrations.json +7 -1
- package/package.json +11 -11
- 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
|
@@ -5,6 +5,7 @@ exports.forceRegisterEsmLoader = forceRegisterEsmLoader;
|
|
|
5
5
|
exports.nodeNextEsmResolveHook = nodeNextEsmResolveHook;
|
|
6
6
|
exports.ensureNodeNextEsmResolverRegistered = ensureNodeNextEsmResolverRegistered;
|
|
7
7
|
exports.ensureCjsResolverPatched = ensureCjsResolverPatched;
|
|
8
|
+
exports.ensureResolveConditionsInjected = ensureResolveConditionsInjected;
|
|
8
9
|
exports.isNativeStripPreferred = isNativeStripPreferred;
|
|
9
10
|
exports.registerTsProject = registerTsProject;
|
|
10
11
|
exports.getSwcTranspiler = getSwcTranspiler;
|
|
@@ -388,6 +389,86 @@ const isInvokedByTsx = (() => {
|
|
|
388
389
|
return (requireArgs.some((a) => isTsxPath(a)) ||
|
|
389
390
|
importArgs.some((a) => isTsxPath(a)));
|
|
390
391
|
})();
|
|
392
|
+
let resolveConditionsInjected = false;
|
|
393
|
+
/**
|
|
394
|
+
* Make Node's module resolver honor the given export conditions for the rest of
|
|
395
|
+
* the process, via `module.registerHooks()`. Used when a local plugin is loaded
|
|
396
|
+
* from source in-process (no child process to pass `--conditions` to at spawn):
|
|
397
|
+
* the hook appends the conditions to every resolution so the plugin's transitive
|
|
398
|
+
* workspace imports resolve to source the same way the plugin entry did.
|
|
399
|
+
*
|
|
400
|
+
* No-op when:
|
|
401
|
+
* - `conditions` is empty (nothing to inject);
|
|
402
|
+
* - every target condition is already active at startup (a spawned plugin
|
|
403
|
+
* worker or daemon was launched with the full `--conditions` set, so Node's
|
|
404
|
+
* resolver already honors them and the per-resolve hook would be redundant);
|
|
405
|
+
* - `module.registerHooks` is unavailable (Node < 22.15 / < 23.5). Those
|
|
406
|
+
* runtimes keep the `NODE_OPTIONS=--conditions` escape hatch.
|
|
407
|
+
*
|
|
408
|
+
* Idempotent and best-effort.
|
|
409
|
+
*/
|
|
410
|
+
function ensureResolveConditionsInjected(conditions) {
|
|
411
|
+
if (resolveConditionsInjected)
|
|
412
|
+
return;
|
|
413
|
+
resolveConditionsInjected = true;
|
|
414
|
+
if (!conditions.length)
|
|
415
|
+
return;
|
|
416
|
+
// Skip only when Node already honors every target condition (e.g. a worker or
|
|
417
|
+
// daemon spawned with the full `--conditions` set); a partial overlap still
|
|
418
|
+
// needs the hook to add the missing ones.
|
|
419
|
+
const activeConditions = getConditionsActiveAtStartup();
|
|
420
|
+
if (conditions.every((condition) => activeConditions.has(condition)))
|
|
421
|
+
return;
|
|
422
|
+
const module = require('node:module');
|
|
423
|
+
const registerHooks = module.registerHooks;
|
|
424
|
+
if (typeof registerHooks !== 'function')
|
|
425
|
+
return;
|
|
426
|
+
try {
|
|
427
|
+
registerHooks.call(module, {
|
|
428
|
+
resolve(specifier, context, nextResolve) {
|
|
429
|
+
const merged = context.conditions
|
|
430
|
+
? [...context.conditions, ...conditions]
|
|
431
|
+
: conditions;
|
|
432
|
+
return nextResolve(specifier, { ...context, conditions: merged });
|
|
433
|
+
},
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
catch {
|
|
437
|
+
// Best-effort: leave Node's native resolution in place rather than failing.
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Export conditions this process was started with, parsed from `--conditions`
|
|
442
|
+
* (or its `-C` alias) in `process.execArgv` and `NODE_OPTIONS`. Node's resolver
|
|
443
|
+
* already honors these, so the injected hook only needs to cover target
|
|
444
|
+
* conditions not present here.
|
|
445
|
+
*/
|
|
446
|
+
function getConditionsActiveAtStartup() {
|
|
447
|
+
const active = new Set();
|
|
448
|
+
const collect = (tokens) => {
|
|
449
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
450
|
+
const token = tokens[i];
|
|
451
|
+
if (token === '--conditions' || token === '-C') {
|
|
452
|
+
const value = tokens[i + 1];
|
|
453
|
+
if (value) {
|
|
454
|
+
active.add(value);
|
|
455
|
+
i++;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
else if (token.startsWith('--conditions=')) {
|
|
459
|
+
active.add(token.slice('--conditions='.length));
|
|
460
|
+
}
|
|
461
|
+
else if (token.startsWith('-C=')) {
|
|
462
|
+
active.add(token.slice('-C='.length));
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
collect(process.execArgv ?? []);
|
|
467
|
+
const nodeOptions = process.env.NODE_OPTIONS;
|
|
468
|
+
if (nodeOptions)
|
|
469
|
+
collect(nodeOptions.split(/\s+/).filter(Boolean));
|
|
470
|
+
return active;
|
|
471
|
+
}
|
|
391
472
|
/**
|
|
392
473
|
* Whether the current Node.js runtime exposes native TypeScript type
|
|
393
474
|
* stripping. This is the authoritative gate - it correctly handles every
|
|
@@ -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 [];
|
|
@@ -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
|
-
|
|
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.
|
|
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,
|
|
@@ -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
|
|
@@ -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). `
|
|
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,
|
|
13
|
+
constructor(value: string, targetPart: string | undefined);
|
|
17
14
|
}
|
|
18
15
|
export declare class RootRef extends NameRef {
|
|
19
16
|
}
|
|
@@ -27,15 +24,15 @@ 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.
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
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;
|
|
40
37
|
constructor(getNameMap?: () => Record<string, ProjectConfiguration>);
|
|
41
38
|
registerNameRefs(pluginResultProjects?: Record<string, Omit<ProjectConfiguration, 'root'> & Partial<ProjectConfiguration>>): void;
|
|
@@ -45,6 +42,6 @@ export declare class ProjectNameInNodePropsManager {
|
|
|
45
42
|
private createRef;
|
|
46
43
|
identifyProjectWithRoot(root: string, name: string): void;
|
|
47
44
|
applySubstitutions(rootMap: Record<string, ProjectConfiguration>): void;
|
|
45
|
+
private substituteInArray;
|
|
48
46
|
private resolveFinalName;
|
|
49
|
-
private writeReplacement;
|
|
50
47
|
}
|
|
@@ -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). `
|
|
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,
|
|
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,15 +41,15 @@ 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.
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
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();
|
|
57
54
|
this.getNameMap = getNameMap ?? (() => ({}));
|
|
58
55
|
}
|
|
@@ -82,26 +79,20 @@ class ProjectNameInNodePropsManager {
|
|
|
82
79
|
processInputs(inputs) {
|
|
83
80
|
for (let i = 0; i < inputs.length; i++) {
|
|
84
81
|
const entry = inputs[i];
|
|
85
|
-
|
|
86
|
-
// original array, so rebind parent to this one.
|
|
87
|
-
if (isNameRef(entry)) {
|
|
88
|
-
entry.parent = inputs;
|
|
82
|
+
if (isNameRef(entry))
|
|
89
83
|
continue;
|
|
90
|
-
}
|
|
91
84
|
if (!entry || typeof entry !== 'object')
|
|
92
85
|
continue;
|
|
93
86
|
if (!('projects' in entry))
|
|
94
87
|
continue;
|
|
95
88
|
const element = entry;
|
|
96
89
|
const projects = element.projects;
|
|
97
|
-
if (isNameRef(projects))
|
|
98
|
-
// Object-parent sentinel — element identity is stable across spread.
|
|
90
|
+
if (isNameRef(projects))
|
|
99
91
|
continue;
|
|
100
|
-
}
|
|
101
92
|
if (typeof projects === 'string') {
|
|
102
93
|
if (projects === 'self' || projects === 'dependencies')
|
|
103
94
|
continue;
|
|
104
|
-
element.projects = this.createRef(projects
|
|
95
|
+
element.projects = this.createRef(projects);
|
|
105
96
|
}
|
|
106
97
|
else if (Array.isArray(projects)) {
|
|
107
98
|
this.processProjectsArray(projects);
|
|
@@ -111,12 +102,8 @@ class ProjectNameInNodePropsManager {
|
|
|
111
102
|
processDependsOn(dependsOn, ownerTargets, ownerName) {
|
|
112
103
|
for (let i = 0; i < dependsOn.length; i++) {
|
|
113
104
|
const dep = dependsOn[i];
|
|
114
|
-
|
|
115
|
-
// merge copied it out of its original.
|
|
116
|
-
if (isNameRef(dep)) {
|
|
117
|
-
dep.parent = dependsOn;
|
|
105
|
+
if (isNameRef(dep))
|
|
118
106
|
continue;
|
|
119
|
-
}
|
|
120
107
|
if (typeof dep === 'string') {
|
|
121
108
|
// `^target` and same-project targets aren't cross-project refs.
|
|
122
109
|
if (dep.startsWith('^') || (ownerTargets && dep in ownerTargets)) {
|
|
@@ -126,7 +113,7 @@ class ProjectNameInNodePropsManager {
|
|
|
126
113
|
if (rest.length === 0)
|
|
127
114
|
continue;
|
|
128
115
|
const targetPart = rest.join(':');
|
|
129
|
-
dependsOn[i] = this.createRef(maybeProject,
|
|
116
|
+
dependsOn[i] = this.createRef(maybeProject, targetPart);
|
|
130
117
|
continue;
|
|
131
118
|
}
|
|
132
119
|
if (!dep || typeof dep !== 'object' || !('projects' in dep))
|
|
@@ -142,7 +129,7 @@ class ProjectNameInNodePropsManager {
|
|
|
142
129
|
projects === 'dependencies') {
|
|
143
130
|
continue;
|
|
144
131
|
}
|
|
145
|
-
element.projects = this.createRef(projects
|
|
132
|
+
element.projects = this.createRef(projects);
|
|
146
133
|
}
|
|
147
134
|
else if (Array.isArray(projects)) {
|
|
148
135
|
this.processProjectsArray(projects);
|
|
@@ -152,24 +139,21 @@ class ProjectNameInNodePropsManager {
|
|
|
152
139
|
processProjectsArray(projects) {
|
|
153
140
|
for (let j = 0; j < projects.length; j++) {
|
|
154
141
|
const name = projects[j];
|
|
155
|
-
if (isNameRef(name))
|
|
156
|
-
name.parent = projects;
|
|
142
|
+
if (isNameRef(name))
|
|
157
143
|
continue;
|
|
158
|
-
}
|
|
159
144
|
if (typeof name !== 'string')
|
|
160
145
|
continue;
|
|
161
146
|
if ((0, globs_1.isGlobPattern)(name))
|
|
162
147
|
continue;
|
|
163
|
-
projects[j] = this.createRef(name
|
|
148
|
+
projects[j] = this.createRef(name);
|
|
164
149
|
}
|
|
165
150
|
}
|
|
166
151
|
// Builds a sentinel and registers it.
|
|
167
|
-
createRef(referencedName,
|
|
152
|
+
createRef(referencedName, targetPart) {
|
|
168
153
|
const referencedRoot = this.getNameMap()[referencedName]?.root;
|
|
169
154
|
const ref = referencedRoot !== undefined
|
|
170
|
-
? new RootRef(referencedRoot,
|
|
171
|
-
: new UsageRef(referencedName,
|
|
172
|
-
this.allRefs.add(ref);
|
|
155
|
+
? new RootRef(referencedRoot, targetPart)
|
|
156
|
+
: new UsageRef(referencedName, targetPart);
|
|
173
157
|
if (ref instanceof UsageRef) {
|
|
174
158
|
let set = this.pendingByName.get(referencedName);
|
|
175
159
|
if (!set) {
|
|
@@ -195,25 +179,72 @@ class ProjectNameInNodePropsManager {
|
|
|
195
179
|
ref.value = root;
|
|
196
180
|
}
|
|
197
181
|
}
|
|
198
|
-
//
|
|
199
|
-
//
|
|
182
|
+
// Resolves every sentinel in the merged rootMap in place. Sweeping the
|
|
183
|
+
// final config (rather than writing through back-references held by the
|
|
184
|
+
// sentinels) covers sentinels that merges copied into arrays other than
|
|
185
|
+
// the one they were created in, e.g. a pattern target's dependsOn applied
|
|
186
|
+
// to every matching target. Called once after all plugin results have
|
|
187
|
+
// been merged.
|
|
200
188
|
applySubstitutions(rootMap) {
|
|
201
189
|
const nameByRoot = {};
|
|
202
190
|
for (const root in rootMap) {
|
|
203
191
|
nameByRoot[root] = rootMap[root]?.name;
|
|
204
192
|
}
|
|
205
|
-
for (const
|
|
206
|
-
const
|
|
207
|
-
if (
|
|
208
|
-
continue;
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
193
|
+
for (const root in rootMap) {
|
|
194
|
+
const targets = rootMap[root]?.targets;
|
|
195
|
+
if (!targets)
|
|
196
|
+
continue;
|
|
197
|
+
for (const targetName in targets) {
|
|
198
|
+
const targetConfig = targets[targetName];
|
|
199
|
+
if (!targetConfig || typeof targetConfig !== 'object')
|
|
200
|
+
continue;
|
|
201
|
+
if (Array.isArray(targetConfig.inputs)) {
|
|
202
|
+
this.substituteInArray(targetConfig.inputs, nameByRoot);
|
|
203
|
+
}
|
|
204
|
+
if (Array.isArray(targetConfig.dependsOn)) {
|
|
205
|
+
this.substituteInArray(targetConfig.dependsOn, nameByRoot);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
213
208
|
}
|
|
214
|
-
this.allRefs.clear();
|
|
215
209
|
this.pendingByName.clear();
|
|
216
210
|
}
|
|
211
|
+
substituteInArray(entries, nameByRoot) {
|
|
212
|
+
for (let i = 0; i < entries.length; i++) {
|
|
213
|
+
const entry = entries[i];
|
|
214
|
+
if (isNameRef(entry)) {
|
|
215
|
+
const finalName = this.resolveFinalName(entry, nameByRoot);
|
|
216
|
+
if (finalName !== undefined) {
|
|
217
|
+
entries[i] =
|
|
218
|
+
entry.targetPart !== undefined
|
|
219
|
+
? `${finalName}:${entry.targetPart}`
|
|
220
|
+
: finalName;
|
|
221
|
+
}
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (!entry || typeof entry !== 'object' || !('projects' in entry)) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const element = entry;
|
|
228
|
+
if (isNameRef(element.projects)) {
|
|
229
|
+
const finalName = this.resolveFinalName(element.projects, nameByRoot);
|
|
230
|
+
if (finalName !== undefined) {
|
|
231
|
+
element.projects = finalName;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
else if (Array.isArray(element.projects)) {
|
|
235
|
+
const projects = element.projects;
|
|
236
|
+
for (let j = 0; j < projects.length; j++) {
|
|
237
|
+
const name = projects[j];
|
|
238
|
+
if (!isNameRef(name))
|
|
239
|
+
continue;
|
|
240
|
+
const finalName = this.resolveFinalName(name, nameByRoot);
|
|
241
|
+
if (finalName !== undefined) {
|
|
242
|
+
projects[j] = finalName;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
217
248
|
resolveFinalName(ref, nameByRoot) {
|
|
218
249
|
if (ref instanceof RootRef) {
|
|
219
250
|
return nameByRoot[ref.value];
|
|
@@ -221,20 +252,5 @@ class ProjectNameInNodePropsManager {
|
|
|
221
252
|
// Unpromoted forward ref — best effort, fall back to the written name.
|
|
222
253
|
return this.getNameMap()[ref.value]?.name ?? ref.value;
|
|
223
254
|
}
|
|
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
255
|
}
|
|
240
256
|
exports.ProjectNameInNodePropsManager = ProjectNameInNodePropsManager;
|
|
@@ -34,7 +34,7 @@ export declare class ProjectNodesManager {
|
|
|
34
34
|
*
|
|
35
35
|
* Pass a different `mergedRootMap` for the default-plugin intermediate
|
|
36
36
|
* pass, then call again with `this.rootMap` after it's applied so
|
|
37
|
-
*
|
|
37
|
+
* name refs introduced by that merge are sentinelized as well.
|
|
38
38
|
*/
|
|
39
39
|
registerNameRefs(pluginResultProjects?: Record<string, Omit<ProjectConfiguration, 'root'> & Partial<ProjectConfiguration>>, mergedRootMap?: Record<string, ProjectConfiguration>): void;
|
|
40
40
|
/**
|
|
@@ -263,7 +263,7 @@ class ProjectNodesManager {
|
|
|
263
263
|
*
|
|
264
264
|
* Pass a different `mergedRootMap` for the default-plugin intermediate
|
|
265
265
|
* pass, then call again with `this.rootMap` after it's applied so
|
|
266
|
-
*
|
|
266
|
+
* name refs introduced by that merge are sentinelized as well.
|
|
267
267
|
*/
|
|
268
268
|
registerNameRefs(pluginResultProjects, mergedRootMap = this.rootMap) {
|
|
269
269
|
if (!pluginResultProjects)
|
|
@@ -225,12 +225,10 @@ function mergeCreateNodesResults(specifiedResults, defaultResults, nxJsonConfigu
|
|
|
225
225
|
}));
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
|
-
// The intermediate apply may have rebuilt dependsOn / inputs arrays
|
|
229
|
-
//
|
|
230
|
-
//
|
|
231
|
-
// the final
|
|
232
|
-
// `parent` to the current array (see
|
|
233
|
-
// ProjectNameInNodePropsManager#processInputs / processDependsOn).
|
|
228
|
+
// The intermediate apply may have rebuilt dependsOn / inputs arrays via
|
|
229
|
+
// spread merges, introducing name-ref strings that weren't visible in any
|
|
230
|
+
// single plugin result. Re-walking the final merged targets sentinelizes
|
|
231
|
+
// them so the final substitution sweep resolves them too.
|
|
234
232
|
nodesManager.registerNameRefs(intermediateDefaultRootMap);
|
|
235
233
|
// Overlay default-plugin attribution onto the main source maps using
|
|
236
234
|
// "only fill missing" semantics. Any key already present in
|
|
@@ -29,15 +29,20 @@ export interface Timespan {
|
|
|
29
29
|
/** `parallelism: false` tasks running during the slice (each one holds every slot). */
|
|
30
30
|
nonParallel: number;
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* A task and how long it ran (ms). The unit the report's task lists are built from —
|
|
34
|
+
* shared with the renderers so the producer and the formatters can't drift.
|
|
35
|
+
*/
|
|
36
|
+
export interface TaskDurationRow {
|
|
37
|
+
id: string;
|
|
38
|
+
duration: number;
|
|
39
|
+
}
|
|
32
40
|
export interface PerformanceSummary {
|
|
33
41
|
runDuration: number;
|
|
34
42
|
criticalPathDuration: number;
|
|
35
43
|
criticalPathTaskCount: number;
|
|
36
44
|
/** Longest critical-path tasks that ran (desc, capped at a few), cache hits excluded; empty when the path was fully cached. */
|
|
37
|
-
criticalPathTop:
|
|
38
|
-
id: string;
|
|
39
|
-
duration: number;
|
|
40
|
-
}>;
|
|
45
|
+
criticalPathTop: TaskDurationRow[];
|
|
41
46
|
/** Ids of tasks that failed (slowest first), for the GitHub Actions summary's failed-tasks list. Continuous tasks and tasks without a complete window are excluded. */
|
|
42
47
|
failedTasks: string[];
|
|
43
48
|
/** runDuration − criticalPathDuration. */
|
|
@@ -60,6 +65,8 @@ export interface PerformanceSummary {
|
|
|
60
65
|
cacheableCount: number;
|
|
61
66
|
cacheSkipped: boolean;
|
|
62
67
|
remoteCacheEnabled: boolean;
|
|
68
|
+
/** The workspace opted out of Nx Cloud (`neverConnectToCloud` / NX_NO_CLOUD) — never recommend it. */
|
|
69
|
+
cloudOptedOut: boolean;
|
|
63
70
|
}
|
|
64
71
|
/** Construction-time inputs for {@link PerformanceLifeCycle}. */
|
|
65
72
|
export interface PerformanceLifeCycleOptions {
|