nx 24.0.0-beta.0 → 24.0.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 (90) hide show
  1. package/dist/nx-cloud-internals.d.ts +1 -0
  2. package/dist/nx-cloud-internals.js +7 -1
  3. package/dist/src/command-line/graph/graph.js +2 -1
  4. package/dist/src/command-line/migrate/agentic/close-agent-session.d.ts +1 -1
  5. package/dist/src/command-line/migrate/agentic/close-agent-session.js +4 -3
  6. package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +19 -23
  7. package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +16 -19
  8. package/dist/src/command-line/migrate/agentic/master/run-master-session.js +25 -13
  9. package/dist/src/command-line/migrate/agentic/master/spawn-master.js +82 -22
  10. package/dist/src/command-line/migrate/agentic/select.js +3 -3
  11. package/dist/src/command-line/migrate/agentic/types.js +1 -3
  12. package/dist/src/command-line/migrate/command-object.js +5 -9
  13. package/dist/src/command-line/migrate/execute-migration.d.ts +2 -2
  14. package/dist/src/command-line/migrate/execute-migration.js +18 -10
  15. package/dist/src/command-line/migrate/migrate-analytics.d.ts +6 -0
  16. package/dist/src/command-line/migrate/migrate-analytics.js +12 -0
  17. package/dist/src/command-line/migrate/migrate-commits.d.ts +4 -1
  18. package/dist/src/command-line/migrate/migrate-commits.js +10 -3
  19. package/dist/src/command-line/migrate/migrate-config.d.ts +6 -6
  20. package/dist/src/command-line/migrate/migrate-config.js +12 -6
  21. package/dist/src/command-line/migrate/migrate.d.ts +3 -30
  22. package/dist/src/command-line/migrate/migrate.js +206 -237
  23. package/dist/src/command-line/migrate/run/agent-output.d.ts +1 -0
  24. package/dist/src/command-line/migrate/run/agent-output.js +5 -0
  25. package/dist/src/command-line/migrate/run/broker.d.ts +14 -5
  26. package/dist/src/command-line/migrate/run/broker.js +68 -20
  27. package/dist/src/command-line/migrate/run/existing-run-report.d.ts +1 -1
  28. package/dist/src/command-line/migrate/run/existing-run-report.js +7 -5
  29. package/dist/src/command-line/migrate/run/index.d.ts +6 -5
  30. package/dist/src/command-line/migrate/run/index.js +12 -1
  31. package/dist/src/command-line/migrate/run/orchestrator.d.ts +21 -0
  32. package/dist/src/command-line/migrate/run/orchestrator.js +149 -32
  33. package/dist/src/command-line/migrate/run/run-state.d.ts +13 -0
  34. package/dist/src/command-line/migrate/run/run-state.js +21 -0
  35. package/dist/src/command-line/migrate/run/runbook.js +19 -0
  36. package/dist/src/command-line/migrate/run/state-machine.js +4 -1
  37. package/dist/src/command-line/migrate/run/util.d.ts +3 -0
  38. package/dist/src/command-line/migrate/run/util.js +7 -1
  39. package/dist/src/command-line/migrate/sort-migrations.d.ts +1 -7
  40. package/dist/src/command-line/migrate/sort-migrations.js +1 -12
  41. package/dist/src/command-line/show/show-target/info.js +1 -1
  42. package/dist/src/config/nx-json.d.ts +13 -4
  43. package/dist/src/config/task-graph.d.ts +1 -1
  44. package/dist/src/config/workspace-json-project-json.d.ts +72 -0
  45. package/dist/src/core/graph/main.js +1 -1
  46. package/dist/src/devkit-exports.d.ts +1 -1
  47. package/dist/src/hasher/check-task-files.js +4 -4
  48. package/dist/src/hasher/native-task-hasher-impl.js +3 -1
  49. package/dist/src/hasher/task-hasher.d.ts +2 -0
  50. package/dist/src/hasher/task-hasher.js +4 -1
  51. package/dist/src/native/index.d.ts +92 -14
  52. package/dist/src/native/native-bindings.js +3 -0
  53. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  54. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  55. package/dist/src/project-graph/utils/project-configuration/target-normalization.js +6 -1
  56. package/dist/src/project-graph/utils/retrieve-workspace-files.js +27 -6
  57. package/dist/src/tasks-runner/create-task-graph.d.ts +7 -16
  58. package/dist/src/tasks-runner/create-task-graph.js +43 -57
  59. package/dist/src/tasks-runner/default-tasks-runner.js +1 -1
  60. package/dist/src/tasks-runner/init-tasks-runner.js +13 -0
  61. package/dist/src/tasks-runner/life-cycle.d.ts +4 -1
  62. package/dist/src/tasks-runner/life-cycle.js +7 -0
  63. package/dist/src/tasks-runner/life-cycles/performance-analysis.d.ts +5 -2
  64. package/dist/src/tasks-runner/life-cycles/performance-analysis.js +13 -7
  65. package/dist/src/tasks-runner/life-cycles/performance-life-cycle.d.ts +4 -3
  66. package/dist/src/tasks-runner/life-cycles/performance-life-cycle.js +9 -2
  67. package/dist/src/tasks-runner/readiness/probes.d.ts +9 -0
  68. package/dist/src/tasks-runner/readiness/probes.js +62 -0
  69. package/dist/src/tasks-runner/readiness/ready-when.d.ts +38 -0
  70. package/dist/src/tasks-runner/readiness/ready-when.js +190 -0
  71. package/dist/src/tasks-runner/run-command.js +7 -6
  72. package/dist/src/tasks-runner/running-tasks/batch-process.js +6 -0
  73. package/dist/src/tasks-runner/running-tasks/node-child-process.js +5 -4
  74. package/dist/src/tasks-runner/task-graph-utils.d.ts +9 -6
  75. package/dist/src/tasks-runner/task-graph-utils.js +17 -12
  76. package/dist/src/tasks-runner/task-orchestrator.d.ts +37 -10
  77. package/dist/src/tasks-runner/task-orchestrator.js +459 -79
  78. package/dist/src/tasks-runner/tasks-runner.d.ts +1 -1
  79. package/dist/src/tasks-runner/tasks-schedule.d.ts +18 -1
  80. package/dist/src/tasks-runner/tasks-schedule.js +64 -6
  81. package/dist/src/tasks-runner/utils.js +6 -22
  82. package/dist/src/utils/child-process.js +1 -0
  83. package/dist/src/utils/git-utils.d.ts +6 -1
  84. package/dist/src/utils/git-utils.js +26 -5
  85. package/dist/src/utils/plugins/core-plugins.js +4 -0
  86. package/dist/src/utils/spawn-without-terminal.d.ts +27 -0
  87. package/dist/src/utils/spawn-without-terminal.js +110 -0
  88. package/package.json +12 -11
  89. package/schemas/nx-schema.json +93 -4
  90. package/schemas/project-schema.json +84 -2
@@ -21,7 +21,7 @@ export { createNodesFromFiles } from './project-graph/plugins';
21
21
  /**
22
22
  * @category Tasks
23
23
  */
24
- export type { Task, TaskGraph } from './config/task-graph';
24
+ export type { Task, TaskGraph, TaskGraphEdge } from './config/task-graph';
25
25
  /**
26
26
  * @category Tasks
27
27
  */
@@ -154,14 +154,14 @@ function getDepsOutputs(taskId, { projectGraph, nxJson }) {
154
154
  function collectUpstreamTaskIds(taskGraph, rootTaskId, transitive) {
155
155
  const direct = taskGraph.dependencies[rootTaskId] ?? [];
156
156
  if (!transitive)
157
- return [...direct];
157
+ return direct.map((dep) => dep.id);
158
158
  const collected = new Set();
159
159
  const walk = (id) => {
160
160
  for (const dep of taskGraph.dependencies[id] ?? []) {
161
- if (collected.has(dep))
161
+ if (collected.has(dep.id))
162
162
  continue;
163
- collected.add(dep);
164
- walk(dep);
163
+ collected.add(dep.id);
164
+ walk(dep.id);
165
165
  }
166
166
  };
167
167
  walk(rootTaskId);
@@ -98,7 +98,9 @@ function taskGraphFingerprint(taskGraph, ultracacheConfigurations) {
98
98
  ];
99
99
  for (const id of Object.keys(taskGraph.tasks).sort()) {
100
100
  const task = taskGraph.tasks[id];
101
- parts.push(id, task.target.project, task.target.target, task.target.configuration ?? '', (task.outputs ?? []).join(','), (taskGraph.dependencies[id] ?? []).join(','), (taskGraph.continuousDependencies[id] ?? []).join(','), (taskGraph.continuousDependenciesWithoutInputs?.[id] ?? []).join(','));
101
+ parts.push(id, task.target.project, task.target.target, task.target.configuration ?? '', (task.outputs ?? []).join(','), (taskGraph.dependencies[id] ?? []).map((edge) => edge.id).join(','), (taskGraph.continuousDependencies[id] ?? [])
102
+ .map((edge) => JSON.stringify([edge.id, edge.inputs === false]))
103
+ .join(','));
102
104
  }
103
105
  return (0, native_1.hashArray)(parts);
104
106
  }
@@ -142,6 +142,7 @@ export declare function getInputs(task: Task, projectGraph: ProjectGraph, nxJson
142
142
  projectInputs: {
143
143
  input: string;
144
144
  projects: string[];
145
+ dependencies?: true;
145
146
  }[];
146
147
  depsFilesets: {
147
148
  fileset: string;
@@ -158,6 +159,7 @@ export declare function splitInputsIntoSelfAndDependencies(inputs: ReadonlyArray
158
159
  projectInputs: {
159
160
  input: string;
160
161
  projects: string[];
162
+ dependencies?: true;
161
163
  }[];
162
164
  selfInputs: ExpandedSelfInput[];
163
165
  depsOutputs: ExpandedDepsOutput[];
@@ -200,7 +200,7 @@ function splitInputsIntoSelfAndDependencies(inputs, namedInputs) {
200
200
  }
201
201
  else if ('input' in d &&
202
202
  !('fileset' in d) &&
203
- (('dependencies' in d && d.dependencies) ||
203
+ (('dependencies' in d && d.dependencies && !('projects' in d)) ||
204
204
  // Todo(@AgentEnder): Remove check in v17
205
205
  ('projects' in d &&
206
206
  typeof d.projects === 'string' &&
@@ -219,6 +219,9 @@ function splitInputsIntoSelfAndDependencies(inputs, namedInputs) {
219
219
  projectInputs.push({
220
220
  input: d.input,
221
221
  projects: Array.isArray(d.projects) ? d.projects : [d.projects],
222
+ ...('dependencies' in d && d.dependencies
223
+ ? { dependencies: true }
224
+ : {}),
222
225
  });
223
226
  }
224
227
  else {
@@ -32,7 +32,7 @@ export declare class ExternalObject<T> {
32
32
  }
33
33
  }
34
34
  export declare class AppLifeCycle {
35
- constructor(tasks: Array<Task>, initiatingTasks: Array<string>, runMode: RunMode, pinnedTasks: Array<string>, tuiCliArgs: TuiCliArgs, tuiConfig: TuiConfig, titleText: string, workspaceRoot: string, taskGraph: TaskGraph, isCloudEnabled?: boolean | undefined | null)
35
+ constructor(tasks: Array<Task>, initiatingTasks: Array<string>, runMode: RunMode, pinnedTasks: Array<string>, tuiCliArgs: TuiCliArgs, tuiConfig: TuiConfig, titleText: string, workspaceRoot: string, taskGraph: TaskGraph, readyDependencies: Record<string, Array<string>>, isCloudEnabled?: boolean | undefined | null)
36
36
  startCommand(threadCount?: number | undefined | null): void
37
37
  scheduleTask(task: Task): void
38
38
  startTasks(tasks: Array<Task>, metadata: object): void
@@ -44,6 +44,7 @@ export declare class AppLifeCycle {
44
44
  registerRunningTaskWithEmptyParser(taskId: string): void
45
45
  appendTaskOutput(taskId: string, output: string, isPtyOutput: boolean): void
46
46
  setTaskStatus(taskId: string, status: TaskStatus): void
47
+ setTaskReadiness(taskId: string, readiness: TaskReadiness): void
47
48
  setTaskTiming(taskId: string, startTime: number, endTime: number): void
48
49
  registerForcedShutdownCallback(forcedShutdownCallback: (() => unknown)): void
49
50
  __setCloudMessage(message: string): Promise<void>
@@ -98,7 +99,8 @@ export declare class HashPlanner {
98
99
  constructor(nxJson: NxJson, projectGraph: ExternalObject<ProjectGraph>)
99
100
  /**
100
101
  * `configurations` is this run's Ultracache configurations; a task with an
101
- * eligible entry hashes its observed reads instead of its declared filesets.
102
+ * eligible entry hashes its observed reads instead of its declared
103
+ * filesets, except `always` ones.
102
104
  * `options` carries the task ids decided in JS, where executors and
103
105
  * target configuration are resolved.
104
106
  */
@@ -127,6 +129,15 @@ export declare class ImportResult {
127
129
  staticImportExpressions: Array<string>
128
130
  }
129
131
 
132
+ /**
133
+ * Done once every pattern has appeared in the fed output, in any order, even
134
+ * split across chunks or interleaved with terminal control sequences.
135
+ */
136
+ export declare class LogMatcher {
137
+ constructor(patterns: Array<string>)
138
+ feed(chunk: string): boolean
139
+ }
140
+
130
141
  export declare class NxCache {
131
142
  cacheDirectory: string
132
143
  constructor(workspaceRoot: string, cachePath: string, dbConnection: ExternalObject<NxDbConnection>, linkTaskDetails?: boolean | undefined | null, maxCacheSize?: number | undefined | null)
@@ -232,15 +243,33 @@ export declare class ProcessMetricsCollector {
232
243
  subscribe(callback: (err: Error | null, event: MetricsUpdate) => void): void
233
244
  }
234
245
 
246
+ /**
247
+ * Retries a probe until it passes, the timeout elapses or `cancel` is called.
248
+ * A failing attempt is never an error.
249
+ */
250
+ export declare class ReadinessProbe {
251
+ constructor(config: ReadinessProbeConfig, cwd: string)
252
+ /** False once the timeout elapses or `cancel` is called. */
253
+ wait(): Promise<boolean>
254
+ cancel(): void
255
+ }
256
+
235
257
  export declare class RunningTasksService {
236
258
  constructor(db: ExternalObject<NxDbConnection>)
237
259
  getRunningTasks(ids: Array<string>): Array<string>
238
260
  addRunningTask(taskId: string): void
239
261
  /**
240
- * Release this process's claim on a task. A row another process has since
241
- * taken over is left in place.
262
+ * Release this process's claim on a task and its readiness. A row another
263
+ * process has since taken over is left in place.
242
264
  */
243
265
  removeRunningTask(taskId: string): void
266
+ /**
267
+ * No-op unless this process holds the task's claim, so a late or displaced
268
+ * probe result cannot overwrite the current owner's verdict.
269
+ */
270
+ setTaskReadiness(taskId: string, status: TaskReadiness): void
271
+ /** `None` when the task is not running. */
272
+ getTaskReadiness(taskId: string): TaskReadiness | null
244
273
  }
245
274
 
246
275
  export declare class RustPseudoTerminal {
@@ -704,8 +733,12 @@ export declare const enum EventType {
704
733
  rescan = 'rescan'
705
734
  }
706
735
 
707
- /** The files an `includeIgnored` fileset group matches on disk, sorted. */
708
- export declare function expandFilesInput(workspaceRoot: string, globs: Array<string>): Array<string>
736
+ /**
737
+ * The files an `includeIgnored` fileset group matches on disk, sorted.
738
+ * Given the workspace's project roots, an entry skips projects nested below
739
+ * the one it reads from, as hashing does.
740
+ */
741
+ export declare function expandFilesInput(workspaceRoot: string, globs: Array<string>, projectRoots?: Array<string> | undefined | null): Array<string>
709
742
 
710
743
  export declare function expandOutputs(directory: string, entries: Array<string>): Array<string>
711
744
 
@@ -751,6 +784,11 @@ export interface FileSetInput {
751
784
  * instead of the workspace file map.
752
785
  */
753
786
  includeIgnored?: boolean
787
+ /**
788
+ * Keep the matched files in the hash even when the task is hashed from
789
+ * its ultracache recording.
790
+ */
791
+ always?: boolean
754
792
  }
755
793
 
756
794
  export declare function findImports(projectFileMap: Record<string, Array<string>>): Array<ImportResult>
@@ -909,8 +947,17 @@ export interface InputMatch {
909
947
 
910
948
  export interface InputsInput {
911
949
  input: string
950
+ /**
951
+ * With `projects`: the selected projects' dependencies' `input`, as `^input`
952
+ * resolves for each, instead of the projects' own files.
953
+ */
912
954
  dependencies?: boolean
913
955
  projects?: string | Array<string>
956
+ /**
957
+ * Keep the files this input resolves to in the hash even when the task
958
+ * is hashed from its ultracache recording.
959
+ */
960
+ always?: boolean
914
961
  }
915
962
 
916
963
  export declare function installNxConsole(): Promise<boolean>
@@ -950,6 +997,11 @@ export interface JsonInput {
950
997
  json: string
951
998
  fields?: Array<string>
952
999
  excludeFields?: Array<string>
1000
+ /**
1001
+ * Keep the file in the hash even when the task is hashed from its
1002
+ * ultracache recording.
1003
+ */
1004
+ always?: boolean
953
1005
  }
954
1006
 
955
1007
  /**
@@ -1116,6 +1168,16 @@ export interface ProjectGraph {
1116
1168
  externalNodes: Record<string, ExternalNode>
1117
1169
  }
1118
1170
 
1171
+ /** Exactly one of `url`, `port` or `command` is set. `interval` absent means backoff. */
1172
+ export interface ReadinessProbeConfig {
1173
+ url?: string
1174
+ port?: number
1175
+ host?: string
1176
+ command?: string
1177
+ timeout: number
1178
+ interval?: number
1179
+ }
1180
+
1119
1181
  export declare function remove(src: string): void
1120
1182
 
1121
1183
  /**
@@ -1287,14 +1349,19 @@ export interface TaskGraph {
1287
1349
  roots: Array<string>
1288
1350
  /** Map of Task IDs to Tasks */
1289
1351
  tasks: Record<string, Task>
1290
- /** Map of Task IDs to IDs of tasks which the task depends on */
1291
- dependencies: Record<string, Array<string>>
1292
- continuousDependencies: Record<string, Array<string>>
1293
- /**
1294
- * The subset of `continuous_dependencies` from `dependsOn` entries with
1295
- * `inputs: false`: still run, but their inputs are not hashed into the task.
1296
- */
1297
- continuousDependenciesWithoutInputs?: Record<string, Array<string>>
1352
+ /** Map of Task IDs to the edges to tasks which the task depends on */
1353
+ dependencies: Record<string, Array<TaskGraphEdge>>
1354
+ continuousDependencies: Record<string, Array<TaskGraphEdge>>
1355
+ }
1356
+
1357
+ /** An edge from a task to one of the tasks it depends on */
1358
+ export interface TaskGraphEdge {
1359
+ /** ID of the task depended on */
1360
+ id: string
1361
+ /** What the dependent waits for on a continuous dependency. Defaults to `started`. */
1362
+ waitFor?: 'started' | 'ready'
1363
+ /** `false` on a continuous dependency whose inputs the dependent does not hash */
1364
+ inputs?: false
1298
1365
  }
1299
1366
 
1300
1367
  /** Details about the composition of a task's hash */
@@ -1339,6 +1406,12 @@ export interface TaskOutputs {
1339
1406
  files?: Array<OutputFile>
1340
1407
  }
1341
1408
 
1409
+ export declare const enum TaskReadiness {
1410
+ Pending = 0,
1411
+ Ready = 1,
1412
+ Failed = 2
1413
+ }
1414
+
1342
1415
  /**
1343
1416
  * The result of a completed Task.
1344
1417
  *
@@ -1386,6 +1459,11 @@ export interface TaskTarget {
1386
1459
  export interface TaskUltracacheSettings {
1387
1460
  /** How this target's tasks participate. Defaults to `on`. */
1388
1461
  mode?: 'on' | 'warn' | 'error' | 'off'
1462
+ /**
1463
+ * Collect successful stat metadata accesses as inputs. Defaults to `false`.
1464
+ * Missing-path probes are tracked independently of this setting.
1465
+ */
1466
+ collectStats?: boolean
1389
1467
  /**
1390
1468
  * Workspace-relative glob patterns for reads that should be excluded
1391
1469
  * from ultracache reports. The first path segment cannot contain `*`,
@@ -584,11 +584,13 @@ module.exports.HashPlanner = nativeBinding.HashPlanner
584
584
  module.exports.HttpRemoteCache = nativeBinding.HttpRemoteCache
585
585
  module.exports.IgnoredIndexReader = nativeBinding.IgnoredIndexReader
586
586
  module.exports.ImportResult = nativeBinding.ImportResult
587
+ module.exports.LogMatcher = nativeBinding.LogMatcher
587
588
  module.exports.NxCache = nativeBinding.NxCache
588
589
  module.exports.NxConsolePreferences = nativeBinding.NxConsolePreferences
589
590
  module.exports.NxPluginCapabilities = nativeBinding.NxPluginCapabilities
590
591
  module.exports.NxTaskHistory = nativeBinding.NxTaskHistory
591
592
  module.exports.ProcessMetricsCollector = nativeBinding.ProcessMetricsCollector
593
+ module.exports.ReadinessProbe = nativeBinding.ReadinessProbe
592
594
  module.exports.RunningTasksService = nativeBinding.RunningTasksService
593
595
  module.exports.RustPseudoTerminal = nativeBinding.RustPseudoTerminal
594
596
  module.exports.TaskDetails = nativeBinding.TaskDetails
@@ -649,6 +651,7 @@ module.exports.restoreTerminal = nativeBinding.restoreTerminal
649
651
  module.exports.RunMode = nativeBinding.RunMode
650
652
  module.exports.SupportedEditor = nativeBinding.SupportedEditor
651
653
  module.exports.sweepBatchOutputs = nativeBinding.sweepBatchOutputs
654
+ module.exports.TaskReadiness = nativeBinding.TaskReadiness
652
655
  module.exports.TaskStatus = nativeBinding.TaskStatus
653
656
  module.exports.testOnlyTransferFileMap = nativeBinding.testOnlyTransferFileMap
654
657
  module.exports.trackEvent = nativeBinding.trackEvent
Binary file
@@ -186,6 +186,7 @@ function warnAboutLegacyCachedTargets(legacyCacheReads) {
186
186
  */
187
187
  const KNOWN_ULTRACACHE_KEYS = new Set([
188
188
  'mode',
189
+ 'collectStats',
189
190
  'ignoredReads',
190
191
  'ignoredWrites',
191
192
  utils_1.NX_SPREAD_TOKEN,
@@ -233,7 +234,7 @@ function validateTargetUltracache(ultracache, projectName, projectRoot, targetNa
233
234
  const errors = [];
234
235
  for (const key of Object.keys(ultracache)) {
235
236
  if (!KNOWN_ULTRACACHE_KEYS.has(key)) {
236
- errors.push(`"ultracache.${key}" for target ${where} is not an ultracache option. Supported options are "mode", "ignoredReads" and "ignoredWrites".`);
237
+ errors.push(`"ultracache.${key}" for target ${where} is not an ultracache option. Supported options are "mode", "collectStats", "ignoredReads" and "ignoredWrites".`);
237
238
  }
238
239
  }
239
240
  if (ultracache.mode !== undefined &&
@@ -243,6 +244,10 @@ function validateTargetUltracache(ultracache, projectName, projectRoot, targetNa
243
244
  ? `"${ultracache.mode}"`
244
245
  : describeUltracacheValue(ultracache.mode)}.`);
245
246
  }
247
+ if (ultracache.collectStats !== undefined &&
248
+ typeof ultracache.collectStats !== 'boolean') {
249
+ errors.push(`"ultracache.collectStats" for target ${where} must be a boolean, but it is ${describeUltracacheValue(ultracache.collectStats)}.`);
250
+ }
246
251
  for (const key of ['ignoredReads', 'ignoredWrites']) {
247
252
  const value = ultracache[key];
248
253
  if (value === undefined)
@@ -12,6 +12,7 @@ const angular_json_1 = require("../../adapter/angular-json");
12
12
  const nx_json_1 = require("../../config/nx-json");
13
13
  const project_configuration_utils_1 = require("./project-configuration-utils");
14
14
  const workspace_context_1 = require("../../utils/workspace-context");
15
+ const minimatch_1 = require("minimatch");
15
16
  const path_1 = require("path");
16
17
  const get_plugins_1 = require("../plugins/get-plugins");
17
18
  /**
@@ -45,11 +46,9 @@ async function retrieveWorkspaceFiles(workspaceRoot, projectRootMap) {
45
46
  async function retrieveProjectConfigurations(separatedPlugins, workspaceRoot, nxJson) {
46
47
  const specifiedWithCreateNodes = separatedPlugins.specifiedPlugins.filter((p) => !!p.createNodes);
47
48
  const defaultWithCreateNodes = separatedPlugins.defaultPlugins.filter((p) => !!p.createNodes);
48
- const specifiedGlobPatterns = getGlobPatternsOfPlugins(specifiedWithCreateNodes);
49
- const defaultGlobPatterns = getGlobPatternsOfPlugins(defaultWithCreateNodes);
50
49
  const [specifiedPluginFiles, defaultPluginFiles] = await Promise.all([
51
- (0, workspace_context_1.multiGlobWithWorkspaceContext)(workspaceRoot, specifiedGlobPatterns),
52
- (0, workspace_context_1.multiGlobWithWorkspaceContext)(workspaceRoot, defaultGlobPatterns),
50
+ globPluginFiles(workspaceRoot, specifiedWithCreateNodes),
51
+ globPluginFiles(workspaceRoot, defaultWithCreateNodes),
53
52
  ]);
54
53
  return (0, project_configuration_utils_1.createProjectConfigurationsWithPlugins)(workspaceRoot, nxJson, {
55
54
  specifiedPluginFiles: specifiedPluginFiles ?? [],
@@ -71,8 +70,7 @@ async function retrieveProjectConfigurationsWithAngularProjects(workspaceRoot, n
71
70
  return res;
72
71
  }
73
72
  async function retrieveProjectConfigurationPaths(root, plugins) {
74
- const projectGlobPatterns = getGlobPatternsOfPlugins(plugins);
75
- const pluginConfigFiles = await (0, workspace_context_1.multiGlobWithWorkspaceContext)(root, projectGlobPatterns);
73
+ const pluginConfigFiles = await globPluginFiles(root, plugins);
76
74
  return pluginConfigFiles.flat();
77
75
  }
78
76
  const projectsWithoutPluginCache = new Map();
@@ -105,6 +103,29 @@ async function retrieveProjectConfigurationsWithoutPluginInference(root) {
105
103
  function clearProjectsWithoutPluginInferenceCache() {
106
104
  projectsWithoutPluginCache.clear();
107
105
  }
106
+ /**
107
+ * Files each plugin's createNodes pattern matches, in plugin order. A plugin
108
+ * scoped by `include` globs only those patterns natively, so a broad pattern
109
+ * (every `.ts` file, say) is never matched against the whole workspace in JS.
110
+ */
111
+ async function globPluginFiles(root, plugins) {
112
+ const unscoped = plugins.filter((p) => !canGlobInclude(p));
113
+ const unscopedFiles = (await (0, workspace_context_1.multiGlobWithWorkspaceContext)(root, unscoped.map((p) => p.createNodes[0]))) ?? [];
114
+ return Promise.all(plugins.map(async (plugin) => {
115
+ if (!canGlobInclude(plugin)) {
116
+ return unscopedFiles[unscoped.indexOf(plugin)] ?? [];
117
+ }
118
+ const pattern = new minimatch_1.Minimatch(plugin.createNodes[0], { dot: true });
119
+ const included = await (0, workspace_context_1.globWithWorkspaceContext)(root, plugin.include);
120
+ return included.filter((file) => pattern.match(file));
121
+ }));
122
+ }
123
+ // Only narrow includes: a `**` scope can return more files than the plugin
124
+ // pattern does. Negations and extglobs keep the JS matcher's semantics.
125
+ function canGlobInclude(plugin) {
126
+ return (plugin.include?.length > 0 &&
127
+ plugin.include.every((pattern) => !pattern.includes('**') && !/[!()]/.test(pattern)));
128
+ }
108
129
  function getGlobPatternsOfPlugins(plugins) {
109
130
  return plugins.map((p) => p.createNodes[0]);
110
131
  }
@@ -1,5 +1,5 @@
1
1
  import { ProjectGraph, ProjectGraphProjectNode } from '../config/project-graph';
2
- import { Task, TaskGraph } from '../config/task-graph';
2
+ import { Task, TaskGraph, TaskGraphEdge } from '../config/task-graph';
3
3
  import { TargetDependencies } from '../config/nx-json';
4
4
  /**
5
5
  * For each task, the overrides every `dependsOn` edge into it would give it,
@@ -20,13 +20,11 @@ export declare class ProcessTasks {
20
20
  [id: string]: Task;
21
21
  };
22
22
  readonly dependencies: {
23
- [k: string]: string[];
23
+ [k: string]: TaskGraphEdge[];
24
24
  };
25
25
  readonly continuousDependencies: {
26
- [k: string]: string[];
26
+ [k: string]: TaskGraphEdge[];
27
27
  };
28
- private readonly continuousEdgesWithoutInputs;
29
- private readonly continuousEdgesWithInputs;
30
28
  readonly dependencyOverrides: DependencyOverrides;
31
29
  private readonly allTargetNames;
32
30
  constructor(extraTargetDependencies: TargetDependencies, projectGraph: ProjectGraph, recordDependencyOverrides?: boolean);
@@ -36,13 +34,6 @@ export declare class ProcessTasks {
36
34
  private processTasksForSingleProject;
37
35
  private processTasksForDependencies;
38
36
  private addContinuousEdge;
39
- /**
40
- * Continuous edges every `dependsOn` path into which has `inputs: false`.
41
- * Undefined when there are none, so graphs without the option are unchanged.
42
- */
43
- continuousDependenciesWithoutInputs(): {
44
- [k: string]: string[];
45
- } | undefined;
46
37
  private recordEdge;
47
38
  private createDummyTask;
48
39
  createTask(id: string, project: ProjectGraphProjectNode, target: string, resolvedConfiguration: string | undefined, overrides: Record<string, unknown>): Task;
@@ -64,11 +55,11 @@ export declare function filterTaskGraphToSelection(projectGraph: ProjectGraph, t
64
55
  * It will manipulate the dependencies object in place
65
56
  */
66
57
  export declare function filterDummyTasks(dependencies: {
67
- [k: string]: string[];
58
+ [k: string]: TaskGraphEdge[];
68
59
  }): void;
69
60
  /**
70
61
  * this function is used to get the non dummy dependencies of a task recursively
71
62
  */
72
- export declare function getNonDummyDeps(currentTask: string, dependencies: {
73
- [k: string]: string[];
74
- }, cycles?: Set<string>, seen?: Set<string>): string[];
63
+ export declare function getNonDummyDeps(currentEdge: TaskGraphEdge, dependencies: {
64
+ [k: string]: TaskGraphEdge[];
65
+ }, cycles?: Set<string>, seen?: Set<string>): TaskGraphEdge[];
@@ -20,11 +20,6 @@ class ProcessTasks {
20
20
  this.tasks = {};
21
21
  this.dependencies = {};
22
22
  this.continuousDependencies = {};
23
- // `from\0to` continuous edges by whether their `dependsOn` entry has
24
- // `inputs: false`. Keyed from the creator's target, like `recordEdge`, so
25
- // an edge through a dummy task names the real task.
26
- this.continuousEdgesWithoutInputs = new Set();
27
- this.continuousEdgesWithInputs = new Set();
28
23
  this.dependencyOverrides = {};
29
24
  const allTargetNames = new Set();
30
25
  for (const projectName in projectGraph.nodes) {
@@ -64,28 +59,14 @@ class ProcessTasks {
64
59
  }
65
60
  }
66
61
  for (let d of Object.keys(this.dependencies)) {
67
- this.dependencies[d] = this.dependencies[d].filter((dd) => !!initialTasks[dd]);
62
+ this.dependencies[d] = this.dependencies[d].filter((dd) => !!initialTasks[dd.id]);
68
63
  }
69
64
  for (let d of Object.keys(this.continuousDependencies)) {
70
- this.continuousDependencies[d] = this.continuousDependencies[d].filter((dd) => !!initialTasks[dd]);
65
+ this.continuousDependencies[d] = this.continuousDependencies[d].filter((dd) => !!initialTasks[dd.id]);
71
66
  }
72
67
  }
73
68
  filterDummyTasks(this.dependencies);
74
- for (const taskId of Object.keys(this.dependencies)) {
75
- if (this.dependencies[taskId].length > 0) {
76
- this.dependencies[taskId] = [
77
- ...new Set(this.dependencies[taskId].filter((d) => d !== taskId)).values(),
78
- ];
79
- }
80
- }
81
69
  filterDummyTasks(this.continuousDependencies);
82
- for (const taskId of Object.keys(this.continuousDependencies)) {
83
- if (this.continuousDependencies[taskId].length > 0) {
84
- this.continuousDependencies[taskId] = [
85
- ...new Set(this.continuousDependencies[taskId].filter((d) => d !== taskId)).values(),
86
- ];
87
- }
88
- }
89
70
  return Object.keys(this.tasks).filter((d) => this.dependencies[d].length === 0 &&
90
71
  this.continuousDependencies[d].length === 0);
91
72
  }
@@ -140,7 +121,7 @@ class ProcessTasks {
140
121
  this.addContinuousEdge(task, selfTaskId, dependencyConfig);
141
122
  }
142
123
  else {
143
- this.dependencies[task.id].push(selfTaskId);
124
+ this.dependencies[task.id].push({ id: selfTaskId });
144
125
  }
145
126
  }
146
127
  }
@@ -164,7 +145,7 @@ class ProcessTasks {
164
145
  this.addContinuousEdge(task, depTargetId, dependencyConfig);
165
146
  }
166
147
  else {
167
- this.dependencies[task.id].push(depTargetId);
148
+ this.dependencies[task.id].push({ id: depTargetId });
168
149
  }
169
150
  }
170
151
  if (!this.tasks[depTargetId]) {
@@ -182,8 +163,8 @@ class ProcessTasks {
182
163
  '__' +
183
164
  dependencyConfig.target +
184
165
  DUMMY_TASK_TARGET, undefined);
185
- this.dependencies[task.id].push(dummyId);
186
- this.continuousDependencies[task.id].push(dummyId);
166
+ this.dependencies[task.id].push({ id: dummyId });
167
+ this.continuousDependencies[task.id].push({ id: dummyId });
187
168
  this.dependencies[dummyId] ??= [];
188
169
  this.continuousDependencies[dummyId] ??= [];
189
170
  const noopTask = this.createDummyTask(dummyId, task);
@@ -192,30 +173,7 @@ class ProcessTasks {
192
173
  }
193
174
  }
194
175
  addContinuousEdge(task, dependencyId, dependencyConfig) {
195
- this.continuousDependencies[task.id].push(dependencyId);
196
- const { project, target, configuration } = task.target;
197
- const edge = `${(0, utils_1.createTaskId)(project, target, configuration)}\0${dependencyId}`;
198
- (dependencyConfig.inputs === false
199
- ? this.continuousEdgesWithoutInputs
200
- : this.continuousEdgesWithInputs).add(edge);
201
- }
202
- /**
203
- * Continuous edges every `dependsOn` path into which has `inputs: false`.
204
- * Undefined when there are none, so graphs without the option are unchanged.
205
- */
206
- continuousDependenciesWithoutInputs() {
207
- let without;
208
- for (const [taskId, deps] of Object.entries(this.continuousDependencies)) {
209
- const excluded = deps.filter((dep) => {
210
- const edge = `${taskId}\0${dep}`;
211
- return (this.continuousEdgesWithoutInputs.has(edge) &&
212
- !this.continuousEdgesWithInputs.has(edge));
213
- });
214
- if (excluded.length > 0) {
215
- (without ??= {})[taskId] = excluded;
216
- }
217
- }
218
- return without;
176
+ this.continuousDependencies[task.id].push(continuousEdge(dependencyId, dependencyConfig));
219
177
  }
220
178
  recordEdge(task, dependencyId, overrides) {
221
179
  if (!this.recordDependencyOverrides) {
@@ -307,15 +265,11 @@ function filterTaskGraphToSelection(projectGraph, taskGraph, dependencyOverrides
307
265
  }
308
266
  function buildTaskGraph(p, projectNames, targets, configuration, overrides, excludeTaskDependencies) {
309
267
  const roots = p.processTasks(projectNames, targets, configuration, overrides, excludeTaskDependencies);
310
- const continuousDependenciesWithoutInputs = p.continuousDependenciesWithoutInputs();
311
268
  return {
312
269
  roots,
313
270
  tasks: p.tasks,
314
271
  dependencies: p.dependencies,
315
272
  continuousDependencies: p.continuousDependencies,
316
- ...(continuousDependenciesWithoutInputs
317
- ? { continuousDependenciesWithoutInputs }
318
- : {}),
319
273
  };
320
274
  }
321
275
  function interpolateOverrides(args, projectName, project) {
@@ -333,6 +287,37 @@ function interpolateOverrides(args, projectName, project) {
333
287
  });
334
288
  return interpolatedArgs;
335
289
  }
290
+ function continuousEdge(id, dependencyConfig) {
291
+ const edge = { id };
292
+ if (dependencyConfig.waitFor === 'ready') {
293
+ edge.waitFor = 'ready';
294
+ }
295
+ if (dependencyConfig.inputs === false) {
296
+ edge.inputs = false;
297
+ }
298
+ return edge;
299
+ }
300
+ // A task reached by several edges waits for ready when any of them does, and
301
+ // keeps its inputs unless all of them opt out
302
+ function mergeEdges(edges) {
303
+ const byId = new Map();
304
+ for (const edge of edges) {
305
+ const seen = byId.get(edge.id);
306
+ if (!seen) {
307
+ byId.set(edge.id, edge);
308
+ continue;
309
+ }
310
+ const merged = { id: edge.id };
311
+ if (seen.waitFor === 'ready' || edge.waitFor === 'ready') {
312
+ merged.waitFor = 'ready';
313
+ }
314
+ if (seen.inputs === false && edge.inputs === false) {
315
+ merged.inputs = false;
316
+ }
317
+ byId.set(edge.id, merged);
318
+ }
319
+ return [...byId.values()];
320
+ }
336
321
  /**
337
322
  * This function is used to filter out the dummy tasks from the dependencies
338
323
  * It will manipulate the dependencies object in place
@@ -345,7 +330,7 @@ function filterDummyTasks(dependencies) {
345
330
  for (const dep of deps) {
346
331
  normalizedDeps.push(...getNonDummyDeps(dep, dependencies, cycles, new Set([key])));
347
332
  }
348
- dependencies[key] = normalizedDeps;
333
+ dependencies[key] = mergeEdges(normalizedDeps);
349
334
  }
350
335
  }
351
336
  for (const key of Object.keys(dependencies)) {
@@ -357,12 +342,13 @@ function filterDummyTasks(dependencies) {
357
342
  /**
358
343
  * this function is used to get the non dummy dependencies of a task recursively
359
344
  */
360
- function getNonDummyDeps(currentTask, dependencies, cycles, seen = new Set()) {
345
+ function getNonDummyDeps(currentEdge, dependencies, cycles, seen = new Set()) {
346
+ const currentTask = currentEdge.id;
361
347
  if (seen.has(currentTask)) {
362
348
  return [];
363
349
  }
364
- seen.add(currentTask);
365
350
  if (currentTask.endsWith(DUMMY_TASK_TARGET)) {
351
+ seen.add(currentTask);
366
352
  if (cycles?.has(currentTask)) {
367
353
  return [];
368
354
  }
@@ -374,7 +360,7 @@ function getNonDummyDeps(currentTask, dependencies, cycles, seen = new Set()) {
374
360
  return deps.flatMap((dep) => getNonDummyDeps(dep, dependencies, cycles, seen));
375
361
  }
376
362
  else {
377
- return [currentTask];
363
+ return [currentEdge];
378
364
  }
379
365
  }
380
366
  function createTaskOverrides(dependencyConfig, cliOverrides, sourceTask, projectGraph) {
@@ -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?.specifiedOutputStyle, context.nxArgs?.resolvedOutputStyle ?? 'static-failures-only', context.taskGraph, context.deferredTaskIds);
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', context.taskGraph, context.hashingDeferredTaskIds);
72
72
  return orchestrator.run();
73
73
  }
74
74
  exports.default = exports.defaultTasksRunner;