nx 23.1.0-beta.4 → 23.1.0-beta.5

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 (55) hide show
  1. package/dist/src/command-line/init/implementation/angular/standalone-workspace.js +12 -18
  2. package/dist/src/command-line/init/implementation/dot-nx/add-nx-scripts.d.ts +1 -0
  3. package/dist/src/command-line/init/implementation/dot-nx/add-nx-scripts.js +6 -1
  4. package/dist/src/command-line/init/implementation/utils.d.ts +9 -1
  5. package/dist/src/command-line/init/implementation/utils.js +67 -14
  6. package/dist/src/command-line/release/version/resolve-current-version.js +4 -1
  7. package/dist/src/command-line/show/show-target/info.js +4 -5
  8. package/dist/src/config/nx-json.d.ts +93 -2
  9. package/dist/src/core/graph/main.js +1 -1
  10. package/dist/src/devkit-exports.d.ts +1 -1
  11. package/dist/src/devkit-internals.d.ts +2 -0
  12. package/dist/src/devkit-internals.js +5 -1
  13. package/dist/src/hasher/task-hasher.js +6 -4
  14. package/dist/src/migrations/update-15-0-0/prefix-outputs.js +9 -4
  15. package/dist/src/migrations/update-16-0-0/update-depends-on-to-tokens.js +54 -69
  16. package/dist/src/migrations/update-16-2-0/remove-run-commands-output-path.js +7 -2
  17. package/dist/src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options.js +6 -2
  18. package/dist/src/migrations/utils/target-defaults.d.ts +28 -0
  19. package/dist/src/migrations/utils/target-defaults.js +46 -0
  20. package/dist/src/native/index.d.ts +0 -2
  21. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  22. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  23. package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.js +1 -3
  24. package/dist/src/plugins/js/package-json/create-package-json.js +22 -1
  25. package/dist/src/plugins/js/project-graph/build-dependencies/target-project-locator.d.ts +7 -0
  26. package/dist/src/plugins/js/project-graph/build-dependencies/target-project-locator.js +26 -1
  27. package/dist/src/project-graph/utils/project-configuration/project-nodes-manager.js +5 -1
  28. package/dist/src/project-graph/utils/project-configuration/source-maps.d.ts +15 -0
  29. package/dist/src/project-graph/utils/project-configuration/source-maps.js +22 -0
  30. package/dist/src/project-graph/utils/project-configuration/target-defaults.d.ts +38 -4
  31. package/dist/src/project-graph/utils/project-configuration/target-defaults.js +389 -88
  32. package/dist/src/project-graph/utils/project-configuration/target-merging.js +6 -2
  33. package/dist/src/project-graph/utils/project-configuration/utils.js +16 -1
  34. package/dist/src/project-graph/utils/project-configuration-utils.d.ts +13 -5
  35. package/dist/src/project-graph/utils/project-configuration-utils.js +38 -6
  36. package/dist/src/tasks-runner/cache.js +6 -0
  37. package/dist/src/tasks-runner/create-task-graph.d.ts +1 -2
  38. package/dist/src/tasks-runner/create-task-graph.js +0 -8
  39. package/dist/src/tasks-runner/life-cycles/performance-analysis.d.ts +13 -3
  40. package/dist/src/tasks-runner/life-cycles/performance-analysis.js +26 -13
  41. package/dist/src/tasks-runner/life-cycles/performance-life-cycle.js +40 -0
  42. package/dist/src/tasks-runner/life-cycles/performance-report.d.ts +25 -32
  43. package/dist/src/tasks-runner/life-cycles/performance-report.js +177 -163
  44. package/dist/src/tasks-runner/pseudo-terminal.d.ts +1 -0
  45. package/dist/src/tasks-runner/pseudo-terminal.js +18 -4
  46. package/dist/src/tasks-runner/run-command.js +24 -18
  47. package/dist/src/utils/find-matching-projects.d.ts +10 -2
  48. package/dist/src/utils/find-matching-projects.js +1 -1
  49. package/dist/src/utils/package-json.d.ts +1 -0
  50. package/dist/src/utils/package-json.js +5 -1
  51. package/dist/src/utils/package-manager.js +10 -0
  52. package/dist/src/utils/spinner.d.ts +13 -1
  53. package/dist/src/utils/spinner.js +20 -4
  54. package/package.json +16 -12
  55. package/schemas/nx-schema.json +87 -2
@@ -54,29 +54,24 @@ function createNxJson(repoRoot, angularJson, cacheableOperations, { eslintProjec
54
54
  : []),
55
55
  ].filter(Boolean),
56
56
  };
57
- nxJson.targetDefaults ??= {};
57
+ const defaults = { ...(nxJson.targetDefaults ?? {}) };
58
58
  if (workspaceTargets.includes('build')) {
59
- nxJson.targetDefaults.build = {
60
- ...nxJson.targetDefaults.build,
59
+ (0, utils_1.upsertTargetDefaultEntry)(defaults, 'build', {
61
60
  dependsOn: ['^build'],
62
61
  inputs: ['production', '^production'],
63
- };
62
+ });
64
63
  }
65
64
  if (workspaceTargets.includes('server')) {
66
- nxJson.targetDefaults.server = {
67
- ...nxJson.targetDefaults.server,
65
+ (0, utils_1.upsertTargetDefaultEntry)(defaults, 'server', {
68
66
  inputs: ['production', '^production'],
69
- };
67
+ });
70
68
  }
71
69
  if (workspaceTargets.includes('test')) {
72
70
  const inputs = ['default', '^production'];
73
71
  if ((0, fileutils_1.fileExists)((0, node_path_1.join)(repoRoot, 'karma.conf.js'))) {
74
72
  inputs.push('{workspaceRoot}/karma.conf.js');
75
73
  }
76
- nxJson.targetDefaults.test = {
77
- ...nxJson.targetDefaults.test,
78
- inputs,
79
- };
74
+ (0, utils_1.upsertTargetDefaultEntry)(defaults, 'test', { inputs });
80
75
  }
81
76
  if (workspaceTargets.includes('lint')) {
82
77
  const inputs = ['default'];
@@ -86,16 +81,15 @@ function createNxJson(repoRoot, angularJson, cacheableOperations, { eslintProjec
86
81
  if ((0, fileutils_1.fileExists)((0, node_path_1.join)(repoRoot, 'eslint.config.cjs'))) {
87
82
  inputs.push('{workspaceRoot}/eslint.config.cjs');
88
83
  }
89
- nxJson.targetDefaults.lint = {
90
- ...nxJson.targetDefaults.lint,
91
- inputs,
92
- };
84
+ (0, utils_1.upsertTargetDefaultEntry)(defaults, 'lint', { inputs });
93
85
  }
94
86
  if (workspaceTargets.includes('e2e')) {
95
- nxJson.targetDefaults.e2e = {
96
- ...nxJson.targetDefaults.e2e,
87
+ (0, utils_1.upsertTargetDefaultEntry)(defaults, 'e2e', {
97
88
  inputs: ['default', '^production'],
98
- };
89
+ });
90
+ }
91
+ if (Object.keys(defaults).length > 0) {
92
+ nxJson.targetDefaults = defaults;
99
93
  }
100
94
  (0, fileutils_1.writeJsonFile)((0, node_path_1.join)(repoRoot, 'nx.json'), nxJson);
101
95
  }
@@ -1,5 +1,6 @@
1
1
  import { Tree } from '../../../../generators/tree';
2
2
  export declare const nxWrapperPath: (p?: typeof import('path')) => string;
3
+ export declare function getDotNxWrapperVersionCommand(platform?: NodeJS.Platform): string;
3
4
  export declare function generateDotNxSetup(version?: string): void;
4
5
  export declare function normalizeVersionForNxJson(pkg: string, version: string): string;
5
6
  export declare function writeMinimalNxJson(host: Tree, version: string): void;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.nxWrapperPath = void 0;
4
+ exports.getDotNxWrapperVersionCommand = getDotNxWrapperVersionCommand;
4
5
  exports.generateDotNxSetup = generateDotNxSetup;
5
6
  exports.normalizeVersionForNxJson = normalizeVersionForNxJson;
6
7
  exports.writeMinimalNxJson = writeMinimalNxJson;
@@ -50,6 +51,10 @@ const SHELL_SCRIPT_CONTENTS = [
50
51
  // Executes the nx wrapper script
51
52
  `node ${path.posix.join('$path_to_root', (0, exports.nxWrapperPath)(path.posix))} "$@"`,
52
53
  ].join('\n');
54
+ // cmd.exe can't run the bash './nx' wrapper; use the generated nx.bat on Windows.
55
+ function getDotNxWrapperVersionCommand(platform = process.platform) {
56
+ return platform === 'win32' ? '.\\nx.bat --version' : './nx --version';
57
+ }
53
58
  function generateDotNxSetup(version) {
54
59
  const host = new tree_1.FsTree(process.cwd(), false, '.nx setup');
55
60
  writeMinimalNxJson(host, version);
@@ -66,7 +71,7 @@ function generateDotNxSetup(version) {
66
71
  // This is needed when using a global nx with dot-nx, otherwise running any nx command using global command will fail due to missing modules.
67
72
  // Pipe stderr so failures surface in telemetry instead of bare "Command failed: ./nx --version".
68
73
  try {
69
- (0, child_process_1.execSync)('./nx --version', {
74
+ (0, child_process_1.execSync)(getDotNxWrapperVersionCommand(), {
70
75
  stdio: ['ignore', 'ignore', 'pipe'],
71
76
  encoding: 'utf8',
72
77
  windowsHide: true,
@@ -1,9 +1,17 @@
1
- import { NxJsonConfiguration } from '../../../config/nx-json';
1
+ import { NxJsonConfiguration, TargetDefaultEntry, TargetDefaults } from '../../../config/nx-json';
2
2
  import { PackageJson } from '../../../utils/package-json';
3
3
  import { PackageManagerCommands } from '../../../utils/package-manager';
4
4
  export declare function createNxJsonFile(repoRoot: string, topologicalTargets: string[], cacheableOperations: string[], scriptOutputs: {
5
5
  [name: string]: string;
6
6
  }): void;
7
+ /**
8
+ * Locate-by-target upsert against an in-memory `targetDefaults` map. Merges
9
+ * `patch`'s config into the unfiltered (catch-all) default for `target`,
10
+ * promoting through the array form when one already exists. Used by `nx init`
11
+ * code paths that operate on raw JSON before a Tree exists — generators
12
+ * should use `upsertTargetDefault` from devkit instead.
13
+ */
14
+ export declare function upsertTargetDefaultEntry(targetDefaults: TargetDefaults, target: string, patch: Partial<TargetDefaultEntry>): void;
7
15
  export declare function createNxJsonFromTurboJson(turboJson: Record<string, any>): NxJsonConfiguration;
8
16
  export declare function addDepsToPackageJson(repoRoot: string, additionalPackages?: string[]): void;
9
17
  export declare function updateGitIgnore(root: string): void;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createNxJsonFile = createNxJsonFile;
4
+ exports.upsertTargetDefaultEntry = upsertTargetDefaultEntry;
4
5
  exports.createNxJsonFromTurboJson = createNxJsonFromTurboJson;
5
6
  exports.addDepsToPackageJson = addDepsToPackageJson;
6
7
  exports.updateGitIgnore = updateGitIgnore;
@@ -34,27 +35,34 @@ function createNxJsonFile(repoRoot, topologicalTargets, cacheableOperations, scr
34
35
  }
35
36
  catch { }
36
37
  nxJson.$schema = './node_modules/nx/schemas/nx-schema.json';
37
- nxJson.targetDefaults ??= {};
38
+ const targetDefaults = { ...(nxJson.targetDefaults ?? {}) };
38
39
  if (topologicalTargets.length > 0) {
39
40
  for (const scriptName of topologicalTargets) {
40
- nxJson.targetDefaults[scriptName] ??= {};
41
- nxJson.targetDefaults[scriptName] = { dependsOn: [`^${scriptName}`] };
41
+ upsertTargetDefaultEntry(targetDefaults, scriptName, {
42
+ dependsOn: [`^${scriptName}`],
43
+ });
42
44
  }
43
45
  }
44
46
  for (const [scriptName, output] of Object.entries(scriptOutputs)) {
45
47
  if (!output) {
46
48
  continue;
47
49
  }
48
- nxJson.targetDefaults[scriptName] ??= {};
49
- nxJson.targetDefaults[scriptName].outputs = [`{projectRoot}/${output}`];
50
+ upsertTargetDefaultEntry(targetDefaults, scriptName, {
51
+ outputs: [`{projectRoot}/${output}`],
52
+ });
50
53
  }
51
54
  for (const target of cacheableOperations) {
52
- nxJson.targetDefaults[target] ??= {};
53
- nxJson.targetDefaults[target].cache ??= true;
55
+ const existing = readUnfilteredTargetDefault(targetDefaults, target);
56
+ if (existing.cache === undefined) {
57
+ upsertTargetDefaultEntry(targetDefaults, target, { cache: true });
58
+ }
54
59
  }
55
- if (Object.keys(nxJson.targetDefaults).length === 0) {
60
+ if (Object.keys(targetDefaults).length === 0) {
56
61
  delete nxJson.targetDefaults;
57
62
  }
63
+ else {
64
+ nxJson.targetDefaults = targetDefaults;
65
+ }
58
66
  const defaultBase = (0, deduce_default_base_1.deduceDefaultBase)();
59
67
  // Do not add defaultBase if it is inferred to be the Nx default value of main
60
68
  if (defaultBase !== 'main') {
@@ -62,6 +70,44 @@ function createNxJsonFile(repoRoot, topologicalTargets, cacheableOperations, scr
62
70
  }
63
71
  (0, fileutils_1.writeJsonFile)(nxJsonPath, nxJson);
64
72
  }
73
+ /**
74
+ * Locate-by-target upsert against an in-memory `targetDefaults` map. Merges
75
+ * `patch`'s config into the unfiltered (catch-all) default for `target`,
76
+ * promoting through the array form when one already exists. Used by `nx init`
77
+ * code paths that operate on raw JSON before a Tree exists — generators
78
+ * should use `upsertTargetDefault` from devkit instead.
79
+ */
80
+ function upsertTargetDefaultEntry(targetDefaults, target, patch) {
81
+ // Drop locator fields — the key is `target` and `nx init` only writes
82
+ // unfiltered defaults.
83
+ const { target: _t, executor: _e, projects: _p, plugin: _pl, ...config } = patch;
84
+ const existing = targetDefaults[target];
85
+ if (Array.isArray(existing)) {
86
+ const idx = existing.findIndex((e) => e.filter === undefined);
87
+ if (idx >= 0) {
88
+ const { filter, ...rest } = existing[idx];
89
+ existing[idx] = { ...rest, ...config };
90
+ }
91
+ else {
92
+ existing.push({ ...config });
93
+ }
94
+ }
95
+ else {
96
+ targetDefaults[target] = { ...(existing ?? {}), ...config };
97
+ }
98
+ }
99
+ /**
100
+ * Read the unfiltered (catch-all) config for `target` from a `targetDefaults`
101
+ * map — the object value, or the filter-less entry of an array value.
102
+ */
103
+ function readUnfilteredTargetDefault(targetDefaults, target) {
104
+ const existing = targetDefaults[target];
105
+ if (existing === undefined)
106
+ return {};
107
+ if (!Array.isArray(existing))
108
+ return existing;
109
+ return existing.find((e) => e.filter === undefined) ?? {};
110
+ }
65
111
  function createNxJsonFromTurboJson(turboJson) {
66
112
  const nxJson = {
67
113
  $schema: './node_modules/nx/schemas/nx-schema.json',
@@ -88,20 +134,20 @@ function createNxJsonFromTurboJson(turboJson) {
88
134
  }
89
135
  // Handle task configurations
90
136
  if (turboJson.tasks) {
91
- nxJson.targetDefaults = {};
137
+ const targetDefaults = {};
92
138
  for (const [taskName, taskConfig] of Object.entries(turboJson.tasks)) {
93
139
  // Skip project-specific tasks (containing #)
94
140
  if (taskName.includes('#'))
95
141
  continue;
96
142
  const config = taskConfig;
97
- nxJson.targetDefaults[taskName] = {};
143
+ const entry = { target: taskName };
98
144
  // Handle dependsOn
99
145
  if (config.dependsOn?.length > 0) {
100
- nxJson.targetDefaults[taskName].dependsOn = config.dependsOn;
146
+ entry.dependsOn = config.dependsOn;
101
147
  }
102
148
  // Handle inputs
103
149
  if (config.inputs?.length > 0) {
104
- nxJson.targetDefaults[taskName].inputs = config.inputs
150
+ entry.inputs = config.inputs
105
151
  .map((input) => {
106
152
  if (input === '$TURBO_DEFAULT$') {
107
153
  return '{projectRoot}/**/*';
@@ -124,7 +170,7 @@ function createNxJsonFromTurboJson(turboJson) {
124
170
  }
125
171
  // Handle outputs
126
172
  if (config.outputs?.length > 0) {
127
- nxJson.targetDefaults[taskName].outputs = config.outputs.map((output) => {
173
+ entry.outputs = config.outputs.map((output) => {
128
174
  // Don't add projectRoot if it's already there
129
175
  if (output.startsWith('{projectRoot}/'))
130
176
  return output;
@@ -136,7 +182,14 @@ function createNxJsonFromTurboJson(turboJson) {
136
182
  });
137
183
  }
138
184
  // Handle cache setting - true by default in Turbo
139
- nxJson.targetDefaults[taskName].cache = config.cache !== false;
185
+ entry.cache = config.cache !== false;
186
+ // Each turbo task maps to a unique key, written as the plain object
187
+ // (unfiltered) value form.
188
+ const { target, ...taskDefault } = entry;
189
+ targetDefaults[target] = taskDefault;
190
+ }
191
+ if (Object.keys(targetDefaults).length > 0) {
192
+ nxJson.targetDefaults = targetDefaults;
140
193
  }
141
194
  }
142
195
  /**
@@ -67,7 +67,10 @@ async function resolveCurrentVersionFromRegistry(tree, projectGraphNode, release
67
67
  return cached.currentVersion;
68
68
  }
69
69
  let registryTxt = '';
70
- const spinner = spinner_1.globalSpinner.start(`Resolving the current version for ${projectGraphNode.name} from the configured registry...`);
70
+ // Skip non-TTY logging the resolved version is reported through the
71
+ // project's batched `ProjectLogger` so the spinner step shouldn't emit an
72
+ // out-of-band line that would bypass the grouped output.
73
+ const spinner = spinner_1.globalSpinner.start(`Resolving the current version for ${projectGraphNode.name} from the configured registry...`, undefined, { skipNonTtyLogging: true });
71
74
  try {
72
75
  const res = await versionActions.readCurrentVersionFromRegistry(tree, finalConfigForProject.currentVersionResolverMetadata);
73
76
  if (!res) {
@@ -20,10 +20,9 @@ function resolveTargetInfoData(t) {
20
20
  allTargetNames.add(name);
21
21
  }
22
22
  }
23
- const extraTargetDeps = Object.fromEntries(Object.entries(nxJson.targetDefaults ?? {})
24
- .filter(([, config]) => config.dependsOn)
25
- .map(([name, config]) => [name, config.dependsOn]));
26
- const depConfigs = (0, utils_1.getDependencyConfigs)({ project: projectName, target: targetName }, extraTargetDeps, graph, [...allTargetNames]) ?? [];
23
+ const depConfigs = (0, utils_1.getDependencyConfigs)({ project: projectName, target: targetName },
24
+ // no programmatic extras — `dependsOn` is already merged into the graph node
25
+ {}, graph, [...allTargetNames]) ?? [];
27
26
  // Determine the hoisted command value and which option key it came from
28
27
  let command;
29
28
  let commandSourceKey;
@@ -44,7 +43,7 @@ function resolveTargetInfoData(t) {
44
43
  command = targetConfig.options.script;
45
44
  commandSourceKey = 'options.script';
46
45
  }
47
- const { dependsOn, depSourceIndices, transitiveTasks } = resolveTaskGraphDependencies(graph, extraTargetDeps, projectName, targetName, configuration, depConfigs);
46
+ const { dependsOn, depSourceIndices, transitiveTasks } = resolveTaskGraphDependencies(graph, {}, projectName, targetName, configuration, depConfigs);
48
47
  const configurations = Object.keys(targetConfig.configurations ?? {});
49
48
  const targetSourceMap = extractTargetSourceMap(node.data.root, targetName, sourceMaps);
50
49
  const usesCustomHasher = (0, utils_2.hasCustomHasher)(projectName, targetName, graph);
@@ -20,7 +20,96 @@ export interface NxAffectedConfig {
20
20
  */
21
21
  defaultBase?: string;
22
22
  }
23
- export type TargetDefaults = Record<string, Partial<TargetConfiguration>>;
23
+ /**
24
+ * A logical, flat view of a single target default — the shape devkit helpers
25
+ * (`upsertTargetDefault` / `findTargetDefault`) and generators author and read.
26
+ * It is **not** the on-disk shape: `upsertTargetDefault` translates it into the
27
+ * map-and-array storage form ({@link TargetDefaults}), and `findTargetDefault`
28
+ * resolves the storage form back to one of these flat entries. Keeping this
29
+ * flat shape stable is what lets generator call sites read `e.target` /
30
+ * `e.projects` / `e.plugin` without knowing about `filter`.
31
+ *
32
+ * Either `target` or `executor` locates the entry; `projects` / `plugin`
33
+ * narrow it; the remaining `TargetConfiguration` fields are the payload.
34
+ */
35
+ export type TargetDefaultEntry = {
36
+ /** Target name or glob pattern (e.g. `build`, `e2e-ci--*`). */
37
+ target?: string;
38
+ /** Executor the default applies to (e.g. `@nx/js:tsc`). */
39
+ executor?: string;
40
+ /**
41
+ * Restrict the default to a subset of projects. Accepts any patterns
42
+ * supported by `findMatchingProjects`.
43
+ */
44
+ projects?: string[];
45
+ /** Restrict the default to targets originated by a specific plugin. */
46
+ plugin?: string;
47
+ } & Partial<TargetConfiguration>;
48
+ /**
49
+ * The `filter` namespace narrows where a {@link TargetDefaultArrayEntry}
50
+ * applies. An entry with no `filter` is a catch-all baseline that applies to
51
+ * every variant of its target key; an entry with a `filter` applies only
52
+ * where the listed criteria all match.
53
+ */
54
+ export type TargetDefaultFilter = {
55
+ /**
56
+ * Restrict the default to targets originated by a specific plugin
57
+ * (e.g. `@nx/vite`). Matches against the plugin that wrote the target's
58
+ * `executor` or `command`.
59
+ */
60
+ plugin?: string;
61
+ /**
62
+ * Restrict the default to a subset of projects. Accepts any patterns
63
+ * supported by `findMatchingProjects` (project names, globs, `tag:foo`,
64
+ * directory globs, negation with `!`).
65
+ */
66
+ projects?: string[];
67
+ /**
68
+ * Restrict the default to targets that resolve to a specific executor
69
+ * (e.g. `@nx/jest:jest`). This narrows *within* a named target key, in
70
+ * addition to the long-standing top-level executor-key form.
71
+ */
72
+ executor?: string;
73
+ };
74
+ /**
75
+ * A single entry in the array-shaped value of a `targetDefaults` key.
76
+ * Carries an optional {@link TargetDefaultFilter} alongside the
77
+ * `TargetConfiguration` fields that get applied when the filter matches.
78
+ *
79
+ * Within a key's array, entries apply in document order, last match winning.
80
+ */
81
+ export type TargetDefaultArrayEntry = {
82
+ filter?: TargetDefaultFilter;
83
+ } & Partial<TargetConfiguration>;
84
+ /**
85
+ * The value stored under a `targetDefaults` key. Either today's plain config
86
+ * object (used when no filtering is needed) or an ordered array of filtered
87
+ * entries (used once a key needs to vary defaults by plugin/projects/executor).
88
+ *
89
+ * The two forms are equivalent for the unfiltered case: `{ cache: true }` and
90
+ * `[{ cache: true }]` mean the same thing.
91
+ *
92
+ * `filter` is only meaningful on array entries; the bare object form forbids it
93
+ * (`filter?: never`) so a stray filter on the unfiltered form is a type error
94
+ * rather than a silently-ignored field.
95
+ */
96
+ export type TargetDefaultValue = (Partial<TargetConfiguration> & {
97
+ filter?: never;
98
+ }) | TargetDefaultArrayEntry[];
99
+ /**
100
+ * `targetDefaults` is a map keyed by logical target name (`build`, `test`),
101
+ * glob (`e2e-ci--*`), or executor (`@nx/js:tsc`). Each value is either a plain
102
+ * config object or an ordered array of filtered entries — see
103
+ * {@link TargetDefaultValue}. The map shape is additive: existing record-shaped
104
+ * configs remain valid because every value is allowed to be a bare object.
105
+ */
106
+ export type TargetDefaults = Record<string, TargetDefaultValue>;
107
+ /**
108
+ * Internal-only: the post-normalization shape consumed by the nx core matcher.
109
+ * Every key's value is normalized to an array, so a bare object value is
110
+ * wrapped into a single-element `[entry]`.
111
+ */
112
+ export type NormalizedTargetDefaults = Record<string, TargetDefaultArrayEntry[]>;
24
113
  export type TargetDependencies = Record<string, (TargetDependencyConfig | string)[]>;
25
114
  export interface NrwlJsPluginConfig {
26
115
  analyzeSourceFiles?: boolean;
@@ -691,7 +780,9 @@ export interface NxJsonConfiguration<T = '*' | string[]> {
691
780
  [inputName: string]: (string | InputDefinition)[];
692
781
  };
693
782
  /**
694
- * Dependencies between different target names across all projects
783
+ * Default configuration applied to targets across all projects. Entries
784
+ * match targets by target name and/or executor, optionally narrowed by
785
+ * the `projects` and `plugin` filters.
695
786
  */
696
787
  targetDefaults?: TargetDefaults;
697
788
  /**