nx-lifecycle 0.2.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/.npmignore +20 -0
  2. package/README.md +203 -119
  3. package/WHY-NX-LIFECYCLE.md +64 -0
  4. package/dist/bin.d.ts +603 -0
  5. package/dist/bin.js +3 -2
  6. package/dist/cli.d.ts +1 -1
  7. package/dist/commands/index.d.ts +752 -2
  8. package/dist/commands/lib/dependencies.d.ts +7 -7
  9. package/dist/commands/lib/dependencies.js +1 -1
  10. package/dist/commands/lifecycle-command.d.ts +1 -1
  11. package/dist/executors/lifecycle/container.d.ts +603 -0
  12. package/dist/executors/lifecycle/container.js +8 -0
  13. package/dist/executors/lifecycle/index.js +2 -6
  14. package/dist/lifecycle/dependencies.d.ts +16 -0
  15. package/dist/lifecycle/{depedencies.js → dependencies.js} +1 -1
  16. package/dist/lifecycle/index.d.ts +754 -3
  17. package/dist/lifecycle/index.js +5 -5
  18. package/dist/lifecycle/lifecycle-internal.d.ts +3 -3
  19. package/dist/lifecycle/lifecycle-internal.js +26 -9
  20. package/dist/lifecycle/normalizer.d.ts +1 -1
  21. package/dist/lifecycle/processor.d.ts +41 -3
  22. package/dist/lifecycle/processor.js +36 -33
  23. package/dist/lifecycle/schema.d.ts +468 -66
  24. package/dist/lifecycle/schema.js +30 -21
  25. package/dist/plugin/container.d.ts +855 -0
  26. package/dist/plugin/container.js +13 -0
  27. package/dist/plugin/dependencies.d.ts +860 -0
  28. package/dist/plugin/dependencies.js +9 -0
  29. package/dist/plugin/index.d.ts +283 -0
  30. package/dist/plugin/index.js +6 -0
  31. package/dist/plugin/lifecycle-plugin.d.ts +21 -0
  32. package/dist/plugin/lifecycle-plugin.js +158 -0
  33. package/dist/plugin/schema.d.ts +353 -0
  34. package/dist/plugin/schema.js +21 -0
  35. package/dist/schemas/configurations.d.ts +5 -5
  36. package/dist/schemas/depend-on.d.ts +39 -9
  37. package/dist/schemas/depend-on.js +4 -2
  38. package/dist/schemas/empty.d.ts +2 -2
  39. package/dist/schemas/empty.js +2 -2
  40. package/dist/schemas/nx-json.d.ts +191 -18
  41. package/dist/schemas/nx-json.js +3 -3
  42. package/dist/schemas/project-json.d.ts +191 -18
  43. package/dist/schemas/project-json.js +3 -3
  44. package/dist/schemas/target.d.ts +79 -25
  45. package/executors.json +2 -2
  46. package/out/nx-executor/schema.json +148 -0
  47. package/out/schema.json +114 -0
  48. package/package.json +39 -40
  49. package/dist/executors/lifecycle/index.cjs +0 -15
  50. package/dist/executors/lifecycle/index.d.cts +0 -99
  51. package/dist/lifecycle/depedencies.d.ts +0 -15
  52. package/dist/schemas/lib/ajv.d.ts +0 -3
  53. package/dist/schemas/lib/ajv.js +0 -9
@@ -0,0 +1,9 @@
1
+ import { logger, readJsonFile } from '@nx/devkit';
2
+ import { bind, createModule, identifier } from 'haywire';
3
+ import { assertProjectJson, assertProjectJsonIdentifier } from '#schemas';
4
+ import { assertLifecyclePluginOptions, assertLifecyclePluginOptionsId } from './schema.js';
5
+ export const pluginLoggerId = identifier();
6
+ export const readJsonFileId = identifier();
7
+ export const pluginDependenciesModule = createModule(bind(pluginLoggerId).withInstance(logger)).addBinding(bind(readJsonFileId).withInstance(readJsonFile)).addBinding(bind(assertProjectJsonIdentifier).withInstance(assertProjectJson)).addBinding(bind(assertLifecyclePluginOptionsId).withInstance(assertLifecyclePluginOptions));
8
+
9
+ //# sourceMappingURL=dependencies.js.map
@@ -0,0 +1,283 @@
1
+ export type { LifecyclePluginOptions } from './schema.js';
2
+ /**
3
+ * Plugin name, as Nx can't attach one to an ES module.
4
+ */
5
+ export declare const name = "nx-lifecycle";
6
+ export declare const createNodes: import("@nx/devkit").CreateNodes<Partial<{
7
+ stages: Record<string, Partial<{
8
+ dependsOn: (string | (Partial<{
9
+ dependencies: boolean;
10
+ projects: never;
11
+ }> & Partial<{
12
+ target: string;
13
+ params: string;
14
+ }> & Required<Pick<{
15
+ target: string;
16
+ params: string;
17
+ }, "target">>) | (string & Partial<{
18
+ dependencies: boolean;
19
+ projects: never;
20
+ }> & Partial<{
21
+ target: string;
22
+ params: string;
23
+ }> & Required<Pick<{
24
+ target: string;
25
+ params: string;
26
+ }, "target">>) | (Partial<{
27
+ dependencies: boolean;
28
+ projects: never;
29
+ }> & Partial<{
30
+ target: string;
31
+ params: string;
32
+ }> & Required<Pick<{
33
+ target: string;
34
+ params: string;
35
+ }, "target">> & string) | (Partial<{
36
+ dependencies: never;
37
+ projects: string | string[];
38
+ }> & Required<Pick<{
39
+ dependencies: never;
40
+ projects: string | string[];
41
+ }, "projects">> & Partial<{
42
+ target: string;
43
+ params: string;
44
+ }> & Required<Pick<{
45
+ target: string;
46
+ params: string;
47
+ }, "target">>) | (string & Partial<{
48
+ dependencies: never;
49
+ projects: string | string[];
50
+ }> & Required<Pick<{
51
+ dependencies: never;
52
+ projects: string | string[];
53
+ }, "projects">> & Partial<{
54
+ target: string;
55
+ params: string;
56
+ }> & Required<Pick<{
57
+ target: string;
58
+ params: string;
59
+ }, "target">>) | (Partial<{
60
+ dependencies: never;
61
+ projects: string | string[];
62
+ }> & Required<Pick<{
63
+ dependencies: never;
64
+ projects: string | string[];
65
+ }, "projects">> & Partial<{
66
+ target: string;
67
+ params: string;
68
+ }> & Required<Pick<{
69
+ target: string;
70
+ params: string;
71
+ }, "target">> & string))[];
72
+ hooks: string[];
73
+ }>>;
74
+ bindings: Record<string, string>;
75
+ }> & Required<Pick<{
76
+ stages: Record<string, Partial<{
77
+ dependsOn: (string | (Partial<{
78
+ dependencies: boolean;
79
+ projects: never;
80
+ }> & Partial<{
81
+ target: string;
82
+ params: string;
83
+ }> & Required<Pick<{
84
+ target: string;
85
+ params: string;
86
+ }, "target">>) | (string & Partial<{
87
+ dependencies: boolean;
88
+ projects: never;
89
+ }> & Partial<{
90
+ target: string;
91
+ params: string;
92
+ }> & Required<Pick<{
93
+ target: string;
94
+ params: string;
95
+ }, "target">>) | (Partial<{
96
+ dependencies: boolean;
97
+ projects: never;
98
+ }> & Partial<{
99
+ target: string;
100
+ params: string;
101
+ }> & Required<Pick<{
102
+ target: string;
103
+ params: string;
104
+ }, "target">> & string) | (Partial<{
105
+ dependencies: never;
106
+ projects: string | string[];
107
+ }> & Required<Pick<{
108
+ dependencies: never;
109
+ projects: string | string[];
110
+ }, "projects">> & Partial<{
111
+ target: string;
112
+ params: string;
113
+ }> & Required<Pick<{
114
+ target: string;
115
+ params: string;
116
+ }, "target">>) | (string & Partial<{
117
+ dependencies: never;
118
+ projects: string | string[];
119
+ }> & Required<Pick<{
120
+ dependencies: never;
121
+ projects: string | string[];
122
+ }, "projects">> & Partial<{
123
+ target: string;
124
+ params: string;
125
+ }> & Required<Pick<{
126
+ target: string;
127
+ params: string;
128
+ }, "target">>) | (Partial<{
129
+ dependencies: never;
130
+ projects: string | string[];
131
+ }> & Required<Pick<{
132
+ dependencies: never;
133
+ projects: string | string[];
134
+ }, "projects">> & Partial<{
135
+ target: string;
136
+ params: string;
137
+ }> & Required<Pick<{
138
+ target: string;
139
+ params: string;
140
+ }, "target">> & string))[];
141
+ hooks: string[];
142
+ }>>;
143
+ bindings: Record<string, string>;
144
+ }, "bindings" | "stages">>>, createDependencies: (options: (Partial<{
145
+ stages: Record<string, Partial<{
146
+ dependsOn: (string | (Partial<{
147
+ dependencies: boolean;
148
+ projects: never;
149
+ }> & Partial<{
150
+ target: string;
151
+ params: string;
152
+ }> & Required<Pick<{
153
+ target: string;
154
+ params: string;
155
+ }, "target">>) | (string & Partial<{
156
+ dependencies: boolean;
157
+ projects: never;
158
+ }> & Partial<{
159
+ target: string;
160
+ params: string;
161
+ }> & Required<Pick<{
162
+ target: string;
163
+ params: string;
164
+ }, "target">>) | (Partial<{
165
+ dependencies: boolean;
166
+ projects: never;
167
+ }> & Partial<{
168
+ target: string;
169
+ params: string;
170
+ }> & Required<Pick<{
171
+ target: string;
172
+ params: string;
173
+ }, "target">> & string) | (Partial<{
174
+ dependencies: never;
175
+ projects: string | string[];
176
+ }> & Required<Pick<{
177
+ dependencies: never;
178
+ projects: string | string[];
179
+ }, "projects">> & Partial<{
180
+ target: string;
181
+ params: string;
182
+ }> & Required<Pick<{
183
+ target: string;
184
+ params: string;
185
+ }, "target">>) | (string & Partial<{
186
+ dependencies: never;
187
+ projects: string | string[];
188
+ }> & Required<Pick<{
189
+ dependencies: never;
190
+ projects: string | string[];
191
+ }, "projects">> & Partial<{
192
+ target: string;
193
+ params: string;
194
+ }> & Required<Pick<{
195
+ target: string;
196
+ params: string;
197
+ }, "target">>) | (Partial<{
198
+ dependencies: never;
199
+ projects: string | string[];
200
+ }> & Required<Pick<{
201
+ dependencies: never;
202
+ projects: string | string[];
203
+ }, "projects">> & Partial<{
204
+ target: string;
205
+ params: string;
206
+ }> & Required<Pick<{
207
+ target: string;
208
+ params: string;
209
+ }, "target">> & string))[];
210
+ hooks: string[];
211
+ }>>;
212
+ bindings: Record<string, string>;
213
+ }> & Required<Pick<{
214
+ stages: Record<string, Partial<{
215
+ dependsOn: (string | (Partial<{
216
+ dependencies: boolean;
217
+ projects: never;
218
+ }> & Partial<{
219
+ target: string;
220
+ params: string;
221
+ }> & Required<Pick<{
222
+ target: string;
223
+ params: string;
224
+ }, "target">>) | (string & Partial<{
225
+ dependencies: boolean;
226
+ projects: never;
227
+ }> & Partial<{
228
+ target: string;
229
+ params: string;
230
+ }> & Required<Pick<{
231
+ target: string;
232
+ params: string;
233
+ }, "target">>) | (Partial<{
234
+ dependencies: boolean;
235
+ projects: never;
236
+ }> & Partial<{
237
+ target: string;
238
+ params: string;
239
+ }> & Required<Pick<{
240
+ target: string;
241
+ params: string;
242
+ }, "target">> & string) | (Partial<{
243
+ dependencies: never;
244
+ projects: string | string[];
245
+ }> & Required<Pick<{
246
+ dependencies: never;
247
+ projects: string | string[];
248
+ }, "projects">> & Partial<{
249
+ target: string;
250
+ params: string;
251
+ }> & Required<Pick<{
252
+ target: string;
253
+ params: string;
254
+ }, "target">>) | (string & Partial<{
255
+ dependencies: never;
256
+ projects: string | string[];
257
+ }> & Required<Pick<{
258
+ dependencies: never;
259
+ projects: string | string[];
260
+ }, "projects">> & Partial<{
261
+ target: string;
262
+ params: string;
263
+ }> & Required<Pick<{
264
+ target: string;
265
+ params: string;
266
+ }, "target">>) | (Partial<{
267
+ dependencies: never;
268
+ projects: string | string[];
269
+ }> & Required<Pick<{
270
+ dependencies: never;
271
+ projects: string | string[];
272
+ }, "projects">> & Partial<{
273
+ target: string;
274
+ params: string;
275
+ }> & Required<Pick<{
276
+ target: string;
277
+ params: string;
278
+ }, "target">> & string))[];
279
+ hooks: string[];
280
+ }>>;
281
+ bindings: Record<string, string>;
282
+ }, "bindings" | "stages">>) | undefined, context: import("@nx/devkit").CreateDependenciesContext) => [];
283
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,6 @@
1
+ import { lifecyclePluginContainer } from './container.js';
2
+ import { LifecyclePlugin } from './lifecycle-plugin.js';
3
+ export const name = 'nx-lifecycle';
4
+ export const { createNodes, createDependencies } = lifecyclePluginContainer.get(LifecyclePlugin);
5
+
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,21 @@
1
+ import type { CreateDependencies, CreateNodes } from '@nx/devkit';
2
+ import type { AssertProjectJson } from '#schemas';
3
+ import type { PluginLogger, ReadJsonFile } from './dependencies.js';
4
+ import type { AssertLifecyclePluginOptions, LifecyclePluginOptions } from './schema.js';
5
+ /**
6
+ * Nx plugin that infers lifecycle targets, instead of writing them to `nx.json` and `project.json`.
7
+ *
8
+ * `createNodes` adds the anchor, hook and stage targets to every project with a `project.json`,
9
+ * and wires the bound targets declared there.
10
+ *
11
+ * Nx applies `nx.json` `targetDefaults` and `project.json` on top of inferred targets,
12
+ * replacing `dependsOn` rather than merging it.
13
+ * `createDependencies` runs on the merged configuration, and fails if a bound target lost its wiring.
14
+ */
15
+ export declare class LifecyclePlugin {
16
+ #private;
17
+ readonly createNodes: CreateNodes<LifecyclePluginOptions>;
18
+ readonly createDependencies: (...args: Parameters<CreateDependencies<LifecyclePluginOptions>>) => [];
19
+ constructor(assertOptions: AssertLifecyclePluginOptions, readJsonFile: ReadJsonFile, assertProjectJson: AssertProjectJson, logger: PluginLogger);
20
+ }
21
+ //# sourceMappingURL=lifecycle-plugin.d.ts.map
@@ -0,0 +1,158 @@
1
+ import Path from 'node:path';
2
+ import { createNodesFromFiles } from '@nx/devkit';
3
+ import { deepEqual } from 'fast-equals';
4
+ import { NOOP_EXECUTOR } from '../lifecycle/constants.js';
5
+ import { planLifecycle } from '../lifecycle/processor.js';
6
+ const describeTarget = ({ kind, name, stage })=>{
7
+ if (kind === 'anchor') {
8
+ return `Start of the ${stage} stage. Waits for the dependencies of the stage`;
9
+ }
10
+ if (kind === 'hook') {
11
+ return `Runs the targets bound to ${name}`;
12
+ }
13
+ return `Runs the ${stage} stage`;
14
+ };
15
+ const normalizeDependency = (dependency)=>{
16
+ if (typeof dependency !== 'string') {
17
+ return dependency;
18
+ }
19
+ if (dependency.startsWith('^')) {
20
+ return {
21
+ target: dependency.slice(1),
22
+ dependencies: true
23
+ };
24
+ }
25
+ return {
26
+ target: dependency
27
+ };
28
+ };
29
+ const dependsOnIncludes = (dependsOn, expected)=>{
30
+ const normalizedExpected = normalizeDependency(expected);
31
+ return (dependsOn ?? []).some((dependency)=>deepEqual(normalizeDependency(dependency), normalizedExpected));
32
+ };
33
+ const validateProject = ({ targets = {} }, { lifecycleTargets, registeredTargets })=>{
34
+ const errors = [];
35
+ for (const [targetName, lifecycleTarget] of lifecycleTargets){
36
+ const target = targets[targetName];
37
+ if (target && (target.executor !== NOOP_EXECUTOR || !lifecycleTarget.dependsOn.every((dependency)=>dependsOnIncludes(target.dependsOn, dependency)))) {
38
+ errors.push(`${targetName} is inferred by nx-lifecycle, but project.json or nx.json targetDefaults override it. Remove it from there`);
39
+ }
40
+ }
41
+ for (const [targetName, lifecycleTarget] of registeredTargets){
42
+ const target = targets[targetName];
43
+ const hook = targets[lifecycleTarget.name];
44
+ if (!target) {
45
+ continue;
46
+ }
47
+ if (!hook) {
48
+ errors.push(`${targetName} is bound to ${lifecycleTarget.name}, but nx-lifecycle only adds lifecycle targets to projects with a project.json. Add one`);
49
+ } else if (!dependsOnIncludes(hook.dependsOn, targetName)) {
50
+ errors.push(`${targetName} is bound to ${lifecycleTarget.name}, but is not declared in project.json. Declare it there so nx-lifecycle can wire it`);
51
+ } else if (!dependsOnIncludes(target.dependsOn, lifecycleTarget.previousHook)) {
52
+ errors.push(`${targetName} is bound to ${lifecycleTarget.name}, so it must depend on ${lifecycleTarget.previousHook}. A dependsOn in project.json or nx.json targetDefaults replaces the inferred one, so add "${lifecycleTarget.previousHook}" to it`);
53
+ }
54
+ }
55
+ return errors;
56
+ };
57
+ export class LifecyclePlugin {
58
+ #assertOptions;
59
+ #readJsonFile;
60
+ #assertProjectJson;
61
+ #logger;
62
+ createNodes;
63
+ createDependencies;
64
+ constructor(assertOptions, readJsonFile, assertProjectJson, logger){
65
+ this.#assertOptions = assertOptions;
66
+ this.#readJsonFile = readJsonFile;
67
+ this.#assertProjectJson = assertProjectJson;
68
+ this.#logger = logger;
69
+ this.createNodes = [
70
+ '**/project.json',
71
+ this.#createNodes.bind(this)
72
+ ];
73
+ this.createDependencies = this.#createDependencies.bind(this);
74
+ }
75
+ #plan(options) {
76
+ try {
77
+ this.#assertOptions(options);
78
+ } catch (err) {
79
+ const issues = err.cause;
80
+ throw new Error([
81
+ 'Invalid nx-lifecycle plugin options in nx.json:',
82
+ ...issues.map(({ instancePath, message })=>`- options${instancePath} ${message}`)
83
+ ].join('\n'), {
84
+ cause: err
85
+ });
86
+ }
87
+ return planLifecycle(options);
88
+ }
89
+ async #createNodes(projectJsonPaths, options, context) {
90
+ const plan = this.#plan(options);
91
+ return createNodesFromFiles((projectJsonPath)=>this.#createProjectNode(projectJsonPath, plan, context), projectJsonPaths, options, context);
92
+ }
93
+ #createProjectNode(projectJsonPath, { lifecycleTargets, registeredTargets }, context) {
94
+ const projectJson = this.#readJsonFile(Path.join(context.workspaceRoot, projectJsonPath));
95
+ this.#assertProjectJson(projectJson);
96
+ const declaredTargets = projectJson.targets ?? {};
97
+ const targets = {};
98
+ for (const [targetName, lifecycleTarget] of lifecycleTargets){
99
+ targets[targetName] = {
100
+ executor: NOOP_EXECUTOR,
101
+ dependsOn: [
102
+ ...lifecycleTarget.dependsOn
103
+ ],
104
+ metadata: {
105
+ description: describeTarget(lifecycleTarget)
106
+ }
107
+ };
108
+ }
109
+ for (const [targetName, lifecycleTarget] of registeredTargets){
110
+ if (targetName in declaredTargets) {
111
+ targets[lifecycleTarget.name].dependsOn.push(targetName);
112
+ targets[targetName] = {
113
+ dependsOn: [
114
+ lifecycleTarget.previousHook
115
+ ]
116
+ };
117
+ }
118
+ }
119
+ return {
120
+ projects: {
121
+ [Path.dirname(projectJsonPath)]: {
122
+ targets
123
+ }
124
+ }
125
+ };
126
+ }
127
+ #createDependencies(options, context) {
128
+ const plan = this.#plan(options);
129
+ const errors = new Map();
130
+ const definedTargets = new Set();
131
+ for (const [projectName, project] of Object.entries(context.projects)){
132
+ for (const error of validateProject(project, plan)){
133
+ const projectNames = errors.get(error) ?? [];
134
+ projectNames.push(projectName);
135
+ errors.set(error, projectNames);
136
+ }
137
+ for (const targetName of Object.keys(project.targets ?? {})){
138
+ definedTargets.add(targetName);
139
+ }
140
+ }
141
+ if (errors.size > 0) {
142
+ throw new Error([
143
+ 'nx-lifecycle targets are not wired as configured:',
144
+ ...[
145
+ ...errors
146
+ ].map(([error, projectNames])=>`- ${error}\n Projects: ${projectNames.join(', ')}`)
147
+ ].join('\n'));
148
+ }
149
+ for (const targetName of plan.registeredTargets.keys()){
150
+ if (!definedTargets.has(targetName)) {
151
+ this.#logger.warn(`Bound target ${targetName} is not defined in any project. Is it a typo?`);
152
+ }
153
+ }
154
+ return [];
155
+ }
156
+ }
157
+
158
+ //# sourceMappingURL=lifecycle-plugin.js.map