@nx/devkit 23.1.0-pr.36127.e594f53 → 23.1.0-rc.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.
- package/dist/internal.d.ts +1 -1
- package/dist/internal.js +6 -1
- package/dist/src/generators/e2e-web-server-info-utils.js +8 -12
- package/dist/src/generators/format-files.js +13 -1
- package/dist/src/generators/plugin-migrations/executor-to-plugin-migrator.d.ts +2 -1
- package/dist/src/generators/plugin-migrations/executor-to-plugin-migrator.js +19 -1
- package/dist/src/generators/target-defaults-utils.d.ts +118 -1
- package/dist/src/generators/target-defaults-utils.js +360 -17
- package/dist/src/utils/catalog/manager-utils.d.ts +9 -0
- package/dist/src/utils/catalog/manager-utils.js +215 -0
- package/dist/src/utils/catalog/pnpm-manager.js +4 -102
- package/dist/src/utils/catalog/yarn-manager.js +4 -102
- package/package.json +4 -3
package/dist/internal.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ 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, updateTargetDefault, upsertTargetDefault, } from './src/generators/target-defaults-utils';
|
|
11
11
|
export { addPlugin } from './src/utils/add-plugin';
|
|
12
12
|
export { getDeclaredPackageVersion, getInstalledPackageVersion, } from './src/utils/installed-version';
|
|
13
13
|
export { assertSupportedInstalledPackageVersion, assertSupportedPackageVersion, } from './src/utils/version-floor';
|
package/dist/internal.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
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.assertSupportedInstalledPackageVersion = exports.getInstalledPackageVersion = exports.getDeclaredPackageVersion = exports.addPlugin = exports.upsertTargetDefault = exports.updateTargetDefault = 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.PromptResolutionError = exports.resolvePrompt = exports.SchemaResolutionError = exports.ImplementationResolutionError = exports.resolveSchema = exports.resolveImplementation = exports.emitPluginWorkerLog = exports.safeWriteFileCache = exports.PluginCache = exports.createProjectRootMappingsFromProjectConfigurations = exports.signalToCode = void 0;
|
|
4
|
+
exports.dasherize = exports.classify = exports.capitalize = exports.camelize = void 0;
|
|
4
5
|
var devkit_internals_1 = require("nx/src/devkit-internals");
|
|
5
6
|
Object.defineProperty(exports, "signalToCode", { enumerable: true, get: function () { return devkit_internals_1.signalToCode; } });
|
|
6
7
|
Object.defineProperty(exports, "createProjectRootMappingsFromProjectConfigurations", { enumerable: true, get: function () { return devkit_internals_1.createProjectRootMappingsFromProjectConfigurations; } });
|
|
@@ -40,6 +41,10 @@ Object.defineProperty(exports, "promptWhenInteractive", { enumerable: true, get:
|
|
|
40
41
|
var target_defaults_utils_1 = require("./src/generators/target-defaults-utils");
|
|
41
42
|
Object.defineProperty(exports, "addBuildTargetDefaults", { enumerable: true, get: function () { return target_defaults_utils_1.addBuildTargetDefaults; } });
|
|
42
43
|
Object.defineProperty(exports, "addE2eCiTargetDefaults", { enumerable: true, get: function () { return target_defaults_utils_1.addE2eCiTargetDefaults; } });
|
|
44
|
+
Object.defineProperty(exports, "findTargetDefault", { enumerable: true, get: function () { return target_defaults_utils_1.findTargetDefault; } });
|
|
45
|
+
Object.defineProperty(exports, "readTargetDefaultsForTarget", { enumerable: true, get: function () { return target_defaults_utils_1.readTargetDefaultsForTarget; } });
|
|
46
|
+
Object.defineProperty(exports, "updateTargetDefault", { enumerable: true, get: function () { return target_defaults_utils_1.updateTargetDefault; } });
|
|
47
|
+
Object.defineProperty(exports, "upsertTargetDefault", { enumerable: true, get: function () { return target_defaults_utils_1.upsertTargetDefault; } });
|
|
43
48
|
// Utils
|
|
44
49
|
var add_plugin_1 = require("./src/utils/add-plugin");
|
|
45
50
|
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
|
-
|
|
37
|
-
defaultValues.defaultServeTargetName
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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}:${
|
|
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}:${
|
|
53
|
-
defaultValues.defaultServeTargetName}`,
|
|
49
|
+
e2eDevServerTarget: `${projectName}:${serveTargetName}`,
|
|
54
50
|
};
|
|
55
51
|
}
|
|
@@ -5,6 +5,18 @@ const tslib_1 = require("tslib");
|
|
|
5
5
|
const devkit_exports_1 = require("nx/src/devkit-exports");
|
|
6
6
|
const devkit_internals_1 = require("nx/src/devkit-internals");
|
|
7
7
|
const path = tslib_1.__importStar(require("path"));
|
|
8
|
+
// Prettier v3 (ESM) exposes its API as named exports; v2 (CJS) exposes it under
|
|
9
|
+
// `.default` when loaded via `import()`. Return whichever carries the API, or
|
|
10
|
+
// null if prettier isn't installed.
|
|
11
|
+
async function importPrettier() {
|
|
12
|
+
try {
|
|
13
|
+
const imported = await import('prettier');
|
|
14
|
+
return (imported.resolveConfig ? imported : imported.default);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
8
20
|
/**
|
|
9
21
|
* Formats all the created or updated files using Prettier
|
|
10
22
|
* @param tree - the file system tree
|
|
@@ -32,7 +44,7 @@ async function formatFiles(tree, options = {}) {
|
|
|
32
44
|
}
|
|
33
45
|
let prettier;
|
|
34
46
|
try {
|
|
35
|
-
prettier = await
|
|
47
|
+
prettier = await importPrettier();
|
|
36
48
|
/**
|
|
37
49
|
* Even after we discovered prettier in node_modules, we need to be sure that the user is intentionally using prettier
|
|
38
50
|
* before proceeding to format with it.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ProjectConfiguration } from 'nx/src/config/workspace-json-project-json';
|
|
2
|
-
import { type CreateNodes, type ProjectGraph, type TargetConfiguration, type Tree } from 'nx/src/devkit-exports';
|
|
2
|
+
import { type CreateNodes, 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: CreateNodes<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,
|
|
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,120 @@
|
|
|
1
|
-
import { type TargetConfiguration, type Tree } from 'nx/src/devkit-exports';
|
|
1
|
+
import { type NxJsonConfiguration, type TargetConfiguration, type TargetDefaultEntry, type TargetDefaultFilter, 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
|
+
* The input is the logical {@link TargetDefaultEntry} shape
|
|
9
|
+
* (`{ target?, executor?, projects?, plugin?, ...config }`). The `projects` /
|
|
10
|
+
* `plugin` filter is *context* describing what the caller is configuring, used
|
|
11
|
+
* to pick which existing entry to merge into — it is never authored as a new
|
|
12
|
+
* filtered entry. For the key (`target` ?? `executor`):
|
|
13
|
+
*
|
|
14
|
+
* - No value yet → create the generic (plain object) value.
|
|
15
|
+
* - Only a generic value → merge into it.
|
|
16
|
+
* - A filtered entry matching the context filter exists → merge into that one
|
|
17
|
+
* (so a caller updating a project the user already scoped a default for edits
|
|
18
|
+
* that scoped entry rather than clobbering the workspace baseline).
|
|
19
|
+
* - Otherwise → merge into the generic, creating one if needed.
|
|
20
|
+
*
|
|
21
|
+
* A lone unfiltered entry is always stored as a plain object, never a
|
|
22
|
+
* single-element array. The entry must set at least one of `target` /
|
|
23
|
+
* `executor`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function upsertTargetDefault(tree: Tree, nxJson: NxJsonConfiguration, options: TargetDefaultEntry): NxJsonConfiguration;
|
|
26
|
+
/**
|
|
27
|
+
* Find a `targetDefaults` entry by its logical locator tuple
|
|
28
|
+
* `(target, executor, projects, plugin)`. Locator keys default to
|
|
29
|
+
* `undefined`, matching only entries that also leave them unset — same
|
|
30
|
+
* semantics as `upsertTargetDefault`.
|
|
31
|
+
*
|
|
32
|
+
* Throws when called with an empty locator (no `target`, `executor`,
|
|
33
|
+
* `projects`, or `plugin`). An empty locator is almost always a bug — the
|
|
34
|
+
* caller intended to find a specific entry but forgot to populate the lookup.
|
|
35
|
+
*/
|
|
36
|
+
export declare function findTargetDefault(targetDefaults: TargetDefaults | undefined, locator: Pick<TargetDefaultEntry, 'target' | 'executor' | 'projects' | 'plugin'>): TargetDefaultEntry | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* The subset of a project graph node that the `projects` narrowing reads —
|
|
39
|
+
* names come from the map keys, `root`/`tags` drive `findMatchingProjects`.
|
|
40
|
+
* Mirrors `MatcherProjectNode` without importing it, so the signature stays
|
|
41
|
+
* stable across the supported nx version range.
|
|
42
|
+
*/
|
|
43
|
+
type ContextProjectNode = {
|
|
44
|
+
data: {
|
|
45
|
+
root: string;
|
|
46
|
+
tags?: string[];
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* What the caller is updating, used to pick which `targetDefaults` entries the
|
|
51
|
+
* callback runs against. At least one of `executor` / `projects` is required.
|
|
52
|
+
*/
|
|
53
|
+
export interface UpdateTargetDefaultContext {
|
|
54
|
+
/**
|
|
55
|
+
* Visit only entries that resolve to this executor — matched against the map
|
|
56
|
+
* key (executor-keyed form), an entry's `executor` field, or its
|
|
57
|
+
* `filter.executor`. When omitted, entries are not filtered by executor.
|
|
58
|
+
*/
|
|
59
|
+
executor?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Project nodes the update is scoped to, keyed by project name. When set, a
|
|
62
|
+
* project-filtered entry is visited only if its `filter.projects` covers
|
|
63
|
+
* *every* one of these projects; unfiltered entries always match. When
|
|
64
|
+
* omitted, project filters are not a constraint and every (executor-matching)
|
|
65
|
+
* entry is visited — the workspace-wide case.
|
|
66
|
+
*/
|
|
67
|
+
projects?: Record<string, ContextProjectNode>;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Describes the entry a callback invocation is operating on, so the callback
|
|
71
|
+
* can tell the executor-keyed form from the target-keyed form and read the
|
|
72
|
+
* entry's filter.
|
|
73
|
+
*/
|
|
74
|
+
export interface UpdateTargetDefaultEntryInfo {
|
|
75
|
+
/** The `targetDefaults` map key the entry lives under. */
|
|
76
|
+
key: string;
|
|
77
|
+
/** Logical target name — `undefined` for executor-keyed entries. */
|
|
78
|
+
target?: string;
|
|
79
|
+
/** Executor the entry resolves to (from the key, `executor` field, or filter). */
|
|
80
|
+
executor?: string;
|
|
81
|
+
/** The entry's filter, present only for filtered array entries. */
|
|
82
|
+
filter?: TargetDefaultFilter;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Called once per matching entry. Receives the entry's flat config (everything
|
|
86
|
+
* but `filter`) and may mutate it in place. Return a new config to replace the
|
|
87
|
+
* entry's payload, return `null` to drop the entry, or return nothing to keep
|
|
88
|
+
* the (possibly mutated) config.
|
|
89
|
+
*/
|
|
90
|
+
export type UpdateTargetDefaultCallback = (config: Partial<TargetConfiguration>, info: UpdateTargetDefaultEntryInfo) => Partial<TargetConfiguration> | null | void;
|
|
91
|
+
/**
|
|
92
|
+
* Walk the `targetDefaults` entries matching `context` and run `callback`
|
|
93
|
+
* against each, mutating `nxJson` in place and returning it. This is the
|
|
94
|
+
* read-modify-write counterpart to {@link upsertTargetDefault}, meant for
|
|
95
|
+
* generators and migrations that need to edit *existing* defaults across both
|
|
96
|
+
* value forms (plain object and filtered array) without re-implementing the
|
|
97
|
+
* normalize → edit → collapse dance by hand.
|
|
98
|
+
*
|
|
99
|
+
* Matching, per the `context`:
|
|
100
|
+
* - `executor` selects entries that reference it as the map key, an `executor`
|
|
101
|
+
* config field, or `filter.executor`.
|
|
102
|
+
* - `projects` narrows to entries whose `filter.projects` covers the scoped
|
|
103
|
+
* projects (unfiltered entries always match). With no `projects` context,
|
|
104
|
+
* project filters are ignored and every executor-matching entry is visited.
|
|
105
|
+
*
|
|
106
|
+
* Removal and collapsing follow the storage-form rules:
|
|
107
|
+
* - Array entry whose callback returns `null` → dropped from the array.
|
|
108
|
+
* - An array that collapses to a single unfiltered entry → stored as the plain
|
|
109
|
+
* object form; an emptied array → the whole key is deleted.
|
|
110
|
+
* - Object-form value whose callback returns `null` → the whole key is deleted.
|
|
111
|
+
* - An emptied `targetDefaults` map → removed from `nxJson` entirely.
|
|
112
|
+
*
|
|
113
|
+
* Throws when neither `executor` nor `projects` is provided — an empty context
|
|
114
|
+
* would match everything and is almost always a bug.
|
|
115
|
+
*/
|
|
116
|
+
export declare function updateTargetDefault(nxJson: NxJsonConfiguration, context: UpdateTargetDefaultContext, callback: UpdateTargetDefaultCallback): NxJsonConfiguration;
|
|
117
|
+
export declare function readTargetDefaultsForTarget(targetName: string, targetDefaults: TargetDefaults | undefined, executor?: string, opts?: Parameters<typeof readTargetDefaultsForTargetFromNx>[3]): Partial<TargetConfiguration> | null;
|
|
2
118
|
export declare function addBuildTargetDefaults(tree: Tree, executorName: string, buildTargetName?: string, extraInputs?: TargetConfiguration['inputs']): void;
|
|
3
119
|
export declare function addE2eCiTargetDefaults(tree: Tree, e2ePlugin: string, buildTarget: string, pathToE2EConfigFile: string): Promise<void>;
|
|
120
|
+
export {};
|
|
@@ -1,14 +1,362 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.upsertTargetDefault = upsertTargetDefault;
|
|
4
|
+
exports.findTargetDefault = findTargetDefault;
|
|
5
|
+
exports.updateTargetDefault = updateTargetDefault;
|
|
6
|
+
exports.readTargetDefaultsForTarget = readTargetDefaultsForTarget;
|
|
3
7
|
exports.addBuildTargetDefaults = addBuildTargetDefaults;
|
|
4
8
|
exports.addE2eCiTargetDefaults = addE2eCiTargetDefaults;
|
|
5
9
|
const devkit_exports_1 = require("nx/src/devkit-exports");
|
|
6
10
|
const devkit_internals_1 = require("nx/src/devkit-internals");
|
|
7
11
|
const minimatch_1 = require("minimatch");
|
|
12
|
+
/**
|
|
13
|
+
* Upsert a `targetDefaults` entry on the provided `nxJson`. Mutates
|
|
14
|
+
* `nxJson` in place and returns it so the caller can chain or batch
|
|
15
|
+
* other edits before persisting via `updateNxJson` exactly once.
|
|
16
|
+
*
|
|
17
|
+
* The input is the logical {@link TargetDefaultEntry} shape
|
|
18
|
+
* (`{ target?, executor?, projects?, plugin?, ...config }`). The `projects` /
|
|
19
|
+
* `plugin` filter is *context* describing what the caller is configuring, used
|
|
20
|
+
* to pick which existing entry to merge into — it is never authored as a new
|
|
21
|
+
* filtered entry. For the key (`target` ?? `executor`):
|
|
22
|
+
*
|
|
23
|
+
* - No value yet → create the generic (plain object) value.
|
|
24
|
+
* - Only a generic value → merge into it.
|
|
25
|
+
* - A filtered entry matching the context filter exists → merge into that one
|
|
26
|
+
* (so a caller updating a project the user already scoped a default for edits
|
|
27
|
+
* that scoped entry rather than clobbering the workspace baseline).
|
|
28
|
+
* - Otherwise → merge into the generic, creating one if needed.
|
|
29
|
+
*
|
|
30
|
+
* A lone unfiltered entry is always stored as a plain object, never a
|
|
31
|
+
* single-element array. The entry must set at least one of `target` /
|
|
32
|
+
* `executor`.
|
|
33
|
+
*/
|
|
34
|
+
function upsertTargetDefault(tree, nxJson, options) {
|
|
35
|
+
const { target, executor, projects, plugin, ...config } = options;
|
|
36
|
+
const key = target ?? executor;
|
|
37
|
+
if (key === undefined) {
|
|
38
|
+
throw new Error('upsertTargetDefault requires at least one of `target` or `executor` to be set.');
|
|
39
|
+
}
|
|
40
|
+
// `executor` is the map key itself when no `target` is given; when a `target`
|
|
41
|
+
// is present it is a configuration field (a default executor) written onto
|
|
42
|
+
// the value.
|
|
43
|
+
const valueConfig = target !== undefined && executor !== undefined
|
|
44
|
+
? { executor, ...config }
|
|
45
|
+
: config;
|
|
46
|
+
const targetDefaults = (nxJson.targetDefaults ??= {});
|
|
47
|
+
targetDefaults[key] = collapse(upsertValue(targetDefaults[key], valueConfig, {
|
|
48
|
+
tree,
|
|
49
|
+
projects,
|
|
50
|
+
plugin,
|
|
51
|
+
executor,
|
|
52
|
+
}));
|
|
53
|
+
return nxJson;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Merge `config` into the right entry of a key's value, per the algorithm
|
|
57
|
+
* documented on {@link upsertTargetDefault}. Returns the new value (always an
|
|
58
|
+
* array here; {@link collapse} downgrades a lone unfiltered one to an object).
|
|
59
|
+
*/
|
|
60
|
+
function upsertValue(existing, config, context) {
|
|
61
|
+
// No value yet → create the generic.
|
|
62
|
+
if (existing === undefined) {
|
|
63
|
+
return { ...config };
|
|
64
|
+
}
|
|
65
|
+
const entries = Array.isArray(existing)
|
|
66
|
+
? [...existing]
|
|
67
|
+
: [existing];
|
|
68
|
+
// A filtered entry that covers what's being configured → merge into it,
|
|
69
|
+
// preserving its filter.
|
|
70
|
+
const matchIdx = entries.findIndex((e) => e.filter !== undefined && filterCoversContext(e.filter, context));
|
|
71
|
+
if (matchIdx >= 0) {
|
|
72
|
+
entries[matchIdx] = { ...entries[matchIdx], ...config };
|
|
73
|
+
return entries;
|
|
74
|
+
}
|
|
75
|
+
// Otherwise merge into the generic (catch-all) entry, creating one if absent.
|
|
76
|
+
const genericIdx = entries.findIndex((e) => e.filter === undefined);
|
|
77
|
+
if (genericIdx >= 0) {
|
|
78
|
+
const { filter: _filter, ...rest } = entries[genericIdx];
|
|
79
|
+
entries[genericIdx] = { ...rest, ...config };
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
entries.push({ ...config });
|
|
83
|
+
}
|
|
84
|
+
return entries;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Whether an entry's `filter` applies to what the caller is configuring:
|
|
88
|
+
* `plugin` / `executor` must match exactly, and `projects` is evaluated as
|
|
89
|
+
* *coverage* — every configured project must match the filter's patterns
|
|
90
|
+
* (names, globs, `tag:`, directories, negation) via `findMatchingProjects`,
|
|
91
|
+
* resolved against each project's tags/root in the tree. A `projects` filter
|
|
92
|
+
* can't be confirmed when no project context is supplied (or the project
|
|
93
|
+
* isn't in the tree), so it falls through to the generic.
|
|
94
|
+
*/
|
|
95
|
+
function filterCoversContext(filter, context) {
|
|
96
|
+
if (filter.plugin !== undefined && filter.plugin !== context.plugin) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
if (filter.executor !== undefined && filter.executor !== context.executor) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
if (filter.projects !== undefined) {
|
|
103
|
+
const configured = context.projects ?? [];
|
|
104
|
+
const nodes = contextProjectNodes(context.tree, configured);
|
|
105
|
+
if (!projectsFilterCovers(filter.projects, nodes, configured)) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Whether a `filter.projects` pattern set covers *every* required project name:
|
|
113
|
+
* each must be returned by `findMatchingProjects` against `nodes`. An empty
|
|
114
|
+
* `requiredNames` can never be "covered", so the entry falls through to the
|
|
115
|
+
* generic baseline. Shared by the upsert and update paths so the coverage
|
|
116
|
+
* semantics live in one place.
|
|
117
|
+
*/
|
|
118
|
+
function projectsFilterCovers(patterns, nodes, requiredNames) {
|
|
119
|
+
if (requiredNames.length === 0) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
const matched = (0, devkit_internals_1.findMatchingProjects)([...patterns], nodes);
|
|
123
|
+
return requiredNames.every((name) => matched.includes(name));
|
|
124
|
+
}
|
|
125
|
+
function contextProjectNodes(tree, projectNames) {
|
|
126
|
+
const projects = (0, devkit_exports_1.getProjects)(tree);
|
|
127
|
+
const nodes = {};
|
|
128
|
+
for (const name of projectNames) {
|
|
129
|
+
const config = projects.get(name);
|
|
130
|
+
if (config) {
|
|
131
|
+
nodes[name] = { data: { root: config.root, tags: config.tags } };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return nodes;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Collapse a key's value back to the plain object form when it has degenerated
|
|
138
|
+
* to a single entry with no filter — at that point the array wrapper carries no
|
|
139
|
+
* information the object form doesn't, and the object form is the tidier,
|
|
140
|
+
* record-shape-compatible representation.
|
|
141
|
+
*/
|
|
142
|
+
function collapse(value) {
|
|
143
|
+
if (Array.isArray(value) &&
|
|
144
|
+
value.length === 1 &&
|
|
145
|
+
value[0].filter === undefined) {
|
|
146
|
+
// Drop the (already-undefined) `filter` key so the result matches the
|
|
147
|
+
// object value form, which forbids `filter`.
|
|
148
|
+
const { filter: _filter, ...config } = value[0];
|
|
149
|
+
return config;
|
|
150
|
+
}
|
|
151
|
+
return value;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Find a `targetDefaults` entry by its logical locator tuple
|
|
155
|
+
* `(target, executor, projects, plugin)`. Locator keys default to
|
|
156
|
+
* `undefined`, matching only entries that also leave them unset — same
|
|
157
|
+
* semantics as `upsertTargetDefault`.
|
|
158
|
+
*
|
|
159
|
+
* Throws when called with an empty locator (no `target`, `executor`,
|
|
160
|
+
* `projects`, or `plugin`). An empty locator is almost always a bug — the
|
|
161
|
+
* caller intended to find a specific entry but forgot to populate the lookup.
|
|
162
|
+
*/
|
|
163
|
+
function findTargetDefault(targetDefaults, locator) {
|
|
164
|
+
if (locator.target === undefined &&
|
|
165
|
+
locator.executor === undefined &&
|
|
166
|
+
locator.projects === undefined &&
|
|
167
|
+
locator.plugin === undefined) {
|
|
168
|
+
throw new Error('findTargetDefault requires at least one of `target`, `executor`, `projects`, or `plugin` on the locator.');
|
|
169
|
+
}
|
|
170
|
+
for (const entry of logicalTargetDefaultEntries(targetDefaults)) {
|
|
171
|
+
if (entry.target === locator.target &&
|
|
172
|
+
entry.executor === locator.executor &&
|
|
173
|
+
projectsEqual(entry.projects, locator.projects) &&
|
|
174
|
+
entry.plugin === locator.plugin) {
|
|
175
|
+
return entry;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Expand the nested `targetDefaults` map into its logical {@link
|
|
182
|
+
* TargetDefaultEntry} view, one entry at a time. The key becomes `target` (or
|
|
183
|
+
* `executor` for executor-shaped keys) and any `filter` is un-nested into the
|
|
184
|
+
* flat `projects`/`plugin`/`executor` siblings. Lazy so callers that find a
|
|
185
|
+
* match early stop iterating. Internal to this module — the flat shape is a
|
|
186
|
+
* lookup convenience, never a `targetDefaults` value form.
|
|
187
|
+
*/
|
|
188
|
+
function* logicalTargetDefaultEntries(targetDefaults) {
|
|
189
|
+
for (const key of Object.keys(targetDefaults ?? {})) {
|
|
190
|
+
const locator = isExecutorLikeKey(key)
|
|
191
|
+
? { executor: key }
|
|
192
|
+
: { target: key };
|
|
193
|
+
const value = targetDefaults[key];
|
|
194
|
+
const entries = Array.isArray(value)
|
|
195
|
+
? value
|
|
196
|
+
: [value ?? {}];
|
|
197
|
+
for (const entry of entries) {
|
|
198
|
+
const { filter, ...config } = entry;
|
|
199
|
+
yield {
|
|
200
|
+
...locator,
|
|
201
|
+
...(filter?.projects !== undefined
|
|
202
|
+
? { projects: filter.projects }
|
|
203
|
+
: {}),
|
|
204
|
+
...(filter?.plugin !== undefined ? { plugin: filter.plugin } : {}),
|
|
205
|
+
...(filter?.executor !== undefined
|
|
206
|
+
? { executor: filter.executor }
|
|
207
|
+
: {}),
|
|
208
|
+
...config,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Mirrors `isGlobPattern` from `nx/src/utils/globs.ts`, which isn't on the
|
|
214
|
+
// devkit-exports surface guaranteed across the supported nx version range.
|
|
215
|
+
//
|
|
216
|
+
// Known limitation: a `:` is also legal in a target name, so a target literally
|
|
217
|
+
// named e.g. `e2e:ci` is classified as an executor key here. This matches how
|
|
218
|
+
// the rest of the target-defaults stack disambiguates keys (executor-shaped
|
|
219
|
+
// keys win), and such target names are vanishingly rare — accepted rather than
|
|
220
|
+
// worked around.
|
|
221
|
+
const GLOB_CHARACTERS = new Set(['*', '|', '{', '}', '(', ')', '[']);
|
|
222
|
+
function isExecutorLikeKey(key) {
|
|
223
|
+
if (!key.includes(':'))
|
|
224
|
+
return false;
|
|
225
|
+
for (const c of key)
|
|
226
|
+
if (GLOB_CHARACTERS.has(c))
|
|
227
|
+
return false;
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Walk the `targetDefaults` entries matching `context` and run `callback`
|
|
232
|
+
* against each, mutating `nxJson` in place and returning it. This is the
|
|
233
|
+
* read-modify-write counterpart to {@link upsertTargetDefault}, meant for
|
|
234
|
+
* generators and migrations that need to edit *existing* defaults across both
|
|
235
|
+
* value forms (plain object and filtered array) without re-implementing the
|
|
236
|
+
* normalize → edit → collapse dance by hand.
|
|
237
|
+
*
|
|
238
|
+
* Matching, per the `context`:
|
|
239
|
+
* - `executor` selects entries that reference it as the map key, an `executor`
|
|
240
|
+
* config field, or `filter.executor`.
|
|
241
|
+
* - `projects` narrows to entries whose `filter.projects` covers the scoped
|
|
242
|
+
* projects (unfiltered entries always match). With no `projects` context,
|
|
243
|
+
* project filters are ignored and every executor-matching entry is visited.
|
|
244
|
+
*
|
|
245
|
+
* Removal and collapsing follow the storage-form rules:
|
|
246
|
+
* - Array entry whose callback returns `null` → dropped from the array.
|
|
247
|
+
* - An array that collapses to a single unfiltered entry → stored as the plain
|
|
248
|
+
* object form; an emptied array → the whole key is deleted.
|
|
249
|
+
* - Object-form value whose callback returns `null` → the whole key is deleted.
|
|
250
|
+
* - An emptied `targetDefaults` map → removed from `nxJson` entirely.
|
|
251
|
+
*
|
|
252
|
+
* Throws when neither `executor` nor `projects` is provided — an empty context
|
|
253
|
+
* would match everything and is almost always a bug.
|
|
254
|
+
*/
|
|
255
|
+
function updateTargetDefault(nxJson, context, callback) {
|
|
256
|
+
const { executor, projects } = context;
|
|
257
|
+
if (executor === undefined && projects === undefined) {
|
|
258
|
+
throw new Error('updateTargetDefault requires at least one of `executor` or `projects` on the context to locate entries to update.');
|
|
259
|
+
}
|
|
260
|
+
const targetDefaults = nxJson.targetDefaults;
|
|
261
|
+
if (!targetDefaults) {
|
|
262
|
+
return nxJson;
|
|
263
|
+
}
|
|
264
|
+
const projectNames = projects ? Object.keys(projects) : [];
|
|
265
|
+
for (const key of Object.keys(targetDefaults)) {
|
|
266
|
+
const value = targetDefaults[key];
|
|
267
|
+
const entries = Array.isArray(value)
|
|
268
|
+
? value
|
|
269
|
+
: [value];
|
|
270
|
+
const kept = [];
|
|
271
|
+
for (const entry of entries) {
|
|
272
|
+
if (!entryMatchesContext(key, entry, executor, projects, projectNames)) {
|
|
273
|
+
kept.push(entry);
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
const { filter, ...config } = entry;
|
|
277
|
+
const result = callback(config, {
|
|
278
|
+
key,
|
|
279
|
+
target: isExecutorLikeKey(key) ? undefined : key,
|
|
280
|
+
executor: isExecutorLikeKey(key)
|
|
281
|
+
? key
|
|
282
|
+
: (config.executor ?? filter?.executor),
|
|
283
|
+
filter,
|
|
284
|
+
});
|
|
285
|
+
// `null` drops the entry; anything else keeps the (mutated or replaced)
|
|
286
|
+
// config, re-attaching the filter so a filtered entry stays filtered.
|
|
287
|
+
if (result === null) {
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
const nextConfig = result ?? config;
|
|
291
|
+
kept.push(filter !== undefined ? { filter, ...nextConfig } : nextConfig);
|
|
292
|
+
}
|
|
293
|
+
if (kept.length === 0) {
|
|
294
|
+
delete targetDefaults[key];
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
targetDefaults[key] = collapse(kept);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (Object.keys(targetDefaults).length === 0) {
|
|
301
|
+
delete nxJson.targetDefaults;
|
|
302
|
+
}
|
|
303
|
+
return nxJson;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Whether `callback` should run against this entry: it must reference the
|
|
307
|
+
* context's `executor` (when given), and — when the context is scoped to
|
|
308
|
+
* `projects` — any `filter.projects` it carries must cover every scoped
|
|
309
|
+
* project. Unfiltered entries always pass the project check.
|
|
310
|
+
*/
|
|
311
|
+
function entryMatchesContext(key, entry, executor, projects, projectNames) {
|
|
312
|
+
if (executor !== undefined) {
|
|
313
|
+
const referencesExecutor = key === executor ||
|
|
314
|
+
entry.executor === executor ||
|
|
315
|
+
entry.filter?.executor === executor;
|
|
316
|
+
if (!referencesExecutor) {
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (projects !== undefined && entry.filter?.projects !== undefined) {
|
|
321
|
+
const patterns = Array.isArray(entry.filter.projects)
|
|
322
|
+
? entry.filter.projects
|
|
323
|
+
: [entry.filter.projects];
|
|
324
|
+
if (!projectsFilterCovers(patterns, projects, projectNames)) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
function readTargetDefaultsForTarget(targetName, targetDefaults, executor, opts) {
|
|
331
|
+
// `targetDefaults` is the nested map; the nx reader resolves either the
|
|
332
|
+
// object or array value form for `targetName` natively.
|
|
333
|
+
return (0, devkit_internals_1.readTargetDefaultsForTarget)(targetName, targetDefaults, executor, opts);
|
|
334
|
+
}
|
|
335
|
+
// Order-insensitive equality so re-upserts with reordered patterns merge into
|
|
336
|
+
// the same entry rather than appending a duplicate. Comparing sorted copies
|
|
337
|
+
// keeps duplicates significant (`['a','a']` ≠ `['a']`) without special-casing.
|
|
338
|
+
function projectsEqual(a, b) {
|
|
339
|
+
if (a === b)
|
|
340
|
+
return true;
|
|
341
|
+
if (!a || !b)
|
|
342
|
+
return false;
|
|
343
|
+
if (a.length !== b.length)
|
|
344
|
+
return false;
|
|
345
|
+
const sortedA = [...a].sort();
|
|
346
|
+
const sortedB = [...b].sort();
|
|
347
|
+
return sortedA.every((value, i) => value === sortedB[i]);
|
|
348
|
+
}
|
|
8
349
|
function addBuildTargetDefaults(tree, executorName, buildTargetName = 'build', extraInputs = []) {
|
|
9
|
-
const nxJson = (0, devkit_exports_1.readNxJson)(tree);
|
|
10
|
-
|
|
11
|
-
|
|
350
|
+
const nxJson = (0, devkit_exports_1.readNxJson)(tree) ?? {};
|
|
351
|
+
// Only skip when an *unfiltered* entry already covers this executor.
|
|
352
|
+
// A filtered entry (`projects:` or `plugin:`) only applies to a subset
|
|
353
|
+
// of targets, so it doesn't satisfy the workspace-wide default this
|
|
354
|
+
// helper writes — we still need to add the unfiltered baseline.
|
|
355
|
+
if (findTargetDefault(nxJson.targetDefaults, { executor: executorName })) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
upsertTargetDefault(tree, nxJson, {
|
|
359
|
+
executor: executorName,
|
|
12
360
|
cache: true,
|
|
13
361
|
dependsOn: [`^${buildTargetName}`],
|
|
14
362
|
inputs: [
|
|
@@ -17,12 +365,12 @@ function addBuildTargetDefaults(tree, executorName, buildTargetName = 'build', e
|
|
|
17
365
|
: ['default', '^default']),
|
|
18
366
|
...extraInputs,
|
|
19
367
|
],
|
|
20
|
-
};
|
|
368
|
+
});
|
|
21
369
|
(0, devkit_exports_1.updateNxJson)(tree, nxJson);
|
|
22
370
|
}
|
|
23
371
|
async function addE2eCiTargetDefaults(tree, e2ePlugin, buildTarget, pathToE2EConfigFile) {
|
|
24
372
|
const nxJson = (0, devkit_exports_1.readNxJson)(tree);
|
|
25
|
-
if (!nxJson
|
|
373
|
+
if (!nxJson?.plugins) {
|
|
26
374
|
return;
|
|
27
375
|
}
|
|
28
376
|
const e2ePluginRegistrations = nxJson.plugins.filter((p) => typeof p === 'string' ? p === e2ePlugin : p.plugin === e2ePlugin);
|
|
@@ -58,18 +406,13 @@ async function addE2eCiTargetDefaults(tree, e2ePlugin, buildTarget, pathToE2ECon
|
|
|
58
406
|
? 'e2e-ci'
|
|
59
407
|
: (foundPluginForApplication.options?.ciTargetName ?? 'e2e-ci');
|
|
60
408
|
const ciTargetNameGlob = `${ciTargetName}--**/**`;
|
|
61
|
-
nxJson.targetDefaults
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
e2eCiTargetDefaults.dependsOn ??= [];
|
|
70
|
-
if (!e2eCiTargetDefaults.dependsOn.includes(buildTarget)) {
|
|
71
|
-
e2eCiTargetDefaults.dependsOn.push(buildTarget);
|
|
72
|
-
}
|
|
409
|
+
const existing = findTargetDefault(nxJson.targetDefaults, {
|
|
410
|
+
target: ciTargetNameGlob,
|
|
411
|
+
});
|
|
412
|
+
const dependsOn = [...(existing?.dependsOn ?? [])];
|
|
413
|
+
if (!dependsOn.includes(buildTarget)) {
|
|
414
|
+
dependsOn.push(buildTarget);
|
|
73
415
|
}
|
|
416
|
+
upsertTargetDefault(tree, nxJson, { target: ciTargetNameGlob, dependsOn });
|
|
74
417
|
(0, devkit_exports_1.updateNxJson)(tree, nxJson);
|
|
75
418
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Tree } from 'nx/src/devkit-exports';
|
|
2
|
+
import type { CatalogDefinitions } from './types';
|
|
3
|
+
export declare function readCatalogConfigFromFs(filename: string, fullPath: string): CatalogDefinitions | null;
|
|
4
|
+
export declare function readCatalogConfigFromTree(filename: string, tree: Tree): CatalogDefinitions | null;
|
|
5
|
+
export declare function updateCatalogVersionsInFile(filename: string, treeOrRoot: Tree | string, updates: Array<{
|
|
6
|
+
packageName: string;
|
|
7
|
+
version: string;
|
|
8
|
+
catalogName?: string;
|
|
9
|
+
}>): void;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readCatalogConfigFromFs = readCatalogConfigFromFs;
|
|
4
|
+
exports.readCatalogConfigFromTree = readCatalogConfigFromTree;
|
|
5
|
+
exports.updateCatalogVersionsInFile = updateCatalogVersionsInFile;
|
|
6
|
+
// Keep in sync with packages/nx/src/utils/catalog/manager-utils.ts; the body below
|
|
7
|
+
// the imports is duplicated because @nx/devkit supports a range of nx majors and
|
|
8
|
+
// this logic isn't part of the nx surface it can import across that range.
|
|
9
|
+
const js_yaml_1 = require("@zkochan/js-yaml");
|
|
10
|
+
const node_fs_1 = require("node:fs");
|
|
11
|
+
const node_path_1 = require("node:path");
|
|
12
|
+
const devkit_exports_1 = require("nx/src/devkit-exports");
|
|
13
|
+
const devkit_internals_1 = require("nx/src/devkit-internals");
|
|
14
|
+
const yaml_1 = require("yaml");
|
|
15
|
+
// Walks `path` resolving aliases at every step so structural checks can
|
|
16
|
+
// see through `key: *anchor` references. Neither `doc.getIn` nor
|
|
17
|
+
// `doc.hasIn` traverse aliases on their own. Returns the resolved node, or
|
|
18
|
+
// `undefined` if any ancestor isn't a map.
|
|
19
|
+
function resolveAt(doc, path) {
|
|
20
|
+
let node = doc.contents;
|
|
21
|
+
for (let i = 0; i < path.length; i++) {
|
|
22
|
+
if ((0, yaml_1.isAlias)(node))
|
|
23
|
+
node = node.resolve(doc);
|
|
24
|
+
if (!(0, yaml_1.isMap)(node))
|
|
25
|
+
return undefined;
|
|
26
|
+
node = node.get(path[i], true);
|
|
27
|
+
}
|
|
28
|
+
if ((0, yaml_1.isAlias)(node))
|
|
29
|
+
node = node.resolve(doc);
|
|
30
|
+
return node;
|
|
31
|
+
}
|
|
32
|
+
function isMapAt(doc, path) {
|
|
33
|
+
return (0, yaml_1.isMap)(resolveAt(doc, path));
|
|
34
|
+
}
|
|
35
|
+
function existsAt(doc, path) {
|
|
36
|
+
return resolveAt(doc, path) !== undefined;
|
|
37
|
+
}
|
|
38
|
+
// `doc.getIn` does not traverse aliases. For aliased paths it returns
|
|
39
|
+
// undefined even when the resolved node has a value. Use the alias-aware
|
|
40
|
+
// walk and unwrap scalar nodes to their JS value.
|
|
41
|
+
function getValueAt(doc, path) {
|
|
42
|
+
const node = resolveAt(doc, path);
|
|
43
|
+
return (0, yaml_1.isScalar)(node) ? node.value : node;
|
|
44
|
+
}
|
|
45
|
+
// Walks `targetPath` resolving aliases at every step and mutates the
|
|
46
|
+
// resolved map directly so anchors are preserved. When a step is missing
|
|
47
|
+
// or holds a null placeholder (`key:` with no value), creates a fresh map
|
|
48
|
+
// inside the current parent and carries over the placeholder's comments
|
|
49
|
+
// and anchor so they aren't dropped. A dropped anchor would leave any
|
|
50
|
+
// alias referencing it unresolved and make `String(doc)` throw. Falls back
|
|
51
|
+
// to `doc.setIn` when the current parent isn't a map: an empty-document
|
|
52
|
+
// root gets bootstrapped, while a non-map value where a catalog map was
|
|
53
|
+
// expected makes `setIn` throw, surfacing the malformed config.
|
|
54
|
+
function setThroughAliases(doc, targetPath, value) {
|
|
55
|
+
let parent = doc.contents;
|
|
56
|
+
if ((0, yaml_1.isAlias)(parent))
|
|
57
|
+
parent = parent.resolve(doc);
|
|
58
|
+
for (let i = 0; i < targetPath.length - 1; i++) {
|
|
59
|
+
if (!(0, yaml_1.isMap)(parent)) {
|
|
60
|
+
doc.setIn(targetPath, value);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
let next = parent.get(targetPath[i], true);
|
|
64
|
+
const nextWasAlias = (0, yaml_1.isAlias)(next);
|
|
65
|
+
if ((0, yaml_1.isAlias)(next))
|
|
66
|
+
next = next.resolve(doc);
|
|
67
|
+
const placeholder = (0, yaml_1.isScalar)(next) && next.value === null ? next : undefined;
|
|
68
|
+
if (next === undefined || placeholder) {
|
|
69
|
+
let cur = parent;
|
|
70
|
+
for (let j = i; j < targetPath.length - 1; j++) {
|
|
71
|
+
const fresh = new yaml_1.YAMLMap();
|
|
72
|
+
if (j === i && placeholder) {
|
|
73
|
+
if (placeholder.comment)
|
|
74
|
+
fresh.comment = placeholder.comment;
|
|
75
|
+
if (placeholder.commentBefore)
|
|
76
|
+
fresh.commentBefore = placeholder.commentBefore;
|
|
77
|
+
// Copy the anchor only for a directly-held placeholder. When it
|
|
78
|
+
// was reached through an alias, the anchor still lives on the
|
|
79
|
+
// definition node, so re-emitting it here would duplicate it.
|
|
80
|
+
if (!nextWasAlias && placeholder.anchor)
|
|
81
|
+
fresh.anchor = placeholder.anchor;
|
|
82
|
+
}
|
|
83
|
+
cur.set(targetPath[j], fresh);
|
|
84
|
+
cur = fresh;
|
|
85
|
+
}
|
|
86
|
+
cur.set(targetPath[targetPath.length - 1], value);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
parent = next;
|
|
90
|
+
}
|
|
91
|
+
if (!(0, yaml_1.isMap)(parent)) {
|
|
92
|
+
doc.setIn(targetPath, value);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
parent.set(targetPath[targetPath.length - 1], value);
|
|
96
|
+
}
|
|
97
|
+
function readCatalogConfigFromFs(filename, fullPath) {
|
|
98
|
+
try {
|
|
99
|
+
return (0, devkit_internals_1.readYamlFile)(fullPath);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
devkit_exports_1.output.warn({
|
|
103
|
+
title: `Unable to parse ${filename}`,
|
|
104
|
+
bodyLines: [error.toString()],
|
|
105
|
+
});
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function readCatalogConfigFromTree(filename, tree) {
|
|
110
|
+
const content = tree.read(filename, 'utf-8');
|
|
111
|
+
try {
|
|
112
|
+
return (0, js_yaml_1.load)(content, { filename });
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
devkit_exports_1.output.warn({
|
|
116
|
+
title: `Unable to parse ${filename}`,
|
|
117
|
+
bodyLines: [error.toString()],
|
|
118
|
+
});
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function updateCatalogVersionsInFile(filename, treeOrRoot, updates) {
|
|
123
|
+
let checkExists;
|
|
124
|
+
let readYaml;
|
|
125
|
+
let writeYaml;
|
|
126
|
+
if (typeof treeOrRoot === 'string') {
|
|
127
|
+
const configPath = (0, node_path_1.join)(treeOrRoot, filename);
|
|
128
|
+
checkExists = () => (0, node_fs_1.existsSync)(configPath);
|
|
129
|
+
readYaml = () => (0, node_fs_1.readFileSync)(configPath, 'utf-8');
|
|
130
|
+
writeYaml = (content) => (0, node_fs_1.writeFileSync)(configPath, content, 'utf-8');
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
checkExists = () => treeOrRoot.exists(filename);
|
|
134
|
+
readYaml = () => treeOrRoot.read(filename, 'utf-8');
|
|
135
|
+
writeYaml = (content) => treeOrRoot.write(filename, content);
|
|
136
|
+
}
|
|
137
|
+
if (!checkExists()) {
|
|
138
|
+
devkit_exports_1.output.warn({
|
|
139
|
+
title: `No ${filename} found`,
|
|
140
|
+
bodyLines: [
|
|
141
|
+
`Cannot update catalog versions without a ${filename} file.`,
|
|
142
|
+
`Create a ${filename} file to use catalogs.`,
|
|
143
|
+
],
|
|
144
|
+
});
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
// parseDocument keeps comments and anchors so a catalog bump doesn't
|
|
149
|
+
// rewrite the user's config file.
|
|
150
|
+
const lineCounter = new yaml_1.LineCounter();
|
|
151
|
+
const doc = (0, yaml_1.parseDocument)(readYaml(), { lineCounter });
|
|
152
|
+
// parseDocument collects syntax errors instead of throwing; surface them
|
|
153
|
+
// now, with their line/column detail, rather than failing later in
|
|
154
|
+
// `String(doc)` with a generic message or skipping a no-op write on a
|
|
155
|
+
// malformed file (the previous js-yaml `load()` threw here).
|
|
156
|
+
if (doc.errors.length > 0) {
|
|
157
|
+
throw new Error(doc.errors.map((e) => e.message).join('\n'));
|
|
158
|
+
}
|
|
159
|
+
// A dangling alias (`*ref` with no matching `&ref`) is not a syntax error,
|
|
160
|
+
// so parseDocument leaves it out of doc.errors. Surface it here with the
|
|
161
|
+
// same line/column detail the old js-yaml `load()` reported; otherwise a
|
|
162
|
+
// broken reference is silently overwritten or written back untouched.
|
|
163
|
+
const unresolvedAliases = [];
|
|
164
|
+
(0, yaml_1.visit)(doc, {
|
|
165
|
+
Alias(_key, node) {
|
|
166
|
+
if (node.resolve(doc) !== undefined)
|
|
167
|
+
return;
|
|
168
|
+
const pos = node.range ? lineCounter.linePos(node.range[0]) : undefined;
|
|
169
|
+
unresolvedAliases.push(pos
|
|
170
|
+
? `Unresolved alias "${node.source}" at line ${pos.line}, column ${pos.col}`
|
|
171
|
+
: `Unresolved alias "${node.source}"`);
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
if (unresolvedAliases.length > 0) {
|
|
175
|
+
throw new Error(unresolvedAliases.join('\n'));
|
|
176
|
+
}
|
|
177
|
+
let hasChanges = false;
|
|
178
|
+
for (const update of updates) {
|
|
179
|
+
const { packageName, version, catalogName } = update;
|
|
180
|
+
const normalizedCatalogName = catalogName === 'default' ? undefined : catalogName;
|
|
181
|
+
let targetPath;
|
|
182
|
+
if (!normalizedCatalogName) {
|
|
183
|
+
// An empty `catalog:` placeholder must not claim the default route
|
|
184
|
+
// when `catalogs.default` is populated; that would create a
|
|
185
|
+
// duplicate-default config rejected by pnpm.
|
|
186
|
+
if (isMapAt(doc, ['catalog'])) {
|
|
187
|
+
targetPath = ['catalog', packageName];
|
|
188
|
+
}
|
|
189
|
+
else if (existsAt(doc, ['catalogs', 'default'])) {
|
|
190
|
+
targetPath = ['catalogs', 'default', packageName];
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
targetPath = ['catalog', packageName];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
targetPath = ['catalogs', normalizedCatalogName, packageName];
|
|
198
|
+
}
|
|
199
|
+
if (getValueAt(doc, targetPath) !== version) {
|
|
200
|
+
setThroughAliases(doc, targetPath, version);
|
|
201
|
+
hasChanges = true;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (hasChanges) {
|
|
205
|
+
writeYaml(String(doc));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
devkit_exports_1.output.error({
|
|
210
|
+
title: 'Failed to update catalog versions',
|
|
211
|
+
bodyLines: [error instanceof Error ? error.message : String(error)],
|
|
212
|
+
});
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PnpmCatalogManager = void 0;
|
|
4
|
-
const js_yaml_1 = require("@zkochan/js-yaml");
|
|
5
4
|
const node_fs_1 = require("node:fs");
|
|
6
5
|
const node_path_1 = require("node:path");
|
|
7
|
-
const devkit_exports_1 = require("nx/src/devkit-exports");
|
|
8
|
-
const devkit_internals_1 = require("nx/src/devkit-internals");
|
|
9
6
|
const manager_1 = require("./manager");
|
|
7
|
+
const manager_utils_1 = require("./manager-utils");
|
|
10
8
|
const PNPM_WORKSPACE_FILENAME = 'pnpm-workspace.yaml';
|
|
11
9
|
/**
|
|
12
10
|
* PNPM-specific catalog manager implementation
|
|
@@ -40,13 +38,13 @@ class PnpmCatalogManager {
|
|
|
40
38
|
if (!(0, node_fs_1.existsSync)(configPath)) {
|
|
41
39
|
return null;
|
|
42
40
|
}
|
|
43
|
-
return
|
|
41
|
+
return (0, manager_utils_1.readCatalogConfigFromFs)(PNPM_WORKSPACE_FILENAME, configPath);
|
|
44
42
|
}
|
|
45
43
|
else {
|
|
46
44
|
if (!treeOrRoot.exists(PNPM_WORKSPACE_FILENAME)) {
|
|
47
45
|
return null;
|
|
48
46
|
}
|
|
49
|
-
return
|
|
47
|
+
return (0, manager_utils_1.readCatalogConfigFromTree)(PNPM_WORKSPACE_FILENAME, treeOrRoot);
|
|
50
48
|
}
|
|
51
49
|
}
|
|
52
50
|
resolveCatalogReference(treeOrRoot, packageName, version) {
|
|
@@ -147,103 +145,7 @@ class PnpmCatalogManager {
|
|
|
147
145
|
}
|
|
148
146
|
}
|
|
149
147
|
updateCatalogVersions(treeOrRoot, updates) {
|
|
150
|
-
|
|
151
|
-
let readYaml;
|
|
152
|
-
let writeYaml;
|
|
153
|
-
if (typeof treeOrRoot === 'string') {
|
|
154
|
-
const configPath = (0, node_path_1.join)(treeOrRoot, PNPM_WORKSPACE_FILENAME);
|
|
155
|
-
checkExists = () => (0, node_fs_1.existsSync)(configPath);
|
|
156
|
-
readYaml = () => (0, node_fs_1.readFileSync)(configPath, 'utf-8');
|
|
157
|
-
writeYaml = (content) => (0, node_fs_1.writeFileSync)(configPath, content, 'utf-8');
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
checkExists = () => treeOrRoot.exists(PNPM_WORKSPACE_FILENAME);
|
|
161
|
-
readYaml = () => treeOrRoot.read(PNPM_WORKSPACE_FILENAME, 'utf-8');
|
|
162
|
-
writeYaml = (content) => treeOrRoot.write(PNPM_WORKSPACE_FILENAME, content);
|
|
163
|
-
}
|
|
164
|
-
if (!checkExists()) {
|
|
165
|
-
devkit_exports_1.output.warn({
|
|
166
|
-
title: `No ${PNPM_WORKSPACE_FILENAME} found`,
|
|
167
|
-
bodyLines: [
|
|
168
|
-
`Cannot update catalog versions without a ${PNPM_WORKSPACE_FILENAME} file.`,
|
|
169
|
-
`Create a ${PNPM_WORKSPACE_FILENAME} file to use catalogs.`,
|
|
170
|
-
],
|
|
171
|
-
});
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
try {
|
|
175
|
-
const configContent = readYaml();
|
|
176
|
-
const configData = (0, js_yaml_1.load)(configContent) || {};
|
|
177
|
-
let hasChanges = false;
|
|
178
|
-
for (const update of updates) {
|
|
179
|
-
const { packageName, version, catalogName } = update;
|
|
180
|
-
const normalizedCatalogName = catalogName === 'default' ? undefined : catalogName;
|
|
181
|
-
let targetCatalog;
|
|
182
|
-
if (!normalizedCatalogName) {
|
|
183
|
-
// Default catalog - update whichever exists, prefer catalog over catalogs.default
|
|
184
|
-
if (configData.catalog) {
|
|
185
|
-
targetCatalog = configData.catalog;
|
|
186
|
-
}
|
|
187
|
-
else if (configData.catalogs?.default) {
|
|
188
|
-
targetCatalog = configData.catalogs.default;
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
// Neither exists, create catalog (shorthand syntax)
|
|
192
|
-
configData.catalog ??= {};
|
|
193
|
-
targetCatalog = configData.catalog;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
// Named catalog
|
|
198
|
-
configData.catalogs ??= {};
|
|
199
|
-
configData.catalogs[normalizedCatalogName] ??= {};
|
|
200
|
-
targetCatalog = configData.catalogs[normalizedCatalogName];
|
|
201
|
-
}
|
|
202
|
-
if (targetCatalog[packageName] !== version) {
|
|
203
|
-
targetCatalog[packageName] = version;
|
|
204
|
-
hasChanges = true;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
if (hasChanges) {
|
|
208
|
-
writeYaml((0, js_yaml_1.dump)(configData, {
|
|
209
|
-
indent: 2,
|
|
210
|
-
quotingType: '"',
|
|
211
|
-
forceQuotes: true,
|
|
212
|
-
}));
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
catch (error) {
|
|
216
|
-
devkit_exports_1.output.error({
|
|
217
|
-
title: 'Failed to update catalog versions',
|
|
218
|
-
bodyLines: [error instanceof Error ? error.message : String(error)],
|
|
219
|
-
});
|
|
220
|
-
throw error;
|
|
221
|
-
}
|
|
148
|
+
(0, manager_utils_1.updateCatalogVersionsInFile)(PNPM_WORKSPACE_FILENAME, treeOrRoot, updates);
|
|
222
149
|
}
|
|
223
150
|
}
|
|
224
151
|
exports.PnpmCatalogManager = PnpmCatalogManager;
|
|
225
|
-
function readConfigFromFs(path) {
|
|
226
|
-
try {
|
|
227
|
-
return (0, devkit_internals_1.readYamlFile)(path);
|
|
228
|
-
}
|
|
229
|
-
catch (error) {
|
|
230
|
-
devkit_exports_1.output.warn({
|
|
231
|
-
title: `Unable to parse ${PNPM_WORKSPACE_FILENAME}`,
|
|
232
|
-
bodyLines: [error.toString()],
|
|
233
|
-
});
|
|
234
|
-
return null;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
function readConfigFromTree(tree, path) {
|
|
238
|
-
const content = tree.read(path, 'utf-8');
|
|
239
|
-
try {
|
|
240
|
-
return (0, js_yaml_1.load)(content, { filename: path });
|
|
241
|
-
}
|
|
242
|
-
catch (error) {
|
|
243
|
-
devkit_exports_1.output.warn({
|
|
244
|
-
title: `Unable to parse ${PNPM_WORKSPACE_FILENAME}`,
|
|
245
|
-
bodyLines: [error.toString()],
|
|
246
|
-
});
|
|
247
|
-
return null;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.YarnCatalogManager = void 0;
|
|
4
|
-
const js_yaml_1 = require("@zkochan/js-yaml");
|
|
5
4
|
const node_fs_1 = require("node:fs");
|
|
6
5
|
const node_path_1 = require("node:path");
|
|
7
|
-
const devkit_exports_1 = require("nx/src/devkit-exports");
|
|
8
|
-
const devkit_internals_1 = require("nx/src/devkit-internals");
|
|
9
6
|
const manager_1 = require("./manager");
|
|
7
|
+
const manager_utils_1 = require("./manager-utils");
|
|
10
8
|
const YARNRC_FILENAME = '.yarnrc.yml';
|
|
11
9
|
/**
|
|
12
10
|
* Yarn Berry (v4+) catalog manager implementation
|
|
@@ -40,13 +38,13 @@ class YarnCatalogManager {
|
|
|
40
38
|
if (!(0, node_fs_1.existsSync)(configPath)) {
|
|
41
39
|
return null;
|
|
42
40
|
}
|
|
43
|
-
return
|
|
41
|
+
return (0, manager_utils_1.readCatalogConfigFromFs)(YARNRC_FILENAME, configPath);
|
|
44
42
|
}
|
|
45
43
|
else {
|
|
46
44
|
if (!treeOrRoot.exists(YARNRC_FILENAME)) {
|
|
47
45
|
return null;
|
|
48
46
|
}
|
|
49
|
-
return
|
|
47
|
+
return (0, manager_utils_1.readCatalogConfigFromTree)(YARNRC_FILENAME, treeOrRoot);
|
|
50
48
|
}
|
|
51
49
|
}
|
|
52
50
|
resolveCatalogReference(treeOrRoot, packageName, version) {
|
|
@@ -147,103 +145,7 @@ class YarnCatalogManager {
|
|
|
147
145
|
}
|
|
148
146
|
}
|
|
149
147
|
updateCatalogVersions(treeOrRoot, updates) {
|
|
150
|
-
|
|
151
|
-
let readYaml;
|
|
152
|
-
let writeYaml;
|
|
153
|
-
if (typeof treeOrRoot === 'string') {
|
|
154
|
-
const configPath = (0, node_path_1.join)(treeOrRoot, YARNRC_FILENAME);
|
|
155
|
-
checkExists = () => (0, node_fs_1.existsSync)(configPath);
|
|
156
|
-
readYaml = () => (0, node_fs_1.readFileSync)(configPath, 'utf-8');
|
|
157
|
-
writeYaml = (content) => (0, node_fs_1.writeFileSync)(configPath, content, 'utf-8');
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
checkExists = () => treeOrRoot.exists(YARNRC_FILENAME);
|
|
161
|
-
readYaml = () => treeOrRoot.read(YARNRC_FILENAME, 'utf-8');
|
|
162
|
-
writeYaml = (content) => treeOrRoot.write(YARNRC_FILENAME, content);
|
|
163
|
-
}
|
|
164
|
-
if (!checkExists()) {
|
|
165
|
-
devkit_exports_1.output.warn({
|
|
166
|
-
title: `No ${YARNRC_FILENAME} found`,
|
|
167
|
-
bodyLines: [
|
|
168
|
-
`Cannot update catalog versions without a ${YARNRC_FILENAME} file.`,
|
|
169
|
-
`Create a ${YARNRC_FILENAME} file to use catalogs.`,
|
|
170
|
-
],
|
|
171
|
-
});
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
try {
|
|
175
|
-
const configContent = readYaml();
|
|
176
|
-
const configData = (0, js_yaml_1.load)(configContent) || {};
|
|
177
|
-
let hasChanges = false;
|
|
178
|
-
for (const update of updates) {
|
|
179
|
-
const { packageName, version, catalogName } = update;
|
|
180
|
-
const normalizedCatalogName = catalogName === 'default' ? undefined : catalogName;
|
|
181
|
-
let targetCatalog;
|
|
182
|
-
if (!normalizedCatalogName) {
|
|
183
|
-
// Default catalog - update whichever exists, prefer catalog over catalogs.default
|
|
184
|
-
if (configData.catalog) {
|
|
185
|
-
targetCatalog = configData.catalog;
|
|
186
|
-
}
|
|
187
|
-
else if (configData.catalogs?.default) {
|
|
188
|
-
targetCatalog = configData.catalogs.default;
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
// Neither exists, create catalog (shorthand syntax)
|
|
192
|
-
configData.catalog ??= {};
|
|
193
|
-
targetCatalog = configData.catalog;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
// Named catalog
|
|
198
|
-
configData.catalogs ??= {};
|
|
199
|
-
configData.catalogs[normalizedCatalogName] ??= {};
|
|
200
|
-
targetCatalog = configData.catalogs[normalizedCatalogName];
|
|
201
|
-
}
|
|
202
|
-
if (targetCatalog[packageName] !== version) {
|
|
203
|
-
targetCatalog[packageName] = version;
|
|
204
|
-
hasChanges = true;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
if (hasChanges) {
|
|
208
|
-
writeYaml((0, js_yaml_1.dump)(configData, {
|
|
209
|
-
indent: 2,
|
|
210
|
-
quotingType: '"',
|
|
211
|
-
forceQuotes: true,
|
|
212
|
-
}));
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
catch (error) {
|
|
216
|
-
devkit_exports_1.output.error({
|
|
217
|
-
title: 'Failed to update catalog versions',
|
|
218
|
-
bodyLines: [error instanceof Error ? error.message : String(error)],
|
|
219
|
-
});
|
|
220
|
-
throw error;
|
|
221
|
-
}
|
|
148
|
+
(0, manager_utils_1.updateCatalogVersionsInFile)(YARNRC_FILENAME, treeOrRoot, updates);
|
|
222
149
|
}
|
|
223
150
|
}
|
|
224
151
|
exports.YarnCatalogManager = YarnCatalogManager;
|
|
225
|
-
function readConfigFromFs(path) {
|
|
226
|
-
try {
|
|
227
|
-
return (0, devkit_internals_1.readYamlFile)(path);
|
|
228
|
-
}
|
|
229
|
-
catch (error) {
|
|
230
|
-
devkit_exports_1.output.warn({
|
|
231
|
-
title: `Unable to parse ${YARNRC_FILENAME}`,
|
|
232
|
-
bodyLines: [error.toString()],
|
|
233
|
-
});
|
|
234
|
-
return null;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
function readConfigFromTree(tree, path) {
|
|
238
|
-
const content = tree.read(path, 'utf-8');
|
|
239
|
-
try {
|
|
240
|
-
return (0, js_yaml_1.load)(content, { filename: path });
|
|
241
|
-
}
|
|
242
|
-
catch (error) {
|
|
243
|
-
devkit_exports_1.output.warn({
|
|
244
|
-
title: `Unable to parse ${YARNRC_FILENAME}`,
|
|
245
|
-
bodyLines: [error.toString()],
|
|
246
|
-
});
|
|
247
|
-
return null;
|
|
248
|
-
}
|
|
249
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/devkit",
|
|
3
|
-
"version": "23.1.0-
|
|
3
|
+
"version": "23.1.0-rc.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"files": [
|
|
@@ -56,11 +56,12 @@
|
|
|
56
56
|
"semver": "^7.6.3",
|
|
57
57
|
"yargs-parser": "21.1.1",
|
|
58
58
|
"minimatch": "10.2.5",
|
|
59
|
-
"enquirer": "~2.3.6"
|
|
59
|
+
"enquirer": "~2.3.6",
|
|
60
|
+
"yaml": "^2.8.3"
|
|
60
61
|
},
|
|
61
62
|
"devDependencies": {
|
|
62
63
|
"jest": "30.3.0",
|
|
63
|
-
"nx": "23.1.0-
|
|
64
|
+
"nx": "23.1.0-rc.0"
|
|
64
65
|
},
|
|
65
66
|
"peerDependencies": {
|
|
66
67
|
"nx": ">= 22 <= 24 || ^23.0.0-0"
|