nx 23.3.0 → 24.0.0-beta.0

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 (66) hide show
  1. package/README.md +3 -6
  2. package/dist/src/adapter/ngcli-adapter.js +1 -0
  3. package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +0 -12
  4. package/dist/src/command-line/graph/graph.js +3 -5
  5. package/dist/src/command-line/init/command-object.js +0 -12
  6. package/dist/src/command-line/init/init-v2.js +0 -2
  7. package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +17 -12
  8. package/dist/src/command-line/release/utils/remote-release-clients/github.js +5 -6
  9. package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +5 -6
  10. package/dist/src/command-line/run/executor-utils.js +1 -0
  11. package/dist/src/command-line/run/run-one.d.ts +20 -1
  12. package/dist/src/command-line/run/run-one.js +153 -5
  13. package/dist/src/command-line/show/show-target/utils.js +1 -0
  14. package/dist/src/command-line/watch/command-object.js +3 -22
  15. package/dist/src/command-line/watch/watch.d.ts +0 -7
  16. package/dist/src/command-line/watch/watch.js +1 -1
  17. package/dist/src/config/misc-interfaces.d.ts +12 -0
  18. package/dist/src/config/workspace-json-project-json.d.ts +5 -0
  19. package/dist/src/daemon/client/client.d.ts +9 -0
  20. package/dist/src/daemon/client/client.js +89 -37
  21. package/dist/src/daemon/server/shutdown-utils.js +0 -3
  22. package/dist/src/daemon/socket-utils.d.ts +9 -4
  23. package/dist/src/daemon/socket-utils.js +12 -49
  24. package/dist/src/daemon/tmp-dir.d.ts +19 -14
  25. package/dist/src/daemon/tmp-dir.js +142 -354
  26. package/dist/src/devkit-internals.d.ts +1 -1
  27. package/dist/src/devkit-internals.js +1 -0
  28. package/dist/src/hasher/file-hasher.js +9 -7
  29. package/dist/src/hasher/hash-task.js +4 -0
  30. package/dist/src/hasher/native-task-hasher-impl.js +1 -1
  31. package/dist/src/migrations/update-24-0-0/find-custom-hashers.d.ts +3 -0
  32. package/dist/src/migrations/update-24-0-0/find-custom-hashers.js +100 -0
  33. package/dist/src/migrations/update-24-0-0/find-custom-hashers.md +63 -0
  34. package/dist/src/migrations/update-24-0-0/move-custom-hashers-to-inputs.md +145 -0
  35. package/dist/src/native/index.d.ts +127 -8
  36. package/dist/src/native/native-bindings.js +5 -0
  37. package/dist/src/native/nx.wasi-browser.js +1 -0
  38. package/dist/src/native/nx.wasi.cjs +1 -0
  39. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  40. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  41. package/dist/src/nx-cloud/update-manager.d.ts +7 -0
  42. package/dist/src/nx-cloud/update-manager.js +237 -50
  43. package/dist/src/project-graph/affected/affected-tasks.js +1 -0
  44. package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +7 -0
  45. package/dist/src/tasks-runner/create-task-graph.d.ts +10 -0
  46. package/dist/src/tasks-runner/create-task-graph.js +37 -2
  47. package/dist/src/tasks-runner/task-env.d.ts +10 -0
  48. package/dist/src/tasks-runner/task-env.js +26 -5
  49. package/dist/src/tasks-runner/task-orchestrator.d.ts +10 -4
  50. package/dist/src/tasks-runner/task-orchestrator.js +87 -53
  51. package/dist/src/tasks-runner/utils.d.ts +20 -5
  52. package/dist/src/tasks-runner/utils.js +80 -29
  53. package/dist/src/ultracache/overrides.js +4 -0
  54. package/dist/src/utils/owned-private-dir.d.ts +1 -17
  55. package/dist/src/utils/owned-private-dir.js +10 -62
  56. package/dist/src/utils/perf-hooks.d.ts +1 -0
  57. package/dist/src/utils/plugins/core-plugins.js +1 -1
  58. package/dist/src/utils/string-similarity.d.ts +38 -0
  59. package/dist/src/utils/string-similarity.js +108 -0
  60. package/migrations.json +7 -0
  61. package/package.json +11 -20
  62. package/readme-template.md +3 -3
  63. package/schemas/nx-schema.json +5 -0
  64. package/schemas/project-schema.json +5 -0
  65. package/dist/src/tasks-runner/legacy-depends-on-warning.d.ts +0 -18
  66. package/dist/src/tasks-runner/legacy-depends-on-warning.js +0 -109
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
- <p style="text-align: center;">
1
+ <p align="center">
2
2
  <picture>
3
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-dark.svg">
4
- <img alt="Nx - Smart Repos · Fast Builds" src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-light.svg" width="100%">
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-logo-light.svg">
4
+ <img alt="Nx Logo" src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-logo.svg" width="140">
5
5
  </picture>
6
6
  </p>
7
7
 
@@ -60,6 +60,3 @@ npx nx@latest init
60
60
  - [Official Nx YouTube Channel](https://www.youtube.com/@NxDevtools)
61
61
  - [Blog Posts About Nx](https://nx.dev/blog)
62
62
 
63
- <p style="text-align: center;"><a href="https://nx.dev/#learning-materials" target="_blank" rel="noreferrer"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-courses-and-videos.svg"
64
- width="100%" alt="Nx - Smart Monorepos · Fast Builds"></a></p>
65
-
@@ -991,6 +991,7 @@ async function getWrappedWorkspaceNodeModulesArchitectHost(workspace, root, proj
991
991
  const batchImplementationFactory = executorConfig.batchImplementation
992
992
  ? this.getImplementationFactory(executorConfig.batchImplementation, executorsDir, nodeModule)
993
993
  : null;
994
+ // TODO(v25): remove custom hasher support.
994
995
  const hasherFactory = executorConfig.hasher
995
996
  ? this.getImplementationFactory(executorConfig.hasher, executorsDir, nodeModule)
996
997
  : null;
@@ -64,18 +64,6 @@ async function configureAiAgentsHandler(args, inner = false) {
64
64
  await resetDaemonAgentStatus();
65
65
  }
66
66
  async function configureAiAgentsHandlerImpl(options) {
67
- // Node 24's stricter readline throws ERR_USE_AFTER_CLOSE when a prompt
68
- // library operates on a closed interface. Added for enquirer, which nx no
69
- // longer uses; verify against Node 24 before removing.
70
- // TODO(v24): drop if @clack/prompts proves not to need it.
71
- process.on('uncaughtException', (error) => {
72
- if (error &&
73
- typeof error === 'object' &&
74
- 'code' in error &&
75
- error['code'] === 'ERR_USE_AFTER_CLOSE')
76
- return;
77
- throw error;
78
- });
79
67
  const normalizedOptions = normalizeOptions(options);
80
68
  const { nonConfiguredAgents, partiallyConfiguredAgents, fullyConfiguredAgents, disabledAgents, } = await (0, utils_1.getAgentConfigurations)(normalizedOptions.agents, workspace_root_1.workspaceRoot);
81
69
  if (disabledAgents.length > 0) {
@@ -372,11 +372,9 @@ async function generateGraph(args, affectedProjects) {
372
372
  title: `Project graph started at ${url.toString()}`,
373
373
  });
374
374
  if (args.open) {
375
- new Function('return import("open")')()
376
- .then((m) => m.default(url.toString()))
377
- .catch(() => {
378
- // Ignore errors when opening browser (e.g. no browser available)
379
- });
375
+ // Best-effort: openUrl never throws, returning false if no opener could be
376
+ // spawned. The URL is printed above, so a failure is silent.
377
+ (0, native_1.openUrl)(url.toString());
380
378
  }
381
379
  return new Promise((res) => {
382
380
  app.once('close', res);
@@ -17,18 +17,6 @@ exports.yargsInitCommand = {
17
17
  return withInitOptions(yargs);
18
18
  },
19
19
  handler: async (args) => {
20
- // Node 24's stricter readline throws ERR_USE_AFTER_CLOSE when a prompt
21
- // library operates on a closed interface. Added for enquirer, which nx no
22
- // longer uses; verify against Node 24 before removing.
23
- // TODO(v24): drop if @clack/prompts proves not to need it.
24
- process.on('uncaughtException', (error) => {
25
- if (error &&
26
- typeof error === 'object' &&
27
- 'code' in error &&
28
- error['code'] === 'ERR_USE_AFTER_CLOSE')
29
- return;
30
- throw error;
31
- });
32
20
  const useV2 = await isInitV2();
33
21
  if (useV2) {
34
22
  await require('./init-v2').initHandler(args);
@@ -442,7 +442,6 @@ function getPluginReason(plugin) {
442
442
  '@nx/next': 'next.js detected in dependencies',
443
443
  '@nx/nuxt': 'nuxt detected in dependencies',
444
444
  '@nx/react-native': 'react-native detected in dependencies',
445
- '@nx/remix': '@remix-run/dev detected in dependencies',
446
445
  '@nx/rsbuild': '@rsbuild/core detected in dependencies',
447
446
  '@nx/react': '@react-router/dev detected in dependencies',
448
447
  '@nx/gradle': 'gradlew detected in workspace',
@@ -493,7 +492,6 @@ const npmPackageToPluginMap = {
493
492
  next: '@nx/next',
494
493
  nuxt: '@nx/nuxt',
495
494
  'react-native': '@nx/react-native',
496
- '@remix-run/dev': '@nx/remix',
497
495
  '@rsbuild/core': '@nx/rsbuild',
498
496
  '@react-router/dev': '@nx/react',
499
497
  };
@@ -155,23 +155,28 @@ async function runConnectToNxCloud(options, command) {
155
155
  installationSource: command ?? installationSource,
156
156
  });
157
157
  const connectCloudUrl = await (0, url_shorten_1.createNxCloudOnboardingURL)('nx-connect', token, undefined, options?.generateToken === true);
158
+ const manualConnectNote = {
159
+ title: `Your Nx Cloud workspace is ready.`,
160
+ bodyLines: [
161
+ `To claim it, connect it to your Nx Cloud account:`,
162
+ `- Go to the following URL to connect your workspace to Nx Cloud:`,
163
+ '',
164
+ `${connectCloudUrl}`,
165
+ ],
166
+ };
158
167
  try {
159
168
  const cloudConnectSpinner = ora(`Opening Nx Cloud ${connectCloudUrl} in your browser to connect your workspace.`).start();
160
169
  await sleep(2000);
161
- const { default: open } = await new Function('return import("open")')();
162
- await open(connectCloudUrl);
163
- cloudConnectSpinner.succeed();
170
+ if ((0, native_1.openUrl)(connectCloudUrl)) {
171
+ cloudConnectSpinner.succeed();
172
+ }
173
+ else {
174
+ cloudConnectSpinner.fail();
175
+ output_1.output.note(manualConnectNote);
176
+ }
164
177
  }
165
178
  catch (e) {
166
- output_1.output.note({
167
- title: `Your Nx Cloud workspace is ready.`,
168
- bodyLines: [
169
- `To claim it, connect it to your Nx Cloud account:`,
170
- `- Go to the following URL to connect your workspace to Nx Cloud:`,
171
- '',
172
- `${connectCloudUrl}`,
173
- ],
174
- });
179
+ output_1.output.note(manualConnectNote);
175
180
  }
176
181
  return true;
177
182
  }
@@ -5,6 +5,7 @@ const tslib_1 = require("tslib");
5
5
  const pc = tslib_1.__importStar(require("picocolors"));
6
6
  const prompt_helpers_1 = require("../../../../utils/prompt-helpers");
7
7
  const node_child_process_1 = require("node:child_process");
8
+ const native_1 = require("../../../../native");
8
9
  const node_fs_1 = require("node:fs");
9
10
  const node_os_1 = require("node:os");
10
11
  const output_1 = require("../../../../utils/output");
@@ -238,18 +239,16 @@ class GithubRemoteReleaseClient extends remote_release_client_1.RemoteReleaseCli
238
239
  if (!shouldContinueInGitHub) {
239
240
  return;
240
241
  }
241
- const { default: open } = await new Function('return import("open")')();
242
- await open(result.url)
243
- .then(() => {
242
+ if ((0, native_1.openUrl)(result.url)) {
244
243
  console.info(`\nFollow up in the browser to manually create the release:\n\n` +
245
244
  pc.underline(pc.cyan(result.url)) +
246
245
  `\n`);
247
- })
248
- .catch(() => {
246
+ }
247
+ else {
249
248
  console.info(`Open this link to manually create a release: \n` +
250
249
  pc.underline(pc.cyan(result.url)) +
251
250
  '\n');
252
- });
251
+ }
253
252
  }
254
253
  async promptForContinueInGitHub() {
255
254
  try {
@@ -5,6 +5,7 @@ const tslib_1 = require("tslib");
5
5
  const pc = tslib_1.__importStar(require("picocolors"));
6
6
  const prompt_helpers_1 = require("../../../../utils/prompt-helpers");
7
7
  const node_child_process_1 = require("node:child_process");
8
+ const native_1 = require("../../../../native");
8
9
  const output_1 = require("../../../../utils/output");
9
10
  const extract_repo_slug_1 = require("./extract-repo-slug");
10
11
  const remote_release_client_1 = require("./remote-release-client");
@@ -157,18 +158,16 @@ class GitLabRemoteReleaseClient extends remote_release_client_1.RemoteReleaseCli
157
158
  if (!shouldContinueInGitLab) {
158
159
  return;
159
160
  }
160
- const { default: open } = await new Function('return import("open")')();
161
- await open(result.url)
162
- .then(() => {
161
+ if ((0, native_1.openUrl)(result.url)) {
163
162
  console.info(`\nFollow up in the browser to manually create the release:\n\n` +
164
163
  pc.underline(pc.cyan(result.url)) +
165
164
  `\n`);
166
- })
167
- .catch(() => {
165
+ }
166
+ else {
168
167
  console.info(`Open this link to manually create a release: \n` +
169
168
  pc.underline(pc.cyan(result.url)) +
170
169
  '\n');
171
- });
170
+ }
172
171
  }
173
172
  async promptForContinueInGitLab() {
174
173
  try {
@@ -44,6 +44,7 @@ projects) {
44
44
  const batchImplementationFactory = executorConfig.batchImplementation
45
45
  ? (0, schema_utils_1.getImplementationFactory)(executorConfig.batchImplementation, executorsDir, nodeModule, projects)
46
46
  : null;
47
+ // TODO(v25): remove custom hasher support.
47
48
  const hasherFactory = executorConfig.hasher
48
49
  ? (0, schema_utils_1.getImplementationFactory)(executorConfig.hasher, executorsDir, nodeModule, projects)
49
50
  : null;
@@ -1,5 +1,5 @@
1
1
  import { NxJsonConfiguration } from '../../config/nx-json';
2
- import { ProjectGraph } from '../../config/project-graph';
2
+ import { ProjectGraph, ProjectGraphProjectNode } from '../../config/project-graph';
3
3
  import { TargetDependencyConfig } from '../../config/workspace-json-project-json';
4
4
  export declare function runOne(cwd: string, args: {
5
5
  [k: string]: any;
@@ -7,6 +7,25 @@ export declare function runOne(cwd: string, args: {
7
7
  excludeTaskDependencies: boolean;
8
8
  loadDotEnvFiles: boolean;
9
9
  }): Promise<void>;
10
+ /**
11
+ * Builds the error shown when the project itself can't be found. Matches the
12
+ * attempted `project:target[:configuration]` against every runnable task id so
13
+ * a project typo (e.g. `webpai:build`) still surfaces the intended task
14
+ * (`webapi:build`).
15
+ */
16
+ export declare function getCannotFindProjectError(projectGraph: ProjectGraph, projectName: string, target: string, configuration?: string): {
17
+ title: string;
18
+ bodyLines: string[];
19
+ };
20
+ /**
21
+ * Validates that `target` exists on the resolved project. When it does not,
22
+ * returns an error describing the available targets (and the closest match, if
23
+ * any) so the user can recover from a typo or discover what they can run.
24
+ */
25
+ export declare function getRunOneTargetError(project: ProjectGraphProjectNode, target: string, configuration?: string): {
26
+ title: string;
27
+ bodyLines: string[];
28
+ } | null;
10
29
  export declare function parseRunOneOptions(cwd: string, parsedArgs: {
11
30
  [k: string]: any;
12
31
  }, projectGraph: ProjectGraph, nxJson: NxJsonConfiguration): {
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runOne = runOne;
4
+ exports.getCannotFindProjectError = getCannotFindProjectError;
5
+ exports.getRunOneTargetError = getRunOneTargetError;
4
6
  exports.parseRunOneOptions = parseRunOneOptions;
5
7
  const calculate_default_project_name_1 = require("../../config/calculate-default-project-name");
6
8
  const configuration_1 = require("../../config/configuration");
@@ -11,6 +13,7 @@ const command_line_utils_1 = require("../../utils/command-line-utils");
11
13
  const find_matching_projects_1 = require("../../utils/find-matching-projects");
12
14
  const output_1 = require("../../utils/output");
13
15
  const split_target_1 = require("../../utils/split-target");
16
+ const string_similarity_1 = require("../../utils/string-similarity");
14
17
  const workspace_root_1 = require("../../utils/workspace-root");
15
18
  const graph_1 = require("../graph/graph");
16
19
  const connect_to_nx_cloud_1 = require("../nx-cloud/connect/connect-to-nx-cloud");
@@ -28,7 +31,12 @@ async function runOne(cwd, args, extraTargetDependencies = {}, extraOptions = {
28
31
  configuration: opts.configuration,
29
32
  targets: [opts.target],
30
33
  }, 'run-one', { printWarnings: args.graph !== 'stdout' }, nxJson);
31
- const { projects, projectName } = getProjects(projectGraph, opts.project);
34
+ const { projects, projectName } = getProjects(projectGraph, opts.project, opts.target, opts.configuration);
35
+ const targetError = getRunOneTargetError(projects[0], opts.target, opts.configuration);
36
+ if (targetError) {
37
+ output_1.output.error(targetError);
38
+ process.exit(1);
39
+ }
32
40
  if (nxArgs.help) {
33
41
  await (await (0, handle_import_1.handleImport)('./run.js', __dirname)).printTargetRunHelp({
34
42
  ...opts,
@@ -59,7 +67,7 @@ async function runOne(cwd, args, extraTargetDependencies = {}, extraOptions = {
59
67
  process.exit(status);
60
68
  }
61
69
  }
62
- function getProjects(projectGraph, projectName) {
70
+ function getProjects(projectGraph, projectName, target, configuration) {
63
71
  if (projectGraph.nodes[projectName]) {
64
72
  return {
65
73
  projectName: projectName,
@@ -90,11 +98,151 @@ function getProjects(projectGraph, projectName) {
90
98
  process.exit(1);
91
99
  }
92
100
  }
93
- output_1.output.error({
94
- title: `Cannot find project '${projectName}'`,
95
- });
101
+ output_1.output.error(getCannotFindProjectError(projectGraph, projectName, target, configuration));
96
102
  process.exit(1);
97
103
  }
104
+ // Keep error output scannable: only show a handful of targets/suggestions.
105
+ const MAX_LISTED_TARGETS = 5;
106
+ const MAX_SUGGESTIONS = 3;
107
+ /**
108
+ * Builds the list of all runnable `project:target` task ids in the workspace,
109
+ * used to suggest the right invocation when a project or target can't be found.
110
+ */
111
+ function getProjectTargetIds(projectGraph) {
112
+ const ids = [];
113
+ for (const projectName of Object.keys(projectGraph.nodes)) {
114
+ const targets = projectGraph.nodes[projectName].data.targets ?? {};
115
+ for (const targetName of Object.keys(targets)) {
116
+ ids.push(`${projectName}:${targetName}`);
117
+ }
118
+ }
119
+ return ids;
120
+ }
121
+ /**
122
+ * A target (and even a configuration) name can legally contain ':'. When the
123
+ * user typos such a name, `splitTarget` splits on every ':' and parses the
124
+ * trailing segment(s) as separate parts (e.g. `nx:zzcustom:variantt` becomes
125
+ * project `nx`, target `zzcustom`, configuration `variantt`).
126
+ *
127
+ * To match the real name we therefore score the fully-rejoined specifier and
128
+ * every progressively shorter form, and keep whichever produced the closest
129
+ * candidate. Rejoining wins for colon-containing names, while the shorter forms
130
+ * keep matching genuine `target` + `configuration` typos (where the real target
131
+ * name does not carry the configuration suffix). Ties go to the longer form,
132
+ * which is the one that reproduces the name the user actually typed.
133
+ */
134
+ function findClosestSpecifier(parts, candidates, limit = 1) {
135
+ const present = parts.filter((part) => !!part);
136
+ let best = [];
137
+ for (let end = present.length; end >= 1; end--) {
138
+ const matches = (0, string_similarity_1.rankSuggestions)(present.slice(0, end).join(':'), candidates);
139
+ if (matches.length &&
140
+ (!best.length || matches[0].distance < best[0].distance)) {
141
+ best = matches;
142
+ }
143
+ }
144
+ return best.slice(0, limit).map(({ candidate }) => candidate);
145
+ }
146
+ /**
147
+ * Finds the closest available target to what the user typed, staying
148
+ * config-aware. When a configuration was parsed off the specifier, the rejoined
149
+ * `target:configuration` is scored too, so a colon-containing real target name
150
+ * (e.g. `zzcustom:variant`) still matches. Both forms are scored and the closest
151
+ * candidate wins, ties going to the rejoined form -- mirroring
152
+ * `findClosestSpecifier`'s preference order. The bare-`target` tier reuses the
153
+ * precomputed `ranked` list (its head, gated by the suggestion threshold)
154
+ * instead of recomputing distances.
155
+ */
156
+ function findClosestTarget(target, configuration, availableTargets, ranked) {
157
+ let best;
158
+ if (configuration) {
159
+ [best] = (0, string_similarity_1.rankSuggestions)(`${target}:${configuration}`, availableTargets);
160
+ }
161
+ const nearest = ranked[0];
162
+ if (nearest &&
163
+ (0, string_similarity_1.isWithinSuggestionThreshold)(target, nearest.candidate, nearest.distance) &&
164
+ (!best || nearest.distance < best.distance)) {
165
+ best = nearest;
166
+ }
167
+ return best?.candidate;
168
+ }
169
+ /**
170
+ * Lists up to `MAX_LISTED_TARGETS` targets from a list already ranked by how
171
+ * closely they resemble the target the user typed (closest first, ties broken
172
+ * alphabetically) so the most likely intended targets surface first.
173
+ * `closestMatch` is dropped because it is already surfaced separately as the
174
+ * "Did you mean" suggestion. Appends a "...and N more" line when the project has
175
+ * more targets than we show, and returns no lines at all when the suggestion was
176
+ * the project's only target -- a bare "Available targets:" header reads as "this
177
+ * project has none".
178
+ */
179
+ function formatAvailableTargets(ranked, closestMatch) {
180
+ const targets = ranked
181
+ .map(({ candidate }) => candidate)
182
+ .filter((candidate) => candidate !== closestMatch);
183
+ if (!targets.length) {
184
+ return [];
185
+ }
186
+ const shown = targets.slice(0, MAX_LISTED_TARGETS);
187
+ const lines = ['Available targets:', ...shown.map((t) => ` - ${t}`)];
188
+ if (targets.length > shown.length) {
189
+ lines.push(` ...and ${targets.length - shown.length} more`);
190
+ }
191
+ return lines;
192
+ }
193
+ /**
194
+ * Builds the error shown when the project itself can't be found. Matches the
195
+ * attempted `project:target[:configuration]` against every runnable task id so
196
+ * a project typo (e.g. `webpai:build`) still surfaces the intended task
197
+ * (`webapi:build`).
198
+ */
199
+ function getCannotFindProjectError(projectGraph, projectName, target, configuration) {
200
+ const bodyLines = [];
201
+ const suggestions = findClosestSpecifier([projectName, target, configuration], getProjectTargetIds(projectGraph), MAX_SUGGESTIONS);
202
+ if (suggestions.length) {
203
+ bodyLines.push('Did you mean one of these?', ...suggestions.map((id) => ` - ${id}`));
204
+ }
205
+ return {
206
+ title: `Cannot find project '${projectName}'`,
207
+ bodyLines,
208
+ };
209
+ }
210
+ /**
211
+ * Validates that `target` exists on the resolved project. When it does not,
212
+ * returns an error describing the available targets (and the closest match, if
213
+ * any) so the user can recover from a typo or discover what they can run.
214
+ */
215
+ function getRunOneTargetError(project, target, configuration) {
216
+ const availableTargets = Object.keys(project.data.targets ?? {});
217
+ if (availableTargets.includes(target)) {
218
+ return null;
219
+ }
220
+ const bodyLines = [];
221
+ // Rank the available targets by distance to the bare target ONCE. This drives
222
+ // the ordered "Available targets" block and doubles as the bare-target tier of
223
+ // the closest-match lookup, so distances are not computed twice.
224
+ const ranked = (0, string_similarity_1.rankByDistance)(target, availableTargets);
225
+ const closestMatch = findClosestTarget(target, configuration, availableTargets, ranked);
226
+ if (closestMatch) {
227
+ bodyLines.push(`Did you mean "${closestMatch}"?`);
228
+ }
229
+ if (!availableTargets.length) {
230
+ bodyLines.push(`The project "${project.name}" does not have any targets configured.`);
231
+ }
232
+ else {
233
+ const targetLines = formatAvailableTargets(ranked, closestMatch);
234
+ if (targetLines.length) {
235
+ if (bodyLines.length) {
236
+ bodyLines.push('');
237
+ }
238
+ bodyLines.push(...targetLines);
239
+ }
240
+ }
241
+ return {
242
+ title: `Cannot find target "${target}" for project "${project.name}"`,
243
+ bodyLines,
244
+ };
245
+ }
98
246
  const targetAliases = {
99
247
  b: 'build',
100
248
  e: 'e2e',
@@ -142,6 +142,7 @@ function validateConfiguration(projectName, targetName, configuration, targetCon
142
142
  }
143
143
  }
144
144
  // ── Custom hasher detection ──────────────────────────────────────────
145
+ // TODO(v25): remove custom hasher support.
145
146
  /**
146
147
  * Checks whether a target's executor defines a custom hasher.
147
148
  * Returns true if the executor has a hasherFactory — meaning the
@@ -13,7 +13,7 @@ exports.yargsWatchCommand = {
13
13
  },
14
14
  };
15
15
  function withWatchOptions(yargs) {
16
- return ((0, shared_options_1.withVerbose)(yargs)
16
+ return (0, shared_options_1.withVerbose)(yargs)
17
17
  .parserConfiguration({
18
18
  'strip-dashed': true,
19
19
  'populate--': true,
@@ -32,12 +32,6 @@ function withWatchOptions(yargs) {
32
32
  type: 'boolean',
33
33
  description: 'When watching selected projects, also include the projects they depend on.',
34
34
  alias: 'd',
35
- })
36
- // TODO(v24): remove the deprecated --includeDependentProjects alias
37
- .option('includeDependentProjects', {
38
- type: 'boolean',
39
- hidden: true,
40
- describe: "Deprecated in favor of --includeDependencies; will be removed in Nx 24. The flag name was misleading since it includes the watched project's dependencies, not its dependents. The new flag is functionally identical.",
41
35
  })
42
36
  .option('includeGlobalWorkspaceFiles', {
43
37
  type: 'boolean',
@@ -59,6 +53,7 @@ function withWatchOptions(yargs) {
59
53
  .conflicts({
60
54
  all: 'projects',
61
55
  })
56
+ .strictOptions()
62
57
  .check((args) => {
63
58
  if (!args.all && !args.projects) {
64
59
  throw Error('Please specify either --all or --projects');
@@ -73,19 +68,5 @@ function withWatchOptions(yargs) {
73
68
  else {
74
69
  throw Error('No command specified for watch mode.');
75
70
  }
76
- // --includeDependentProjects was renamed to --includeDependencies in
77
- // Nx 23 because the original name was misleading: it includes the
78
- // watched project's *dependencies*, not its dependents. The new flag
79
- // is functionally identical — only the name changed. Map the legacy
80
- // name through so existing scripts keep working during the
81
- // deprecation window.
82
- // TODO(v24): remove the legacy includeDependentProjects pass-through
83
- const a = args;
84
- if (a.includeDependentProjects !== undefined &&
85
- a.includeDependencies === undefined) {
86
- a.includeDependencies = a.includeDependentProjects;
87
- // eslint-disable-next-line no-console
88
- console.warn("--includeDependentProjects is deprecated in favor of --includeDependencies and will be removed in Nx 24. The flag name was misleading since it includes the watched project's dependencies, not its dependents. The new flag is functionally identical.");
89
- }
90
- }, true));
71
+ }, true);
91
72
  }
@@ -3,13 +3,6 @@ export interface WatchArguments {
3
3
  projects?: string[];
4
4
  all?: boolean;
5
5
  includeDependencies?: boolean;
6
- /**
7
- * @deprecated Renamed to {@link WatchArguments.includeDependencies}; will be
8
- * removed in Nx 24. The original name was misleading: this flag includes
9
- * the watched project's dependencies, not its dependents. The new property
10
- * is functionally identical — only the name changed.
11
- */
12
- includeDependentProjects?: boolean;
13
6
  includeGlobalWorkspaceFiles?: boolean;
14
7
  verbose?: boolean;
15
8
  command?: string;
@@ -149,7 +149,7 @@ async function watch(args) {
149
149
  }
150
150
  await client_1.daemonClient.registerFileWatcher({
151
151
  watchProjects: whatToWatch,
152
- includeDependencies: args.includeDependencies ?? args.includeDependentProjects,
152
+ includeDependencies: args.includeDependencies,
153
153
  includeGlobalWorkspaceFiles: args.includeGlobalWorkspaceFiles,
154
154
  }, async (err, data) => {
155
155
  if (err === 'reconnecting') {
@@ -32,6 +32,9 @@ export interface ExecutorJsonEntryConfig {
32
32
  batchImplementation?: string;
33
33
  preferBatch?: boolean;
34
34
  description?: string;
35
+ /**
36
+ * @deprecated Use target inputs instead. This will be removed in Nx 25.
37
+ */
35
38
  hasher?: string;
36
39
  }
37
40
  export type ExecutorsJsonEntry = string | ExecutorJsonEntryConfig;
@@ -143,6 +146,9 @@ export interface ExecutorConfig {
143
146
  outputCapture?: OutputCaptureMethod;
144
147
  continuous?: boolean;
145
148
  } & Schema;
149
+ /**
150
+ * @deprecated Use target inputs instead. This will be removed in Nx 25.
151
+ */
146
152
  hasherFactory?: () => CustomHasher;
147
153
  implementationFactory: () => Executor;
148
154
  batchImplementationFactory?: () => TaskGraphExecutor;
@@ -172,6 +178,9 @@ options: T, context: ExecutorContext) => AsyncIterableIterator<{
172
178
  * Implementation of a target of a project
173
179
  */
174
180
  export type Executor<T = any> = PromiseExecutor<T> | AsyncIteratorExecutor<T>;
181
+ /**
182
+ * @deprecated Use target inputs instead. This will be removed in Nx 25.
183
+ */
175
184
  export interface HasherContext {
176
185
  hasher: TaskHasher;
177
186
  projectGraph: ProjectGraph;
@@ -179,6 +188,9 @@ export interface HasherContext {
179
188
  projectsConfigurations: ProjectsConfigurations;
180
189
  nxJsonConfiguration: NxJsonConfiguration;
181
190
  }
191
+ /**
192
+ * @deprecated Use target inputs instead. This will be removed in Nx 25.
193
+ */
182
194
  export type CustomHasher = (task: Task, context: HasherContext) => Promise<Hash>;
183
195
  export type TaskResult = {
184
196
  success: boolean;
@@ -187,6 +187,11 @@ export interface TargetDependencyConfig {
187
187
  * Whether to forward task options to the dependency target.
188
188
  */
189
189
  options?: 'ignore' | 'forward';
190
+ /**
191
+ * Set to `false` to keep a continuous dependency's inputs out of this
192
+ * target's hash. It still runs first. Has no effect on other dependencies.
193
+ */
194
+ inputs?: boolean;
190
195
  }
191
196
  export type InputDefinition = {
192
197
  input: string;
@@ -23,6 +23,11 @@ export type ChangedFile = {
23
23
  path: string;
24
24
  type: 'create' | 'update' | 'delete';
25
25
  };
26
+ export declare enum DaemonStatus {
27
+ CONNECTING = 0,
28
+ DISCONNECTED = 1,
29
+ CONNECTED = 2
30
+ }
26
31
  /**
27
32
  * The daemon's workspace watcher died. Nothing it serves will see file changes
28
33
  * again, so a watching client has to restart rather than keep waiting.
@@ -43,6 +48,8 @@ export declare class DaemonClient {
43
48
  private _daemonStatus;
44
49
  private _waitForDaemonReady;
45
50
  private _daemonReady;
51
+ private _daemonReadyFailed;
52
+ private _pluginWorkerConnectionLost;
46
53
  private fileWatcherMessenger;
47
54
  private fileWatcherReconnecting;
48
55
  private fileWatcherFramingFailures;
@@ -54,6 +61,8 @@ export declare class DaemonClient {
54
61
  private projectGraphListenerCallbacks;
55
62
  enabled(): boolean;
56
63
  reset(): void;
64
+ private createReadyPromise;
65
+ private failDaemonReady;
57
66
  private getSocketPath;
58
67
  requestShutdown(): Promise<void>;
59
68
  getProjectGraphAndSourceMaps(): Promise<{