nx-lifecycle 1.0.0 → 1.1.1

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 (60) hide show
  1. package/README.md +42 -4
  2. package/WHY-NX-LIFECYCLE.md +1 -1
  3. package/dist/bin.d.ts +0 -603
  4. package/dist/bin.js +3 -13
  5. package/dist/cli.d.ts +8 -6
  6. package/dist/cli.js +17 -20
  7. package/dist/commands/explain-command.d.ts +13 -0
  8. package/dist/commands/explain-command.js +60 -0
  9. package/dist/commands/lib/types.d.ts +14 -4
  10. package/dist/commands/lib/types.js +2 -1
  11. package/dist/commands/lifecycle-command.d.ts +2 -11
  12. package/dist/commands/lifecycle-command.js +16 -22
  13. package/dist/container.d.ts +3 -0
  14. package/dist/container.js +8 -0
  15. package/dist/executors/lifecycle/index.d.ts +1 -4
  16. package/dist/executors/lifecycle/index.js +1 -4
  17. package/dist/executors/lifecycle/lifecycle.d.ts +8 -9
  18. package/dist/executors/lifecycle/lifecycle.js +13 -20
  19. package/dist/lifecycle/explain.d.ts +68 -0
  20. package/dist/lifecycle/explain.js +157 -0
  21. package/dist/lifecycle/lifecycle-internal.d.ts +4 -12
  22. package/dist/lifecycle/lifecycle-internal.js +92 -109
  23. package/dist/lifecycle/normalizer.d.ts +13 -7
  24. package/dist/lifecycle/normalizer.js +29 -36
  25. package/dist/lifecycle/processor.d.ts +1 -3
  26. package/dist/lifecycle/processor.js +22 -14
  27. package/dist/lifecycle/schema.d.ts +244 -714
  28. package/dist/lifecycle/schema.js +3 -2
  29. package/dist/plugin/index.d.ts +1 -277
  30. package/dist/plugin/index.js +1 -3
  31. package/dist/plugin/lifecycle-plugin.d.ts +18 -15
  32. package/dist/plugin/lifecycle-plugin.js +90 -106
  33. package/dist/plugin/schema.d.ts +33 -343
  34. package/dist/plugin/schema.js +0 -2
  35. package/dist/schemas/configurations.d.ts +5 -6
  36. package/dist/schemas/depend-on.d.ts +38 -74
  37. package/dist/schemas/depend-on.js +5 -4
  38. package/dist/schemas/nx-json.d.ts +38 -223
  39. package/dist/schemas/nx-json.js +0 -2
  40. package/dist/schemas/project-json.d.ts +38 -223
  41. package/dist/schemas/project-json.js +0 -2
  42. package/dist/schemas/target.d.ts +72 -140
  43. package/out/nx-executor/schema.json +2 -2
  44. package/out/schema.json +1 -1
  45. package/package.json +29 -14
  46. package/.npmignore +0 -20
  47. package/dist/commands/index.d.ts +0 -755
  48. package/dist/commands/index.js +0 -14
  49. package/dist/commands/lib/dependencies.d.ts +0 -10
  50. package/dist/commands/lib/dependencies.js +0 -12
  51. package/dist/executors/lifecycle/container.d.ts +0 -603
  52. package/dist/executors/lifecycle/container.js +0 -8
  53. package/dist/lifecycle/dependencies.d.ts +0 -16
  54. package/dist/lifecycle/dependencies.js +0 -14
  55. package/dist/lifecycle/index.d.ts +0 -756
  56. package/dist/lifecycle/index.js +0 -25
  57. package/dist/plugin/container.d.ts +0 -855
  58. package/dist/plugin/container.js +0 -13
  59. package/dist/plugin/dependencies.d.ts +0 -860
  60. package/dist/plugin/dependencies.js +0 -9
@@ -1,119 +1,102 @@
1
+ import { readFile, writeFile } from 'node:fs/promises';
1
2
  import { deepEqual } from 'fast-equals';
2
- export class LifecycleInternal {
3
- #normalizer;
4
- #readFile;
5
- #writeFile;
6
- #formatFiles;
7
- #processNxAndProjectJsons;
8
- #assertNxJson;
9
- #assertProjectJson;
10
- #logger;
11
- lifecycleInternal;
12
- constructor(normalizer, readFile, writeFile, formatFiles, processNxAndProjectJsons, assertNxJson, assertProjectJson, logger){
13
- this.#normalizer = normalizer;
14
- this.#readFile = readFile;
15
- this.#writeFile = writeFile;
16
- this.#formatFiles = formatFiles;
17
- this.#processNxAndProjectJsons = processNxAndProjectJsons;
18
- this.#assertNxJson = assertNxJson;
19
- this.#assertProjectJson = assertProjectJson;
20
- this.#logger = logger;
21
- this.lifecycleInternal = this.#lifecycleInternal.bind(this);
22
- }
23
- async #lifecycleInternal(options, context) {
24
- const normalized = await this.#normalizer.normalizeOptions(options, context);
25
- const { nxJson, projectJsons } = await this.#loadJsonConfigs(normalized);
26
- this.#warnUndeclaredBindings(normalized.bindings, projectJsons.map(({ data })=>data));
27
- const { processedNxJson, processedProjectJsons } = this.#processNxAndProjectJsons({
28
- nxJson: nxJson.data,
29
- projectJsons: projectJsons.map(({ data })=>data),
30
- options: normalized
31
- });
32
- await this.#saveJsonConfigs({
33
- jsons: [
34
- {
35
- ...nxJson,
36
- processed: processedNxJson
37
- },
38
- ...projectJsons.map((projectJson, i)=>({
39
- ...projectJson,
40
- processed: processedProjectJsons[i]
41
- }))
42
- ],
43
- options: normalized
3
+ import { formatFiles } from 'format-file';
4
+ import { assertNxJson, assertProjectJson } from '#schemas';
5
+ import { normalizeOptions } from './normalizer.js';
6
+ import { processNxAndProjectJsons } from './processor.js';
7
+ const loadJsonConfigs = async ({ nxJsonPath, packageJsonPaths })=>{
8
+ const [rawNxJson, ...rawProjectJsons] = await Promise.all([
9
+ readFile(nxJsonPath, 'utf8'),
10
+ ...packageJsonPaths.map(async ({ name, path })=>({
11
+ name,
12
+ path,
13
+ rawData: await readFile(path, 'utf8')
14
+ }))
15
+ ]);
16
+ const parsedNxJson = JSON.parse(rawNxJson);
17
+ try {
18
+ assertNxJson(parsedNxJson);
19
+ } catch (err) {
20
+ throw new Error('Failed to parse nx.json', {
21
+ cause: err
44
22
  });
45
23
  }
46
- async #loadJsonConfigs({ nxJsonPath, packageJsonPaths }) {
47
- const [rawNxJson, ...rawProjectJsons] = await Promise.all([
48
- this.#readFile(nxJsonPath, 'utf8'),
49
- ...packageJsonPaths.map(async ({ name, path })=>({
50
- name,
51
- path,
52
- rawData: await this.#readFile(path, 'utf8')
53
- }))
54
- ]);
55
- const parsedNxJson = JSON.parse(rawNxJson);
56
- try {
57
- this.#assertNxJson(parsedNxJson);
58
- } catch (err) {
59
- throw new Error('Failed to parse nx.json', {
60
- cause: err
61
- });
62
- }
63
- return {
64
- nxJson: {
65
- name: 'nx.json',
66
- path: nxJsonPath,
67
- data: parsedNxJson
68
- },
69
- projectJsons: rawProjectJsons.map(({ name, path, rawData })=>{
70
- const data = JSON.parse(rawData);
71
- try {
72
- this.#assertProjectJson(data);
73
- } catch (err) {
74
- throw new Error(`Failed to parse ${path}`, {
75
- cause: err
76
- });
77
- }
78
- return {
79
- name,
80
- path,
81
- data
82
- };
83
- })
84
- };
85
- }
86
- #warnUndeclaredBindings(bindings, projectJsons) {
87
- const declaredTargets = new Set(projectJsons.flatMap((projectJson)=>Object.keys(projectJson.targets ?? {})));
88
- for (const targetName of Object.keys(bindings)){
89
- if (!declaredTargets.has(targetName)) {
90
- this.#logger.warn(`Bound target ${targetName} is not declared in any project.json. Is it a typo?`);
24
+ return {
25
+ nxJson: {
26
+ name: 'nx.json',
27
+ path: nxJsonPath,
28
+ data: parsedNxJson
29
+ },
30
+ projectJsons: rawProjectJsons.map(({ name, path, rawData })=>{
31
+ const data = JSON.parse(rawData);
32
+ try {
33
+ assertProjectJson(data);
34
+ } catch (err) {
35
+ throw new Error(`Failed to parse ${path}`, {
36
+ cause: err
37
+ });
91
38
  }
39
+ return {
40
+ name,
41
+ path,
42
+ data
43
+ };
44
+ })
45
+ };
46
+ };
47
+ const warnUndeclaredBindings = (bindings, projectJsons)=>{
48
+ const declaredTargets = new Set(projectJsons.flatMap((projectJson)=>Object.keys(projectJson.targets ?? {})));
49
+ for (const targetName of Object.keys(bindings)){
50
+ if (!declaredTargets.has(targetName)) {
51
+ console.warn(`Bound target ${targetName} is not declared in any project.json. Is it a typo?`);
92
52
  }
93
53
  }
94
- async #saveJsonConfigs({ jsons, options }) {
95
- const filesToUpdate = [];
96
- for (const { path, data, processed } of jsons){
97
- if (deepEqual(data, processed)) {
98
- continue;
99
- }
100
- if (options.check) {
101
- throw new Error(`File ${path} is not up to date`);
102
- }
103
- filesToUpdate.push({
104
- path,
105
- processed
106
- });
54
+ };
55
+ const saveJsonConfigs = async ({ jsons, options })=>{
56
+ const filesToUpdate = [];
57
+ for (const { path, data, processed } of jsons){
58
+ if (deepEqual(data, processed)) {
59
+ continue;
107
60
  }
108
- await Promise.all(filesToUpdate.map(async ({ path, processed })=>{
109
- this.#logger.info(`Updating ${path}`);
110
- if (options.dryRun) {
111
- return;
112
- }
113
- return this.#writeFile(path, JSON.stringify(processed), 'utf8');
114
- }));
115
- await this.#formatFiles(filesToUpdate.map((file)=>file.path));
61
+ if (options.check) {
62
+ throw new Error(`File ${path} is not up to date`);
63
+ }
64
+ filesToUpdate.push({
65
+ path,
66
+ processed
67
+ });
116
68
  }
117
- }
69
+ for (const { path } of filesToUpdate){
70
+ console.info(`Updating ${path}`);
71
+ }
72
+ if (options.dryRun) {
73
+ return;
74
+ }
75
+ await Promise.all(filesToUpdate.map(async ({ path, processed })=>writeFile(path, JSON.stringify(processed), 'utf8')));
76
+ await formatFiles(filesToUpdate.map((file)=>file.path));
77
+ };
78
+ export const lifecycleInternal = async (options, context)=>{
79
+ const normalized = await normalizeOptions(options, context);
80
+ const { nxJson, projectJsons } = await loadJsonConfigs(normalized);
81
+ warnUndeclaredBindings(normalized.bindings, projectJsons.map(({ data })=>data));
82
+ const { processedNxJson, processedProjectJsons } = processNxAndProjectJsons({
83
+ nxJson: nxJson.data,
84
+ projectJsons: projectJsons.map(({ data })=>data),
85
+ options: normalized
86
+ });
87
+ await saveJsonConfigs({
88
+ jsons: [
89
+ {
90
+ ...nxJson,
91
+ processed: processedNxJson
92
+ },
93
+ ...projectJsons.map((projectJson, i)=>({
94
+ ...projectJson,
95
+ processed: processedProjectJsons[i]
96
+ }))
97
+ ],
98
+ options: normalized
99
+ });
100
+ };
118
101
 
119
102
  //# sourceMappingURL=lifecycle-internal.js.map
@@ -1,5 +1,3 @@
1
- import type { readFile as ReadFile } from 'node:fs/promises';
2
- import type { ParseCwd } from 'parse-cwd';
3
1
  import type { LifecycleOptions, LifecycleOptionsOrConfig } from './schema.js';
4
2
  import type { NxContext } from './types.js';
5
3
  export interface NormalizedOptions {
@@ -13,13 +11,21 @@ export interface NormalizedOptions {
13
11
  stages: LifecycleOptions['stages'];
14
12
  bindings: LifecycleOptions['bindings'];
15
13
  }
14
+ /**
15
+ * Options passed directly, or loaded from a config file.
16
+ *
17
+ * @param options - options, or where to load them from
18
+ * @returns stages and bindings, with any options passed directly
19
+ * @throws {Error} when the config file is invalid
20
+ */
21
+ export declare const loadOptions: (options: LifecycleOptionsOrConfig) => Promise<LifecycleOptions>;
16
22
  /**
17
23
  * Standardizes the options passed to this executor,
18
24
  * based on input, reasonable defaults, and project state.
25
+ *
26
+ * @param options - options provided directly, or where to load them from
27
+ * @param context - workspace root and projects
28
+ * @returns options with defaults and file paths resolved
19
29
  */
20
- export declare class Normalizer {
21
- #private;
22
- constructor(isCI: () => boolean, parseCwd: ParseCwd, readFile: typeof ReadFile);
23
- normalizeOptions(options: LifecycleOptionsOrConfig, context: NxContext): Promise<NormalizedOptions>;
24
- }
30
+ export declare const normalizeOptions: (options: LifecycleOptionsOrConfig, context: NxContext) => Promise<NormalizedOptions>;
25
31
  //# sourceMappingURL=normalizer.d.ts.map
@@ -1,44 +1,37 @@
1
+ import { readFile } from 'node:fs/promises';
1
2
  import Path from 'node:path';
3
+ import { isCI } from 'ci-info';
4
+ import { parseCwd } from 'parse-cwd';
2
5
  import { isLifecycleOptions } from './schema.js';
3
- export class Normalizer {
4
- #isCI;
5
- #parseCwd;
6
- #readFile;
7
- constructor(isCI, parseCwd, readFile){
8
- this.#isCI = isCI;
9
- this.#parseCwd = parseCwd;
10
- this.#readFile = readFile;
6
+ export const loadOptions = async (options)=>{
7
+ if ('stages' in options) {
8
+ return options;
11
9
  }
12
- async normalizeOptions(options, context) {
13
- const loadedOptions = await this.#loadOptions(options);
10
+ const cwd = await parseCwd(options.cwd);
11
+ const configFile = Path.join(cwd, options.configFile ?? './lifecycle.json');
12
+ const loadedConfig = await readFile(configFile, 'utf8');
13
+ const parsedConfig = JSON.parse(loadedConfig);
14
+ if (isLifecycleOptions(parsedConfig)) {
14
15
  return {
15
- check: loadedOptions.check ?? this.#isCI(),
16
- dryRun: loadedOptions.dryRun ?? false,
17
- nxJsonPath: Path.join(context.root, 'nx.json'),
18
- packageJsonPaths: context.projects.map((project)=>({
19
- name: project.name,
20
- path: Path.join(context.root, project.root, 'project.json')
21
- })),
22
- stages: loadedOptions.stages,
23
- bindings: loadedOptions.bindings
16
+ ...parsedConfig,
17
+ ...options
24
18
  };
25
19
  }
26
- async #loadOptions(options) {
27
- if ('stages' in options) {
28
- return options;
29
- }
30
- const cwd = await this.#parseCwd(options.cwd);
31
- const configFile = Path.join(cwd, options.configFile ?? './lifecycle.json');
32
- const loadedConfig = await this.#readFile(configFile, 'utf8');
33
- const parsedConfig = JSON.parse(loadedConfig);
34
- if (isLifecycleOptions(parsedConfig)) {
35
- return {
36
- ...parsedConfig,
37
- ...options
38
- };
39
- }
40
- throw new Error(`Invalid config loaded from ${configFile}`);
41
- }
42
- }
20
+ throw new Error(`Invalid config loaded from ${configFile}`);
21
+ };
22
+ export const normalizeOptions = async (options, context)=>{
23
+ const loadedOptions = await loadOptions(options);
24
+ return {
25
+ check: loadedOptions.check ?? isCI,
26
+ dryRun: loadedOptions.dryRun ?? false,
27
+ nxJsonPath: Path.join(context.root, 'nx.json'),
28
+ packageJsonPaths: context.projects.map((project)=>({
29
+ name: project.name,
30
+ path: Path.join(context.root, project.root, 'project.json')
31
+ })),
32
+ stages: loadedOptions.stages,
33
+ bindings: loadedOptions.bindings
34
+ };
35
+ };
43
36
 
44
37
  //# sourceMappingURL=normalizer.js.map
@@ -39,7 +39,7 @@ export interface LifecyclePlan {
39
39
  * @throws if stages or bindings are inconsistent
40
40
  */
41
41
  export declare const planLifecycle: (options: ProcessorOptions) => LifecyclePlan;
42
- export type NxAndProjectJsonProcessor = ({ nxJson, projectJsons, options, }: {
42
+ export declare const processNxAndProjectJsons: ({ nxJson, projectJsons, options, }: {
43
43
  nxJson: NxJson;
44
44
  projectJsons: ProjectJson[];
45
45
  options: ProcessorOptions;
@@ -47,7 +47,5 @@ export type NxAndProjectJsonProcessor = ({ nxJson, projectJsons, options, }: {
47
47
  processedNxJson: NxJson;
48
48
  processedProjectJsons: ProjectJson[];
49
49
  };
50
- export declare const nxAndProjectJsonProcessorIdentifier: import("haywire").HaywireId<NxAndProjectJsonProcessor, null, null, false, false, false, false, false>;
51
- export declare const processNxAndProjectJsons: NxAndProjectJsonProcessor;
52
50
  export {};
53
51
  //# sourceMappingURL=processor.d.ts.map
@@ -1,4 +1,3 @@
1
- import { identifier } from 'haywire';
2
1
  import { isEmpty } from '#schemas';
3
2
  import { NOOP_EXECUTOR } from './constants.js';
4
3
  const calculateTargets = ({ stages })=>{
@@ -61,12 +60,15 @@ const registerTargets = ({ lifecycleTargets, options })=>{
61
60
  };
62
61
  const calculateTargetsToRemove = ({ lifecycleTargets, nxJson })=>{
63
62
  const targetsToRemove = new Set();
64
- for (const [targetName, target] of lifecycleTargets.entries()){
63
+ for (const [targetName, target] of lifecycleTargets){
65
64
  if (target.kind !== 'base') {
66
65
  targetsToRemove.add(targetName);
67
66
  }
68
67
  }
69
- for (const [targetName, target] of Object.entries(nxJson.targetDefaults ?? {})){
68
+ if (!nxJson.targetDefaults) {
69
+ return targetsToRemove;
70
+ }
71
+ for (const [targetName, target] of Object.entries(nxJson.targetDefaults)){
70
72
  if (!lifecycleTargets.has(targetName) && '__lifecycle' in (target.configurations ?? {})) {
71
73
  targetsToRemove.add(targetName);
72
74
  }
@@ -78,12 +80,15 @@ const normalizeDependencyTarget = (dependencyTarget)=>dependencyTarget.replace(/
78
80
  const removeDependencyTargets = ({ target, targetsToRemove })=>(target.dependsOn ?? []).filter((dependency)=>!targetsToRemove.has(normalizeDependencyTarget(getDependencyTarget(dependency))));
79
81
  const validateLifecycleDependencies = ({ lifecycleTargets, options })=>{
80
82
  for (const [stageName, { dependsOn }] of Object.entries(options.stages)){
81
- for (const dependency of dependsOn ?? []){
83
+ if (!dependsOn) {
84
+ continue;
85
+ }
86
+ for (const dependency of dependsOn){
82
87
  const dependencyTarget = getDependencyTarget(dependency);
83
88
  const normalized = normalizeDependencyTarget(dependencyTarget);
84
89
  const lifecycleTarget = lifecycleTargets.get(normalized);
85
90
  if (lifecycleTarget && lifecycleTarget.kind !== 'base') {
86
- const suggested = dependencyTarget.replace(normalized, lifecycleTarget.stage);
91
+ const suggested = dependencyTarget.replace(normalized, ()=>lifecycleTarget.stage);
87
92
  throw new Error(`Lifecycle stage ${stageName} cannot depend on ${dependencyTarget}, which is internal to stage ${lifecycleTarget.stage}. Depend on ${suggested} instead`);
88
93
  }
89
94
  }
@@ -91,7 +96,10 @@ const validateLifecycleDependencies = ({ lifecycleTargets, options })=>{
91
96
  };
92
97
  const validateStaleDependencies = ({ options, targetsToRemove })=>{
93
98
  for (const [stageName, { dependsOn }] of Object.entries(options.stages)){
94
- for (const dependency of dependsOn ?? []){
99
+ if (!dependsOn) {
100
+ continue;
101
+ }
102
+ for (const dependency of dependsOn){
95
103
  const dependencyTarget = getDependencyTarget(dependency);
96
104
  if (targetsToRemove.has(normalizeDependencyTarget(dependencyTarget))) {
97
105
  throw new Error(`Lifecycle stage ${stageName} cannot depend on ${dependencyTarget}, which is a stale lifecycle target in nx.json`);
@@ -119,7 +127,7 @@ const processNxJson = ({ nxJson, lifecycleTargets, registeredTargets, targetsToR
119
127
  ...originalTargets
120
128
  };
121
129
  const lifecycles = {};
122
- for (const [targetName, target] of lifecycleTargets.entries()){
130
+ for (const [targetName, target] of lifecycleTargets){
123
131
  lifecycles[targetName] = {
124
132
  executor: NOOP_EXECUTOR,
125
133
  dependsOn: [
@@ -147,7 +155,7 @@ const processNxJson = ({ nxJson, lifecycleTargets, registeredTargets, targetsToR
147
155
  };
148
156
  }
149
157
  }
150
- for (const [targetName, lifecycleTarget] of registeredTargets.entries()){
158
+ for (const [targetName, lifecycleTarget] of registeredTargets){
151
159
  const registeredTarget = targetDefaults[targetName] ?? {};
152
160
  registeredTarget.dependsOn ??= [];
153
161
  registeredTarget.dependsOn.push(lifecycleTarget.previousHook);
@@ -156,11 +164,12 @@ const processNxJson = ({ nxJson, lifecycleTargets, registeredTargets, targetsToR
156
164
  }
157
165
  Object.assign(targetDefaults, lifecycles);
158
166
  for (const [targetName, target] of Object.entries(targetDefaults)){
159
- if (target.dependsOn.length === 0) {
160
- const originalTarget = originalTargets[targetName] ?? {};
161
- if (!originalTarget.dependsOn) {
162
- delete target.dependsOn;
163
- }
167
+ if (target.dependsOn.length > 0) {
168
+ continue;
169
+ }
170
+ const originalTarget = originalTargets[targetName] ?? {};
171
+ if (!originalTarget.dependsOn) {
172
+ delete target.dependsOn;
164
173
  }
165
174
  }
166
175
  return {
@@ -207,7 +216,6 @@ const processProjectJson = ({ projectJson, lifecycleTargets, registeredTargets,
207
216
  targets: processedTargets
208
217
  };
209
218
  };
210
- export const nxAndProjectJsonProcessorIdentifier = identifier();
211
219
  export const processNxAndProjectJsons = ({ nxJson, projectJsons, options })=>{
212
220
  const { lifecycleTargets, registeredTargets } = planLifecycle(options);
213
221
  const targetsToRemove = calculateTargetsToRemove({