nx 22.7.0-beta.12 → 22.7.0-beta.13

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/dist/schemas/nx-schema.json +25 -0
  2. package/dist/schemas/project-schema.json +25 -0
  3. package/dist/src/adapter/ngcli-adapter.js +11 -12
  4. package/dist/src/command-line/daemon/daemon.js +8 -4
  5. package/dist/src/command-line/graph/graph.js +8 -1
  6. package/dist/src/command-line/show/show-target/utils.js +4 -3
  7. package/dist/src/command-line/yargs-utils/shared-options.js +5 -1
  8. package/dist/src/config/nx-json.d.ts +3 -1
  9. package/dist/src/config/workspace-json-project-json.d.ts +2 -1
  10. package/dist/src/core/graph/main.js +1 -1
  11. package/dist/src/daemon/client/client.d.ts +10 -3
  12. package/dist/src/daemon/client/client.js +21 -31
  13. package/dist/src/daemon/client/daemon-environment.d.ts +4 -0
  14. package/dist/src/daemon/client/daemon-environment.js +119 -0
  15. package/dist/src/daemon/client/daemon-socket-messenger.d.ts +2 -5
  16. package/dist/src/daemon/client/daemon-socket-messenger.js +1 -1
  17. package/dist/src/daemon/message-types/daemon-message.d.ts +6 -0
  18. package/dist/src/daemon/message-types/daemon-message.js +6 -0
  19. package/dist/src/daemon/server/handle-hash-tasks.d.ts +1 -1
  20. package/dist/src/daemon/server/handle-hash-tasks.js +1 -1
  21. package/dist/src/daemon/server/handle-outputs-tracking.d.ts +4 -4
  22. package/dist/src/daemon/server/handle-outputs-tracking.js +11 -11
  23. package/dist/src/daemon/server/outputs-tracking.d.ts +18 -3
  24. package/dist/src/daemon/server/outputs-tracking.js +49 -22
  25. package/dist/src/daemon/server/project-graph-incremental-recomputation.d.ts +2 -1
  26. package/dist/src/daemon/server/project-graph-incremental-recomputation.js +20 -4
  27. package/dist/src/daemon/server/server.js +71 -40
  28. package/dist/src/executors/run-commands/running-tasks.js +2 -3
  29. package/dist/src/executors/run-script/run-script.impl.js +16 -8
  30. package/dist/src/hasher/hash-task.d.ts +9 -1
  31. package/dist/src/hasher/hash-task.js +41 -14
  32. package/dist/src/hasher/native-task-hasher-impl.d.ts +1 -1
  33. package/dist/src/hasher/native-task-hasher-impl.js +4 -6
  34. package/dist/src/hasher/task-hasher.d.ts +20 -9
  35. package/dist/src/hasher/task-hasher.js +34 -6
  36. package/dist/src/native/index.d.ts +34 -7
  37. package/dist/src/native/native-bindings.js +1 -1
  38. package/dist/src/native/nx.wasi-browser.js +0 -1
  39. package/dist/src/native/nx.wasi.cjs +0 -1
  40. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  41. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  42. package/dist/src/plugins/js/utils/register.js +83 -4
  43. package/dist/src/project-graph/error-types.js +31 -11
  44. package/dist/src/project-graph/plugins/isolation/isolated-plugin.d.ts +1 -0
  45. package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +9 -0
  46. package/dist/src/project-graph/plugins/isolation/message-types.d.ts +1 -1
  47. package/dist/src/project-graph/plugins/isolation/messaging.d.ts +9 -0
  48. package/dist/src/project-graph/plugins/isolation/messaging.js +2 -0
  49. package/dist/src/project-graph/plugins/isolation/plugin-worker.js +36 -68
  50. package/dist/src/project-graph/plugins/loaded-nx-plugin.d.ts +6 -0
  51. package/dist/src/tasks-runner/cache.d.ts +6 -0
  52. package/dist/src/tasks-runner/cache.js +58 -0
  53. package/dist/src/tasks-runner/init-tasks-runner.js +13 -7
  54. package/dist/src/tasks-runner/run-command.js +13 -5
  55. package/dist/src/tasks-runner/task-env.js +17 -1
  56. package/dist/src/tasks-runner/task-orchestrator.d.ts +79 -7
  57. package/dist/src/tasks-runner/task-orchestrator.js +327 -120
  58. package/dist/src/tasks-runner/tasks-schedule.d.ts +5 -2
  59. package/dist/src/tasks-runner/tasks-schedule.js +31 -17
  60. package/dist/src/tasks-runner/utils.d.ts +3 -3
  61. package/dist/src/tasks-runner/utils.js +5 -6
  62. package/package.json +12 -12
  63. package/dist/src/daemon/client/exec-is-server-available.d.ts +0 -1
  64. package/dist/src/daemon/client/exec-is-server-available.js +0 -11
  65. package/dist/src/daemon/client/generate-help-output.d.ts +0 -1
  66. package/dist/src/daemon/client/generate-help-output.js +0 -26
@@ -44,7 +44,7 @@ export declare class DaemonClient {
44
44
  sourceMaps: ConfigurationSourceMaps;
45
45
  }>;
46
46
  getAllFileData(): Promise<FileData[]>;
47
- hashTasks(runnerOptions: any, tasks: Task[], taskGraph: TaskGraph, env: NodeJS.ProcessEnv, cwd: string, collectInputs?: boolean): Promise<Hash[]>;
47
+ hashTasks(runnerOptions: any, tasks: Task[], taskGraph: TaskGraph, perTaskEnvs: Record<string, NodeJS.ProcessEnv>, cwd: string, collectInputs?: boolean): Promise<Hash[]>;
48
48
  registerFileWatcher(config: {
49
49
  watchProjects: string[] | 'all';
50
50
  includeGlobalWorkspaceFiles?: boolean;
@@ -62,8 +62,14 @@ export declare class DaemonClient {
62
62
  } | null) => void): Promise<UnregisterCallback>;
63
63
  private reconnectProjectGraphListener;
64
64
  processInBackground(requirePath: string, data: any): Promise<any>;
65
- recordOutputsHash(outputs: string[], hash: string): Promise<any>;
66
- outputsHashesMatch(outputs: string[], hash: string): Promise<any>;
65
+ recordOutputsHashBatch(entries: {
66
+ outputs: string[];
67
+ hash: string;
68
+ }[]): Promise<any>;
69
+ outputsHashesMatchBatch(entries: {
70
+ outputs: string[];
71
+ hash: string;
72
+ }[]): Promise<boolean[]>;
67
73
  glob(globs: string[], exclude?: string[]): Promise<string[]>;
68
74
  multiGlob(globs: string[], exclude?: string[]): Promise<string[][]>;
69
75
  getWorkspaceContextFileData(): Promise<FileData[]>;
@@ -100,6 +106,7 @@ export declare class DaemonClient {
100
106
  * Used for reconnection - throws VersionMismatchError if daemon version differs.
101
107
  */
102
108
  private waitForServerToBeAvailable;
109
+ private envReflectionSent;
103
110
  private sendMessageToDaemon;
104
111
  private registerDaemonProcessWithMetricsService;
105
112
  private handleMessage;
@@ -15,13 +15,13 @@ const native_1 = require("../../native");
15
15
  const error_types_1 = require("../../project-graph/error-types");
16
16
  const project_graph_1 = require("../../project-graph/project-graph");
17
17
  const consume_messages_from_socket_1 = require("../../utils/consume-messages-from-socket");
18
- const wait_for_socket_connection_1 = require("../../utils/wait-for-socket-connection");
19
18
  const delayed_spinner_1 = require("../../utils/delayed-spinner");
20
19
  const handle_import_1 = require("../../utils/handle-import");
21
20
  const is_ci_1 = require("../../utils/is-ci");
22
21
  const is_sandbox_1 = require("../../utils/is-sandbox");
23
22
  const output_1 = require("../../utils/output");
24
23
  const promised_based_queue_1 = require("../../utils/promised-based-queue");
24
+ const wait_for_socket_connection_1 = require("../../utils/wait-for-socket-connection");
25
25
  const workspace_root_1 = require("../../utils/workspace-root");
26
26
  const cache_1 = require("../cache");
27
27
  const is_nx_version_mismatch_1 = require("../is-nx-version-mismatch");
@@ -41,15 +41,7 @@ const task_history_1 = require("../message-types/task-history");
41
41
  const update_workspace_context_1 = require("../message-types/update-workspace-context");
42
42
  const tmp_dir_1 = require("../tmp-dir");
43
43
  const daemon_socket_messenger_1 = require("./daemon-socket-messenger");
44
- const DAEMON_ENV_REQUIRED_SETTINGS = {
45
- NX_PROJECT_GLOB_CACHE: 'false',
46
- NX_CACHE_PROJECTS_CONFIG: 'false',
47
- };
48
- const DAEMON_ENV_OVERRIDABLE_SETTINGS = {
49
- NX_VERBOSE_LOGGING: 'true',
50
- NX_PERF_LOGGING: 'true',
51
- NX_NATIVE_LOGGING: 'nx=debug',
52
- };
44
+ const daemon_environment_1 = require("./daemon-environment");
53
45
  var DaemonStatus;
54
46
  (function (DaemonStatus) {
55
47
  DaemonStatus[DaemonStatus["CONNECTING"] = 0] = "CONNECTING";
@@ -72,6 +64,7 @@ class DaemonClient {
72
64
  this.fileWatcherConfigs = new Map();
73
65
  this.projectGraphListenerReconnecting = false;
74
66
  this.projectGraphListenerCallbacks = new Map();
67
+ this.envReflectionSent = false;
75
68
  try {
76
69
  this.nxJson = (0, configuration_1.readNxJson)();
77
70
  }
@@ -187,13 +180,11 @@ class DaemonClient {
187
180
  async getAllFileData() {
188
181
  return await this.sendToDaemonViaQueue({ type: 'REQUEST_FILE_DATA' });
189
182
  }
190
- hashTasks(runnerOptions, tasks, taskGraph, env, cwd, collectInputs) {
183
+ hashTasks(runnerOptions, tasks, taskGraph, perTaskEnvs, cwd, collectInputs) {
191
184
  return this.sendToDaemonViaQueue({
192
185
  type: 'HASH_TASKS',
193
186
  runnerOptions,
194
- env: process.env.NX_USE_V8_SERIALIZER !== 'false'
195
- ? structuredClone(process.env)
196
- : env,
187
+ perTaskEnvs,
197
188
  tasks,
198
189
  taskGraph,
199
190
  cwd,
@@ -525,22 +516,16 @@ class DaemonClient {
525
516
  // so we force JSON serialization here
526
517
  }, 'json');
527
518
  }
528
- recordOutputsHash(outputs, hash) {
519
+ recordOutputsHashBatch(entries) {
529
520
  return this.sendToDaemonViaQueue({
530
- type: 'RECORD_OUTPUTS_HASH',
531
- data: {
532
- outputs,
533
- hash,
534
- },
521
+ type: 'RECORD_OUTPUTS_HASH_BATCH',
522
+ data: entries,
535
523
  });
536
524
  }
537
- outputsHashesMatch(outputs, hash) {
525
+ outputsHashesMatchBatch(entries) {
538
526
  return this.sendToDaemonViaQueue({
539
- type: 'OUTPUTS_HASHES_MATCH',
540
- data: {
541
- outputs,
542
- hash,
543
- },
527
+ type: 'OUTPUTS_HASHES_MATCH_BATCH',
528
+ data: entries,
544
529
  });
545
530
  }
546
531
  glob(globs, exclude) {
@@ -868,6 +853,12 @@ class DaemonClient {
868
853
  return false;
869
854
  }
870
855
  async sendMessageToDaemon(message, force) {
856
+ // the first message sent to the daemon includes an env prop
857
+ // that updates the process.env values on the daemon.
858
+ if (!this.envReflectionSent && !global.NX_PLUGIN_WORKER) {
859
+ message.env = (0, daemon_environment_1.getDaemonEnv)();
860
+ this.envReflectionSent = true;
861
+ }
871
862
  await this.startDaemonIfNecessary();
872
863
  let keepAlive;
873
864
  return new Promise((resolve, reject) => {
@@ -947,12 +938,11 @@ class DaemonClient {
947
938
  detached: true,
948
939
  windowsHide: true,
949
940
  shell: false,
950
- env: {
951
- ...DAEMON_ENV_OVERRIDABLE_SETTINGS,
952
- ...process.env,
953
- ...DAEMON_ENV_REQUIRED_SETTINGS,
954
- },
941
+ env: (0, daemon_environment_1.getDaemonEnv)(),
955
942
  });
943
+ // if this process is the process that spawned the daemon,
944
+ // the daemon env is already up to date
945
+ this.envReflectionSent = true;
956
946
  backgroundProcess.unref();
957
947
  /**
958
948
  * Ensure the server is actually available to connect to via IPC before resolving
@@ -0,0 +1,4 @@
1
+ export declare function getDaemonEnv(): NodeJS.ProcessEnv & {
2
+ NX_PROJECT_GLOB_CACHE: string;
3
+ NX_CACHE_PROJECTS_CONFIG: string;
4
+ };
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDaemonEnv = getDaemonEnv;
4
+ const DAEMON_ENV_REQUIRED_SETTINGS = {
5
+ NX_PROJECT_GLOB_CACHE: 'false',
6
+ NX_CACHE_PROJECTS_CONFIG: 'false',
7
+ };
8
+ const DAEMON_ENV_OVERRIDABLE_SETTINGS = {
9
+ NX_VERBOSE_LOGGING: 'true',
10
+ NX_PERF_LOGGING: 'true',
11
+ NX_NATIVE_LOGGING: 'nx=debug',
12
+ };
13
+ /**
14
+ * Env vars that should NOT be sent to the daemon because they cannot affect
15
+ * the project graph. Only vars that can actually affect the project graph
16
+ * (e.g. PATH, JAVA_HOME, GRADLE_HOME) should be allowed through.
17
+ */
18
+ const DAEMON_ENV_VARS_EXCLUSIONS = new Set([
19
+ // Nx task-scoped vars
20
+ 'NX_TASK_TARGET_CONFIGURATION',
21
+ 'NX_TASK_TARGET_PROJECT',
22
+ 'NX_TASK_TARGET_TARGET',
23
+ 'NX_TASK_HASH',
24
+ 'NX_TERMINAL_OUTPUT_PATH',
25
+ 'NX_TERMINAL_CAPTURE_STDERR',
26
+ 'NX_STREAM_OUTPUT',
27
+ 'NX_PREFIX_OUTPUT',
28
+ 'NX_FORKED_TASK_EXECUTOR',
29
+ 'NX_SET_CLI',
30
+ 'NX_INVOKED_BY_RUNNER',
31
+ 'NX_LOAD_DOT_ENV_FILES',
32
+ 'NX_SKIP_NX_CACHE',
33
+ 'NX_CACHE_FAILURES',
34
+ 'NX_REJECT_UNKNOWN_LOCAL_CACHE',
35
+ 'NX_IGNORE_CYCLES',
36
+ 'NX_BATCH_MODE',
37
+ 'NX_CI_EXECUTION_ID',
38
+ 'NX_DAEMON_PROCESS',
39
+ // Nx UI/logging vars (don't affect graph structure)
40
+ 'NX_TUI',
41
+ 'NX_TUI_AUTO_EXIT',
42
+ 'NX_TUI_SKIP_CAPABILITY_CHECK',
43
+ 'NX_VERBOSE_LOGGING',
44
+ 'NX_PERF_LOGGING',
45
+ 'NX_NATIVE_LOGGING',
46
+ 'NX_PROFILE',
47
+ 'NX_DAEMON_VERBOSE_LOGGING',
48
+ // AI agent detection vars (the daemon itself is not an AI agent)
49
+ 'CLAUDECODE',
50
+ 'CLAUDE_CODE',
51
+ 'REPL_ID',
52
+ 'CURSOR_TRACE_ID',
53
+ 'COMPOSER_NO_INTERACTION',
54
+ 'OPENCODE',
55
+ 'GEMINI_CLI',
56
+ // Shell mechanics
57
+ '_',
58
+ 'SHLVL',
59
+ 'OLDPWD',
60
+ 'SHELL_SESSION_ID',
61
+ 'TERM_SESSION_ID',
62
+ 'SECURITYSESSIONID',
63
+ 'COMMAND_MODE',
64
+ 'WINDOWID',
65
+ 'COLUMNS',
66
+ 'LINES',
67
+ 'TMPDIR',
68
+ // Session / auth
69
+ 'SSH_AUTH_SOCK',
70
+ 'SSH_AGENT_PID',
71
+ 'XDG_SESSION_ID',
72
+ 'DBUS_SESSION_BUS_ADDRESS',
73
+ 'DISPLAY',
74
+ // macOS internals
75
+ '__CF_USER_TEXT_ENCODING',
76
+ '__CFBundleIdentifier',
77
+ ]);
78
+ /**
79
+ * Env var prefixes that should never be sent to the daemon.
80
+ * These cover CI platforms, package managers, editors, and terminals.
81
+ */
82
+ const DAEMON_ENV_PREFIX_EXCLUSIONS = [
83
+ // CI platforms
84
+ 'GITHUB_',
85
+ 'RUNNER_',
86
+ 'CI_JOB_',
87
+ 'CI_PIPELINE_',
88
+ 'CIRCLE_',
89
+ 'JENKINS_',
90
+ 'BUILD_',
91
+ 'AGENT_',
92
+ 'SYSTEM_TASK',
93
+ // Package managers (process-scoped)
94
+ 'npm_',
95
+ 'pnpm_',
96
+ // Editors / IDEs
97
+ 'VSCODE_',
98
+ 'JETBRAINS_',
99
+ // Terminal emulators
100
+ 'ITERM_',
101
+ 'KITTY_',
102
+ 'WEZTERM_',
103
+ 'ALACRITTY_',
104
+ 'KONSOLE_',
105
+ 'TMUX',
106
+ ];
107
+ function isExcludedEnvVar(key) {
108
+ return (DAEMON_ENV_VARS_EXCLUSIONS.has(key) ||
109
+ DAEMON_ENV_PREFIX_EXCLUSIONS.some((prefix) => key.startsWith(prefix)));
110
+ }
111
+ function getDaemonEnv() {
112
+ const env = { ...DAEMON_ENV_OVERRIDABLE_SETTINGS };
113
+ for (const key in process.env) {
114
+ if (!isExcludedEnvVar(key)) {
115
+ env[key] = process.env[key];
116
+ }
117
+ }
118
+ return Object.assign(env, DAEMON_ENV_REQUIRED_SETTINGS);
119
+ }
@@ -1,15 +1,12 @@
1
1
  import { Socket } from 'net';
2
- export interface Message extends Record<string, any> {
3
- type: string;
4
- data?: any;
5
- }
2
+ import { DaemonMessage } from '../message-types/daemon-message';
6
3
  export declare class VersionMismatchError extends Error {
7
4
  constructor();
8
5
  }
9
6
  export declare class DaemonSocketMessenger {
10
7
  private socket;
11
8
  constructor(socket: Socket);
12
- sendMessage(messageToDaemon: Message, force?: 'v8' | 'json'): void;
9
+ sendMessage<T extends DaemonMessage>(messageToDaemon: T, force?: 'v8' | 'json'): void;
13
10
  listen(onData: (message: string) => void, onClose?: () => void, onError?: (err: Error) => void): DaemonSocketMessenger;
14
11
  close(): void;
15
12
  }
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DaemonSocketMessenger = exports.VersionMismatchError = void 0;
4
4
  const perf_hooks_1 = require("perf_hooks");
5
5
  const consume_messages_from_socket_1 = require("../../utils/consume-messages-from-socket");
6
- const socket_utils_1 = require("../socket-utils");
7
6
  const logger_1 = require("../logger");
7
+ const socket_utils_1 = require("../socket-utils");
8
8
  class VersionMismatchError extends Error {
9
9
  constructor() {
10
10
  super('Version mismatch with daemon server');
@@ -0,0 +1,6 @@
1
+ export type DaemonMessage = {
2
+ type: string;
3
+ env?: Record<string, string>;
4
+ data?: any;
5
+ };
6
+ export declare function isDaemonMessage(msg: unknown): msg is DaemonMessage;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isDaemonMessage = isDaemonMessage;
4
+ function isDaemonMessage(msg) {
5
+ return typeof msg === 'object' && msg && 'type' in msg;
6
+ }
@@ -1,9 +1,9 @@
1
1
  import { Task, TaskGraph } from '../../config/task-graph';
2
2
  export declare function handleHashTasks(payload: {
3
3
  runnerOptions: any;
4
- env: any;
5
4
  tasks: Task[];
6
5
  taskGraph: TaskGraph;
6
+ perTaskEnvs: Record<string, NodeJS.ProcessEnv>;
7
7
  cwd: string;
8
8
  collectInputs?: boolean;
9
9
  }): Promise<{
@@ -20,7 +20,7 @@ async function handleHashTasks(payload) {
20
20
  storedProjectGraph = projectGraph;
21
21
  storedHasher = new task_hasher_1.InProcessTaskHasher(projectGraph, nxJson, rustReferences, payload.runnerOptions);
22
22
  }
23
- const response = await storedHasher.hashTasks(payload.tasks, payload.taskGraph, payload.env, payload.cwd, payload.collectInputs);
23
+ const response = await storedHasher.hashTasks(payload.tasks, payload.taskGraph, payload.perTaskEnvs, payload.cwd, payload.collectInputs);
24
24
  return {
25
25
  response,
26
26
  description: 'handleHashTasks',
@@ -1,15 +1,15 @@
1
1
  import { HandlerResult } from './server';
2
- export declare function handleRecordOutputsHash(payload: {
2
+ export declare function handleRecordOutputsHashBatch(payload: {
3
3
  type: string;
4
4
  data: {
5
5
  outputs: string[];
6
6
  hash: string;
7
- };
7
+ }[];
8
8
  }): Promise<HandlerResult>;
9
- export declare function handleOutputsHashesMatch(payload: {
9
+ export declare function handleOutputsHashesMatchBatch(payload: {
10
10
  type: string;
11
11
  data: {
12
12
  outputs: string[];
13
13
  hash: string;
14
- };
14
+ }[];
15
15
  }): Promise<HandlerResult>;
@@ -1,34 +1,34 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleRecordOutputsHash = handleRecordOutputsHash;
4
- exports.handleOutputsHashesMatch = handleOutputsHashesMatch;
3
+ exports.handleRecordOutputsHashBatch = handleRecordOutputsHashBatch;
4
+ exports.handleOutputsHashesMatchBatch = handleOutputsHashesMatchBatch;
5
5
  const outputs_tracking_1 = require("./outputs-tracking");
6
- async function handleRecordOutputsHash(payload) {
6
+ async function handleRecordOutputsHashBatch(payload) {
7
7
  try {
8
- await (0, outputs_tracking_1.recordOutputsHash)(payload.data.outputs, payload.data.hash);
8
+ (0, outputs_tracking_1.recordOutputsHashBatch)(payload.data);
9
9
  return {
10
- description: 'recordOutputsHash',
10
+ description: 'recordOutputsHashBatch',
11
11
  response: '{}',
12
12
  };
13
13
  }
14
14
  catch (e) {
15
15
  return {
16
- description: 'recordOutputsHash failed',
16
+ description: 'recordOutputsHashBatch failed',
17
17
  error: new Error(`Critical error when recording metadata about outputs: '${e.message}'.`),
18
18
  };
19
19
  }
20
20
  }
21
- async function handleOutputsHashesMatch(payload) {
21
+ async function handleOutputsHashesMatchBatch(payload) {
22
22
  try {
23
- const res = await (0, outputs_tracking_1.outputsHashesMatch)(payload.data.outputs, payload.data.hash);
23
+ const results = (0, outputs_tracking_1.outputsHashesMatchBatch)(payload.data);
24
24
  return {
25
- response: res,
26
- description: 'outputsHashesMatch',
25
+ response: results,
26
+ description: 'outputsHashesMatchBatch',
27
27
  };
28
28
  }
29
29
  catch (e) {
30
30
  return {
31
- description: 'outputsHashesMatch failed',
31
+ description: 'outputsHashesMatchBatch failed',
32
32
  error: new Error(`Critical error when verifying the contents of the outputs haven't changed: '${e.message}'.`),
33
33
  };
34
34
  }
@@ -1,8 +1,23 @@
1
1
  import { WatchEvent } from '../../native';
2
2
  export declare function _recordOutputsHash(outputs: string[], hash: string): void;
3
3
  export declare function _outputsHashesMatch(outputs: string[], hash: string): boolean;
4
- export declare function recordedHash(output: string): string;
5
- export declare function recordOutputsHash(_outputs: string[], hash: string): Promise<void>;
6
- export declare function outputsHashesMatch(_outputs: string[], hash: string): Promise<boolean>;
7
4
  export declare function processFileChangesInOutputs(changeEvents: WatchEvent[], now?: number): void;
5
+ /**
6
+ * Check whether the on-disk outputs of each entry still match the hash
7
+ * the daemon recorded for them. Uses Rayon-parallel filesystem scanning
8
+ * for uncached entries.
9
+ */
10
+ export declare function outputsHashesMatchBatch(entries: {
11
+ outputs: string[];
12
+ hash: string;
13
+ }[]): boolean[];
14
+ /**
15
+ * Record the hash of each entry's on-disk outputs so future
16
+ * outputsHashesMatchBatch calls can skip redundant cache copies.
17
+ * Uses Rayon-parallel filesystem scanning.
18
+ */
19
+ export declare function recordOutputsHashBatch(entries: {
20
+ outputs: string[];
21
+ hash: string;
22
+ }[]): void;
8
23
  export declare function disableOutputsTracking(): void;
@@ -2,10 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports._recordOutputsHash = _recordOutputsHash;
4
4
  exports._outputsHashesMatch = _outputsHashesMatch;
5
- exports.recordedHash = recordedHash;
6
- exports.recordOutputsHash = recordOutputsHash;
7
- exports.outputsHashesMatch = outputsHashesMatch;
8
5
  exports.processFileChangesInOutputs = processFileChangesInOutputs;
6
+ exports.outputsHashesMatchBatch = outputsHashesMatchBatch;
7
+ exports.recordOutputsHashBatch = recordOutputsHashBatch;
9
8
  exports.disableOutputsTracking = disableOutputsTracking;
10
9
  const path_1 = require("path");
11
10
  const native_1 = require("../../native");
@@ -44,25 +43,6 @@ function _outputsHashesMatch(outputs, hash) {
44
43
  }
45
44
  return true;
46
45
  }
47
- function recordedHash(output) {
48
- return recordedHashes[output];
49
- }
50
- async function recordOutputsHash(_outputs, hash) {
51
- const outputs = await normalizeOutputs(_outputs);
52
- if (disabled)
53
- return;
54
- _recordOutputsHash(outputs, hash);
55
- }
56
- async function outputsHashesMatch(_outputs, hash) {
57
- const outputs = await normalizeOutputs(_outputs);
58
- if (disabled)
59
- return false;
60
- return _outputsHashesMatch(outputs, hash);
61
- }
62
- async function normalizeOutputs(outputs) {
63
- let expandedOutputs = (0, collapse_expanded_outputs_1.collapseExpandedOutputs)((0, native_1.getFilesForOutputs)(workspace_root_1.workspaceRoot, outputs));
64
- return expandedOutputs;
65
- }
66
46
  function processFileChangesInOutputs(changeEvents, now = undefined) {
67
47
  if (!now) {
68
48
  now = new Date().getTime();
@@ -88,6 +68,53 @@ function processFileChangesInOutputs(changeEvents, now = undefined) {
88
68
  }
89
69
  }
90
70
  }
71
+ /**
72
+ * Check whether the on-disk outputs of each entry still match the hash
73
+ * the daemon recorded for them. Uses Rayon-parallel filesystem scanning
74
+ * for uncached entries.
75
+ */
76
+ function outputsHashesMatchBatch(entries) {
77
+ if (disabled)
78
+ return entries.map(() => false);
79
+ // Fast path: skip filesystem scan for entries with no recorded hash.
80
+ // _outputsHashesMatch will return false immediately if the hash isn't
81
+ // in numberOfExpandedOutputs, so scanning the filesystem is wasted work.
82
+ const needsScan = [];
83
+ const results = new Array(entries.length);
84
+ for (let i = 0; i < entries.length; i++) {
85
+ if (numberOfExpandedOutputs[entries[i].hash] === undefined) {
86
+ results[i] = false;
87
+ }
88
+ else {
89
+ needsScan.push(i);
90
+ }
91
+ }
92
+ if (needsScan.length > 0) {
93
+ // Only scan outputs for entries that have recorded hashes
94
+ const outputsBatch = needsScan.map((i) => entries[i].outputs);
95
+ const expandedBatch = (0, native_1.getFilesForOutputsBatch)(workspace_root_1.workspaceRoot, outputsBatch);
96
+ for (let j = 0; j < needsScan.length; j++) {
97
+ const expanded = (0, collapse_expanded_outputs_1.collapseExpandedOutputs)(expandedBatch[j]);
98
+ results[needsScan[j]] = _outputsHashesMatch(expanded, entries[needsScan[j]].hash);
99
+ }
100
+ }
101
+ return results;
102
+ }
103
+ /**
104
+ * Record the hash of each entry's on-disk outputs so future
105
+ * outputsHashesMatchBatch calls can skip redundant cache copies.
106
+ * Uses Rayon-parallel filesystem scanning.
107
+ */
108
+ function recordOutputsHashBatch(entries) {
109
+ if (disabled)
110
+ return;
111
+ const outputsBatch = entries.map((e) => e.outputs);
112
+ const expandedBatch = (0, native_1.getFilesForOutputsBatch)(workspace_root_1.workspaceRoot, outputsBatch);
113
+ for (let i = 0; i < entries.length; i++) {
114
+ const expanded = (0, collapse_expanded_outputs_1.collapseExpandedOutputs)(expandedBatch[i]);
115
+ _recordOutputsHash(expanded, entries[i].hash);
116
+ }
117
+ }
91
118
  function disableOutputsTracking() {
92
119
  disabled = true;
93
120
  }
@@ -1,6 +1,6 @@
1
1
  import { FileData, FileMap, ProjectGraph } from '../../config/project-graph';
2
- import { FileMapCache } from '../../project-graph/nx-deps-cache';
3
2
  import { NxWorkspaceFilesExternals } from '../../native';
3
+ import { FileMapCache } from '../../project-graph/nx-deps-cache';
4
4
  import { ConfigurationSourceMaps } from '../../project-graph/utils/project-configuration/source-maps';
5
5
  interface SerializedProjectGraph {
6
6
  error: Error | null;
@@ -24,4 +24,5 @@ export declare let currentSourceMaps: ConfigurationSourceMaps | undefined;
24
24
  export declare function getCachedSerializedProjectGraphPromise(): Promise<SerializedProjectGraph>;
25
25
  export declare function addUpdatedAndDeletedFiles(createdFiles: string[], updatedFiles: string[], deletedFiles: string[]): void;
26
26
  export declare function registerProjectGraphRecomputationListener(listener: (projectGraph: ProjectGraph, sourceMaps: ConfigurationSourceMaps, error: Error | null) => void): void;
27
+ export declare function invalidateGraphCache(): void;
27
28
  export {};
@@ -4,22 +4,23 @@ exports.currentSourceMaps = exports.currentProjectGraph = exports.currentProject
4
4
  exports.getCachedSerializedProjectGraphPromise = getCachedSerializedProjectGraphPromise;
5
5
  exports.addUpdatedAndDeletedFiles = addUpdatedAndDeletedFiles;
6
6
  exports.registerProjectGraphRecomputationListener = registerProjectGraphRecomputationListener;
7
+ exports.invalidateGraphCache = invalidateGraphCache;
7
8
  const perf_hooks_1 = require("perf_hooks");
8
9
  const nx_json_1 = require("../../config/nx-json");
9
10
  const file_hasher_1 = require("../../hasher/file-hasher");
10
11
  const build_project_graph_1 = require("../../project-graph/build-project-graph");
12
+ const error_types_1 = require("../../project-graph/error-types");
11
13
  const file_map_utils_1 = require("../../project-graph/file-map-utils");
12
14
  const nx_deps_cache_1 = require("../../project-graph/nx-deps-cache");
15
+ const get_plugins_1 = require("../../project-graph/plugins/get-plugins");
13
16
  const retrieve_workspace_files_1 = require("../../project-graph/utils/retrieve-workspace-files");
14
17
  const fileutils_1 = require("../../utils/fileutils");
15
18
  const workspace_context_1 = require("../../utils/workspace-context");
16
19
  const workspace_root_1 = require("../../utils/workspace-root");
17
- const file_watcher_sockets_1 = require("./file-watching/file-watcher-sockets");
20
+ const logger_1 = require("../logger");
18
21
  const file_change_events_1 = require("./file-watching/file-change-events");
22
+ const file_watcher_sockets_1 = require("./file-watching/file-watcher-sockets");
19
23
  const project_graph_listener_sockets_1 = require("./project-graph-listener-sockets");
20
- const logger_1 = require("../logger");
21
- const error_types_1 = require("../../project-graph/error-types");
22
- const get_plugins_1 = require("../../project-graph/plugins/get-plugins");
23
24
  let cachedSerializedProjectGraphPromise;
24
25
  // Maps file path to a version counter that increments on each modification.
25
26
  // This lets us detect mid-flight re-modifications when clearing processed files.
@@ -86,9 +87,16 @@ async function getCachedSerializedProjectGraphPromise() {
86
87
  const writeFn = graphWasRecomputed ? nx_deps_cache_1.writeCache : nx_deps_cache_1.writeCacheIfStale;
87
88
  writeFn(result.projectFileMapCache, result.projectGraph, result.sourceMaps, errors);
88
89
  }
90
+ if (errors?.length) {
91
+ cachedSerializedProjectGraphPromise = null;
92
+ }
89
93
  return result;
90
94
  }
91
95
  catch (e) {
96
+ // We return the project graph, but we don't want to persist the cache to
97
+ // serve the same state, as it could cause issues if the error is caused by something
98
+ // transient
99
+ cachedSerializedProjectGraphPromise = null;
92
100
  return {
93
101
  error: e,
94
102
  serializedProjectGraph: null,
@@ -180,6 +188,14 @@ async function processCollectedUpdatedAndDeletedFiles({ projects, externalNodes,
180
188
  throw e;
181
189
  }
182
190
  }
191
+ function invalidateGraphCache() {
192
+ // Clear the cached promise so the next request triggers a fresh computation.
193
+ // We intentionally do NOT call getCachedSerializedProjectGraphPromise() here
194
+ // because assigning its return Promise to the module-level variable causes a
195
+ // deadlock: the async function resumes, sees the variable is non-null (pointing
196
+ // at its own Promise), takes the "reuse" branch, and awaits itself forever.
197
+ cachedSerializedProjectGraphPromise = null;
198
+ }
183
199
  async function processFilesAndCreateAndSerializeProjectGraph(plugins) {
184
200
  const myGeneration = ++recomputationGeneration;
185
201
  // Helper to check if this recomputation is stale (a newer one has started)