nx 23.3.0-beta.0 → 23.3.0-beta.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.
Files changed (184) hide show
  1. package/dist/src/command-line/format/format.js +2 -18
  2. package/dist/src/command-line/graph/graph.js +42 -3
  3. package/dist/src/command-line/init/implementation/add-nx-to-monorepo.js +0 -3
  4. package/dist/src/command-line/init/implementation/add-nx-to-nest.js +0 -3
  5. package/dist/src/command-line/init/implementation/add-nx-to-npm-repo.js +0 -3
  6. package/dist/src/command-line/init/implementation/angular/index.js +0 -3
  7. package/dist/src/command-line/init/implementation/angular/legacy-angular-versions.js +0 -3
  8. package/dist/src/command-line/init/implementation/format.js +2 -3
  9. package/dist/src/command-line/init/implementation/utils.d.ts +0 -1
  10. package/dist/src/command-line/init/implementation/utils.js +0 -8
  11. package/dist/src/command-line/init/init-v2.js +0 -3
  12. package/dist/src/command-line/migrate/agentic/definitions.js +75 -19
  13. package/dist/src/command-line/migrate/agentic/format-command.d.ts +14 -0
  14. package/dist/src/command-line/migrate/agentic/format-command.js +44 -0
  15. package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +3 -3
  16. package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +3 -3
  17. package/dist/src/command-line/migrate/agentic/handoff.d.ts +27 -17
  18. package/dist/src/command-line/migrate/agentic/handoff.js +107 -24
  19. package/dist/src/command-line/migrate/agentic/instruction-files.d.ts +21 -0
  20. package/dist/src/command-line/migrate/agentic/instruction-files.js +36 -0
  21. package/dist/src/command-line/migrate/agentic/prompts/fragments.d.ts +16 -0
  22. package/dist/src/command-line/migrate/agentic/prompts/fragments.js +70 -0
  23. package/dist/src/command-line/migrate/agentic/prompts/generic-validation.js +4 -3
  24. package/dist/src/command-line/migrate/agentic/prompts/system-prompt.d.ts +25 -1
  25. package/dist/src/command-line/migrate/agentic/prompts/system-prompt.js +57 -34
  26. package/dist/src/command-line/migrate/agentic/run-step.js +28 -4
  27. package/dist/src/command-line/migrate/agentic/runner.d.ts +17 -8
  28. package/dist/src/command-line/migrate/agentic/runner.js +110 -55
  29. package/dist/src/command-line/migrate/agentic/types.d.ts +23 -8
  30. package/dist/src/command-line/migrate/agentic/types.js +10 -7
  31. package/dist/src/command-line/migrate/migrate-commits.d.ts +1 -0
  32. package/dist/src/command-line/migrate/migrate-commits.js +9 -0
  33. package/dist/src/command-line/migrate/migrate-output.d.ts +3 -7
  34. package/dist/src/command-line/migrate/migrate-output.js +3 -7
  35. package/dist/src/command-line/migrate/migrate.js +21 -8
  36. package/dist/src/command-line/migrate/run/agent-output.d.ts +10 -0
  37. package/dist/src/command-line/migrate/run/agent-output.js +16 -2
  38. package/dist/src/command-line/migrate/run/agent-work-payload.d.ts +37 -0
  39. package/dist/src/command-line/migrate/run/agent-work-payload.js +142 -0
  40. package/dist/src/command-line/migrate/run/atomic-write.d.ts +8 -0
  41. package/dist/src/command-line/migrate/run/atomic-write.js +17 -0
  42. package/dist/src/command-line/migrate/run/index.d.ts +1 -1
  43. package/dist/src/command-line/migrate/run/issues.d.ts +106 -0
  44. package/dist/src/command-line/migrate/run/issues.js +980 -0
  45. package/dist/src/command-line/migrate/run/orchestrator.d.ts +1 -0
  46. package/dist/src/command-line/migrate/run/orchestrator.js +662 -105
  47. package/dist/src/command-line/migrate/run/run-state.d.ts +43 -8
  48. package/dist/src/command-line/migrate/run/run-state.js +184 -20
  49. package/dist/src/command-line/migrate/run/runbook.d.ts +17 -0
  50. package/dist/src/command-line/migrate/run/runbook.js +97 -0
  51. package/dist/src/command-line/migrate/run/state-lock.d.ts +2 -2
  52. package/dist/src/command-line/migrate/run/state-lock.js +2 -2
  53. package/dist/src/command-line/migrate/run/state-machine.d.ts +5 -1
  54. package/dist/src/command-line/migrate/run/state-machine.js +27 -1
  55. package/dist/src/command-line/migrate/run/worker.js +206 -56
  56. package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +5 -11
  57. package/dist/src/command-line/release/publish.js +11 -4
  58. package/dist/src/command-line/release/utils/remote-release-clients/github.js +3 -3
  59. package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +3 -3
  60. package/dist/src/command-line/release/utils/remote-release-clients/remote-release-client.d.ts +2 -0
  61. package/dist/src/command-line/release/utils/remote-release-clients/remote-release-client.js +35 -0
  62. package/dist/src/command-line/report/report.d.ts +2 -0
  63. package/dist/src/command-line/report/report.js +28 -1
  64. package/dist/src/command-line/yargs-utils/shared-options.d.ts +1 -1
  65. package/dist/src/command-line/yargs-utils/shared-options.js +22 -4
  66. package/dist/src/config/workspace-json-project-json.d.ts +2 -0
  67. package/dist/src/core/graph/main.js +1 -1
  68. package/dist/src/daemon/client/client.d.ts +6 -2
  69. package/dist/src/daemon/client/client.js +27 -11
  70. package/dist/src/daemon/message-types/get-plugin-capabilities.d.ts +5 -0
  71. package/dist/src/daemon/message-types/get-plugin-capabilities.js +11 -0
  72. package/dist/src/daemon/message-types/run-tasks-execution-hooks.d.ts +3 -2
  73. package/dist/src/daemon/server/handle-get-plugin-capabilities.d.ts +2 -0
  74. package/dist/src/daemon/server/handle-get-plugin-capabilities.js +13 -0
  75. package/dist/src/daemon/server/handle-hash-tasks.d.ts +8 -2
  76. package/dist/src/daemon/server/handle-hash-tasks.js +16 -3
  77. package/dist/src/daemon/server/handle-outputs-changes.d.ts +2 -2
  78. package/dist/src/daemon/server/handle-outputs-changes.js +21 -6
  79. package/dist/src/daemon/server/handle-tasks-execution-hooks.d.ts +3 -2
  80. package/dist/src/daemon/server/outputs-tracking.d.ts +6 -0
  81. package/dist/src/daemon/server/outputs-tracking.js +18 -0
  82. package/dist/src/daemon/server/project-graph-incremental-recomputation.d.ts +22 -7
  83. package/dist/src/daemon/server/project-graph-incremental-recomputation.js +100 -70
  84. package/dist/src/daemon/server/restart-checks.d.ts +22 -0
  85. package/dist/src/daemon/server/restart-checks.js +77 -0
  86. package/dist/src/daemon/server/server.js +33 -18
  87. package/dist/src/daemon/server/shutdown-utils.d.ts +0 -5
  88. package/dist/src/daemon/server/shutdown-utils.js +3 -26
  89. package/dist/src/hasher/hash-task.js +20 -16
  90. package/dist/src/hasher/native-task-hasher-impl.d.ts +8 -0
  91. package/dist/src/hasher/native-task-hasher-impl.js +46 -3
  92. package/dist/src/hasher/task-hasher.d.ts +9 -0
  93. package/dist/src/hasher/task-hasher.js +14 -0
  94. package/dist/src/native/index.d.ts +222 -21
  95. package/dist/src/native/index.js +3 -3
  96. package/dist/src/native/native-bindings.js +4 -1
  97. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  98. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  99. package/dist/src/plugins/js/lock-file/pnpm-parser.js +1 -1
  100. package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.d.ts +2 -0
  101. package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.js +22 -0
  102. package/dist/src/plugins/js/utils/register.js +2 -1
  103. package/dist/src/project-graph/affected/locators/project-glob-changes.js +19 -26
  104. package/dist/src/project-graph/error-types.d.ts +5 -0
  105. package/dist/src/project-graph/error-types.js +44 -5
  106. package/dist/src/project-graph/file-utils.d.ts +2 -0
  107. package/dist/src/project-graph/file-utils.js +6 -1
  108. package/dist/src/project-graph/nx-deps-cache.d.ts +5 -2
  109. package/dist/src/project-graph/nx-deps-cache.js +4 -4
  110. package/dist/src/project-graph/plugins/get-plugins.d.ts +3 -1
  111. package/dist/src/project-graph/plugins/get-plugins.js +60 -82
  112. package/dist/src/project-graph/plugins/in-process-loader.d.ts +1 -1
  113. package/dist/src/project-graph/plugins/in-process-loader.js +1 -4
  114. package/dist/src/project-graph/plugins/isolation/index.d.ts +1 -1
  115. package/dist/src/project-graph/plugins/isolation/index.js +3 -1
  116. package/dist/src/project-graph/plugins/isolation/isolated-plugin.d.ts +8 -0
  117. package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +42 -6
  118. package/dist/src/project-graph/plugins/isolation/load-isolated-plugin.d.ts +10 -1
  119. package/dist/src/project-graph/plugins/isolation/load-isolated-plugin.js +67 -17
  120. package/dist/src/project-graph/plugins/isolation/messaging.d.ts +3 -2
  121. package/dist/src/project-graph/plugins/isolation/plugin-worker.js +5 -1
  122. package/dist/src/project-graph/plugins/loaded-nx-plugin.d.ts +5 -2
  123. package/dist/src/project-graph/plugins/loaded-nx-plugin.js +14 -1
  124. package/dist/src/project-graph/plugins/nx-plugin-capabilities.d.ts +6 -0
  125. package/dist/src/project-graph/plugins/nx-plugin-capabilities.js +28 -0
  126. package/dist/src/project-graph/plugins/task-results-stub.d.ts +50 -0
  127. package/dist/src/project-graph/plugins/task-results-stub.js +111 -0
  128. package/dist/src/project-graph/plugins/tasks-execution-hooks.d.ts +3 -2
  129. package/dist/src/project-graph/plugins/tasks-execution-hooks.js +14 -1
  130. package/dist/src/project-graph/project-graph.js +43 -42
  131. package/dist/src/tasks-runner/batch/run-batch.js +15 -0
  132. package/dist/src/tasks-runner/cache.d.ts +35 -0
  133. package/dist/src/tasks-runner/cache.js +40 -0
  134. package/dist/src/tasks-runner/create-task-graph.js +1 -0
  135. package/dist/src/tasks-runner/default-tasks-runner.js +1 -1
  136. package/dist/src/tasks-runner/forked-process-task-runner.d.ts +1 -1
  137. package/dist/src/tasks-runner/forked-process-task-runner.js +8 -7
  138. package/dist/src/tasks-runner/init-tasks-runner.js +5 -1
  139. package/dist/src/tasks-runner/is-tui-enabled.js +4 -3
  140. package/dist/src/tasks-runner/life-cycle.d.ts +7 -0
  141. package/dist/src/tasks-runner/life-cycle.js +7 -0
  142. package/dist/src/tasks-runner/life-cycles/summary-terminal-output-life-cycle.d.ts +35 -0
  143. package/dist/src/tasks-runner/life-cycles/summary-terminal-output-life-cycle.js +146 -0
  144. package/dist/src/tasks-runner/prune-task-graph.d.ts +17 -0
  145. package/dist/src/tasks-runner/prune-task-graph.js +45 -0
  146. package/dist/src/tasks-runner/run-command.js +14 -3
  147. package/dist/src/tasks-runner/running-tasks/batch-process.d.ts +93 -19
  148. package/dist/src/tasks-runner/running-tasks/batch-process.js +184 -88
  149. package/dist/src/tasks-runner/task-orchestrator.d.ts +64 -2
  150. package/dist/src/tasks-runner/task-orchestrator.js +232 -46
  151. package/dist/src/tasks-runner/terminal-output-path.d.ts +11 -0
  152. package/dist/src/tasks-runner/terminal-output-path.js +18 -0
  153. package/dist/src/tasks-runner/utils.js +5 -2
  154. package/dist/src/utils/ab-testing.d.ts +4 -1
  155. package/dist/src/utils/ab-testing.js +11 -7
  156. package/dist/src/utils/chunkify.d.ts +1 -7
  157. package/dist/src/utils/chunkify.js +2 -8
  158. package/dist/src/utils/command-line-utils.d.ts +19 -0
  159. package/dist/src/utils/command-line-utils.js +10 -3
  160. package/dist/src/utils/formatters/index.d.ts +6 -0
  161. package/dist/src/utils/formatters/index.js +5 -0
  162. package/dist/src/utils/formatters/prettier.d.ts +0 -8
  163. package/dist/src/utils/formatters/prettier.js +4 -26
  164. package/dist/src/utils/handle-errors.js +5 -43
  165. package/dist/src/utils/installed-nx-version.js +2 -48
  166. package/dist/src/utils/min-release-age/packument.js +1 -1
  167. package/dist/src/utils/output.d.ts +22 -3
  168. package/dist/src/utils/output.js +28 -5
  169. package/dist/src/utils/package-json.d.ts +25 -0
  170. package/dist/src/utils/package-json.js +41 -6
  171. package/dist/src/utils/package-manager.d.ts +6 -0
  172. package/dist/src/utils/package-manager.js +10 -0
  173. package/dist/src/utils/plugins/plugin-capabilities.js +7 -2
  174. package/dist/src/utils/provenance.js +20 -4
  175. package/dist/src/utils/serialize-overrides-into-command-line.js +2 -4
  176. package/dist/src/utils/workspace-context.d.ts +47 -3
  177. package/dist/src/utils/workspace-context.js +162 -10
  178. package/package.json +12 -13
  179. package/schemas/nx-schema.json +4 -0
  180. package/schemas/project-schema.json +4 -0
  181. package/dist/src/daemon/server/file-watching/route-workspace-changes.d.ts +0 -9
  182. package/dist/src/daemon/server/file-watching/route-workspace-changes.js +0 -76
  183. package/dist/src/daemon/server/watcher.d.ts +0 -18
  184. package/dist/src/daemon/server/watcher.js +0 -126
@@ -26,9 +26,11 @@ const logger_1 = require("../utils/logger");
26
26
  const output_1 = require("../utils/output");
27
27
  const strip_indents_1 = require("../utils/strip-indents");
28
28
  const workspace_root_1 = require("../utils/workspace-root");
29
+ const workspace_context_1 = require("../utils/workspace-context");
29
30
  const build_project_graph_1 = require("./build-project-graph");
30
31
  const error_types_1 = require("./error-types");
31
32
  const nx_deps_cache_1 = require("./nx-deps-cache");
33
+ const nx_plugin_capabilities_1 = require("./plugins/nx-plugin-capabilities");
32
34
  const get_plugins_1 = require("./plugins/get-plugins");
33
35
  const retrieve_workspace_files_1 = require("./utils/retrieve-workspace-files");
34
36
  const handle_import_1 = require("../utils/handle-import");
@@ -39,16 +41,21 @@ const handle_import_1 = require("../utils/handle-import");
39
41
  * @throws {Error} if there is no cached ProjectGraph to read from
40
42
  */
41
43
  function readCachedProjectGraph(minimumComputedAt) {
42
- const projectGraphCache = (0, nx_deps_cache_1.readProjectGraphCache)(minimumComputedAt);
43
- if (!projectGraphCache) {
44
- const angularSpecificError = (0, fileutils_1.fileExists)(`${workspace_root_1.workspaceRoot}/angular.json`)
45
- ? (0, strip_indents_1.stripIndents) `
44
+ const cached = (0, nx_deps_cache_1.readProjectGraphCache)(minimumComputedAt);
45
+ if (!cached) {
46
+ throw noCachedProjectGraphError();
47
+ }
48
+ return cached.projectGraph;
49
+ }
50
+ function noCachedProjectGraphError() {
51
+ const angularSpecificError = (0, fileutils_1.fileExists)(`${workspace_root_1.workspaceRoot}/angular.json`)
52
+ ? (0, strip_indents_1.stripIndents) `
46
53
  Make sure invoke 'node ./decorate-angular-cli.js' in your postinstall script.
47
54
  The decorated CLI will compute the project graph.
48
55
  'ng --help' should say 'Smart Monorepos · Fast Builds'.
49
56
  `
50
- : '';
51
- throw new Error((0, strip_indents_1.stripIndents) `
57
+ : '';
58
+ return new Error((0, strip_indents_1.stripIndents) `
52
59
  [readCachedProjectGraph] ERROR: No cached ProjectGraph is available.
53
60
 
54
61
  If you are leveraging \`readCachedProjectGraph()\` directly then you will need to refactor your usage to first ensure that
@@ -58,8 +65,6 @@ function readCachedProjectGraph(minimumComputedAt) {
58
65
 
59
66
  ${angularSpecificError}
60
67
  `);
61
- }
62
- return projectGraphCache;
63
68
  }
64
69
  function readCachedProjectConfiguration(projectName) {
65
70
  const graph = readCachedProjectGraph();
@@ -85,8 +90,11 @@ function readProjectsConfigurationFromProjectGraph(projectGraph) {
85
90
  }
86
91
  async function buildProjectGraphAndSourceMapsWithoutDaemon() {
87
92
  preventRecursionInGraphConstruction();
93
+ // Answer from the plugins loaded below, not an earlier cached graph's rows.
94
+ (0, nx_plugin_capabilities_1.noteGraphReadFromCache)(undefined);
88
95
  global.NX_GRAPH_CREATION = true;
89
96
  const nxJson = (0, nx_json_1.readNxJson)();
97
+ (0, workspace_context_1.refreshWorkspaceContext)(workspace_root_1.workspaceRoot);
90
98
  perf_hooks_1.performance.mark('retrieve-project-configurations:start');
91
99
  let configurationResult;
92
100
  let projectConfigurationsError;
@@ -136,7 +144,12 @@ async function buildProjectGraphAndSourceMapsWithoutDaemon() {
136
144
  ...(projectGraphError?.errors ?? []),
137
145
  ];
138
146
  if (cacheEnabled) {
139
- (0, nx_deps_cache_1.writeCache)(projectFileMapCache, projectGraph, sourceMaps, errors);
147
+ const computedAt = Date.now();
148
+ // Before the graph, so a cached graph never lacks its row.
149
+ if (errors.length === 0) {
150
+ (0, nx_plugin_capabilities_1.getNxPluginCapabilitiesStore)()?.record(computedAt, plugins.map((plugin) => plugin.capabilities()));
151
+ }
152
+ (0, nx_deps_cache_1.writeCache)(projectFileMapCache, projectGraph, sourceMaps, errors, computedAt);
140
153
  }
141
154
  if (errors.length > 0) {
142
155
  throw new error_types_1.ProjectGraphError(errors, projectGraph, sourceMaps);
@@ -149,14 +162,7 @@ function handleProjectGraphError(opts, e) {
149
162
  if (opts.exitOnError) {
150
163
  const isVerbose = process.env.NX_VERBOSE_LOGGING === 'true';
151
164
  if (e instanceof error_types_1.ProjectGraphError) {
152
- let title = e.message;
153
- const bodyLines = isVerbose
154
- ? [e.stack]
155
- : ['Pass --verbose to see the stacktraces.'];
156
- output_1.output.error({
157
- title,
158
- bodyLines: bodyLines,
159
- });
165
+ output_1.output.error((0, error_types_1.formatProjectGraphError)(e, isVerbose));
160
166
  }
161
167
  else if (typeof e.message === 'string') {
162
168
  const lines = e.message.split('\n');
@@ -178,7 +184,12 @@ function handleProjectGraphError(opts, e) {
178
184
  }
179
185
  }
180
186
  async function readCachedGraphAndHydrateFileMap(minimumComputedAt) {
181
- const graph = readCachedProjectGraph(minimumComputedAt);
187
+ const cached = (0, nx_deps_cache_1.readProjectGraphCache)(minimumComputedAt);
188
+ if (!cached) {
189
+ throw noCachedProjectGraphError();
190
+ }
191
+ const graph = cached.projectGraph;
192
+ (0, nx_plugin_capabilities_1.noteGraphReadFromCache)(cached.computedAt);
182
193
  const projectRootMap = Object.fromEntries(Object.entries(graph.nodes).map(([project, { data }]) => [
183
194
  data.root,
184
195
  project,
@@ -260,45 +271,33 @@ async function createProjectGraphAndSourceMapsAsync(opts = {
260
271
  const lock = !native_1.IS_WASM
261
272
  ? new native_1.FileLock((0, path_1.join)(cache_directory_1.workspaceDataDirectory, 'project-graph.lock'))
262
273
  : null;
263
- let locked = lock?.locked;
264
- while (locked) {
274
+ let holdingLock = lock?.tryLock() ?? false;
275
+ while (lock && !holdingLock) {
265
276
  logger_1.logger.verbose('Waiting for graph construction in another process to complete');
266
277
  const spinner = new delayed_spinner_1.DelayedSpinner('Waiting for graph construction in another process to complete');
267
278
  const start = Date.now();
268
- await lock.wait();
269
- spinner.cleanup();
270
- // Note: This will currently throw if any of the caches are missing...
271
- // It would be nice if one of the processes that was waiting for the lock
272
- // could pick up the slack and build the graph if it's missing, but
273
- // we wouldn't want either of the below to happen:
274
- // - All of the waiting processes to build the graph
275
- // - Even one of the processes building the graph on a legitimate error
276
279
  try {
277
- // Ensuring that computedAt was after this process started
278
- // waiting for the graph to complete, means that the graph
279
- // was computed by the process was already working.
280
+ await lock.wait();
281
+ // A graph computed after this process started waiting is the holder's.
280
282
  const graph = await readCachedGraphAndHydrateFileMap(start);
281
283
  const sourceMaps = (0, nx_deps_cache_1.readSourceMapsCache)();
282
284
  if (!sourceMaps) {
283
285
  throw new Error('The project graph was computed in another process, but the source maps are missing.');
284
286
  }
285
- return {
286
- projectGraph: graph,
287
- sourceMaps,
288
- };
287
+ return { projectGraph: graph, sourceMaps };
289
288
  }
290
289
  catch (e) {
291
- // If the error is that the cached graph is stale after unlock,
292
- // the process that was working on the graph must have been canceled,
293
- // so we will fall through to the normal flow to ensure
294
- // its created by one of the processes that was waiting
295
290
  if (!(e instanceof error_types_1.StaleProjectGraphCacheError)) {
296
291
  throw e;
297
292
  }
293
+ // The holder stopped without writing a graph: build it here, or wait for
294
+ // whoever took the lock first.
295
+ holdingLock = lock.tryLock();
296
+ }
297
+ finally {
298
+ spinner.cleanup();
298
299
  }
299
- locked = lock.check();
300
300
  }
301
- lock?.lock();
302
301
  try {
303
302
  const res = await buildProjectGraphAndSourceMapsWithoutDaemon();
304
303
  perf_hooks_1.performance.measure('createProjectGraphAsync >> retrieve-project-configurations', 'retrieve-project-configurations:start', 'retrieve-project-configurations:end');
@@ -323,7 +322,9 @@ async function createProjectGraphAndSourceMapsAsync(opts = {
323
322
  handleProjectGraphError(opts, e);
324
323
  }
325
324
  finally {
326
- lock?.unlock();
325
+ if (holdingLock) {
326
+ lock.unlock();
327
+ }
327
328
  }
328
329
  }
329
330
  else {
@@ -62,13 +62,28 @@ async function runTasks(executorName, projectGraph, batchTaskGraph, fullTaskGrap
62
62
  catch (e) {
63
63
  const isVerbose = tasks[0].overrides.verbose;
64
64
  console.error(isVerbose ? e : e.message);
65
+ // `process.exit` does not wait for pipe writes, so the reason this worker
66
+ // died could otherwise be the one line that never arrives.
67
+ await flushStdio();
65
68
  process.exit(1);
66
69
  }
67
70
  }
71
+ /**
72
+ * Waits until everything written to stdout and stderr so far has reached the
73
+ * pipe. Writes to a pipe are asynchronous, and the parent learns the batch is
74
+ * over through a separate IPC channel - so without this it can be told the batch
75
+ * finished, or see the process exit, while the end of its output is still in
76
+ * memory here. That tail is where a tool reports what went wrong.
77
+ */
78
+ function flushStdio() {
79
+ const flush = (stream) => new Promise((resolve) => stream.write('', () => resolve()));
80
+ return Promise.all([flush(process.stdout), flush(process.stderr)]).then(() => undefined);
81
+ }
68
82
  process.on('message', async (message) => {
69
83
  switch (message.type) {
70
84
  case batch_messages_1.BatchMessageType.RunTasks: {
71
85
  const results = await runTasks(message.executorName, message.projectGraph, message.batchTaskGraph, message.fullTaskGraph);
86
+ await flushStdio();
72
87
  process.send({
73
88
  type: batch_messages_1.BatchMessageType.CompleteBatchExecution,
74
89
  results,
@@ -13,6 +13,23 @@ export type TaskWithCachedResult = {
13
13
  };
14
14
  export declare function dbCacheEnabled(): boolean;
15
15
  export declare function getCache(options: DefaultTasksRunnerOptions): DbCache | Cache;
16
+ /**
17
+ * Collects the batch worker logs written by `batchOutputPathForKey`.
18
+ *
19
+ * Guarded rather than called straight through: `cache.rs` is
20
+ * `#[cfg(not(target_arch = "wasm32"))]`, so this export does not exist in the
21
+ * WASM binding and calling it there is a `TypeError`. Every other native cache
22
+ * entry point is kept off that path by `dbCacheEnabled()`; this one is a free
23
+ * function, so it needs its own guard. The logs are collected on the next
24
+ * non-WASM run.
25
+ */
26
+ export declare function sweepBatchOutputs(): void;
27
+ /**
28
+ * Where a batch worker's own log lives. Mirrors `batch_outputs_path` in
29
+ * cache.rs, which is what sweeps the directory — the same mirroring
30
+ * `terminalOutputPathForHash` does for `get_task_outputs_path_internal`.
31
+ */
32
+ export declare function batchOutputPathForKey(key: string): string;
16
33
  export declare class DbCache {
17
34
  private readonly options;
18
35
  private nxJson;
@@ -32,6 +49,16 @@ export declare class DbCache {
32
49
  getUsedCacheSpace(): number;
33
50
  private applyRemoteCacheResults;
34
51
  put(task: Task, terminalOutput: string | null, outputs: string[], code: number): Promise<void>;
52
+ /**
53
+ * Register terminal outputs that were written outside of `put` — uncacheable
54
+ * tasks, and cacheable ones run with `--skip-nx-cache`. `removeOldCacheRecords`
55
+ * only collects hashes it finds in the database, so without this the files
56
+ * would never be cleaned up.
57
+ */
58
+ recordTerminalOutputs(records: {
59
+ hash: string;
60
+ size: number;
61
+ }[]): void;
35
62
  copyFilesFromCache(_: string, cachedResult: CachedResult, outputs: string[]): Promise<number>;
36
63
  removeOldCacheRecords(): void;
37
64
  temporaryOutputPath(task: Task): string;
@@ -63,6 +90,14 @@ export declare class Cache {
63
90
  put(task: Task, terminalOutput: string | null, outputs: string[], code: number): Promise<void>;
64
91
  copyFilesFromCache(hash: string, cachedResult: CachedResult, outputs: string[]): Promise<void>;
65
92
  temporaryOutputPath(task: Task): string;
93
+ /**
94
+ * No-op: the legacy cache has no database to record against, and its
95
+ * collection is directory-based rather than driven by cache records.
96
+ */
97
+ recordTerminalOutputs(_records: {
98
+ hash: string;
99
+ size: number;
100
+ }[]): void;
66
101
  private expandOutputsInWorkspace;
67
102
  private expandOutputsInCache;
68
103
  private _expandOutputs;
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Cache = exports.DbCache = void 0;
4
4
  exports.dbCacheEnabled = dbCacheEnabled;
5
5
  exports.getCache = getCache;
6
+ exports.sweepBatchOutputs = sweepBatchOutputs;
7
+ exports.batchOutputPathForKey = batchOutputPathForKey;
6
8
  exports.resolveMaxCacheSize = resolveMaxCacheSize;
7
9
  exports.parseMaxCacheSize = parseMaxCacheSize;
8
10
  exports.formatCacheSize = formatCacheSize;
@@ -54,6 +56,30 @@ function getCache(options) {
54
56
  })
55
57
  : new Cache(options);
56
58
  }
59
+ /**
60
+ * Collects the batch worker logs written by `batchOutputPathForKey`.
61
+ *
62
+ * Guarded rather than called straight through: `cache.rs` is
63
+ * `#[cfg(not(target_arch = "wasm32"))]`, so this export does not exist in the
64
+ * WASM binding and calling it there is a `TypeError`. Every other native cache
65
+ * entry point is kept off that path by `dbCacheEnabled()`; this one is a free
66
+ * function, so it needs its own guard. The logs are collected on the next
67
+ * non-WASM run.
68
+ */
69
+ function sweepBatchOutputs() {
70
+ if (native_1.IS_WASM) {
71
+ return;
72
+ }
73
+ (0, native_1.sweepBatchOutputs)(cache_directory_1.cacheDir);
74
+ }
75
+ /**
76
+ * Where a batch worker's own log lives. Mirrors `batch_outputs_path` in
77
+ * cache.rs, which is what sweeps the directory — the same mirroring
78
+ * `terminalOutputPathForHash` does for `get_task_outputs_path_internal`.
79
+ */
80
+ function batchOutputPathForKey(key) {
81
+ return (0, path_1.join)(cache_directory_1.cacheDir, 'batchOutputs', `${key}.log`);
82
+ }
57
83
  class DbCache {
58
84
  constructor(options) {
59
85
  this.options = options;
@@ -161,6 +187,15 @@ class DbCache {
161
187
  }
162
188
  });
163
189
  }
190
+ /**
191
+ * Register terminal outputs that were written outside of `put` — uncacheable
192
+ * tasks, and cacheable ones run with `--skip-nx-cache`. `removeOldCacheRecords`
193
+ * only collects hashes it finds in the database, so without this the files
194
+ * would never be cleaned up.
195
+ */
196
+ recordTerminalOutputs(records) {
197
+ this.cache.recordTerminalOutputs(records);
198
+ }
164
199
  copyFilesFromCache(_, cachedResult, outputs) {
165
200
  return tryAndRetry(async () => this.cache.copyFilesFromCache(cachedResult, outputs));
166
201
  }
@@ -421,6 +456,11 @@ class Cache {
421
456
  temporaryOutputPath(task) {
422
457
  return (0, path_1.join)(this.terminalOutputsDir, task.hash);
423
458
  }
459
+ /**
460
+ * No-op: the legacy cache has no database to record against, and its
461
+ * collection is directory-based rather than driven by cache records.
462
+ */
463
+ recordTerminalOutputs(_records) { }
424
464
  async expandOutputsInWorkspace(outputs) {
425
465
  return this._expandOutputs(outputs, workspace_root_1.workspaceRoot);
426
466
  }
@@ -172,6 +172,7 @@ class ProcessTasks {
172
172
  dependencyConfig.target +
173
173
  DUMMY_TASK_TARGET, undefined);
174
174
  this.dependencies[task.id].push(dummyId);
175
+ this.continuousDependencies[task.id].push(dummyId);
175
176
  this.dependencies[dummyId] ??= [];
176
177
  this.continuousDependencies[dummyId] ??= [];
177
178
  const noopTask = this.createDummyTask(dummyId, task);
@@ -68,7 +68,7 @@ const defaultTasksRunner = async (tasks, options, context) => {
68
68
  };
69
69
  exports.defaultTasksRunner = defaultTasksRunner;
70
70
  async function runAllTasks(options, context) {
71
- const orchestrator = new task_orchestrator_1.TaskOrchestrator(context.hasher, context.initiatingProject, context.initiatingTasks, context.projectGraph, context.taskGraph, context.nxJson, options, context.nxArgs?.nxBail, context.daemon, context.nxArgs?.outputStyle);
71
+ const orchestrator = new task_orchestrator_1.TaskOrchestrator(context.hasher, context.initiatingProject, context.initiatingTasks, context.projectGraph, context.taskGraph, context.nxJson, options, context.nxArgs?.nxBail, context.daemon, context.nxArgs?.specifiedOutputStyle, context.nxArgs?.resolvedOutputStyle ?? 'static-failures-only');
72
72
  return orchestrator.run();
73
73
  }
74
74
  exports.default = exports.defaultTasksRunner;
@@ -17,7 +17,7 @@ export declare class ForkedProcessTaskRunner {
17
17
  private pseudoTerminals;
18
18
  constructor(options: DefaultTasksRunnerOptions, tuiEnabled: boolean);
19
19
  init(): Promise<void>;
20
- forkProcessForBatch({ id: batchId, executorName, taskGraph: batchTaskGraph }: Batch, projectGraph: ProjectGraph, fullTaskGraph: TaskGraph, env: NodeJS.ProcessEnv): Promise<BatchProcess>;
20
+ forkProcessForBatch({ id: batchId, executorName, taskGraph: batchTaskGraph }: Batch, projectGraph: ProjectGraph, fullTaskGraph: TaskGraph, env: NodeJS.ProcessEnv, printsOutput?: boolean): Promise<BatchProcess>;
21
21
  cleanUpBatchProcesses(): void;
22
22
  forkProcessLegacy(task: Task, { temporaryOutputPath, streamOutput, pipeOutput, taskGraph, env, }: {
23
23
  temporaryOutputPath: string;
@@ -5,6 +5,7 @@ const child_process_1 = require("child_process");
5
5
  const fs_1 = require("fs");
6
6
  const path_1 = require("path");
7
7
  const output_1 = require("../utils/output");
8
+ const prune_task_graph_1 = require("./prune-task-graph");
8
9
  const strip_indents_1 = require("../utils/strip-indents");
9
10
  const batch_messages_1 = require("./batch/batch-messages");
10
11
  const process_metrics_service_1 = require("./process-metrics-service");
@@ -29,7 +30,7 @@ class ForkedProcessTaskRunner {
29
30
  this.setupProcessEventListeners();
30
31
  }
31
32
  // TODO: vsavkin delegate terminal output printing
32
- async forkProcessForBatch({ id: batchId, executorName, taskGraph: batchTaskGraph }, projectGraph, fullTaskGraph, env) {
33
+ async forkProcessForBatch({ id: batchId, executorName, taskGraph: batchTaskGraph }, projectGraph, fullTaskGraph, env, printsOutput = true) {
33
34
  const count = Object.keys(batchTaskGraph.tasks).length;
34
35
  if (count > 1) {
35
36
  output_1.output.logSingleLine(`Running ${output_1.output.bold(count)} ${output_1.output.bold('tasks')} with ${output_1.output.bold(executorName)}`);
@@ -50,7 +51,7 @@ class ForkedProcessTaskRunner {
50
51
  const taskIds = Object.keys(batchTaskGraph.tasks);
51
52
  (0, process_metrics_service_1.getProcessMetricsService)().registerBatch(batchId, taskIds, p.pid);
52
53
  }
53
- const cp = new batch_process_1.BatchProcess(p, executorName);
54
+ const cp = new batch_process_1.BatchProcess(p, executorName, printsOutput, batchId);
54
55
  this.processes.add(cp);
55
56
  cp.onExit(() => {
56
57
  this.processes.delete(cp);
@@ -60,8 +61,8 @@ class ForkedProcessTaskRunner {
60
61
  type: batch_messages_1.BatchMessageType.RunTasks,
61
62
  executorName,
62
63
  projectGraph,
63
- batchTaskGraph,
64
- fullTaskGraph,
64
+ batchTaskGraph: (0, prune_task_graph_1.pruneTaskGraph)(batchTaskGraph),
65
+ fullTaskGraph: (0, prune_task_graph_1.pruneTaskGraph)(fullTaskGraph),
65
66
  });
66
67
  return cp;
67
68
  }
@@ -148,7 +149,7 @@ class ForkedProcessTaskRunner {
148
149
  p.send({
149
150
  targetDescription: task.target,
150
151
  overrides: task.overrides,
151
- taskGraph,
152
+ taskGraph: (0, prune_task_graph_1.pruneTaskGraph)(taskGraph),
152
153
  isVerbose: this.verbose,
153
154
  });
154
155
  this.processes.add(p);
@@ -186,7 +187,7 @@ class ForkedProcessTaskRunner {
186
187
  p.send({
187
188
  targetDescription: task.target,
188
189
  overrides: task.overrides,
189
- taskGraph,
190
+ taskGraph: (0, prune_task_graph_1.pruneTaskGraph)(taskGraph),
190
191
  isVerbose: this.verbose,
191
192
  });
192
193
  const cp = new node_child_process_1.NodeChildProcessWithNonDirectOutput(p, {
@@ -231,7 +232,7 @@ class ForkedProcessTaskRunner {
231
232
  p.send({
232
233
  targetDescription: task.target,
233
234
  overrides: task.overrides,
234
- taskGraph,
235
+ taskGraph: (0, prune_task_graph_1.pruneTaskGraph)(taskGraph),
235
236
  isVerbose: this.verbose,
236
237
  });
237
238
  cp.onExit((code, signal) => {
@@ -43,7 +43,11 @@ async function createOrchestrator(tasks, projectGraph, fullTaskGraph, nxJson, li
43
43
  lifeCycle: compositedLifeCycle,
44
44
  };
45
45
  (0, run_command_1.setEnvVarsBasedOnArgs)(nxArgs, true);
46
- const orchestrator = new task_orchestrator_1.TaskOrchestrator(hasher, null, tasks, projectGraph, taskGraph, nxJson, nxArgs, false, client_1.daemonClient, undefined, fullTaskGraph);
46
+ const orchestrator = new task_orchestrator_1.TaskOrchestrator(hasher, null, tasks, projectGraph, taskGraph, nxJson, nxArgs, false, client_1.daemonClient,
47
+ // No argv on this path, so the output-style middleware never ran and both
48
+ // fields are unset - this always renders `static-failures-only`. `nx.json`'s
49
+ // `outputStyle` does not reach them; it merges under its own name.
50
+ nxArgs.specifiedOutputStyle, nxArgs.resolvedOutputStyle ?? 'static-failures-only', fullTaskGraph);
47
51
  await orchestrator.init();
48
52
  orchestrator.processAllScheduledTasks();
49
53
  return orchestrator;
@@ -39,12 +39,13 @@ function shouldUseTui(nxJson, nxArgs, skipCapabilityCheck = process.env.NX_TUI_S
39
39
  if (!isCapable) {
40
40
  return false;
41
41
  }
42
- if ((0, output_1.isStaticOutputStyle)(nxArgs.outputStyle) ||
43
- ['stream', 'stream-without-prefixes', 'dynamic-legacy'].includes(nxArgs.outputStyle)) {
42
+ if ((0, output_1.isStaticOutputStyle)(nxArgs.specifiedOutputStyle) ||
43
+ ['summary', 'stream', 'stream-without-prefixes', 'dynamic-legacy'].includes(nxArgs.specifiedOutputStyle)) {
44
44
  // If the user has specified a non-TUI output style, we disable the TUI
45
45
  return false;
46
46
  }
47
- if (nxArgs.outputStyle === 'dynamic' || nxArgs.outputStyle === 'tui') {
47
+ if (nxArgs.specifiedOutputStyle === 'dynamic' ||
48
+ nxArgs.specifiedOutputStyle === 'tui') {
48
49
  return true;
49
50
  }
50
51
  // The environment variable takes precedence over the nx.json config, but
@@ -53,6 +53,12 @@ export interface LifeCycle {
53
53
  setEstimatedTaskTimings?(timings: Record<string, number>): void;
54
54
  registerRunningBatch?(batchId: string, batchInfo: BatchInfo): void;
55
55
  appendBatchOutput?(batchId: string, output: string): void;
56
+ /**
57
+ * The batch worker's own log is on disk and worth reading — its tasks did not
58
+ * all succeed. One log per worker, so this is addressed once for the batch
59
+ * rather than copied into any task's output.
60
+ */
61
+ batchOutputAvailable?(batchId: string, path: string): void;
56
62
  setBatchStatus?(batchId: string, status: BatchStatus): void;
57
63
  /**
58
64
  * Set a clickable Nx Cloud link in the terminal UI: `label` is the text
@@ -81,6 +87,7 @@ export declare class CompositeLifeCycle implements LifeCycle {
81
87
  setEstimatedTaskTimings(timings: Record<string, number>): void;
82
88
  registerRunningBatch(batchId: string, batchInfo: BatchInfo): void;
83
89
  appendBatchOutput(batchId: string, output: string): void;
90
+ batchOutputAvailable(batchId: string, path: string): void;
84
91
  setBatchStatus(batchId: string, status: BatchStatus): void;
85
92
  setCloudLink(label: string, url: string): Promise<void>;
86
93
  }
@@ -130,6 +130,13 @@ class CompositeLifeCycle {
130
130
  }
131
131
  }
132
132
  }
133
+ batchOutputAvailable(batchId, path) {
134
+ for (let l of this.lifeCycles) {
135
+ if (l.batchOutputAvailable) {
136
+ l.batchOutputAvailable(batchId, path);
137
+ }
138
+ }
139
+ }
133
140
  setBatchStatus(batchId, status) {
134
141
  for (let l of this.lifeCycles) {
135
142
  if (l.setBatchStatus) {
@@ -0,0 +1,35 @@
1
+ import { Task } from '../../config/task-graph';
2
+ import type { LifeCycle, TaskResult } from '../life-cycle';
3
+ /**
4
+ * Prints a run as a handful of lines rather than a transcript: counts, then one
5
+ * line per failure naming the file that holds its output. Task output stops
6
+ * being a stream to read and becomes an artifact to address, so the size of this
7
+ * renderer's output depends on how many tasks failed, never on how loud they
8
+ * were.
9
+ *
10
+ * Selected by `--output-style=summary`, and by default when Nx is driven by an
11
+ * AI agent, which otherwise reads thousands of lines of passing output to find
12
+ * the one failure.
13
+ */
14
+ export declare class SummaryTerminalOutputLifeCycle implements LifeCycle {
15
+ private readonly tasks;
16
+ private readonly failed;
17
+ private readonly stopped;
18
+ private readonly completed;
19
+ private succeeded;
20
+ private cached;
21
+ private cloudLink;
22
+ private readonly batchLogs;
23
+ constructor(tasks: Task[]);
24
+ setCloudLink(label: string, url: string): void;
25
+ batchOutputAvailable(batchId: string, path: string): void;
26
+ /**
27
+ * Nothing is printed as tasks complete. A summary that interleaved with
28
+ * progress would be a transcript again.
29
+ */
30
+ printTaskTerminalOutput(): void;
31
+ endTasks(taskResults: TaskResult[]): void;
32
+ endCommand(): void;
33
+ private countsLine;
34
+ private failureBlock;
35
+ }