@nx/devkit 23.0.0-beta.12 → 23.0.0-beta.14

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.
@@ -7,7 +7,8 @@ export { migrateProjectExecutorsToPlugin, migrateProjectExecutorsToPluginV1, NoT
7
7
  export { processTargetOutputs, deleteMatchingProperties, toProjectRelativePath, } from './src/generators/plugin-migrations/plugin-migration-utils';
8
8
  export { determineProjectNameAndRootOptions, ensureRootProjectName, resolveImportPath, } from './src/generators/project-name-and-root-utils';
9
9
  export { promptWhenInteractive } from './src/generators/prompt';
10
- export { addBuildTargetDefaults, addE2eCiTargetDefaults, } from './src/generators/target-defaults-utils';
10
+ export { addBuildTargetDefaults, addE2eCiTargetDefaults, findTargetDefault, readTargetDefaultsForTarget, upsertTargetDefault, } from './src/generators/target-defaults-utils';
11
+ export { downgradeTargetDefaults, normalizeTargetDefaults, } from './src/utils/normalize-target-defaults';
11
12
  export { addPlugin } from './src/utils/add-plugin';
12
13
  export { getDeclaredPackageVersion, getInstalledPackageVersion, } from './src/utils/installed-version';
13
14
  export { assertSupportedPackageVersion } from './src/utils/version-floor';
package/dist/internal.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.dasherize = exports.classify = exports.capitalize = exports.camelize = exports.checkAndCleanWithSemver = exports.eachValueFrom = exports.logShowProjectCommand = exports.getNamedInputs = exports.findPluginForConfigFile = exports.clearRequireCache = exports.loadConfigFile = exports.getCatalogManager = exports.calculateHashesForCreateNodes = exports.calculateHashForCreateNodes = exports.mapAsyncIterable = exports.combineAsyncIterables = exports.createAsyncIterable = exports.assertSupportedPackageVersion = exports.getInstalledPackageVersion = exports.getDeclaredPackageVersion = exports.addPlugin = exports.addE2eCiTargetDefaults = exports.addBuildTargetDefaults = exports.promptWhenInteractive = exports.resolveImportPath = exports.ensureRootProjectName = exports.determineProjectNameAndRootOptions = exports.toProjectRelativePath = exports.deleteMatchingProperties = exports.processTargetOutputs = exports.NoTargetsToMigrateError = exports.migrateProjectExecutorsToPluginV1 = exports.migrateProjectExecutorsToPlugin = exports.AggregatedLog = exports.forEachExecutorOptions = exports.getE2EWebServerInfo = exports.getRelativeCwd = exports.determineArtifactNameAndDirectoryOptions = exports.emitPluginWorkerLog = exports.safeWriteFileCache = exports.PluginCache = exports.createProjectRootMappingsFromProjectConfigurations = exports.signalToCode = void 0;
3
+ exports.dasherize = exports.classify = exports.capitalize = exports.camelize = exports.checkAndCleanWithSemver = exports.eachValueFrom = exports.logShowProjectCommand = exports.getNamedInputs = exports.findPluginForConfigFile = exports.clearRequireCache = exports.loadConfigFile = exports.getCatalogManager = exports.calculateHashesForCreateNodes = exports.calculateHashForCreateNodes = exports.mapAsyncIterable = exports.combineAsyncIterables = exports.createAsyncIterable = exports.assertSupportedPackageVersion = exports.getInstalledPackageVersion = exports.getDeclaredPackageVersion = exports.addPlugin = exports.normalizeTargetDefaults = exports.downgradeTargetDefaults = exports.upsertTargetDefault = exports.readTargetDefaultsForTarget = exports.findTargetDefault = exports.addE2eCiTargetDefaults = exports.addBuildTargetDefaults = exports.promptWhenInteractive = exports.resolveImportPath = exports.ensureRootProjectName = exports.determineProjectNameAndRootOptions = exports.toProjectRelativePath = exports.deleteMatchingProperties = exports.processTargetOutputs = exports.NoTargetsToMigrateError = exports.migrateProjectExecutorsToPluginV1 = exports.migrateProjectExecutorsToPlugin = exports.AggregatedLog = exports.forEachExecutorOptions = exports.getE2EWebServerInfo = exports.getRelativeCwd = exports.determineArtifactNameAndDirectoryOptions = exports.emitPluginWorkerLog = exports.safeWriteFileCache = exports.PluginCache = exports.createProjectRootMappingsFromProjectConfigurations = exports.signalToCode = void 0;
4
4
  var devkit_internals_1 = require("nx/src/devkit-internals");
5
5
  Object.defineProperty(exports, "signalToCode", { enumerable: true, get: function () { return devkit_internals_1.signalToCode; } });
6
6
  Object.defineProperty(exports, "createProjectRootMappingsFromProjectConfigurations", { enumerable: true, get: function () { return devkit_internals_1.createProjectRootMappingsFromProjectConfigurations; } });
@@ -34,6 +34,12 @@ Object.defineProperty(exports, "promptWhenInteractive", { enumerable: true, get:
34
34
  var target_defaults_utils_1 = require("./src/generators/target-defaults-utils");
35
35
  Object.defineProperty(exports, "addBuildTargetDefaults", { enumerable: true, get: function () { return target_defaults_utils_1.addBuildTargetDefaults; } });
36
36
  Object.defineProperty(exports, "addE2eCiTargetDefaults", { enumerable: true, get: function () { return target_defaults_utils_1.addE2eCiTargetDefaults; } });
37
+ Object.defineProperty(exports, "findTargetDefault", { enumerable: true, get: function () { return target_defaults_utils_1.findTargetDefault; } });
38
+ Object.defineProperty(exports, "readTargetDefaultsForTarget", { enumerable: true, get: function () { return target_defaults_utils_1.readTargetDefaultsForTarget; } });
39
+ Object.defineProperty(exports, "upsertTargetDefault", { enumerable: true, get: function () { return target_defaults_utils_1.upsertTargetDefault; } });
40
+ var normalize_target_defaults_1 = require("./src/utils/normalize-target-defaults");
41
+ Object.defineProperty(exports, "downgradeTargetDefaults", { enumerable: true, get: function () { return normalize_target_defaults_1.downgradeTargetDefaults; } });
42
+ Object.defineProperty(exports, "normalizeTargetDefaults", { enumerable: true, get: function () { return normalize_target_defaults_1.normalizeTargetDefaults; } });
37
43
  // Utils
38
44
  var add_plugin_1 = require("./src/utils/add-plugin");
39
45
  Object.defineProperty(exports, "addPlugin", { enumerable: true, get: function () { return add_plugin_1.addPlugin; } });
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getE2EWebServerInfo = getE2EWebServerInfo;
4
4
  const devkit_exports_1 = require("nx/src/devkit-exports");
5
+ const target_defaults_utils_1 = require("./target-defaults-utils");
5
6
  const find_plugin_for_config_file_1 = require("../utils/find-plugin-for-config-file");
6
7
  async function getE2EWebServerInfo(tree, projectName, pluginOptions, defaultValues, isPluginBeingAdded) {
7
8
  const pm = (0, devkit_exports_1.getPackageManagerCommand)((0, devkit_exports_1.detectPackageManager)(tree.root));
@@ -33,23 +34,18 @@ async function getE2EWebServerInfoForPlugin(tree, projectName, pluginOptions, de
33
34
  }
34
35
  const nxJson = (0, devkit_exports_1.readNxJson)(tree);
35
36
  let e2ePort = defaultValues.defaultE2EPort ?? 4200;
36
- if (nxJson.targetDefaults?.[foundPlugin.options[pluginOptions.serveTargetName] ??
37
- defaultValues.defaultServeTargetName] &&
38
- nxJson.targetDefaults?.[foundPlugin.options[pluginOptions.serveTargetName] ??
39
- defaultValues.defaultServeTargetName].options?.port) {
40
- e2ePort =
41
- nxJson.targetDefaults?.[foundPlugin.options[pluginOptions.serveTargetName] ??
42
- defaultValues.defaultServeTargetName].options?.port;
43
- }
37
+ const serveTargetName = foundPlugin.options[pluginOptions.serveTargetName] ??
38
+ defaultValues.defaultServeTargetName;
39
+ e2ePort =
40
+ (0, target_defaults_utils_1.readTargetDefaultsForTarget)(serveTargetName, nxJson.targetDefaults)?.options
41
+ ?.port ?? e2ePort;
44
42
  const e2eWebServerAddress = defaultValues.defaultE2EWebServerAddress.replace(/:\d+/, `:${e2ePort}`);
45
43
  return {
46
44
  e2eWebServerAddress,
47
- e2eWebServerCommand: `${pm.exec} nx run ${projectName}:${foundPlugin.options[pluginOptions.serveTargetName] ??
48
- defaultValues.defaultServeTargetName}`,
45
+ e2eWebServerCommand: `${pm.exec} nx run ${projectName}:${serveTargetName}`,
49
46
  e2eCiWebServerCommand: `${pm.exec} nx run ${projectName}:${foundPlugin.options[pluginOptions.serveStaticTargetName] ??
50
47
  defaultValues.defaultServeStaticTargetName}`,
51
48
  e2eCiBaseUrl: defaultValues.defaultE2ECiBaseUrl,
52
- e2eDevServerTarget: `${projectName}:${foundPlugin.options[pluginOptions.serveTargetName] ??
53
- defaultValues.defaultServeTargetName}`,
49
+ e2eDevServerTarget: `${projectName}:${serveTargetName}`,
54
50
  };
55
51
  }
@@ -1,5 +1,5 @@
1
1
  import type { ProjectConfiguration } from 'nx/src/config/workspace-json-project-json';
2
- import { type CreateNodesV2, type ProjectGraph, type TargetConfiguration, type Tree } from 'nx/src/devkit-exports';
2
+ import { type CreateNodesV2, type NxJsonConfiguration, type ProjectGraph, type TargetConfiguration, type Tree } from 'nx/src/devkit-exports';
3
3
  import { logger as devkitLogger } from 'nx/src/devkit-exports';
4
4
  export type InferredTargetConfiguration = TargetConfiguration & {
5
5
  name: string;
@@ -13,6 +13,7 @@ type SkipProjectFilter = (projectConfiguration: ProjectConfiguration) => false |
13
13
  export declare class NoTargetsToMigrateError extends Error {
14
14
  constructor();
15
15
  }
16
+ export declare function readTargetDefaultsForExecutor(executor: string, targetDefaults: NxJsonConfiguration['targetDefaults'] | undefined): Partial<TargetConfiguration> | undefined;
16
17
  export declare function migrateProjectExecutorsToPlugin<T>(tree: Tree, projectGraph: ProjectGraph, pluginPath: string, createNodesV2: CreateNodesV2<T>, defaultPluginOptions: T, migrations: Array<{
17
18
  executors: string[];
18
19
  targetPluginOptionMapper: (targetName: string) => Partial<T>;
@@ -2,6 +2,7 @@
2
2
  var _ExecutorToPluginMigrator_instances, _ExecutorToPluginMigrator_projectGraph, _ExecutorToPluginMigrator_executor, _ExecutorToPluginMigrator_pluginPath, _ExecutorToPluginMigrator_pluginOptionsBuilder, _ExecutorToPluginMigrator_postTargetTransformer, _ExecutorToPluginMigrator_skipTargetFilter, _ExecutorToPluginMigrator_skipProjectFilter, _ExecutorToPluginMigrator_specificProjectToMigrate, _ExecutorToPluginMigrator_logger, _ExecutorToPluginMigrator_nxJson, _ExecutorToPluginMigrator_targetDefaultsForExecutor, _ExecutorToPluginMigrator_targetAndProjectsToMigrate, _ExecutorToPluginMigrator_createNodes, _ExecutorToPluginMigrator_createNodesV2, _ExecutorToPluginMigrator_createNodesResultsForTargets, _ExecutorToPluginMigrator_skippedProjects, _ExecutorToPluginMigrator_init, _ExecutorToPluginMigrator_migrateTarget, _ExecutorToPluginMigrator_migrateProject, _ExecutorToPluginMigrator_mergeInputs, _ExecutorToPluginMigrator_getTargetAndProjectsToMigrate, _ExecutorToPluginMigrator_getTargetDefaultsForExecutor, _ExecutorToPluginMigrator_getCreatedTargetForProjectRoot, _ExecutorToPluginMigrator_getCreateNodesResults;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.NoTargetsToMigrateError = void 0;
5
+ exports.readTargetDefaultsForExecutor = readTargetDefaultsForExecutor;
5
6
  exports.migrateProjectExecutorsToPlugin = migrateProjectExecutorsToPlugin;
6
7
  exports.migrateProjectExecutorsToPluginV1 = migrateProjectExecutorsToPluginV1;
7
8
  const tslib_1 = require("tslib");
@@ -11,6 +12,7 @@ const posix_1 = require("node:path/posix");
11
12
  const devkit_exports_1 = require("nx/src/devkit-exports");
12
13
  const devkit_internals_1 = require("nx/src/devkit-internals");
13
14
  const executor_options_utils_1 = require("../executor-options-utils");
15
+ const target_defaults_utils_1 = require("../target-defaults-utils");
14
16
  const plugin_migration_utils_1 = require("./plugin-migration-utils");
15
17
  const devkit_exports_2 = require("nx/src/devkit-exports");
16
18
  class ExecutorToPluginMigrator {
@@ -169,7 +171,7 @@ _ExecutorToPluginMigrator_projectGraph = new WeakMap(), _ExecutorToPluginMigrato
169
171
  }
170
172
  });
171
173
  }, _ExecutorToPluginMigrator_getTargetDefaultsForExecutor = function _ExecutorToPluginMigrator_getTargetDefaultsForExecutor() {
172
- tslib_1.__classPrivateFieldSet(this, _ExecutorToPluginMigrator_targetDefaultsForExecutor, structuredClone(tslib_1.__classPrivateFieldGet(this, _ExecutorToPluginMigrator_nxJson, "f").targetDefaults?.[tslib_1.__classPrivateFieldGet(this, _ExecutorToPluginMigrator_executor, "f")]), "f");
174
+ tslib_1.__classPrivateFieldSet(this, _ExecutorToPluginMigrator_targetDefaultsForExecutor, structuredClone(readTargetDefaultsForExecutor(tslib_1.__classPrivateFieldGet(this, _ExecutorToPluginMigrator_executor, "f"), tslib_1.__classPrivateFieldGet(this, _ExecutorToPluginMigrator_nxJson, "f").targetDefaults) ?? {}), "f");
173
175
  }, _ExecutorToPluginMigrator_getCreatedTargetForProjectRoot = function _ExecutorToPluginMigrator_getCreatedTargetForProjectRoot(targetName, projectRoot) {
174
176
  const entry = Object.entries(tslib_1.__classPrivateFieldGet(this, _ExecutorToPluginMigrator_createNodesResultsForTargets, "f").get(targetName)?.projects ?? {}).find(([root]) => root === projectRoot);
175
177
  if (!entry) {
@@ -204,6 +206,22 @@ class NoTargetsToMigrateError extends Error {
204
206
  }
205
207
  }
206
208
  exports.NoTargetsToMigrateError = NoTargetsToMigrateError;
209
+ function readTargetDefaultsForExecutor(executor, targetDefaults) {
210
+ // Preserve the legacy record-shape semantics this migrator used before
211
+ // array support: only an unfiltered default keyed directly by executor
212
+ // applies here. Target-scoped or filtered array entries remain opt-in
213
+ // behaviors for callers that can evaluate them in project context.
214
+ const entry = (0, target_defaults_utils_1.findTargetDefault)(targetDefaults, { executor });
215
+ if (!entry) {
216
+ return undefined;
217
+ }
218
+ const config = { ...entry };
219
+ delete config.target;
220
+ delete config.executor;
221
+ delete config.projects;
222
+ delete config.plugin;
223
+ return config;
224
+ }
207
225
  async function migrateProjectExecutorsToPlugin(tree, projectGraph, pluginPath, createNodesV2, defaultPluginOptions, migrations, specificProjectToMigrate, logger) {
208
226
  const projects = await migrateProjects(tree, projectGraph, pluginPath, undefined, createNodesV2, defaultPluginOptions, migrations, specificProjectToMigrate, logger);
209
227
  return projects;
@@ -1,3 +1,31 @@
1
- import { type TargetConfiguration, type Tree } from 'nx/src/devkit-exports';
1
+ import { type NxJsonConfiguration, type TargetConfiguration, type TargetDefaultEntry, type TargetDefaults, type Tree } from 'nx/src/devkit-exports';
2
+ import { readTargetDefaultsForTarget as readTargetDefaultsForTargetFromNx } from 'nx/src/devkit-internals';
3
+ /**
4
+ * Upsert a `targetDefaults` entry on the provided `nxJson`. Mutates
5
+ * `nxJson` in place and returns it so the caller can chain or batch
6
+ * other edits before persisting via `updateNxJson` exactly once.
7
+ *
8
+ * Always writes the array shape — if the underlying value still uses
9
+ * the legacy record shape, it is upgraded in place. Finds a matching
10
+ * entry by the `(target, executor, projects, plugin)` tuple and merges
11
+ * the given config into it, or appends a new entry. The entry must set
12
+ * at least one of `target` / `executor`.
13
+ */
14
+ export declare function upsertTargetDefault(tree: Tree, nxJson: NxJsonConfiguration, options: TargetDefaultEntry): NxJsonConfiguration;
15
+ /**
16
+ * Find a `targetDefaults` entry by its locator tuple
17
+ * `(target, executor, projects, plugin)`. Locator keys default to
18
+ * `undefined`, matching only entries that also leave them unset — same
19
+ * semantics as `upsertTargetDefault`. Accepts either array or legacy
20
+ * record shape.
21
+ *
22
+ * Throws when called with an empty locator (no `target`, `executor`,
23
+ * `projects`, or `plugin`). An empty locator is almost always a bug —
24
+ * the caller intended to find a specific entry but forgot to populate
25
+ * the lookup. Returning the first matching entry (or `undefined`) would
26
+ * silently mask the mistake.
27
+ */
28
+ export declare function findTargetDefault(targetDefaults: TargetDefaults | undefined, locator: Pick<TargetDefaultEntry, 'target' | 'executor' | 'projects' | 'plugin'>): TargetDefaultEntry | undefined;
29
+ export declare function readTargetDefaultsForTarget(targetName: string, targetDefaults: TargetDefaults | undefined, executor?: string, opts?: Parameters<typeof readTargetDefaultsForTargetFromNx>[3]): Partial<TargetConfiguration> | null;
2
30
  export declare function addBuildTargetDefaults(tree: Tree, executorName: string, buildTargetName?: string, extraInputs?: TargetConfiguration['inputs']): void;
3
31
  export declare function addE2eCiTargetDefaults(tree: Tree, e2ePlugin: string, buildTarget: string, pathToE2EConfigFile: string): Promise<void>;
@@ -1,13 +1,167 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.upsertTargetDefault = upsertTargetDefault;
4
+ exports.findTargetDefault = findTargetDefault;
5
+ exports.readTargetDefaultsForTarget = readTargetDefaultsForTarget;
3
6
  exports.addBuildTargetDefaults = addBuildTargetDefaults;
4
7
  exports.addE2eCiTargetDefaults = addE2eCiTargetDefaults;
5
8
  const devkit_exports_1 = require("nx/src/devkit-exports");
6
9
  const devkit_internals_1 = require("nx/src/devkit-internals");
10
+ const semver_1 = require("semver");
11
+ const package_json_1 = require("../utils/package-json");
12
+ const normalize_target_defaults_1 = require("../utils/normalize-target-defaults");
13
+ // Only write the array shape on nx >= 23 (workspace placeholder "0.0.1"
14
+ // counts as modern); older nx can't validate it. Major-only compare so
15
+ // pre-release tags like `23.0.0-beta.8` count as nx 23.
16
+ const SUPPORTS_ARRAY_TARGET_DEFAULTS = !(0, semver_1.valid)(package_json_1.NX_VERSION) || package_json_1.NX_VERSION === '0.0.1' || (0, semver_1.major)(package_json_1.NX_VERSION) >= 23;
17
+ /**
18
+ * Upsert a `targetDefaults` entry on the provided `nxJson`. Mutates
19
+ * `nxJson` in place and returns it so the caller can chain or batch
20
+ * other edits before persisting via `updateNxJson` exactly once.
21
+ *
22
+ * Always writes the array shape — if the underlying value still uses
23
+ * the legacy record shape, it is upgraded in place. Finds a matching
24
+ * entry by the `(target, executor, projects, plugin)` tuple and merges
25
+ * the given config into it, or appends a new entry. The entry must set
26
+ * at least one of `target` / `executor`.
27
+ */
28
+ function upsertTargetDefault(tree, nxJson, options) {
29
+ if (options.target === undefined && options.executor === undefined) {
30
+ throw new Error('upsertTargetDefault requires at least one of `target` or `executor` to be set.');
31
+ }
32
+ const { target, executor, projects, plugin, ...config } = options;
33
+ const originalShape = nxJson.targetDefaults;
34
+ // Copy — `normalizeTargetDefaults` returns the input array as-is when
35
+ // it's already array shape. Without the spread, `entries[matchIndex] = ...`
36
+ // and `entries.push(...)` would mutate the user's array reference (and
37
+ // any other holders of the same `nxJson.targetDefaults` reference).
38
+ const entries = [...normalizeTargetDefaultsForUpsert(tree, originalShape)];
39
+ const matchIndex = entries.findIndex((e) => e.target === target &&
40
+ e.executor === executor &&
41
+ projectsEqual(e.projects, projects) &&
42
+ e.plugin === plugin);
43
+ if (matchIndex >= 0) {
44
+ const existing = entries[matchIndex];
45
+ const { target: et, executor: ee, projects: ep, plugin: ep2, ...existingRest } = existing;
46
+ entries[matchIndex] = buildTargetDefaultEntry(target ?? et, projects ?? ep, plugin ?? ep2, executor ?? ee, { ...existingRest, ...config });
47
+ }
48
+ else {
49
+ entries.push(buildTargetDefaultEntry(target, projects, plugin, executor, config));
50
+ }
51
+ // Preserve the record shape on pre-v23 nx when possible; promote to
52
+ // array if any entry can't be represented in record form.
53
+ if (SUPPORTS_ARRAY_TARGET_DEFAULTS || Array.isArray(originalShape)) {
54
+ nxJson.targetDefaults = entries;
55
+ }
56
+ else {
57
+ try {
58
+ nxJson.targetDefaults = (0, normalize_target_defaults_1.downgradeTargetDefaults)(entries);
59
+ }
60
+ catch {
61
+ nxJson.targetDefaults = entries;
62
+ }
63
+ }
64
+ return nxJson;
65
+ }
66
+ /**
67
+ * Find a `targetDefaults` entry by its locator tuple
68
+ * `(target, executor, projects, plugin)`. Locator keys default to
69
+ * `undefined`, matching only entries that also leave them unset — same
70
+ * semantics as `upsertTargetDefault`. Accepts either array or legacy
71
+ * record shape.
72
+ *
73
+ * Throws when called with an empty locator (no `target`, `executor`,
74
+ * `projects`, or `plugin`). An empty locator is almost always a bug —
75
+ * the caller intended to find a specific entry but forgot to populate
76
+ * the lookup. Returning the first matching entry (or `undefined`) would
77
+ * silently mask the mistake.
78
+ */
79
+ function findTargetDefault(targetDefaults, locator) {
80
+ if (locator.target === undefined &&
81
+ locator.executor === undefined &&
82
+ locator.projects === undefined &&
83
+ locator.plugin === undefined) {
84
+ throw new Error('findTargetDefault requires at least one of `target`, `executor`, `projects`, or `plugin` on the locator.');
85
+ }
86
+ return (0, normalize_target_defaults_1.normalizeTargetDefaults)(targetDefaults).find((e) => e.target === locator.target &&
87
+ e.executor === locator.executor &&
88
+ projectsEqual(e.projects, locator.projects) &&
89
+ e.plugin === locator.plugin);
90
+ }
91
+ function readTargetDefaultsForTarget(targetName, targetDefaults, executor, opts) {
92
+ if (targetDefaults &&
93
+ !Array.isArray(targetDefaults) &&
94
+ Object.prototype.hasOwnProperty.call(targetDefaults, targetName)) {
95
+ return targetDefaults[targetName] ?? null;
96
+ }
97
+ return (0, devkit_internals_1.readTargetDefaultsForTarget)(targetName, targetDefaults, executor, opts);
98
+ }
99
+ function normalizeTargetDefaultsForUpsert(tree, targetDefaults) {
100
+ if (!targetDefaults) {
101
+ return [];
102
+ }
103
+ if (Array.isArray(targetDefaults)) {
104
+ return targetDefaults;
105
+ }
106
+ return (0, devkit_internals_1.normalizeTargetDefaultsAgainstRootMaps)(targetDefaults, buildProjectRootMap((0, devkit_exports_1.getProjects)(tree)));
107
+ }
108
+ function buildProjectRootMap(projects) {
109
+ return Object.fromEntries([...projects.values()].map((project) => [project.root, project]));
110
+ }
111
+ /**
112
+ * Construct a `TargetDefaultEntry` with the canonical key order
113
+ * `target → projects → plugin → executor → ...rest`. Locators land first
114
+ * so an entry's filter shape is obvious at a glance; `executor` follows
115
+ * because it doubles as a payload field.
116
+ */
117
+ function buildTargetDefaultEntry(target, projects, plugin, executor, rest) {
118
+ return {
119
+ ...(target !== undefined ? { target } : {}),
120
+ ...(projects !== undefined ? { projects } : {}),
121
+ ...(plugin !== undefined ? { plugin } : {}),
122
+ ...(executor !== undefined ? { executor } : {}),
123
+ ...rest,
124
+ };
125
+ }
126
+ // Order-insensitive equality so re-upserts with reordered patterns
127
+ // merge into the same entry rather than appending a duplicate.
128
+ function projectsEqual(a, b) {
129
+ if (a === b)
130
+ return true;
131
+ const aArr = a === undefined ? undefined : Array.isArray(a) ? a : [a];
132
+ const bArr = b === undefined ? undefined : Array.isArray(b) ? b : [b];
133
+ if (!aArr || !bArr)
134
+ return false;
135
+ if (aArr.length !== bArr.length)
136
+ return false;
137
+ const aSet = new Set(aArr);
138
+ if (aSet.size !== aArr.length) {
139
+ // Duplicates inside one array — fall back to positional comparison so
140
+ // we don't silently merge `['a','a']` with `['a']`.
141
+ for (let i = 0; i < aArr.length; i++)
142
+ if (aArr[i] !== bArr[i])
143
+ return false;
144
+ return true;
145
+ }
146
+ for (const item of bArr)
147
+ if (!aSet.has(item))
148
+ return false;
149
+ return true;
150
+ }
7
151
  function addBuildTargetDefaults(tree, executorName, buildTargetName = 'build', extraInputs = []) {
8
- const nxJson = (0, devkit_exports_1.readNxJson)(tree);
9
- nxJson.targetDefaults ??= {};
10
- nxJson.targetDefaults[executorName] ??= {
152
+ const nxJson = (0, devkit_exports_1.readNxJson)(tree) ?? {};
153
+ const entries = (0, normalize_target_defaults_1.normalizeTargetDefaults)(nxJson.targetDefaults);
154
+ // Only skip when an *unfiltered* entry already covers this executor.
155
+ // A filtered entry (`projects:` or `plugin:`) only applies to a subset
156
+ // of targets, so it doesn't satisfy the workspace-wide default this
157
+ // helper writes — we still need to add the unfiltered baseline.
158
+ if (entries.some((e) => e.executor === executorName &&
159
+ e.projects === undefined &&
160
+ e.plugin === undefined)) {
161
+ return;
162
+ }
163
+ upsertTargetDefault(tree, nxJson, {
164
+ executor: executorName,
11
165
  cache: true,
12
166
  dependsOn: [`^${buildTargetName}`],
13
167
  inputs: [
@@ -16,12 +170,12 @@ function addBuildTargetDefaults(tree, executorName, buildTargetName = 'build', e
16
170
  : ['default', '^default']),
17
171
  ...extraInputs,
18
172
  ],
19
- };
173
+ });
20
174
  (0, devkit_exports_1.updateNxJson)(tree, nxJson);
21
175
  }
22
176
  async function addE2eCiTargetDefaults(tree, e2ePlugin, buildTarget, pathToE2EConfigFile) {
23
177
  const nxJson = (0, devkit_exports_1.readNxJson)(tree);
24
- if (!nxJson.plugins) {
178
+ if (!nxJson?.plugins) {
25
179
  return;
26
180
  }
27
181
  const e2ePluginRegistrations = nxJson.plugins.filter((p) => typeof p === 'string' ? p === e2ePlugin : p.plugin === e2ePlugin);
@@ -50,18 +204,13 @@ async function addE2eCiTargetDefaults(tree, e2ePlugin, buildTarget, pathToE2ECon
50
204
  ? 'e2e-ci'
51
205
  : (foundPluginForApplication.options?.ciTargetName ?? 'e2e-ci');
52
206
  const ciTargetNameGlob = `${ciTargetName}--**/**`;
53
- nxJson.targetDefaults ??= {};
54
- const e2eCiTargetDefaults = nxJson.targetDefaults[ciTargetNameGlob];
55
- if (!e2eCiTargetDefaults) {
56
- nxJson.targetDefaults[ciTargetNameGlob] = {
57
- dependsOn: [buildTarget],
58
- };
59
- }
60
- else {
61
- e2eCiTargetDefaults.dependsOn ??= [];
62
- if (!e2eCiTargetDefaults.dependsOn.includes(buildTarget)) {
63
- e2eCiTargetDefaults.dependsOn.push(buildTarget);
64
- }
207
+ const existing = findTargetDefault(nxJson.targetDefaults, {
208
+ target: ciTargetNameGlob,
209
+ });
210
+ const dependsOn = [...(existing?.dependsOn ?? [])];
211
+ if (!dependsOn.includes(buildTarget)) {
212
+ dependsOn.push(buildTarget);
65
213
  }
214
+ upsertTargetDefault(tree, nxJson, { target: ciTargetNameGlob, dependsOn });
66
215
  (0, devkit_exports_1.updateNxJson)(tree, nxJson);
67
216
  }
@@ -0,0 +1,32 @@
1
+ import type { TargetDefaultEntry, TargetDefaults, TargetDefaultsRecord } from 'nx/src/devkit-exports';
2
+ /**
3
+ * Convert an nx.json `targetDefaults` value (either the legacy record shape
4
+ * or the new array shape) into the normalized array shape.
5
+ *
6
+ * Record entries become `{ target: key, ...value }` preserving insertion
7
+ * order — except executor-shaped keys (e.g. `@nx/vite:test`,
8
+ * `nx:run-commands`), which become `{ executor: key, ...value }`.
9
+ *
10
+ * Returns the array directly when already normalized — callers must not
11
+ * mutate the result if they want to preserve the underlying nx.json.
12
+ */
13
+ export declare function normalizeTargetDefaults(raw: TargetDefaults | undefined): TargetDefaultEntry[];
14
+ /**
15
+ * Project an array of `TargetDefaultEntry` back into the legacy
16
+ * record shape. The intended caller is a pre-v23 migration that
17
+ * normalized to array internally but wants to preserve the original
18
+ * on-disk record shape so it remains valid against pre-v23 nx.json
19
+ * schemas.
20
+ *
21
+ * Each entry's key is its `target` (if set) or `executor`. Entries that
22
+ * have both keep the locator role on `target` and retain `executor` as
23
+ * a value field.
24
+ *
25
+ * Throws when the input contains entries that the record shape cannot
26
+ * represent — `projects`/`plugin` filters, two entries that would collapse
27
+ * to the same key, or entries with neither `target` nor `executor`. The
28
+ * caller must keep array shape in those cases (or refuse to write the
29
+ * change). Silently dropping these entries would corrupt nx.json without
30
+ * the user noticing.
31
+ */
32
+ export declare function downgradeTargetDefaults(entries: TargetDefaultEntry[]): TargetDefaultsRecord;
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeTargetDefaults = normalizeTargetDefaults;
4
+ exports.downgradeTargetDefaults = downgradeTargetDefaults;
5
+ // Mirrors `GLOB_CHARACTERS` / `isGlobPattern` from
6
+ // `nx/src/utils/globs.ts`. We can't import from there directly: devkit
7
+ // supports nx +/- 1 major version and that file isn't part of the
8
+ // devkit-exports surface guaranteed across the range. Keeping a local
9
+ // copy that exactly matches the canonical implementation avoids the
10
+ // import while staying behaviorally aligned.
11
+ const GLOB_CHARACTERS = new Set(['*', '|', '{', '}', '(', ')', '[']);
12
+ /**
13
+ * Convert an nx.json `targetDefaults` value (either the legacy record shape
14
+ * or the new array shape) into the normalized array shape.
15
+ *
16
+ * Record entries become `{ target: key, ...value }` preserving insertion
17
+ * order — except executor-shaped keys (e.g. `@nx/vite:test`,
18
+ * `nx:run-commands`), which become `{ executor: key, ...value }`.
19
+ *
20
+ * Returns the array directly when already normalized — callers must not
21
+ * mutate the result if they want to preserve the underlying nx.json.
22
+ */
23
+ function normalizeTargetDefaults(raw) {
24
+ if (!raw)
25
+ return [];
26
+ if (Array.isArray(raw))
27
+ return raw;
28
+ const out = [];
29
+ const record = raw;
30
+ for (const key of Object.keys(record)) {
31
+ const value = record[key] ?? {};
32
+ out.push(isExecutorLikeKey(key)
33
+ ? { ...value, executor: key }
34
+ : { ...value, target: key });
35
+ }
36
+ return out;
37
+ }
38
+ function isExecutorLikeKey(key) {
39
+ if (!key.includes(':'))
40
+ return false;
41
+ for (const c of key)
42
+ if (GLOB_CHARACTERS.has(c))
43
+ return false;
44
+ return true;
45
+ }
46
+ /**
47
+ * Project an array of `TargetDefaultEntry` back into the legacy
48
+ * record shape. The intended caller is a pre-v23 migration that
49
+ * normalized to array internally but wants to preserve the original
50
+ * on-disk record shape so it remains valid against pre-v23 nx.json
51
+ * schemas.
52
+ *
53
+ * Each entry's key is its `target` (if set) or `executor`. Entries that
54
+ * have both keep the locator role on `target` and retain `executor` as
55
+ * a value field.
56
+ *
57
+ * Throws when the input contains entries that the record shape cannot
58
+ * represent — `projects`/`plugin` filters, two entries that would collapse
59
+ * to the same key, or entries with neither `target` nor `executor`. The
60
+ * caller must keep array shape in those cases (or refuse to write the
61
+ * change). Silently dropping these entries would corrupt nx.json without
62
+ * the user noticing.
63
+ */
64
+ function downgradeTargetDefaults(entries) {
65
+ const out = {};
66
+ for (const entry of entries) {
67
+ if (entry.projects !== undefined || entry.plugin !== undefined) {
68
+ throw new Error(`Cannot downgrade targetDefaults to legacy record shape: entry ${JSON.stringify(entry)} uses a \`projects\`/\`plugin\` filter, which is only supported in the array shape.`);
69
+ }
70
+ const { target, executor, projects, plugin, ...rest } = entry;
71
+ const key = target ?? executor;
72
+ if (key === undefined) {
73
+ throw new Error(`Cannot downgrade targetDefaults to legacy record shape: entry ${JSON.stringify(entry)} has neither \`target\` nor \`executor\` to use as the record key.`);
74
+ }
75
+ if (Object.prototype.hasOwnProperty.call(out, key)) {
76
+ throw new Error(`Cannot downgrade targetDefaults to legacy record shape: two entries collapse to the same key \`${key}\`. Keep the array shape so both can coexist.`);
77
+ }
78
+ const value = { ...rest };
79
+ if (target && executor)
80
+ value.executor = executor;
81
+ out[key] = value;
82
+ }
83
+ return out;
84
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/devkit",
3
- "version": "23.0.0-beta.12",
3
+ "version": "23.0.0-beta.14",
4
4
  "private": false,
5
5
  "type": "commonjs",
6
6
  "files": [
@@ -60,7 +60,7 @@
60
60
  },
61
61
  "devDependencies": {
62
62
  "jest": "30.3.0",
63
- "nx": "23.0.0-beta.12"
63
+ "nx": "23.0.0-beta.14"
64
64
  },
65
65
  "peerDependencies": {
66
66
  "nx": ">= 22 <= 24 || ^23.0.0-0"