@leyman/nx-plugin 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +216 -2
- package/dist/executors/targets.d.ts +30 -0
- package/dist/executors/targets.js +31 -0
- package/dist/generators/init.d.ts +82 -0
- package/dist/generators/init.js +190 -0
- package/dist/generators/package.d.ts +79 -0
- package/dist/generators/package.js +208 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +12 -0
- package/dist/lib/infer.d.ts +45 -0
- package/dist/lib/infer.js +66 -0
- package/dist/lib/options.d.ts +154 -0
- package/dist/lib/options.js +75 -0
- package/dist/lib/plugin.d.ts +12 -0
- package/dist/lib/plugin.js +38 -0
- package/dist/lib/projects.d.ts +27 -0
- package/dist/lib/projects.js +44 -0
- package/dist/lib/schema.d.ts +83 -0
- package/dist/lib/schema.js +255 -0
- package/dist/lib/sync.d.ts +31 -0
- package/dist/lib/sync.js +109 -0
- package/dist/lib/targets.d.ts +17 -0
- package/dist/lib/targets.js +266 -0
- package/dist/lib/versions.d.ts +42 -0
- package/dist/lib/versions.js +65 -0
- package/executors.json +9 -0
- package/generators.json +14 -0
- package/out/executors/targets/schema.json +22 -0
- package/out/generators/init/schema.json +25 -0
- package/out/generators/package/schema.json +79 -0
- package/out/schema.json +624 -0
- package/package.json +86 -3
- package/scripts/biome-format.sh +10 -0
- package/scripts/clean-dist.sh +26 -0
- package/scripts/coverage-report.sh +22 -0
- package/scripts/swc-compile.sh +51 -0
- package/scripts/sync-injected.mjs +143 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { access, readFile } from 'node:fs/promises';
|
|
2
|
+
import Path from 'node:path';
|
|
3
|
+
import { inferencePaths } from './infer.js';
|
|
4
|
+
export const readJson = async (path)=>JSON.parse(await readFile(path, 'utf8'));
|
|
5
|
+
const isPresent = async (path)=>{
|
|
6
|
+
try {
|
|
7
|
+
await access(path);
|
|
8
|
+
return true;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const readJsonIfPresent = async (path)=>await isPresent(path) ? readJson(path) : undefined;
|
|
14
|
+
export const loadProjects = async (workspaceRoot, roots)=>{
|
|
15
|
+
const loaded = await Promise.all(roots.map(async (root)=>{
|
|
16
|
+
const projectRoot = Path.join(workspaceRoot, root);
|
|
17
|
+
const [projectJson, packageJson, present] = await Promise.all([
|
|
18
|
+
readJsonIfPresent(Path.join(projectRoot, 'project.json')),
|
|
19
|
+
readJsonIfPresent(Path.join(projectRoot, 'package.json')),
|
|
20
|
+
Promise.all(inferencePaths.map(async (path)=>isPresent(Path.join(projectRoot, path))))
|
|
21
|
+
]);
|
|
22
|
+
return {
|
|
23
|
+
root: root.replaceAll(Path.sep, '/'),
|
|
24
|
+
present: new Set(inferencePaths.filter((_path, i)=>present[i] === true)),
|
|
25
|
+
projectJson: projectJson,
|
|
26
|
+
packageJson: packageJson
|
|
27
|
+
};
|
|
28
|
+
}));
|
|
29
|
+
const dependencies = new Set(loaded.flatMap(({ packageJson })=>[
|
|
30
|
+
packageJson?.dependencies,
|
|
31
|
+
packageJson?.devDependencies,
|
|
32
|
+
packageJson?.optionalDependencies,
|
|
33
|
+
packageJson?.peerDependencies
|
|
34
|
+
].flatMap((field)=>Object.keys(field ?? {}))));
|
|
35
|
+
return loaded.map(({ packageJson, present, projectJson, root })=>({
|
|
36
|
+
packageJson,
|
|
37
|
+
projectJson,
|
|
38
|
+
root,
|
|
39
|
+
isDependency: packageJson?.name !== undefined && dependencies.has(packageJson.name),
|
|
40
|
+
has: (path)=>present.has(path)
|
|
41
|
+
}));
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=projects.js.map
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { NxPluginOptions } from './options.js';
|
|
2
|
+
/**
|
|
3
|
+
* JSON schema of the options, for `nx.json` or the file the `targets` executor reads.
|
|
4
|
+
*/
|
|
5
|
+
export declare const optionsJsonSchema: import("juniper").JsonSchema<{
|
|
6
|
+
targets?: {
|
|
7
|
+
[x: string]: false | {
|
|
8
|
+
name?: string;
|
|
9
|
+
include?: string[];
|
|
10
|
+
exclude?: string[];
|
|
11
|
+
} | undefined;
|
|
12
|
+
};
|
|
13
|
+
configs?: {
|
|
14
|
+
swc?: string;
|
|
15
|
+
c8?: string;
|
|
16
|
+
vitest?: string;
|
|
17
|
+
};
|
|
18
|
+
scripts?: string;
|
|
19
|
+
coverage?: {
|
|
20
|
+
branches?: number;
|
|
21
|
+
functions?: number;
|
|
22
|
+
lines?: number;
|
|
23
|
+
statements?: number;
|
|
24
|
+
};
|
|
25
|
+
checkPackage?: {
|
|
26
|
+
profile?: "esm-only" | "node16" | "strict";
|
|
27
|
+
floorTsconfig?: string | false;
|
|
28
|
+
inputs?: ((string | import("juniper").EmptyObject) & (string | import("juniper").EmptyObject | undefined))[];
|
|
29
|
+
};
|
|
30
|
+
inputs?: {
|
|
31
|
+
configs?: ((string | import("juniper").EmptyObject) & (string | import("juniper").EmptyObject | undefined))[];
|
|
32
|
+
tools?: ((string | import("juniper").EmptyObject) & (string | import("juniper").EmptyObject | undefined))[];
|
|
33
|
+
};
|
|
34
|
+
namedInputs?: {
|
|
35
|
+
[x: string]: ((string | import("juniper").EmptyObject) & (string | import("juniper").EmptyObject | undefined))[] | undefined;
|
|
36
|
+
};
|
|
37
|
+
$schema?: string;
|
|
38
|
+
}>;
|
|
39
|
+
/**
|
|
40
|
+
* JSON schema of the `targets` executor's options.
|
|
41
|
+
*/
|
|
42
|
+
export declare const executorJsonSchema: import("juniper").JsonSchema<{
|
|
43
|
+
config: string;
|
|
44
|
+
check?: boolean;
|
|
45
|
+
}>;
|
|
46
|
+
/**
|
|
47
|
+
* Major of the oldest Node.js a package the `package` generator adds supports, unless told otherwise.
|
|
48
|
+
*/
|
|
49
|
+
export declare const defaultFloor = 22;
|
|
50
|
+
/**
|
|
51
|
+
* JSON schema of the `package` generator's options.
|
|
52
|
+
*/
|
|
53
|
+
export declare const packageGeneratorJsonSchema: import("juniper").JsonSchema<{
|
|
54
|
+
directory: string;
|
|
55
|
+
name?: string;
|
|
56
|
+
description: string;
|
|
57
|
+
published?: boolean;
|
|
58
|
+
floor?: number;
|
|
59
|
+
test?: string;
|
|
60
|
+
repository?: string;
|
|
61
|
+
author?: string;
|
|
62
|
+
license?: string;
|
|
63
|
+
devDependencies?: string[];
|
|
64
|
+
config?: string;
|
|
65
|
+
skipInstall?: boolean;
|
|
66
|
+
}>;
|
|
67
|
+
/**
|
|
68
|
+
* JSON schema of the `init` generator's options.
|
|
69
|
+
*/
|
|
70
|
+
export declare const initGeneratorJsonSchema: import("juniper").JsonSchema<{
|
|
71
|
+
lifecycle?: boolean;
|
|
72
|
+
skill?: boolean;
|
|
73
|
+
skipInstall?: boolean;
|
|
74
|
+
}>;
|
|
75
|
+
/**
|
|
76
|
+
* Asserts the plugin options are valid, with each problem in the error's message.
|
|
77
|
+
*
|
|
78
|
+
* @param options - the options, from `nx.json` or a file
|
|
79
|
+
* @param where - where they are from, for the message
|
|
80
|
+
* @throws {Error} naming each invalid option
|
|
81
|
+
*/
|
|
82
|
+
export declare const assertOptions: (options: unknown, where: string) => asserts options is NxPluginOptions;
|
|
83
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { arraySchema, booleanSchema, enumSchema, mergeSchema, numberSchema, objectSchema, stringSchema } from 'juniper';
|
|
2
|
+
import { makeValidator } from 'juniper-validator';
|
|
3
|
+
import { namedInputNames, targetNames } from './options.js';
|
|
4
|
+
const inputsSchema = arraySchema(mergeSchema().oneOf([
|
|
5
|
+
stringSchema({
|
|
6
|
+
description: 'A file pattern, or the name of a named input'
|
|
7
|
+
}),
|
|
8
|
+
objectSchema({
|
|
9
|
+
description: 'An input object, such as `{ "externalDependencies": ["pkg"] }`'
|
|
10
|
+
})
|
|
11
|
+
]));
|
|
12
|
+
const rootsSchema = arraySchema(stringSchema({
|
|
13
|
+
description: 'Root of a project, e.g. `packages/a`'
|
|
14
|
+
}));
|
|
15
|
+
const targetSchema = mergeSchema().oneOf([
|
|
16
|
+
enumSchema({
|
|
17
|
+
enum: [
|
|
18
|
+
false
|
|
19
|
+
],
|
|
20
|
+
description: 'Infer the target nowhere'
|
|
21
|
+
}),
|
|
22
|
+
objectSchema({
|
|
23
|
+
properties: {
|
|
24
|
+
name: stringSchema({
|
|
25
|
+
description: "Name of the target. Defaults to the target's own"
|
|
26
|
+
}),
|
|
27
|
+
include: rootsSchema.description('Projects that get the target whatever their files'),
|
|
28
|
+
exclude: rootsSchema.description('Projects that never get the target')
|
|
29
|
+
},
|
|
30
|
+
additionalProperties: false
|
|
31
|
+
})
|
|
32
|
+
]);
|
|
33
|
+
const maxPercent = 100;
|
|
34
|
+
const percentSchema = numberSchema({
|
|
35
|
+
minimum: 0,
|
|
36
|
+
maximum: maxPercent
|
|
37
|
+
});
|
|
38
|
+
const optionsProperties = {
|
|
39
|
+
targets: objectSchema({
|
|
40
|
+
description: "Each target's options, or `false` to infer it nowhere",
|
|
41
|
+
properties: Object.fromEntries(targetNames.map((name)=>[
|
|
42
|
+
name,
|
|
43
|
+
targetSchema
|
|
44
|
+
])),
|
|
45
|
+
additionalProperties: false
|
|
46
|
+
}),
|
|
47
|
+
configs: objectSchema({
|
|
48
|
+
description: 'Paths of the shared configs, relative to each project',
|
|
49
|
+
properties: {
|
|
50
|
+
swc: stringSchema({
|
|
51
|
+
default: 'node_modules/@leyman/configs/swcrc.jsonc'
|
|
52
|
+
}),
|
|
53
|
+
c8: stringSchema({
|
|
54
|
+
default: 'node_modules/@leyman/configs/c8rc.json'
|
|
55
|
+
}),
|
|
56
|
+
vitest: stringSchema({
|
|
57
|
+
default: 'node_modules/@leyman/configs/vitest.config.js'
|
|
58
|
+
})
|
|
59
|
+
},
|
|
60
|
+
additionalProperties: false
|
|
61
|
+
}),
|
|
62
|
+
scripts: stringSchema({
|
|
63
|
+
description: "Directory to run the plugin's scripts from, instead of the package's bins (`leyman-swc-compile`...)"
|
|
64
|
+
}),
|
|
65
|
+
coverage: objectSchema({
|
|
66
|
+
description: "Coverage thresholds `coverage-report` passes to c8, over the c8 config's",
|
|
67
|
+
properties: {
|
|
68
|
+
branches: percentSchema,
|
|
69
|
+
functions: percentSchema,
|
|
70
|
+
lines: percentSchema,
|
|
71
|
+
statements: percentSchema
|
|
72
|
+
},
|
|
73
|
+
additionalProperties: false
|
|
74
|
+
}),
|
|
75
|
+
checkPackage: objectSchema({
|
|
76
|
+
description: 'Options of `check-package`',
|
|
77
|
+
properties: {
|
|
78
|
+
profile: enumSchema({
|
|
79
|
+
enum: [
|
|
80
|
+
'esm-only',
|
|
81
|
+
'node16',
|
|
82
|
+
'strict'
|
|
83
|
+
],
|
|
84
|
+
description: 'The module resolutions every entry point must work under'
|
|
85
|
+
}),
|
|
86
|
+
floorTsconfig: mergeSchema().oneOf([
|
|
87
|
+
stringSchema({
|
|
88
|
+
description: "The config a package's `tsconfig.json` must extend, with `{major}` for its support floor",
|
|
89
|
+
default: '@leyman/configs/tsconfig.node-{major}.json'
|
|
90
|
+
}),
|
|
91
|
+
enumSchema({
|
|
92
|
+
enum: [
|
|
93
|
+
false
|
|
94
|
+
],
|
|
95
|
+
description: 'Not checked'
|
|
96
|
+
})
|
|
97
|
+
]),
|
|
98
|
+
inputs: inputsSchema.description('What a change of check-package itself is hashed by')
|
|
99
|
+
},
|
|
100
|
+
additionalProperties: false
|
|
101
|
+
}),
|
|
102
|
+
inputs: objectSchema({
|
|
103
|
+
description: 'What changes of the shared tools are hashed by',
|
|
104
|
+
properties: {
|
|
105
|
+
configs: inputsSchema.description('The shared configs, for every target that reads one'),
|
|
106
|
+
tools: inputsSchema.description("This plugin's scripts, for every target that runs one")
|
|
107
|
+
},
|
|
108
|
+
additionalProperties: false
|
|
109
|
+
}),
|
|
110
|
+
namedInputs: objectSchema({
|
|
111
|
+
description: 'Inputs added to the end of each named input, such as a file the tests read',
|
|
112
|
+
properties: Object.fromEntries(namedInputNames.map((name)=>[
|
|
113
|
+
name,
|
|
114
|
+
inputsSchema
|
|
115
|
+
])),
|
|
116
|
+
additionalProperties: false
|
|
117
|
+
})
|
|
118
|
+
};
|
|
119
|
+
const optionsSchema = objectSchema({
|
|
120
|
+
title: '@leyman/nx-plugin',
|
|
121
|
+
description: 'Infers the targets of TypeScript ESM packages',
|
|
122
|
+
properties: optionsProperties,
|
|
123
|
+
additionalProperties: false
|
|
124
|
+
});
|
|
125
|
+
export const optionsJsonSchema = optionsSchema.properties({
|
|
126
|
+
$schema: stringSchema({
|
|
127
|
+
description: 'Path or URL of this JSON schema'
|
|
128
|
+
})
|
|
129
|
+
}).toJSON({
|
|
130
|
+
schema: true
|
|
131
|
+
});
|
|
132
|
+
const executorSchema = objectSchema({
|
|
133
|
+
title: 'targets',
|
|
134
|
+
description: "Writes the plugin's named inputs and target defaults into nx.json, and the targets each project gets into its project.json",
|
|
135
|
+
properties: {
|
|
136
|
+
config: stringSchema({
|
|
137
|
+
description: 'Path of the file holding the plugin options, relative to the workspace root'
|
|
138
|
+
}),
|
|
139
|
+
check: booleanSchema({
|
|
140
|
+
description: 'Fail rather than write when a file is out of date. Defaults to true in CI'
|
|
141
|
+
})
|
|
142
|
+
},
|
|
143
|
+
required: [
|
|
144
|
+
'config'
|
|
145
|
+
],
|
|
146
|
+
additionalProperties: false
|
|
147
|
+
});
|
|
148
|
+
export const executorJsonSchema = executorSchema.metadata({
|
|
149
|
+
version: 1,
|
|
150
|
+
outputCapture: 'direct-nodejs',
|
|
151
|
+
cli: 'nx'
|
|
152
|
+
}).toJSON();
|
|
153
|
+
export const defaultFloor = 22;
|
|
154
|
+
const packageGeneratorSchema = objectSchema({
|
|
155
|
+
title: 'package',
|
|
156
|
+
description: 'Adds a TypeScript ESM package, with a project.json declaring the targets the plugin infers',
|
|
157
|
+
properties: {
|
|
158
|
+
directory: stringSchema({
|
|
159
|
+
description: 'Where the package goes, relative to the workspace root'
|
|
160
|
+
}).metadata({
|
|
161
|
+
$default: {
|
|
162
|
+
$source: 'argv',
|
|
163
|
+
index: 0
|
|
164
|
+
},
|
|
165
|
+
'x-prompt': 'Which directory should the package go in?'
|
|
166
|
+
}),
|
|
167
|
+
name: stringSchema({
|
|
168
|
+
description: "Name of the package. Defaults to the directory's last segment"
|
|
169
|
+
}),
|
|
170
|
+
description: stringSchema({
|
|
171
|
+
description: "Description, for package.json and the README"
|
|
172
|
+
}).metadata('x-prompt', 'What does the package do?'),
|
|
173
|
+
published: booleanSchema({
|
|
174
|
+
description: 'Published to npm, rather than private',
|
|
175
|
+
default: false
|
|
176
|
+
}),
|
|
177
|
+
floor: numberSchema({
|
|
178
|
+
type: 'integer',
|
|
179
|
+
description: 'Major of the oldest Node.js a published package supports',
|
|
180
|
+
default: defaultFloor
|
|
181
|
+
}),
|
|
182
|
+
test: enumSchema({
|
|
183
|
+
enum: [
|
|
184
|
+
'mocha',
|
|
185
|
+
'vitest',
|
|
186
|
+
'none'
|
|
187
|
+
],
|
|
188
|
+
description: 'Test runner, or none',
|
|
189
|
+
default: 'mocha'
|
|
190
|
+
}),
|
|
191
|
+
repository: stringSchema({
|
|
192
|
+
description: "URL of the workspace's GitHub repository, for a published package's repository, homepage and bugs"
|
|
193
|
+
}),
|
|
194
|
+
author: stringSchema({
|
|
195
|
+
description: 'author of package.json'
|
|
196
|
+
}),
|
|
197
|
+
license: stringSchema({
|
|
198
|
+
description: "license of package.json. A published package also gets a copy of the workspace's LICENSE"
|
|
199
|
+
}),
|
|
200
|
+
devDependencies: arraySchema(stringSchema()).description('More packages to add to devDependencies'),
|
|
201
|
+
config: stringSchema({
|
|
202
|
+
description: 'File of the plugin options, relative to the workspace root, for a workspace running the targets executor. Defaults to the options in nx.json'
|
|
203
|
+
}),
|
|
204
|
+
skipInstall: booleanSchema({
|
|
205
|
+
description: "Don't install dependencies afterwards",
|
|
206
|
+
default: false
|
|
207
|
+
})
|
|
208
|
+
},
|
|
209
|
+
required: [
|
|
210
|
+
'directory',
|
|
211
|
+
"description"
|
|
212
|
+
],
|
|
213
|
+
additionalProperties: false
|
|
214
|
+
});
|
|
215
|
+
export const packageGeneratorJsonSchema = packageGeneratorSchema.metadata('cli', 'nx').toJSON({
|
|
216
|
+
schema: true
|
|
217
|
+
});
|
|
218
|
+
const initGeneratorSchema = objectSchema({
|
|
219
|
+
title: 'init',
|
|
220
|
+
description: 'Sets an Nx workspace up for the plugin: nx.json, root devDependencies, the Biome config, pnpm injection and the principles skill',
|
|
221
|
+
properties: {
|
|
222
|
+
lifecycle: booleanSchema({
|
|
223
|
+
description: "Wire the targets' order with nx-lifecycle",
|
|
224
|
+
default: true
|
|
225
|
+
}),
|
|
226
|
+
skill: booleanSchema({
|
|
227
|
+
description: 'Write a .claude/skills/principles/SKILL.md pointing coding agents at the principles behind the setup',
|
|
228
|
+
default: true
|
|
229
|
+
}),
|
|
230
|
+
skipInstall: booleanSchema({
|
|
231
|
+
description: "Don't install dependencies afterwards",
|
|
232
|
+
default: false
|
|
233
|
+
})
|
|
234
|
+
},
|
|
235
|
+
additionalProperties: false
|
|
236
|
+
});
|
|
237
|
+
export const initGeneratorJsonSchema = initGeneratorSchema.metadata('cli', 'nx').toJSON({
|
|
238
|
+
schema: true
|
|
239
|
+
});
|
|
240
|
+
const optionsValidator = makeValidator(optionsJsonSchema);
|
|
241
|
+
export const assertOptions = (options, where)=>{
|
|
242
|
+
const { issues } = optionsValidator.validate(options);
|
|
243
|
+
if (issues === undefined) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
throw new Error([
|
|
247
|
+
`Invalid @leyman/nx-plugin options in ${where}:`,
|
|
248
|
+
...issues.map(({ message, path = [] })=>{
|
|
249
|
+
const at = path.map((key)=>`/${String(key)}`).join('');
|
|
250
|
+
return `- options${at} ${message}`;
|
|
251
|
+
})
|
|
252
|
+
].join('\n'));
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A project of the workspace, as Nx knows it.
|
|
3
|
+
*/
|
|
4
|
+
export interface WorkspaceProject {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
/**
|
|
7
|
+
* Relative to the workspace root.
|
|
8
|
+
*/
|
|
9
|
+
readonly root: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Writes the plugin's named inputs and target defaults into `nx.json`, and the targets each project gets into its
|
|
13
|
+
* `project.json`, for a workspace that runs the plugin's targets without loading it as a plugin.
|
|
14
|
+
*
|
|
15
|
+
* @param params - what to update, and how
|
|
16
|
+
* @param params.root - the workspace root
|
|
17
|
+
* @param params.projects - every project of the workspace
|
|
18
|
+
* @param params.options - plugin options, not yet validated
|
|
19
|
+
* @param params.optionsSource - where the options are from, for errors
|
|
20
|
+
* @param params.check - fail rather than write when a file is out of date
|
|
21
|
+
* @returns the path of each file written, or that would be
|
|
22
|
+
* @rejects {Error} when the options are invalid, or a file is out of date in `check` mode
|
|
23
|
+
*/
|
|
24
|
+
export declare const syncTargets: ({ root, projects, options, optionsSource, check, }: {
|
|
25
|
+
root: string;
|
|
26
|
+
projects: readonly WorkspaceProject[];
|
|
27
|
+
options: unknown;
|
|
28
|
+
optionsSource: string;
|
|
29
|
+
check: boolean;
|
|
30
|
+
}) => Promise<string[]>;
|
|
31
|
+
//# sourceMappingURL=sync.d.ts.map
|
package/dist/lib/sync.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { writeFile } from 'node:fs/promises';
|
|
2
|
+
import Path from 'node:path';
|
|
3
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
4
|
+
import { inferTargets } from './infer.js';
|
|
5
|
+
import { namedInputNames, resolveOptions, targetNames } from './options.js';
|
|
6
|
+
import { loadProjects, readJson } from './projects.js';
|
|
7
|
+
import { assertOptions } from './schema.js';
|
|
8
|
+
import { namedInputs, targetConfigurations } from './targets.js';
|
|
9
|
+
const updateKeys = (object, updates, place = (_key, keys)=>keys.at(-1))=>{
|
|
10
|
+
const keys = Object.keys(object);
|
|
11
|
+
for (const [key, value] of updates){
|
|
12
|
+
if (value === undefined || keys.includes(key)) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const after = place(key, keys);
|
|
16
|
+
keys.splice(after === undefined ? 0 : keys.indexOf(after) + 1, 0, key);
|
|
17
|
+
}
|
|
18
|
+
const entries = keys.map((key)=>[
|
|
19
|
+
key,
|
|
20
|
+
updates.has(key) ? updates.get(key) : object[key]
|
|
21
|
+
]);
|
|
22
|
+
return Object.fromEntries(entries.filter(([, value])=>value !== undefined));
|
|
23
|
+
};
|
|
24
|
+
const updateNxJson = (nxJson, options)=>{
|
|
25
|
+
const inputs = namedInputs(options);
|
|
26
|
+
const configurations = targetConfigurations(options);
|
|
27
|
+
const targetDefaults = nxJson.targetDefaults ?? {};
|
|
28
|
+
const defaults = new Map();
|
|
29
|
+
for (const target of targetNames){
|
|
30
|
+
const targetOptions = options.targets[target];
|
|
31
|
+
if (targetOptions === false) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const { dependsOn } = targetDefaults[targetOptions.name] ?? {};
|
|
35
|
+
defaults.set(targetOptions.name, {
|
|
36
|
+
...configurations[target],
|
|
37
|
+
...dependsOn !== undefined && {
|
|
38
|
+
dependsOn
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const ownNamedInputs = new Map(namedInputNames.map((name)=>[
|
|
43
|
+
name,
|
|
44
|
+
inputs[name]
|
|
45
|
+
]));
|
|
46
|
+
return updateKeys(nxJson, new Map([
|
|
47
|
+
[
|
|
48
|
+
'namedInputs',
|
|
49
|
+
updateKeys(nxJson.namedInputs ?? {}, ownNamedInputs)
|
|
50
|
+
],
|
|
51
|
+
[
|
|
52
|
+
'targetDefaults',
|
|
53
|
+
updateKeys(targetDefaults, defaults)
|
|
54
|
+
]
|
|
55
|
+
]));
|
|
56
|
+
};
|
|
57
|
+
const updateProjectJson = (projectJson, inferred, options)=>{
|
|
58
|
+
const targets = projectJson.targets ?? {};
|
|
59
|
+
const names = targetNames.map((target)=>{
|
|
60
|
+
const targetOptions = options.targets[target];
|
|
61
|
+
return targetOptions === false ? target : targetOptions.name;
|
|
62
|
+
});
|
|
63
|
+
const updates = new Map();
|
|
64
|
+
for (const [i, target] of targetNames.entries()){
|
|
65
|
+
const name = names[i];
|
|
66
|
+
const current = targets[name];
|
|
67
|
+
if (inferred.includes(target)) {
|
|
68
|
+
updates.set(name, current ?? {});
|
|
69
|
+
} else if (isDeepStrictEqual(current, {})) {
|
|
70
|
+
updates.set(name, undefined);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return updateKeys(projectJson, new Map([
|
|
74
|
+
[
|
|
75
|
+
'targets',
|
|
76
|
+
updateKeys(targets, updates, (key, keys)=>names.slice(0, names.indexOf(key)).findLast((name)=>keys.includes(name)))
|
|
77
|
+
]
|
|
78
|
+
]));
|
|
79
|
+
};
|
|
80
|
+
export const syncTargets = async ({ root, projects, options, optionsSource, check })=>{
|
|
81
|
+
assertOptions(options, optionsSource);
|
|
82
|
+
const resolved = resolveOptions(options);
|
|
83
|
+
const loaded = await loadProjects(root, projects.map((project)=>project.root));
|
|
84
|
+
const nxJsonPath = Path.join(root, 'nx.json');
|
|
85
|
+
const nxJson = await readJson(nxJsonPath);
|
|
86
|
+
const files = [
|
|
87
|
+
{
|
|
88
|
+
path: nxJsonPath,
|
|
89
|
+
current: nxJson,
|
|
90
|
+
updated: updateNxJson(nxJson, resolved)
|
|
91
|
+
},
|
|
92
|
+
...loaded.filter((project)=>project.projectJson !== undefined).map((project)=>({
|
|
93
|
+
path: Path.join(root, project.root, 'project.json'),
|
|
94
|
+
current: project.projectJson,
|
|
95
|
+
updated: updateProjectJson(project.projectJson, inferTargets(project, resolved), resolved)
|
|
96
|
+
}))
|
|
97
|
+
];
|
|
98
|
+
const outdated = files.filter(({ current, updated })=>JSON.stringify(current) !== JSON.stringify(updated));
|
|
99
|
+
if (check && outdated.length > 0) {
|
|
100
|
+
throw new Error([
|
|
101
|
+
'Not up to date with @leyman/nx-plugin; run its targets executor:',
|
|
102
|
+
...outdated.map(({ path })=>`- ${Path.relative(root, path)}`)
|
|
103
|
+
].join('\n'));
|
|
104
|
+
}
|
|
105
|
+
await Promise.all(outdated.map(async ({ path, updated })=>writeFile(path, `${JSON.stringify(updated, null, 2)}\n`)));
|
|
106
|
+
return outdated.map(({ path })=>path);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TargetConfiguration } from '@nx/devkit';
|
|
2
|
+
import type { Input, NamedInputName, ResolvedOptions, TargetName } from './options.js';
|
|
3
|
+
/**
|
|
4
|
+
* The named inputs the targets use.
|
|
5
|
+
*
|
|
6
|
+
* @param options - resolved options
|
|
7
|
+
* @returns each named input
|
|
8
|
+
*/
|
|
9
|
+
export declare const namedInputs: (options: ResolvedOptions) => Record<NamedInputName, Input[]>;
|
|
10
|
+
/**
|
|
11
|
+
* How each target runs, without when: the lifecycle's `dependsOn` is nx-lifecycle's to add.
|
|
12
|
+
*
|
|
13
|
+
* @param options - resolved options
|
|
14
|
+
* @returns each target's configuration, by the target's own name
|
|
15
|
+
*/
|
|
16
|
+
export declare const targetConfigurations: (options: ResolvedOptions) => Record<TargetName, TargetConfiguration>;
|
|
17
|
+
//# sourceMappingURL=targets.d.ts.map
|