nx 22.7.0 → 23.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/src/config/workspace-json-project-json.d.ts +10 -0
  2. package/dist/src/daemon/server/handle-hash-tasks.js +1 -1
  3. package/dist/src/daemon/server/project-graph-incremental-recomputation.d.ts +1 -4
  4. package/dist/src/daemon/server/project-graph-incremental-recomputation.js +11 -20
  5. package/dist/src/executors/utils/convert-nx-executor.js +2 -2
  6. package/dist/src/hasher/create-task-hasher.js +1 -1
  7. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  8. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  9. package/dist/src/project-graph/build-project-graph.d.ts +2 -4
  10. package/dist/src/project-graph/build-project-graph.js +2 -7
  11. package/dist/src/project-graph/file-map-utils.d.ts +2 -4
  12. package/dist/src/project-graph/file-map-utils.js +0 -3
  13. package/dist/src/project-graph/plugins/get-plugins.d.ts +15 -0
  14. package/dist/src/project-graph/plugins/get-plugins.js +21 -3
  15. package/dist/src/project-graph/project-graph.js +7 -6
  16. package/dist/src/project-graph/utils/project-configuration/name-substitution-manager.d.ts +40 -64
  17. package/dist/src/project-graph/utils/project-configuration/name-substitution-manager.js +182 -411
  18. package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.d.ts +10 -4
  19. package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.js +22 -8
  20. package/dist/src/project-graph/utils/project-configuration/source-maps.d.ts +4 -61
  21. package/dist/src/project-graph/utils/project-configuration/source-maps.js +14 -59
  22. package/dist/src/project-graph/utils/project-configuration/target-defaults.d.ts +16 -0
  23. package/dist/src/project-graph/utils/project-configuration/target-defaults.js +117 -0
  24. package/dist/src/project-graph/utils/project-configuration/target-merging.d.ts +1 -4
  25. package/dist/src/project-graph/utils/project-configuration/target-merging.js +261 -136
  26. package/dist/src/project-graph/utils/project-configuration/target-normalization.js +0 -7
  27. package/dist/src/project-graph/utils/project-configuration/utils.d.ts +23 -0
  28. package/dist/src/project-graph/utils/project-configuration/utils.js +164 -0
  29. package/dist/src/project-graph/utils/project-configuration-utils.d.ts +33 -9
  30. package/dist/src/project-graph/utils/project-configuration-utils.js +153 -65
  31. package/dist/src/project-graph/utils/retrieve-workspace-files.d.ts +6 -3
  32. package/dist/src/project-graph/utils/retrieve-workspace-files.js +32 -13
  33. package/package.json +11 -11
@@ -5,13 +5,10 @@ exports.resolveCommandSyntacticSugar = resolveCommandSyntacticSugar;
5
5
  exports.mergeMetadata = mergeMetadata;
6
6
  exports.mergeTargetConfigurations = mergeTargetConfigurations;
7
7
  exports.isCompatibleTarget = isCompatibleTarget;
8
- exports.mergeTargetDefaultWithTargetDefinition = mergeTargetDefaultWithTargetDefinition;
9
8
  exports.resolveNxTokensInOptions = resolveNxTokensInOptions;
10
- exports.readTargetDefaultsForTarget = readTargetDefaultsForTarget;
11
9
  const logger_1 = require("../../../utils/logger");
12
- const globs_1 = require("../../../utils/globs");
13
10
  const source_maps_1 = require("./source-maps");
14
- const minimatch_1 = require("minimatch");
11
+ const utils_1 = require("./utils");
15
12
  function deepClone(obj) {
16
13
  return structuredClone(obj);
17
14
  }
@@ -91,40 +88,154 @@ function mergeMetadata(sourceMap, sourceInformation, baseSourceMapPath, metadata
91
88
  }
92
89
  return result;
93
90
  }
94
- function mergeOptions(newOptions, baseOptions, projectConfigSourceMap, sourceInformation, targetIdentifier) {
95
- const mergedOptions = {
96
- ...(baseOptions ?? {}),
97
- ...(newOptions ?? {}),
98
- };
99
- // record new options & option properties in source map
100
- if (projectConfigSourceMap) {
101
- for (const newOption in newOptions) {
102
- projectConfigSourceMap[`${targetIdentifier}.options.${newOption}`] =
103
- sourceInformation;
104
- }
91
+ function mergeOptions(newOptions, baseOptions, projectConfigSourceMap, sourceInformation, targetIdentifier, deferSpreadsWithoutBase) {
92
+ // `'...'` at the options level uses object-spread semantics.
93
+ if (newOptions?.[utils_1.NX_SPREAD_TOKEN] === true) {
94
+ return (0, utils_1.getMergeValueResult)(baseOptions, newOptions, projectConfigSourceMap
95
+ ? {
96
+ sourceMap: projectConfigSourceMap,
97
+ key: `${targetIdentifier}.options`,
98
+ sourceInformation,
99
+ }
100
+ : undefined, deferSpreadsWithoutBase);
101
+ }
102
+ const mergedOptionKeys = (0, utils_1.uniqueKeysInObjects)(baseOptions ?? {}, newOptions ?? {});
103
+ const mergedOptions = {};
104
+ for (const optionKey of mergedOptionKeys) {
105
+ mergedOptions[optionKey] = (0, utils_1.getMergeValueResult)(baseOptions ? baseOptions[optionKey] : undefined, newOptions ? newOptions[optionKey] : undefined, projectConfigSourceMap
106
+ ? {
107
+ sourceMap: projectConfigSourceMap,
108
+ key: `${targetIdentifier}.options.${optionKey}`,
109
+ sourceInformation,
110
+ }
111
+ : undefined, deferSpreadsWithoutBase);
105
112
  }
106
113
  return mergedOptions;
107
114
  }
108
- function mergeConfigurations(newConfigurations, baseConfigurations, projectConfigSourceMap, sourceInformation, targetIdentifier) {
115
+ // Merges a single named configuration, keyed under its own identifier
116
+ // (e.g. `targets.build.configurations.prod`) rather than under `.options`.
117
+ // Source-map correctness for the spread case is handled inside
118
+ // `getMergeValueResult`'s object-spread path — no post-merge fix-up needed.
119
+ function mergeConfigurationValue(newConfig, baseConfig, projectConfigSourceMap, sourceInformation, configIdentifier, deferSpreadsWithoutBase) {
120
+ if (newConfig?.[utils_1.NX_SPREAD_TOKEN] === true) {
121
+ return (0, utils_1.getMergeValueResult)(baseConfig, newConfig, projectConfigSourceMap && configIdentifier
122
+ ? {
123
+ sourceMap: projectConfigSourceMap,
124
+ key: configIdentifier,
125
+ sourceInformation,
126
+ }
127
+ : undefined, deferSpreadsWithoutBase);
128
+ }
129
+ const mergedKeys = (0, utils_1.uniqueKeysInObjects)(baseConfig ?? {}, newConfig ?? {});
130
+ const merged = {};
131
+ for (const key of mergedKeys) {
132
+ merged[key] = (0, utils_1.getMergeValueResult)(baseConfig ? baseConfig[key] : undefined, newConfig ? newConfig[key] : undefined, projectConfigSourceMap && configIdentifier
133
+ ? {
134
+ sourceMap: projectConfigSourceMap,
135
+ key: `${configIdentifier}.${key}`,
136
+ sourceInformation,
137
+ }
138
+ : undefined, deferSpreadsWithoutBase);
139
+ }
140
+ return merged;
141
+ }
142
+ function mergeConfigurations(newConfigurations, baseConfigurations, projectConfigSourceMap, sourceInformation, targetIdentifier, deferSpreadsWithoutBase) {
109
143
  const mergedConfigurations = {};
110
- const configurations = new Set([
111
- ...Object.keys(baseConfigurations ?? {}),
112
- ...Object.keys(newConfigurations ?? {}),
113
- ]);
114
- for (const configuration of configurations) {
115
- mergedConfigurations[configuration] = {
116
- ...(baseConfigurations?.[configuration] ?? {}),
117
- ...(newConfigurations?.[configuration] ?? {}),
144
+ // Keys before '...' let base win for shared names; keys after '...'
145
+ // (or when there's no spread) merge normally with new winning.
146
+ const newKeys = Object.keys(newConfigurations ?? {});
147
+ const spreadPosInNew = newKeys.indexOf(utils_1.NX_SPREAD_TOKEN);
148
+ const hasSpread = spreadPosInNew >= 0;
149
+ const keysBeforeSpread = hasSpread
150
+ ? new Set(newKeys.slice(0, spreadPosInNew))
151
+ : new Set();
152
+ // Integer-like keys get hoisted to newKeys[0], making their position
153
+ // relative to '...' unrecoverable.
154
+ if (hasSpread && newKeys[0] && utils_1.INTEGER_LIKE_KEY_PATTERN.test(newKeys[0])) {
155
+ throw new utils_1.IntegerLikeSpreadKeyError(newKeys[0], targetIdentifier
156
+ ? `Configurations at "${targetIdentifier}.configurations"`
157
+ : 'Configurations');
158
+ }
159
+ // Preserving the unresolved `'...'` sentinel in authored position lets
160
+ // a later merge layer (which actually has a base) classify the keys as
161
+ // pre/post-spread correctly.
162
+ const preserveSpreadSentinel = hasSpread && deferSpreadsWithoutBase && baseConfigurations === undefined;
163
+ const processConfigName = (configName) => {
164
+ const configIdentifier = targetIdentifier
165
+ ? `${targetIdentifier}.configurations.${configName}`
166
+ : undefined;
167
+ const baseHasConfig = configName in (baseConfigurations ?? {});
168
+ const newHasConfig = !!newConfigurations && configName in newConfigurations;
169
+ if (hasSpread && keysBeforeSpread.has(configName)) {
170
+ // Before '...': base wins for shared names. Keep base's source-map
171
+ // entries when it owns the config.
172
+ if (baseHasConfig) {
173
+ mergedConfigurations[configName] = baseConfigurations[configName];
174
+ }
175
+ else {
176
+ mergedConfigurations[configName] = mergeConfigurationValue(newConfigurations?.[configName], undefined, projectConfigSourceMap, sourceInformation, configIdentifier, deferSpreadsWithoutBase);
177
+ if (projectConfigSourceMap && configIdentifier) {
178
+ projectConfigSourceMap[configIdentifier] = sourceInformation;
179
+ }
180
+ }
181
+ return;
182
+ }
183
+ mergedConfigurations[configName] = mergeConfigurationValue(newConfigurations?.[configName], baseConfigurations?.[configName], projectConfigSourceMap, sourceInformation, configIdentifier, deferSpreadsWithoutBase);
184
+ // Only reattribute the config name when the new plugin introduced it.
185
+ if (projectConfigSourceMap &&
186
+ configIdentifier &&
187
+ newHasConfig &&
188
+ !baseHasConfig) {
189
+ projectConfigSourceMap[configIdentifier] = sourceInformation;
190
+ }
191
+ };
192
+ if (hasSpread) {
193
+ // Authored positions of new's own keys relative to `'...'` drive
194
+ // pre/post-spread classification, so those keys go in authored order.
195
+ // Base-only keys land right before `'...'` — they weren't authored by
196
+ // the new layer, so default semantics places them with the pre-spread
197
+ // keys (the "base layer" slot).
198
+ const baseOnlyKeys = baseConfigurations
199
+ ? Object.keys(baseConfigurations).filter((k) => k !== utils_1.NX_SPREAD_TOKEN && !(k in (newConfigurations ?? {})))
200
+ : [];
201
+ let baseOnlyInserted = false;
202
+ const insertBaseOnlyKeys = () => {
203
+ if (baseOnlyInserted)
204
+ return;
205
+ baseOnlyInserted = true;
206
+ for (const configName of baseOnlyKeys)
207
+ processConfigName(configName);
118
208
  };
209
+ for (const configName of newKeys) {
210
+ if (configName === utils_1.NX_SPREAD_TOKEN) {
211
+ insertBaseOnlyKeys();
212
+ if (preserveSpreadSentinel) {
213
+ mergedConfigurations[utils_1.NX_SPREAD_TOKEN] =
214
+ true;
215
+ }
216
+ continue;
217
+ }
218
+ processConfigName(configName);
219
+ }
220
+ insertBaseOnlyKeys();
119
221
  }
120
- // record new configurations & configuration properties in source map
121
- if (projectConfigSourceMap) {
122
- for (const newConfiguration in newConfigurations) {
123
- projectConfigSourceMap[`${targetIdentifier}.configurations.${newConfiguration}`] = sourceInformation;
124
- for (const configurationProperty in newConfigurations[newConfiguration]) {
125
- projectConfigSourceMap[`${targetIdentifier}.configurations.${newConfiguration}.${configurationProperty}`] = sourceInformation;
222
+ else {
223
+ // No spread — classic `{ ...base, ...new }` ordering: base keys
224
+ // first, new-only keys after. Shared configs stay at base's position.
225
+ if (baseConfigurations) {
226
+ for (const configName of Object.keys(baseConfigurations)) {
227
+ if (configName === utils_1.NX_SPREAD_TOKEN)
228
+ continue;
229
+ processConfigName(configName);
126
230
  }
127
231
  }
232
+ for (const configName of newKeys) {
233
+ if (configName === utils_1.NX_SPREAD_TOKEN)
234
+ continue;
235
+ if (configName in mergedConfigurations)
236
+ continue;
237
+ processConfigName(configName);
238
+ }
128
239
  }
129
240
  return mergedConfigurations;
130
241
  }
@@ -141,7 +252,7 @@ function mergeConfigurations(newConfigurations, baseConfigurations, projectConfi
141
252
  * @param targetIdentifier The identifier for the target to merge, used for source map
142
253
  * @returns A merged target configuration
143
254
  */
144
- function mergeTargetConfigurations(target, baseTarget, projectConfigSourceMap, sourceInformation, targetIdentifier) {
255
+ function mergeTargetConfigurations(target, baseTarget, projectConfigSourceMap, sourceInformation, targetIdentifier, deferSpreadsWithoutBase) {
145
256
  const { configurations: defaultConfigurations, options: defaultOptions, ...baseTargetProperties } = baseTarget ?? {};
146
257
  // Target is "compatible", e.g. executor is defined only once or is the same
147
258
  // in both places. This means that it is likely safe to merge
@@ -156,28 +267,136 @@ function mergeTargetConfigurations(target, baseTarget, projectConfigSourceMap, s
156
267
  }
157
268
  }
158
269
  // merge top level properties if they're compatible
159
- const result = {
160
- ...(isCompatible ? baseTargetProperties : {}),
161
- ...target,
270
+ const result = {};
271
+ const mergeBase = isCompatible ? baseTargetProperties : {};
272
+ // Keys before '...' let base win; keys after '...' (or when there's no
273
+ // spread) merge normally with target winning.
274
+ const targetKeys = Object.keys(target);
275
+ const spreadPosInTarget = targetKeys.indexOf(utils_1.NX_SPREAD_TOKEN);
276
+ const hasSpread = isCompatible && spreadPosInTarget >= 0;
277
+ const keysBeforeSpread = hasSpread
278
+ ? new Set(targetKeys.slice(0, spreadPosInTarget))
279
+ : new Set();
280
+ // Integer-like keys get hoisted to targetKeys[0], making their position
281
+ // relative to '...' unrecoverable.
282
+ if (hasSpread &&
283
+ targetKeys[0] &&
284
+ utils_1.INTEGER_LIKE_KEY_PATTERN.test(targetKeys[0])) {
285
+ throw new utils_1.IntegerLikeSpreadKeyError(targetKeys[0], targetIdentifier ? `Target at "${targetIdentifier}"` : 'Target');
286
+ }
287
+ // Preserving the unresolved `'...'` sentinel in authored position lets a
288
+ // later merge layer (which actually has a base) classify sibling keys as
289
+ // pre/post-spread correctly.
290
+ const preserveSpreadSentinel = spreadPosInTarget >= 0 &&
291
+ deferSpreadsWithoutBase &&
292
+ baseTarget === undefined;
293
+ const skipForOwnMerge = new Set([
294
+ 'options',
295
+ 'configurations',
296
+ utils_1.NX_SPREAD_TOKEN,
297
+ ]);
298
+ const processKey = (key) => {
299
+ if (skipForOwnMerge.has(key))
300
+ return;
301
+ if (hasSpread && keysBeforeSpread.has(key)) {
302
+ // Before '...': base wins; fall through to target only if base lacks it.
303
+ result[key] =
304
+ key in mergeBase
305
+ ? mergeBase[key]
306
+ : (0, utils_1.getMergeValueResult)(undefined, target[key], projectConfigSourceMap
307
+ ? {
308
+ sourceMap: projectConfigSourceMap,
309
+ key: `${targetIdentifier}.${key}`,
310
+ sourceInformation,
311
+ }
312
+ : undefined, deferSpreadsWithoutBase);
313
+ return;
314
+ }
315
+ if (key in target) {
316
+ result[key] = (0, utils_1.getMergeValueResult)(mergeBase[key], target[key], projectConfigSourceMap
317
+ ? {
318
+ sourceMap: projectConfigSourceMap,
319
+ key: `${targetIdentifier}.${key}`,
320
+ sourceInformation,
321
+ }
322
+ : undefined, deferSpreadsWithoutBase);
323
+ }
324
+ else {
325
+ result[key] = mergeBase[key];
326
+ }
162
327
  };
163
- // record top level properties in source map
328
+ if (isCompatible) {
329
+ if (hasSpread) {
330
+ // Authored positions of the target's own keys relative to `'...'`
331
+ // drive pre/post-spread classification, so those keys go in
332
+ // authored order. Base-only keys land right before `'...'` — they
333
+ // weren't authored, so default semantics ("base layer that yields
334
+ // to a higher-priority layer") places them with the rest of the
335
+ // pre-spread keys.
336
+ const baseOnlyKeys = Object.keys(baseTargetProperties).filter((k) => !skipForOwnMerge.has(k) && !(k in target));
337
+ let baseOnlyInserted = false;
338
+ const insertBaseOnlyKeys = () => {
339
+ if (baseOnlyInserted)
340
+ return;
341
+ baseOnlyInserted = true;
342
+ for (const key of baseOnlyKeys)
343
+ processKey(key);
344
+ };
345
+ for (const key of targetKeys) {
346
+ if (key === utils_1.NX_SPREAD_TOKEN) {
347
+ insertBaseOnlyKeys();
348
+ if (preserveSpreadSentinel) {
349
+ result[utils_1.NX_SPREAD_TOKEN] = true;
350
+ }
351
+ continue;
352
+ }
353
+ if (skipForOwnMerge.has(key))
354
+ continue;
355
+ processKey(key);
356
+ }
357
+ // Safety for a sentinel-less iteration (shouldn't happen when
358
+ // hasSpread is true, but keeps the base-only keys emitted).
359
+ insertBaseOnlyKeys();
360
+ }
361
+ else {
362
+ // No spread — classic `{ ...base, ...target }` ordering: base keys
363
+ // first (preserving their own-key order), target-only keys after.
364
+ // Shared keys stay at base's position with per-key merged value.
365
+ const mergedKeys = (0, utils_1.uniqueKeysInObjects)(baseTargetProperties, target);
366
+ for (const key of mergedKeys) {
367
+ if (skipForOwnMerge.has(key))
368
+ continue;
369
+ processKey(key);
370
+ }
371
+ }
372
+ }
373
+ else {
374
+ for (const key of targetKeys) {
375
+ if (skipForOwnMerge.has(key))
376
+ continue;
377
+ processKey(key);
378
+ }
379
+ }
380
+ // Update source map once after loop
164
381
  if (projectConfigSourceMap) {
165
382
  projectConfigSourceMap[targetIdentifier] = sourceInformation;
166
- // record root level target properties to source map
167
- for (const targetProperty in target) {
168
- const targetPropertyId = `${targetIdentifier}.${targetProperty}`;
169
- projectConfigSourceMap[targetPropertyId] = sourceInformation;
170
- }
171
383
  }
172
384
  // merge options if there are any
173
385
  // if the targets aren't compatible, we simply discard the old options during the merge
174
386
  if (target.options || defaultOptions) {
175
- result.options = mergeOptions(target.options, isCompatible ? defaultOptions : undefined, projectConfigSourceMap, sourceInformation, targetIdentifier);
387
+ result.options = mergeOptions(target.options, isCompatible ? defaultOptions : undefined, projectConfigSourceMap, sourceInformation, targetIdentifier, deferSpreadsWithoutBase);
388
+ if (projectConfigSourceMap && target.options) {
389
+ projectConfigSourceMap[`${targetIdentifier}.options`] = sourceInformation;
390
+ }
176
391
  }
177
392
  // merge configurations if there are any
178
393
  // if the targets aren't compatible, we simply discard the old configurations during the merge
179
394
  if (target.configurations || defaultConfigurations) {
180
- result.configurations = mergeConfigurations(target.configurations, isCompatible ? defaultConfigurations : undefined, projectConfigSourceMap, sourceInformation, targetIdentifier);
395
+ result.configurations = mergeConfigurations(target.configurations, isCompatible ? defaultConfigurations : undefined, projectConfigSourceMap, sourceInformation, targetIdentifier, deferSpreadsWithoutBase);
396
+ if (projectConfigSourceMap && target.configurations) {
397
+ projectConfigSourceMap[`${targetIdentifier}.configurations`] =
398
+ sourceInformation;
399
+ }
181
400
  }
182
401
  if (target.metadata) {
183
402
  result.metadata = mergeMetadata(projectConfigSourceMap, sourceInformation, `${targetIdentifier}.metadata`, target.metadata, baseTarget?.metadata);
@@ -216,73 +435,6 @@ function isCompatibleTarget(a, b) {
216
435
  }
217
436
  return true;
218
437
  }
219
- function targetDefaultShouldBeApplied(key, sourceMap) {
220
- const sourceInfo = sourceMap[key];
221
- if (!sourceInfo) {
222
- return true;
223
- }
224
- // The defined value of the target is from a plugin that
225
- // isn't part of Nx's core plugins, so target defaults are
226
- // applied on top of it.
227
- const [, plugin] = sourceInfo;
228
- return !plugin?.startsWith('nx/');
229
- }
230
- function mergeTargetDefaultWithTargetDefinition(targetName, project, targetDefault, sourceMap) {
231
- const targetDefinition = project.targets[targetName] ?? {};
232
- const result = deepClone(targetDefinition);
233
- for (const key in targetDefault) {
234
- switch (key) {
235
- case 'options': {
236
- const normalizedDefaults = resolveNxTokensInOptions(targetDefault.options, project, targetName);
237
- for (const optionKey in normalizedDefaults) {
238
- const sourceMapKey = (0, source_maps_1.targetOptionSourceMapKey)(targetName, optionKey);
239
- if (targetDefinition.options[optionKey] === undefined ||
240
- targetDefaultShouldBeApplied(sourceMapKey, sourceMap)) {
241
- result.options[optionKey] = targetDefault.options[optionKey];
242
- sourceMap[sourceMapKey] = ['nx.json', 'nx/target-defaults'];
243
- }
244
- }
245
- break;
246
- }
247
- case 'configurations': {
248
- if (!result.configurations) {
249
- result.configurations = {};
250
- sourceMap[(0, source_maps_1.targetConfigurationsSourceMapKey)(targetName)] = [
251
- 'nx.json',
252
- 'nx/target-defaults',
253
- ];
254
- }
255
- for (const configuration in targetDefault.configurations) {
256
- if (!result.configurations[configuration]) {
257
- result.configurations[configuration] = {};
258
- sourceMap[(0, source_maps_1.targetConfigurationsSourceMapKey)(targetName, configuration)] = ['nx.json', 'nx/target-defaults'];
259
- }
260
- const normalizedConfigurationDefaults = resolveNxTokensInOptions(targetDefault.configurations[configuration], project, targetName);
261
- for (const configurationKey in normalizedConfigurationDefaults) {
262
- const sourceMapKey = (0, source_maps_1.targetConfigurationsSourceMapKey)(targetName, configuration, configurationKey);
263
- if (targetDefinition.configurations?.[configuration]?.[configurationKey] === undefined ||
264
- targetDefaultShouldBeApplied(sourceMapKey, sourceMap)) {
265
- result.configurations[configuration][configurationKey] =
266
- targetDefault.configurations[configuration][configurationKey];
267
- sourceMap[sourceMapKey] = ['nx.json', 'nx/target-defaults'];
268
- }
269
- }
270
- }
271
- break;
272
- }
273
- default: {
274
- const sourceMapKey = `targets.${targetName}.${key}`;
275
- if (targetDefinition[key] === undefined ||
276
- targetDefaultShouldBeApplied(sourceMapKey, sourceMap)) {
277
- result[key] = targetDefault[key];
278
- sourceMap[sourceMapKey] = ['nx.json', 'nx/target-defaults'];
279
- }
280
- break;
281
- }
282
- }
283
- }
284
- return result;
285
- }
286
438
  function resolveNxTokensInOptions(object, project, key) {
287
439
  const result = Array.isArray(object) ? [...object] : { ...object };
288
440
  for (let [opt, value] of Object.entries(object ?? {})) {
@@ -303,30 +455,3 @@ function resolveNxTokensInOptions(object, project, key) {
303
455
  }
304
456
  return result;
305
457
  }
306
- function readTargetDefaultsForTarget(targetName, targetDefaults, executor) {
307
- if (executor && targetDefaults?.[executor]) {
308
- // If an executor is defined in project.json, defaults should be read
309
- // from the most specific key that matches that executor.
310
- // e.g. If executor === run-commands, and the target is named build:
311
- // Use, use nx:run-commands if it is present
312
- // If not, use build if it is present.
313
- return targetDefaults?.[executor];
314
- }
315
- else if (targetDefaults?.[targetName]) {
316
- // If the executor is not defined, the only key we have is the target name.
317
- return targetDefaults?.[targetName];
318
- }
319
- let matchingTargetDefaultKey = null;
320
- for (const key in targetDefaults ?? {}) {
321
- if ((0, globs_1.isGlobPattern)(key) && (0, minimatch_1.minimatch)(targetName, key)) {
322
- if (!matchingTargetDefaultKey ||
323
- matchingTargetDefaultKey.length < key.length) {
324
- matchingTargetDefaultKey = key;
325
- }
326
- }
327
- }
328
- if (matchingTargetDefaultKey) {
329
- return targetDefaults[matchingTargetDefaultKey];
330
- }
331
- return null;
332
- }
@@ -73,13 +73,6 @@ projects) {
73
73
  const targetErrorMessage = [];
74
74
  for (const targetName in project.targets) {
75
75
  project.targets[targetName] = normalizeTarget(project.targets[targetName], project, workspaceRoot, projects, [project.root, targetName].join(':'));
76
- const projectSourceMaps = sourceMaps[project.root];
77
- const targetConfig = project.targets[targetName];
78
- const targetDefaults = (0, target_merging_1.deepClone)((0, target_merging_1.readTargetDefaultsForTarget)(targetName, nxJsonConfiguration.targetDefaults, targetConfig.executor));
79
- // We only apply defaults if they exist
80
- if (targetDefaults && (0, target_merging_1.isCompatibleTarget)(targetConfig, targetDefaults)) {
81
- project.targets[targetName] = (0, target_merging_1.mergeTargetDefaultWithTargetDefinition)(targetName, project, normalizeTarget(targetDefaults, project, workspaceRoot, projects, ['nx.json[targetDefaults]', targetName].join(':')), projectSourceMaps);
82
- }
83
76
  const target = project.targets[targetName];
84
77
  if (
85
78
  // If the target has no executor or command, it doesn't do anything
@@ -0,0 +1,23 @@
1
+ import { type SourceInformation } from './source-maps';
2
+ export declare const NX_SPREAD_TOKEN = "...";
3
+ /**
4
+ * Returns the union of keys across every provided object.
5
+ */
6
+ export declare function uniqueKeysInObjects(...objs: Array<object | null | undefined>): Set<string>;
7
+ export declare const INTEGER_LIKE_KEY_PATTERN: RegExp;
8
+ export declare class IntegerLikeSpreadKeyError extends Error {
9
+ constructor(key: string, context: string);
10
+ }
11
+ type SourceMapContext = {
12
+ sourceMap: Record<string, SourceInformation>;
13
+ key: string;
14
+ sourceInformation: SourceInformation;
15
+ };
16
+ /**
17
+ * `"..."` in `newValue` (as an array element or a key set to `true`)
18
+ * expands the base at that position; otherwise `newValue` replaces
19
+ * `baseValue`. With `deferSpreadsWithoutBase`, an unresolvable spread is
20
+ * preserved so a later merge layer can expand it.
21
+ */
22
+ export declare function getMergeValueResult<T>(baseValue: unknown, newValue: T | undefined, sourceMapContext?: SourceMapContext, deferSpreadsWithoutBase?: boolean): T | undefined;
23
+ export {};
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IntegerLikeSpreadKeyError = exports.INTEGER_LIKE_KEY_PATTERN = exports.NX_SPREAD_TOKEN = void 0;
4
+ exports.uniqueKeysInObjects = uniqueKeysInObjects;
5
+ exports.getMergeValueResult = getMergeValueResult;
6
+ const source_maps_1 = require("./source-maps");
7
+ exports.NX_SPREAD_TOKEN = '...';
8
+ /**
9
+ * Returns the union of keys across every provided object.
10
+ */
11
+ function uniqueKeysInObjects(...objs) {
12
+ const keys = new Set();
13
+ for (const obj of objs) {
14
+ if (obj) {
15
+ for (const key of Object.keys(obj)) {
16
+ keys.add(key);
17
+ }
18
+ }
19
+ }
20
+ return keys;
21
+ }
22
+ // Integer-like string keys (`"0"`, `"42"`) are enumerated before
23
+ // insertion-order keys, so we can't tell if they were authored before or
24
+ // after `'...'`. Spread sites reject them instead of guessing.
25
+ exports.INTEGER_LIKE_KEY_PATTERN = /^(0|[1-9]\d*)$/;
26
+ class IntegerLikeSpreadKeyError extends Error {
27
+ constructor(key, context) {
28
+ super(`${context} uses an integer-like key (${JSON.stringify(key)}) alongside the '...' spread token. Integer-like keys are enumerated before other keys regardless of authored order, so their position relative to '...' is ambiguous. Rename the key (e.g. add a non-numeric prefix) or restructure the object.`);
29
+ this.name = 'IntegerLikeSpreadKeyError';
30
+ }
31
+ }
32
+ exports.IntegerLikeSpreadKeyError = IntegerLikeSpreadKeyError;
33
+ /**
34
+ * `"..."` in `newValue` (as an array element or a key set to `true`)
35
+ * expands the base at that position; otherwise `newValue` replaces
36
+ * `baseValue`. With `deferSpreadsWithoutBase`, an unresolvable spread is
37
+ * preserved so a later merge layer can expand it.
38
+ */
39
+ function getMergeValueResult(baseValue, newValue, sourceMapContext, deferSpreadsWithoutBase) {
40
+ if (newValue === undefined && baseValue !== undefined) {
41
+ return baseValue;
42
+ }
43
+ if (Array.isArray(newValue)) {
44
+ return mergeArrayValue(baseValue, newValue, sourceMapContext, deferSpreadsWithoutBase);
45
+ }
46
+ if (isObject(newValue) && newValue[exports.NX_SPREAD_TOKEN] === true) {
47
+ return mergeObjectWithSpread(baseValue, newValue, sourceMapContext, deferSpreadsWithoutBase);
48
+ }
49
+ // Scalar / null / plain object replace — newValue fully wins.
50
+ writeTopLevelSourceMap(sourceMapContext);
51
+ return newValue;
52
+ }
53
+ function mergeArrayValue(baseValue, newValue, sourceMapContext, deferSpreadsWithoutBase) {
54
+ const newSpreadIndex = newValue.findIndex((v) => v === exports.NX_SPREAD_TOKEN);
55
+ if (newSpreadIndex === -1) {
56
+ // No spread: newValue replaces baseValue entirely.
57
+ if (sourceMapContext) {
58
+ for (let i = 0; i < newValue.length; i++) {
59
+ sourceMapContext.sourceMap[`${sourceMapContext.key}.${i}`] =
60
+ sourceMapContext.sourceInformation;
61
+ }
62
+ }
63
+ writeTopLevelSourceMap(sourceMapContext);
64
+ return newValue;
65
+ }
66
+ const baseArray = Array.isArray(baseValue) ? baseValue : [];
67
+ // Snapshot per-index base sources before we start writing — the loop
68
+ // writes into the same `${key}.${i}` entries it needs to read back when
69
+ // the spread expands. Unlike object spread, array spreads can overwrite
70
+ // indices during their own expansion (when new authors a prefix before
71
+ // `'...'`), so lazy capture isn't sufficient here.
72
+ const basePerIndexSources = sourceMapContext
73
+ ? baseArray.map((_, i) => (0, source_maps_1.readArrayItemSourceInfo)(sourceMapContext.sourceMap, sourceMapContext.key, i))
74
+ : [];
75
+ const result = [];
76
+ const recordAt = (resultIdx, info) => {
77
+ if (sourceMapContext && info) {
78
+ sourceMapContext.sourceMap[`${sourceMapContext.key}.${resultIdx}`] = info;
79
+ }
80
+ };
81
+ for (let newValueIndex = 0; newValueIndex < newValue.length; newValueIndex++) {
82
+ const element = newValue[newValueIndex];
83
+ if (element === exports.NX_SPREAD_TOKEN) {
84
+ if (deferSpreadsWithoutBase && baseValue === undefined) {
85
+ recordAt(result.length, sourceMapContext?.sourceInformation);
86
+ result.push(exports.NX_SPREAD_TOKEN);
87
+ }
88
+ else {
89
+ for (let baseIndex = 0; baseIndex < baseArray.length; baseIndex++) {
90
+ recordAt(result.length, basePerIndexSources[baseIndex]);
91
+ result.push(baseArray[baseIndex]);
92
+ }
93
+ }
94
+ continue;
95
+ }
96
+ recordAt(result.length, sourceMapContext?.sourceInformation);
97
+ result.push(element);
98
+ }
99
+ writeTopLevelSourceMap(sourceMapContext);
100
+ return result;
101
+ }
102
+ function mergeObjectWithSpread(baseValue, newValue, sourceMapContext, deferSpreadsWithoutBase) {
103
+ const baseObj = isObject(baseValue) ? baseValue : {};
104
+ const result = {};
105
+ const errorContext = sourceMapContext?.key
106
+ ? `Object at "${sourceMapContext.key}"`
107
+ : 'Object';
108
+ const newKeys = Object.keys(newValue);
109
+ // Integer-like keys are hoisted to the front of enumeration, so if one
110
+ // exists alongside `'...'` it must be newKeys[0].
111
+ if (newKeys[0] && exports.INTEGER_LIKE_KEY_PATTERN.test(newKeys[0])) {
112
+ throw new IntegerLikeSpreadKeyError(newKeys[0], errorContext);
113
+ }
114
+ // Base per-key sources captured lazily — only for shared keys the new
115
+ // object overwrites before `'...'`, since writing their new source
116
+ // clobbers the base entry the spread will need to restore.
117
+ const capturedBaseSources = {};
118
+ for (const newKey of newKeys) {
119
+ if (newKey === exports.NX_SPREAD_TOKEN) {
120
+ if (deferSpreadsWithoutBase && baseValue === undefined) {
121
+ // Keep the sentinel for a later merge layer to resolve.
122
+ result[exports.NX_SPREAD_TOKEN] = true;
123
+ continue;
124
+ }
125
+ for (const baseKey of Object.keys(baseObj)) {
126
+ result[baseKey] = baseObj[baseKey];
127
+ if (sourceMapContext) {
128
+ // If we captured a shared key pre-spread, use that; otherwise
129
+ // the source map still holds the base entry untouched.
130
+ const baseSource = Object.prototype.hasOwnProperty.call(capturedBaseSources, baseKey)
131
+ ? capturedBaseSources[baseKey]
132
+ : (0, source_maps_1.readObjectPropertySourceInfo)(sourceMapContext.sourceMap, sourceMapContext.key, baseKey);
133
+ if (baseSource) {
134
+ sourceMapContext.sourceMap[`${sourceMapContext.key}.${baseKey}`] =
135
+ baseSource;
136
+ }
137
+ }
138
+ }
139
+ continue;
140
+ }
141
+ // About to overwrite a base key's source — capture it first so the
142
+ // spread can restore it.
143
+ if (sourceMapContext &&
144
+ newKey in baseObj &&
145
+ !Object.prototype.hasOwnProperty.call(capturedBaseSources, newKey)) {
146
+ capturedBaseSources[newKey] = (0, source_maps_1.readObjectPropertySourceInfo)(sourceMapContext.sourceMap, sourceMapContext.key, newKey);
147
+ }
148
+ result[newKey] = newValue[newKey];
149
+ if (sourceMapContext) {
150
+ sourceMapContext.sourceMap[`${sourceMapContext.key}.${newKey}`] =
151
+ sourceMapContext.sourceInformation;
152
+ }
153
+ }
154
+ writeTopLevelSourceMap(sourceMapContext);
155
+ return result;
156
+ }
157
+ function writeTopLevelSourceMap(ctx) {
158
+ if (ctx) {
159
+ ctx.sourceMap[ctx.key] = ctx.sourceInformation;
160
+ }
161
+ }
162
+ function isObject(value) {
163
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
164
+ }