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
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkFilesAreInputs = checkFilesAreInputs;
|
|
4
|
+
exports.checkFilesAreOutputs = checkFilesAreOutputs;
|
|
5
|
+
exports.getTaskRawInputs = getTaskRawInputs;
|
|
6
|
+
exports.getTaskOutputs = getTaskOutputs;
|
|
7
|
+
exports._resetContextForTesting = _resetContextForTesting;
|
|
8
|
+
const path_1 = require("path");
|
|
9
|
+
const nx_json_1 = require("../config/nx-json");
|
|
10
|
+
const native_1 = require("../native");
|
|
11
|
+
const project_graph_1 = require("../project-graph/project-graph");
|
|
12
|
+
const create_task_graph_1 = require("../tasks-runner/create-task-graph");
|
|
13
|
+
const utils_1 = require("../tasks-runner/utils");
|
|
14
|
+
const path_2 = require("../utils/path");
|
|
15
|
+
const project_graph_utils_1 = require("../utils/project-graph-utils");
|
|
16
|
+
const split_target_1 = require("../utils/split-target");
|
|
17
|
+
const workspace_root_1 = require("../utils/workspace-root");
|
|
18
|
+
const hash_plan_inspector_1 = require("./hash-plan-inspector");
|
|
19
|
+
const task_hasher_1 = require("./task-hasher");
|
|
20
|
+
let cachedContext = null;
|
|
21
|
+
function getContext(seed) {
|
|
22
|
+
// Only a fulfilled context is cached — caching a rejected promise would
|
|
23
|
+
// poison the resolver for the rest of the process after one transient failure.
|
|
24
|
+
return (cachedContext ??= loadContext(seed).catch((e) => {
|
|
25
|
+
cachedContext = null;
|
|
26
|
+
throw e;
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
async function loadContext(seed) {
|
|
30
|
+
const projectGraph = seed?.projectGraph ?? (await (0, project_graph_1.createProjectGraphAsync)());
|
|
31
|
+
const nxJson = seed?.nxJson ?? (0, nx_json_1.readNxJson)(workspace_root_1.workspaceRoot) ?? {};
|
|
32
|
+
let inspector = null;
|
|
33
|
+
const getInspector = () =>
|
|
34
|
+
// As with the context itself, a rejection is not cached.
|
|
35
|
+
(inspector ??= initInspector(projectGraph, nxJson).catch((e) => {
|
|
36
|
+
inspector = null;
|
|
37
|
+
throw e;
|
|
38
|
+
}));
|
|
39
|
+
return { projectGraph, nxJson, getInspector };
|
|
40
|
+
}
|
|
41
|
+
async function initInspector(projectGraph, nxJson) {
|
|
42
|
+
const inspector = new hash_plan_inspector_1.HashPlanInspector(projectGraph, workspace_root_1.workspaceRoot, nxJson);
|
|
43
|
+
await inspector.init();
|
|
44
|
+
return inspector;
|
|
45
|
+
}
|
|
46
|
+
const identityCache = new Map();
|
|
47
|
+
const hashInputsCache = new Map();
|
|
48
|
+
const outputsCache = new Map();
|
|
49
|
+
const taskGraphCache = new Map();
|
|
50
|
+
const depsOutputsCache = new Map();
|
|
51
|
+
// ── Internal resolution helpers ──────────────────────────────────────────────
|
|
52
|
+
function resolveIdentity(taskId, projectGraph) {
|
|
53
|
+
const cached = identityCache.get(taskId);
|
|
54
|
+
if (cached)
|
|
55
|
+
return cached;
|
|
56
|
+
const [project, target, configuration] = (0, split_target_1.splitTarget)(taskId, projectGraph);
|
|
57
|
+
if (!project || !target) {
|
|
58
|
+
throw new Error(`Invalid taskId "${taskId}" — expected "project:target[:configuration]"`);
|
|
59
|
+
}
|
|
60
|
+
const projectNode = projectGraph.nodes[project];
|
|
61
|
+
if (!projectNode) {
|
|
62
|
+
throw new Error(`Invalid taskId "${taskId}" — project "${project}" does not exist in the project graph.`);
|
|
63
|
+
}
|
|
64
|
+
const targetConfig = projectNode.data?.targets?.[target];
|
|
65
|
+
if (!targetConfig) {
|
|
66
|
+
throw new Error(`Invalid taskId "${taskId}" — project "${project}" has no target "${target}".`);
|
|
67
|
+
}
|
|
68
|
+
// Substituting defaultConfiguration for a configuration that does not exist
|
|
69
|
+
// would answer confidently about a *different* task — and configurations
|
|
70
|
+
// routinely change `outputPath`, so the answer could be wrong in either
|
|
71
|
+
// direction. `nx run` errors here; so do we.
|
|
72
|
+
if (configuration &&
|
|
73
|
+
!(0, project_graph_utils_1.projectHasTargetAndConfiguration)(projectNode, target, configuration)) {
|
|
74
|
+
const available = Object.keys(targetConfig.configurations ?? {});
|
|
75
|
+
throw new Error(`Invalid taskId "${taskId}" — target "${target}" of project "${project}" has no configuration "${configuration}".` +
|
|
76
|
+
(available.length
|
|
77
|
+
? ` Available configurations: ${available.join(', ')}.`
|
|
78
|
+
: ' It has no configurations.'));
|
|
79
|
+
}
|
|
80
|
+
const effectiveConfiguration = configuration ?? targetConfig.defaultConfiguration;
|
|
81
|
+
const identity = {
|
|
82
|
+
project,
|
|
83
|
+
target,
|
|
84
|
+
configuration: effectiveConfiguration,
|
|
85
|
+
canonicalTaskId: (0, utils_1.createTaskId)(project, target, effectiveConfiguration),
|
|
86
|
+
projectNode,
|
|
87
|
+
};
|
|
88
|
+
identityCache.set(taskId, identity);
|
|
89
|
+
return identity;
|
|
90
|
+
}
|
|
91
|
+
async function getRawInputs(taskId, { projectGraph, getInspector }) {
|
|
92
|
+
if (hashInputsCache.has(taskId)) {
|
|
93
|
+
return hashInputsCache.get(taskId) ?? null;
|
|
94
|
+
}
|
|
95
|
+
const { project, target, configuration, canonicalTaskId } = resolveIdentity(taskId, projectGraph);
|
|
96
|
+
const inspector = await getInspector();
|
|
97
|
+
// `null` means "this task is absent from the hash plan" — any other failure
|
|
98
|
+
// is a real error and propagates to the caller.
|
|
99
|
+
const planResult = inspector.inspectTaskInputs({
|
|
100
|
+
project,
|
|
101
|
+
target,
|
|
102
|
+
configuration,
|
|
103
|
+
});
|
|
104
|
+
const result = planResult[canonicalTaskId] ?? null;
|
|
105
|
+
hashInputsCache.set(taskId, result);
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
function getOutputs(taskId, projectGraph) {
|
|
109
|
+
const cached = outputsCache.get(taskId);
|
|
110
|
+
if (cached !== undefined)
|
|
111
|
+
return cached;
|
|
112
|
+
const { project, target, configuration, projectNode } = resolveIdentity(taskId, projectGraph);
|
|
113
|
+
const outputs = (0, utils_1.getOutputsForTargetAndConfiguration)({ project, target, configuration }, {}, projectNode).map(path_2.normalizePath);
|
|
114
|
+
outputsCache.set(taskId, outputs);
|
|
115
|
+
return outputs;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Configured outputs that `getOutputsForTargetAndConfiguration` dropped because
|
|
119
|
+
* an `{options.x}` token had no value to interpolate. The resolver discards them
|
|
120
|
+
* silently, so they have to be recovered from the target configuration.
|
|
121
|
+
*/
|
|
122
|
+
function getUnresolvedOutputs(taskId, projectGraph) {
|
|
123
|
+
const { target, configuration, projectNode } = resolveIdentity(taskId, projectGraph);
|
|
124
|
+
const targetConfig = projectNode.data.targets[target];
|
|
125
|
+
const options = {
|
|
126
|
+
...targetConfig.options,
|
|
127
|
+
...(configuration
|
|
128
|
+
? targetConfig.configurations?.[configuration]
|
|
129
|
+
: undefined),
|
|
130
|
+
};
|
|
131
|
+
return (targetConfig.outputs ?? []).filter((output) => [...output.matchAll(/\{options\.([^}]+)\}/g)].some(([, key]) => {
|
|
132
|
+
const value = key.split('.').reduce((acc, k) => acc?.[k], options);
|
|
133
|
+
return value === undefined;
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
function getTaskGraph(taskId, projectGraph) {
|
|
137
|
+
const cached = taskGraphCache.get(taskId);
|
|
138
|
+
if (cached)
|
|
139
|
+
return cached;
|
|
140
|
+
const { project, target, configuration } = resolveIdentity(taskId, projectGraph);
|
|
141
|
+
const taskGraph = (0, create_task_graph_1.createTaskGraph)(projectGraph, {}, [project], [target], configuration, {}, false);
|
|
142
|
+
taskGraphCache.set(taskId, taskGraph);
|
|
143
|
+
return taskGraph;
|
|
144
|
+
}
|
|
145
|
+
function getDepsOutputs(taskId, { projectGraph, nxJson }) {
|
|
146
|
+
if (depsOutputsCache.has(taskId))
|
|
147
|
+
return depsOutputsCache.get(taskId);
|
|
148
|
+
const { project, target } = resolveIdentity(taskId, projectGraph);
|
|
149
|
+
const result = (0, task_hasher_1.getInputs)({ target: { project, target } }, projectGraph, nxJson)
|
|
150
|
+
.depsOutputs ?? [];
|
|
151
|
+
depsOutputsCache.set(taskId, result);
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
function collectUpstreamTaskIds(taskGraph, rootTaskId, transitive) {
|
|
155
|
+
const direct = taskGraph.dependencies[rootTaskId] ?? [];
|
|
156
|
+
if (!transitive)
|
|
157
|
+
return [...direct];
|
|
158
|
+
const collected = new Set();
|
|
159
|
+
const walk = (id) => {
|
|
160
|
+
for (const dep of taskGraph.dependencies[id] ?? []) {
|
|
161
|
+
if (collected.has(dep))
|
|
162
|
+
continue;
|
|
163
|
+
collected.add(dep);
|
|
164
|
+
walk(dep);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
walk(rootTaskId);
|
|
168
|
+
return [...collected];
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Matches a single path against a task's whole output pattern list using the
|
|
172
|
+
* native glob engine (`globset`) that the task runner's expand_outputs also
|
|
173
|
+
* builds on: non-glob patterns match themselves and anything nested under them,
|
|
174
|
+
* and negated (`!`-prefixed) patterns act as exclusions over the full set.
|
|
175
|
+
*/
|
|
176
|
+
function isOutput(taskId, path, projectGraph) {
|
|
177
|
+
const patterns = getOutputs(taskId, projectGraph);
|
|
178
|
+
return (0, native_1.matchOutputPaths)(patterns, [(0, path_2.normalizePath)(path)])[0];
|
|
179
|
+
}
|
|
180
|
+
function matchesDependentTaskOutputs(taskId, path, ctx) {
|
|
181
|
+
const normalized = (0, path_2.normalizePath)(path);
|
|
182
|
+
const depsOutputs = getDepsOutputs(taskId, ctx);
|
|
183
|
+
if (depsOutputs.length === 0)
|
|
184
|
+
return false;
|
|
185
|
+
const taskGraph = getTaskGraph(taskId, ctx.projectGraph);
|
|
186
|
+
const { canonicalTaskId } = resolveIdentity(taskId, ctx.projectGraph);
|
|
187
|
+
if (!taskGraph.tasks[canonicalTaskId])
|
|
188
|
+
return false;
|
|
189
|
+
for (const { dependentTasksOutputFiles, transitive } of depsOutputs) {
|
|
190
|
+
const glob = (0, path_2.normalizePath)(dependentTasksOutputFiles);
|
|
191
|
+
if (!(0, native_1.matchGlobPaths)([glob], [normalized])[0])
|
|
192
|
+
continue;
|
|
193
|
+
const upstreamIds = collectUpstreamTaskIds(taskGraph, canonicalTaskId, !!transitive);
|
|
194
|
+
for (const upstreamId of upstreamIds) {
|
|
195
|
+
if (isOutput(upstreamId, normalized, ctx.projectGraph))
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Coerces a caller-supplied path to the workspace-relative, forward-slashed
|
|
203
|
+
* form the hash plan and output patterns are expressed in. A path outside the
|
|
204
|
+
* workspace stays outside (`../…`) and simply matches nothing — it cannot be a
|
|
205
|
+
* declared input or output, so "unmatched" is the true answer rather than an
|
|
206
|
+
* error.
|
|
207
|
+
*/
|
|
208
|
+
function toWorkspaceRelativePath(candidatePath) {
|
|
209
|
+
// Backslash separators must be split *before* anchoring: on POSIX a backslash
|
|
210
|
+
// is an ordinary filename character, so `dep\..\..` would ride through
|
|
211
|
+
// join/relative as one opaque segment and only become a live `..` traversal
|
|
212
|
+
// after the swap. Swapped directly rather than via normalizePath, which also
|
|
213
|
+
// strips a Windows drive letter: isAbsolute accepts the drive-less form too,
|
|
214
|
+
// but relative() then resolves it against the cwd's drive, so a path on
|
|
215
|
+
// another drive (`D:\…`) could be relativized to *inside* the workspace — a
|
|
216
|
+
// Windows-only fail-open no POSIX test can catch.
|
|
217
|
+
const forwardSlashed = candidatePath.replace(/\\/g, '/');
|
|
218
|
+
// Anchoring a relative path to the workspace root before relativizing it back
|
|
219
|
+
// resolves any `..` segments. Left in, they would traverse *through* a pattern
|
|
220
|
+
// that globset had already matched — `dist/libs/dep/../../../secrets.env`
|
|
221
|
+
// matching an output of `dist/libs/dep`.
|
|
222
|
+
const absolute = (0, path_1.isAbsolute)(forwardSlashed)
|
|
223
|
+
? forwardSlashed
|
|
224
|
+
: (0, path_1.join)(workspace_root_1.workspaceRoot, forwardSlashed);
|
|
225
|
+
return (0, path_2.normalizePath)((0, path_1.relative)(workspace_root_1.workspaceRoot, absolute));
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* The task's hash inputs, or an error. A task missing from its own hash plan is
|
|
229
|
+
* a failure to *determine* the inputs — reporting every file as unmatched would
|
|
230
|
+
* tell a sandbox-violation consumer that all of them are illegal.
|
|
231
|
+
*/
|
|
232
|
+
async function requireRawInputs(taskId, ctx) {
|
|
233
|
+
const raw = await getRawInputs(taskId, ctx);
|
|
234
|
+
if (!raw) {
|
|
235
|
+
throw new Error(`Could not determine the inputs of task "${taskId}" — it is not present in its own hash plan.`);
|
|
236
|
+
}
|
|
237
|
+
return raw;
|
|
238
|
+
}
|
|
239
|
+
function classifyInput(taskId, candidate, raw, ctx) {
|
|
240
|
+
// `environment`, `runtime` and `external` hold names rather than paths, so
|
|
241
|
+
// they are matched against the value exactly as the caller supplied it.
|
|
242
|
+
if (raw.environment.includes(candidate.value))
|
|
243
|
+
return 'environment';
|
|
244
|
+
if (raw.runtime.includes(candidate.value))
|
|
245
|
+
return 'runtime';
|
|
246
|
+
if (raw.external.includes(candidate.value))
|
|
247
|
+
return 'external';
|
|
248
|
+
const path = toWorkspaceRelativePath(candidate.path);
|
|
249
|
+
if (raw.files.includes(path))
|
|
250
|
+
return 'files';
|
|
251
|
+
if (raw.depOutputs.includes(path))
|
|
252
|
+
return 'depOutputs';
|
|
253
|
+
return matchesDependentTaskOutputs(taskId, path, ctx)
|
|
254
|
+
? 'dependentTasksOutputFiles'
|
|
255
|
+
: null;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Check which values are legitimate inputs for the given task. A value matches
|
|
259
|
+
* when it is:
|
|
260
|
+
* - a declared environment variable, runtime input, or external dependency;
|
|
261
|
+
* - a file in the task's declared input file list;
|
|
262
|
+
* - a file in the task's materialized `depOutputs` (upstream has run);
|
|
263
|
+
* - a file matching a `dependentTasksOutputFiles` glob declared on the task
|
|
264
|
+
* that lies inside the declared outputs of an upstream task in the task
|
|
265
|
+
* graph (static — works even when upstream tasks have not yet run).
|
|
266
|
+
*
|
|
267
|
+
* `categories` records the rule each matched value satisfied. Paths may be
|
|
268
|
+
* workspace-relative or absolute; absolute ones are relativized against the
|
|
269
|
+
* workspace root, and a path outside the workspace simply matches nothing. A
|
|
270
|
+
* caller resolving paths against a cwd passes an {@link InputCandidate} so that
|
|
271
|
+
* names are still matched verbatim.
|
|
272
|
+
*/
|
|
273
|
+
async function checkFilesAreInputs(taskId, files) {
|
|
274
|
+
const ctx = await getContext();
|
|
275
|
+
// Resolve the task and its hash plan eagerly, so an unknown task or an
|
|
276
|
+
// undeterminable plan errors even when the file list is empty — rather than
|
|
277
|
+
// being reported as "none of these files are inputs".
|
|
278
|
+
resolveIdentity(taskId, ctx.projectGraph);
|
|
279
|
+
const raw = await requireRawInputs(taskId, ctx);
|
|
280
|
+
const matched = [];
|
|
281
|
+
const unmatched = [];
|
|
282
|
+
const categories = new Map();
|
|
283
|
+
// Results are keyed by value, so a value given two path forms could land in
|
|
284
|
+
// both matched and unmatched at once. Exact duplicates are collapsed instead.
|
|
285
|
+
const seenPaths = new Map();
|
|
286
|
+
for (const file of files) {
|
|
287
|
+
const candidate = typeof file === 'string' ? { value: file, path: file } : file;
|
|
288
|
+
const seenPath = seenPaths.get(candidate.value);
|
|
289
|
+
if (seenPath !== undefined) {
|
|
290
|
+
if (seenPath !== candidate.path) {
|
|
291
|
+
throw new Error(`Value "${candidate.value}" was given conflicting path forms "${seenPath}" and "${candidate.path}".`);
|
|
292
|
+
}
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
seenPaths.set(candidate.value, candidate.path);
|
|
296
|
+
const category = classifyInput(taskId, candidate, raw, ctx);
|
|
297
|
+
if (category) {
|
|
298
|
+
matched.push(candidate.value);
|
|
299
|
+
categories.set(candidate.value, category);
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
unmatched.push(candidate.value);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return { matched, unmatched, categories };
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Check which files match the output globs declared for the given task.
|
|
309
|
+
* Uses the same path-matching logic as the task runner (directory containment
|
|
310
|
+
* + glob matching through the native `globset` engine), including negated
|
|
311
|
+
* (`!`-prefixed) patterns acting as exclusions over the whole pattern set.
|
|
312
|
+
*
|
|
313
|
+
* Paths may be workspace-relative or absolute; absolute ones are relativized
|
|
314
|
+
* against the workspace root. An output pattern whose `{options.*}` token has no
|
|
315
|
+
* value resolves to nothing — exactly as the task runner drops it — so a file it
|
|
316
|
+
* would have covered is reported `unmatched`, like any other non-output.
|
|
317
|
+
*
|
|
318
|
+
* That last case makes `unmatched` two answers in one: "not an output" and
|
|
319
|
+
* "the outputs could not be determined". A consumer judging sandbox violations
|
|
320
|
+
* cannot tell them apart, and would call the second one illegal. `getTaskOutputs`
|
|
321
|
+
* already computes the `unresolved` list this would need; surfacing it here is
|
|
322
|
+
* deliberately deferred until a consumer's contract asks for the distinction.
|
|
323
|
+
*/
|
|
324
|
+
async function checkFilesAreOutputs(taskId, files) {
|
|
325
|
+
const ctx = await getContext();
|
|
326
|
+
// Validate taskId eagerly so callers always get an error for an unknown or
|
|
327
|
+
// malformed task, even when the file list is empty.
|
|
328
|
+
resolveIdentity(taskId, ctx.projectGraph);
|
|
329
|
+
const patterns = getOutputs(taskId, ctx.projectGraph);
|
|
330
|
+
const results = (0, native_1.matchOutputPaths)(patterns, files.map(toWorkspaceRelativePath));
|
|
331
|
+
const matched = [];
|
|
332
|
+
const unmatched = [];
|
|
333
|
+
files.forEach((file, i) => {
|
|
334
|
+
if (results[i]) {
|
|
335
|
+
matched.push(file);
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
unmatched.push(file);
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
return { matched, unmatched };
|
|
342
|
+
}
|
|
343
|
+
// ── Renderer helpers (used by `nx show target`) ──────────────────────────────
|
|
344
|
+
/**
|
|
345
|
+
* Returns the full hash inputs for a task (files + runtime + env + depOutputs
|
|
346
|
+
* + external). Used internally by the `nx show target --inputs` renderer.
|
|
347
|
+
*/
|
|
348
|
+
async function getTaskRawInputs(taskId, seed) {
|
|
349
|
+
const ctx = await getContext(seed);
|
|
350
|
+
return getRawInputs(taskId, ctx);
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Returns the outputs declared for a task, resolved against its effective
|
|
354
|
+
* configuration. Used internally by the `nx show target --outputs` renderer.
|
|
355
|
+
*/
|
|
356
|
+
async function getTaskOutputs(taskId, seed) {
|
|
357
|
+
const ctx = await getContext(seed);
|
|
358
|
+
const resolved = getOutputs(taskId, ctx.projectGraph);
|
|
359
|
+
return {
|
|
360
|
+
resolved,
|
|
361
|
+
expanded: (0, native_1.expandOutputs)(workspace_root_1.workspaceRoot, resolved),
|
|
362
|
+
unresolved: getUnresolvedOutputs(taskId, ctx.projectGraph),
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
// ── Test utilities ───────────────────────────────────────────────────────────
|
|
366
|
+
/**
|
|
367
|
+
* Resets all module-level caches. Call this in `beforeEach` when testing so
|
|
368
|
+
* each test gets a fresh context load. Not part of the public API.
|
|
369
|
+
* @internal
|
|
370
|
+
*/
|
|
371
|
+
function _resetContextForTesting() {
|
|
372
|
+
cachedContext = null;
|
|
373
|
+
identityCache.clear();
|
|
374
|
+
hashInputsCache.clear();
|
|
375
|
+
outputsCache.clear();
|
|
376
|
+
taskGraphCache.clear();
|
|
377
|
+
depsOutputsCache.clear();
|
|
378
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Tree } from '../../generators/tree';
|
|
2
|
+
/**
|
|
3
|
+
* Target defaults resolve to a single key rather than merging, so an executor
|
|
4
|
+
* key hides the target name key entirely. Before Nx 23 a hidden `cache: true`
|
|
5
|
+
* still took effect, because cacheability was also derived from target *names*
|
|
6
|
+
* via `cacheableOperations`; Nx 23 removed that derivation and those targets
|
|
7
|
+
* silently stopped being cacheable.
|
|
8
|
+
*
|
|
9
|
+
* Write the intent into the executor key so it no longer depends on the
|
|
10
|
+
* deprecated fallback that restores the old behavior at run time.
|
|
11
|
+
*/
|
|
12
|
+
export default function update(tree: Tree): Promise<void>;
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = update;
|
|
4
|
+
const executor_utils_1 = require("../../command-line/run/executor-utils");
|
|
5
|
+
const format_changed_files_with_prettier_if_available_1 = require("../../generators/internal-utils/format-changed-files-with-prettier-if-available");
|
|
6
|
+
const json_1 = require("../../generators/utils/json");
|
|
7
|
+
const project_configuration_1 = require("../../generators/utils/project-configuration");
|
|
8
|
+
const path_1 = require("../../utils/path");
|
|
9
|
+
// TODO(v24): remove this migration alongside the runtime fallback it exists to
|
|
10
|
+
// retire (`isLegacyCachedTarget` in `target-normalization.ts`), along with its
|
|
11
|
+
// registration in `packages/nx/migrations.json`.
|
|
12
|
+
/**
|
|
13
|
+
* Target defaults resolve to a single key rather than merging, so an executor
|
|
14
|
+
* key hides the target name key entirely. Before Nx 23 a hidden `cache: true`
|
|
15
|
+
* still took effect, because cacheability was also derived from target *names*
|
|
16
|
+
* via `cacheableOperations`; Nx 23 removed that derivation and those targets
|
|
17
|
+
* silently stopped being cacheable.
|
|
18
|
+
*
|
|
19
|
+
* Write the intent into the executor key so it no longer depends on the
|
|
20
|
+
* deprecated fallback that restores the old behavior at run time.
|
|
21
|
+
*/
|
|
22
|
+
async function update(tree) {
|
|
23
|
+
const nxJson = (0, project_configuration_1.readNxJson)(tree);
|
|
24
|
+
const targetDefaults = nxJson?.targetDefaults;
|
|
25
|
+
if (!targetDefaults) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const projects = (0, project_configuration_1.getProjects)(tree);
|
|
29
|
+
const projectsMap = {};
|
|
30
|
+
// `cache` on an executor key reaches every target that resolves through it,
|
|
31
|
+
// not just the one whose target name key enables caching. Collect them all
|
|
32
|
+
// per key so the decision below can be made for the key as a whole. The
|
|
33
|
+
// collection is a list rather than a map keyed by target name: two projects
|
|
34
|
+
// routinely declare the same target name through the same executor, and only
|
|
35
|
+
// one of them may be continuous.
|
|
36
|
+
const targetsByExecutorKey = new Map();
|
|
37
|
+
const collect = (targetName, target) => {
|
|
38
|
+
// `hasOwnProperty` rather than a lookup: an executor named `__proto__` or
|
|
39
|
+
// `constructor` resolves through the prototype chain to a truthy object, so
|
|
40
|
+
// a plain lookup would collect the target and stamp `Object.prototype`.
|
|
41
|
+
if (!target?.executor ||
|
|
42
|
+
!Object.prototype.hasOwnProperty.call(targetDefaults, target.executor)) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const targets = targetsByExecutorKey.get(target.executor) ?? [];
|
|
46
|
+
targets.push([targetName, target]);
|
|
47
|
+
targetsByExecutorKey.set(target.executor, targets);
|
|
48
|
+
};
|
|
49
|
+
for (const [projectName, project] of projects) {
|
|
50
|
+
projectsMap[projectName] = project;
|
|
51
|
+
for (const [targetName, target] of Object.entries(project.targets ?? {})) {
|
|
52
|
+
collect(targetName, target);
|
|
53
|
+
}
|
|
54
|
+
for (const [targetName, target] of Object.entries(packageJsonTargets(tree, project.root))) {
|
|
55
|
+
collect(targetName, target);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
let changed = false;
|
|
59
|
+
for (const [key, targets] of targetsByExecutorKey) {
|
|
60
|
+
if (!canEnableCache(targetDefaults, key, targets, tree.root, projectsMap)) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
catchAllConfig(targetDefaults[key]).cache = true;
|
|
64
|
+
changed = true;
|
|
65
|
+
}
|
|
66
|
+
if (!changed) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
(0, project_configuration_1.updateNxJson)(tree, nxJson);
|
|
70
|
+
await (0, format_changed_files_with_prettier_if_available_1.formatChangedFilesWithPrettierIfAvailable)(tree);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Whether `cache: true` can be written onto `key` without changing behavior for
|
|
74
|
+
* any target that resolves through it.
|
|
75
|
+
*
|
|
76
|
+
* The executor key outranks every target name key, so stamping it caches all of
|
|
77
|
+
* `targets` — including any that is continuous, which makes `nx.json` fail graph
|
|
78
|
+
* construction outright. The key is only safe when every target through it
|
|
79
|
+
* independently wants caching and none of them can be continuous. Anything else
|
|
80
|
+
* is left to the runtime fallback, which decides per target after normalization
|
|
81
|
+
* and can therefore see continuity this migration cannot.
|
|
82
|
+
*/
|
|
83
|
+
function canEnableCache(targetDefaults, key, targets, workspaceRoot, projectsMap) {
|
|
84
|
+
// Targets written with `command` are rewritten to `nx:run-commands` and
|
|
85
|
+
// `package.json` scripts to `nx:run-script` before key selection, so neither
|
|
86
|
+
// names the key it resolves through. Plugins also infer continuous targets
|
|
87
|
+
// (`watch-deps`, dev servers) into projects whose config never mentions them.
|
|
88
|
+
// The target list here can never be trusted to be complete for these keys.
|
|
89
|
+
if (key === 'nx:run-commands' || key === 'nx:run-script') {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
// Already decided; never override the user's value. A filtered entry counts
|
|
93
|
+
// as decided because its value can't be evaluated without project context.
|
|
94
|
+
if (declaresAnyCache(targetDefaults[key])) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
// `continuous` is a valid field on a target default, and this key applies it
|
|
98
|
+
// to every target that resolves through it. Adding `cache` would make each of
|
|
99
|
+
// them both cacheable and continuous, which fails graph construction — so no
|
|
100
|
+
// target list can make this key safe.
|
|
101
|
+
if (declaresAnyContinuous(targetDefaults[key])) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
// Only an unfiltered entry can be amended. Appending a catch-all to an
|
|
105
|
+
// all-filtered key would make it match targets that previously fell through
|
|
106
|
+
// to the target name key, silently dropping that key's `dependsOn`/`inputs`.
|
|
107
|
+
if (!catchAllConfig(targetDefaults[key])) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
// Continuity declared by the executor's schema is resolved at graph
|
|
111
|
+
// construction for every target through this key, so one lookup covers them
|
|
112
|
+
// all. For what an executor that cannot be resolved here costs, see
|
|
113
|
+
// {@link executorDeclaresContinuous}.
|
|
114
|
+
if (executorDeclaresContinuous(key, workspaceRoot, projectsMap)) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
for (const [targetName, target] of targets) {
|
|
118
|
+
if (!declaresCacheTrue(targetDefaults[targetName])) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
if (target.continuous || isLongRunningTargetName(targetName)) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Whether the target name key declares `cache: true` on an entry that always
|
|
129
|
+
* applies. Mirrors `declaresCacheTrue` in `target-normalization.ts`: a filtered
|
|
130
|
+
* entry declaring `cache` makes the value unknowable without project context,
|
|
131
|
+
* so nothing is claimed. Among unfiltered entries the last wins.
|
|
132
|
+
*/
|
|
133
|
+
function declaresCacheTrue(value) {
|
|
134
|
+
if (!value) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
let declared;
|
|
138
|
+
for (const entry of configEntries(value)) {
|
|
139
|
+
if (entry.cache === undefined)
|
|
140
|
+
continue;
|
|
141
|
+
if (entry.filter)
|
|
142
|
+
return false;
|
|
143
|
+
declared = entry.cache;
|
|
144
|
+
}
|
|
145
|
+
return declared === true;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Whether any entry declares `cache`, filtered or not — i.e. whether the value
|
|
149
|
+
* is already decided and must be left alone. Deliberately broader than
|
|
150
|
+
* {@link declaresCacheTrue}: a filtered `cache` blocks here rather than being
|
|
151
|
+
* read past, so the two never disagree about a filtered entry.
|
|
152
|
+
*/
|
|
153
|
+
function declaresAnyCache(value) {
|
|
154
|
+
if (!value) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
return configEntries(value).some((entry) => entry.cache !== undefined);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Whether any entry declares `continuous: true`. Filtered entries count: which
|
|
161
|
+
* projects the filter matches is unknowable here, and the only case that
|
|
162
|
+
* matters is the one where it matches.
|
|
163
|
+
*/
|
|
164
|
+
function declaresAnyContinuous(value) {
|
|
165
|
+
if (!value) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
return configEntries(value).some((entry) => entry.continuous === true);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* The config blocks of a `targetDefaults` value. Entries that are not config
|
|
172
|
+
* objects are dropped rather than inspected: `nx.json` is hand-edited, and a
|
|
173
|
+
* `null` or scalar entry would otherwise throw while reading `.cache` off it.
|
|
174
|
+
*/
|
|
175
|
+
function configEntries(value) {
|
|
176
|
+
const entries = Array.isArray(value) ? value : [value];
|
|
177
|
+
return entries.filter((entry) => !!entry && typeof entry === 'object');
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* The targets a project's `package.json` contributes to the graph.
|
|
181
|
+
*
|
|
182
|
+
* {@link getProjects} builds a root's configuration from its `project.json`
|
|
183
|
+
* alone whenever one exists, but the package.json plugin still creates the
|
|
184
|
+
* sibling `package.json`'s targets — it accepts any `package.json` that is
|
|
185
|
+
* either in the package manager workspaces or next to a `project.json`
|
|
186
|
+
* (`plugins/package-json/create-nodes.ts`). Those targets resolve through the
|
|
187
|
+
* same executor keys, so they belong in this decision.
|
|
188
|
+
*
|
|
189
|
+
* Targets derived from `scripts` are still not covered; they resolve through
|
|
190
|
+
* `nx:run-script`, which is never stamped for exactly that reason.
|
|
191
|
+
*/
|
|
192
|
+
function packageJsonTargets(tree, projectRoot) {
|
|
193
|
+
const packageJsonPath = (0, path_1.joinPathFragments)(projectRoot, 'package.json');
|
|
194
|
+
if (!tree.exists(packageJsonPath)) {
|
|
195
|
+
return {};
|
|
196
|
+
}
|
|
197
|
+
// A parse failure is left to throw: `readJson` names the offending file,
|
|
198
|
+
// which is the one thing a user can act on. Shapes that parse but aren't the
|
|
199
|
+
// one this reads contribute no targets rather than crashing the run.
|
|
200
|
+
const targets = (0, json_1.readJson)(tree, packageJsonPath)?.nx?.targets;
|
|
201
|
+
if (!targets || typeof targets !== 'object' || Array.isArray(targets)) {
|
|
202
|
+
return {};
|
|
203
|
+
}
|
|
204
|
+
return targets;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The unfiltered config block of a `targetDefaults` value, or undefined when the
|
|
208
|
+
* array form carries only filtered entries and when the value is not a config
|
|
209
|
+
* object at all. Never creates one — see {@link canEnableCache}.
|
|
210
|
+
*/
|
|
211
|
+
function catchAllConfig(value) {
|
|
212
|
+
// The filter check applies to both spellings: the object and single-element
|
|
213
|
+
// array forms of the same config have to reach the same verdict.
|
|
214
|
+
return configEntries(value).find((entry) => entry.filter === undefined);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Whether the executor's schema marks its targets continuous, resolved through
|
|
218
|
+
* the same `getExecutorInformation` call `normalizeTarget` makes at graph
|
|
219
|
+
* construction, so the two cannot drift on what continuous means.
|
|
220
|
+
*
|
|
221
|
+
* An executor that cannot be resolved is read as not continuous. That is the
|
|
222
|
+
* same answer `normalizeTarget` gives, but not the same trade: its lookup is
|
|
223
|
+
* repeated on every graph construction, so a failure there corrects itself,
|
|
224
|
+
* whereas this one decides a permanent edit to `nx.json`. An executor that is
|
|
225
|
+
* unresolvable while the migration runs and resolvable afterwards therefore
|
|
226
|
+
* keeps its stamped `cache` while the runtime starts marking its targets
|
|
227
|
+
* continuous — which `normalizeTargets` rejects with a `WorkspaceValidityError`,
|
|
228
|
+
* failing graph construction until `nx.json` is edited by hand.
|
|
229
|
+
*/
|
|
230
|
+
function executorDeclaresContinuous(executor, workspaceRoot, projectsMap) {
|
|
231
|
+
try {
|
|
232
|
+
const [nodeModule, name] = (0, executor_utils_1.parseExecutor)(executor);
|
|
233
|
+
const { schema } = (0, executor_utils_1.getExecutorInformation)(nodeModule, name, workspaceRoot, projectsMap);
|
|
234
|
+
return !!schema.continuous;
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Target names the pre-23 `longRunningTask` guard excluded from caching.
|
|
242
|
+
* Mirrors `isLongRunningTarget` in `target-normalization.ts`.
|
|
243
|
+
*/
|
|
244
|
+
function isLongRunningTargetName(targetName) {
|
|
245
|
+
return (targetName.endsWith(':watch') ||
|
|
246
|
+
targetName.endsWith('-watch') ||
|
|
247
|
+
targetName === 'serve' ||
|
|
248
|
+
targetName === 'dev' ||
|
|
249
|
+
targetName === 'start');
|
|
250
|
+
}
|