nx-lifecycle 0.0.2
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/CHANGELOG.md +25 -0
- package/LICENSE +21 -0
- package/README.md +196 -0
- package/dist/executors/lifecycle/constants.d.ts +2 -0
- package/dist/executors/lifecycle/constants.js +2 -0
- package/dist/executors/lifecycle/depedencies.d.ts +13 -0
- package/dist/executors/lifecycle/depedencies.js +11 -0
- package/dist/executors/lifecycle/index.cjs +13 -0
- package/dist/executors/lifecycle/index.d.cts +49 -0
- package/dist/executors/lifecycle/index.d.ts +47 -0
- package/dist/executors/lifecycle/index.js +22 -0
- package/dist/executors/lifecycle/lifecycle.d.ts +23 -0
- package/dist/executors/lifecycle/lifecycle.js +102 -0
- package/dist/executors/lifecycle/normalizer.d.ts +23 -0
- package/dist/executors/lifecycle/normalizer.js +21 -0
- package/dist/executors/lifecycle/processor.d.ts +15 -0
- package/dist/executors/lifecycle/processor.js +239 -0
- package/dist/executors/lifecycle/types.d.ts +3 -0
- package/dist/executors/lifecycle/types.js +2 -0
- package/dist/schemas/configurations.d.ts +10 -0
- package/dist/schemas/configurations.js +21 -0
- package/dist/schemas/depend-on.d.ts +52 -0
- package/dist/schemas/depend-on.js +37 -0
- package/dist/schemas/empty.d.ts +6 -0
- package/dist/schemas/empty.js +7 -0
- package/dist/schemas/index.d.ts +7 -0
- package/dist/schemas/index.js +7 -0
- package/dist/schemas/lib/ajv.d.ts +3 -0
- package/dist/schemas/lib/ajv.js +7 -0
- package/dist/schemas/nx-json.d.ts +60 -0
- package/dist/schemas/nx-json.js +13 -0
- package/dist/schemas/project-json.d.ts +60 -0
- package/dist/schemas/project-json.js +13 -0
- package/dist/schemas/target.d.ts +99 -0
- package/dist/schemas/target.js +14 -0
- package/executors.json +9 -0
- package/package.json +72 -0
- package/src/executors/lifecycle/schema.json +98 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# nx-lifecycle
|
|
2
|
+
|
|
3
|
+
## 0.0.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 31f81fa: Internal dependency updates
|
|
8
|
+
- 689ef38: Add README
|
|
9
|
+
- 3e7ee18: Dependency bumps
|
|
10
|
+
- f6a4729: Update year in LICENSE
|
|
11
|
+
- 9c786d0: Update dependencies
|
|
12
|
+
- Updated dependencies [3dc29d2]
|
|
13
|
+
- Updated dependencies [31f81fa]
|
|
14
|
+
- Updated dependencies [3e7ee18]
|
|
15
|
+
- Updated dependencies [f6a4729]
|
|
16
|
+
- Updated dependencies [cab8478]
|
|
17
|
+
- Updated dependencies [c2c0990]
|
|
18
|
+
- Updated dependencies [9c786d0]
|
|
19
|
+
- Updated dependencies [5984ed5]
|
|
20
|
+
- haywire@0.1.0
|
|
21
|
+
- juniper@1.1.11
|
|
22
|
+
- common-proxy@0.0.2
|
|
23
|
+
- default-import@1.1.6
|
|
24
|
+
- format-file@0.0.5
|
|
25
|
+
- nx-plugin-handler@0.0.3
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Jacob Ley
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
<div style="text-align:center">
|
|
2
|
+
|
|
3
|
+
# nx-lifecycle
|
|
4
|
+
An [Nx](https://nx.dev/) plugin that injects specific targets into high level workflows.
|
|
5
|
+
|
|
6
|
+
[](https://www.npmjs.com/package/nx-lifecycle)
|
|
7
|
+
[](https://github.com/JacobLey/leyman/blob/main/tools/nx-lifecycle/LICENSE)
|
|
8
|
+
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [Introduction](#introduction)
|
|
14
|
+
- [Usage](#usage)
|
|
15
|
+
- [API](#api)
|
|
16
|
+
- [check](#check)
|
|
17
|
+
- [dryRun](#dryrun)
|
|
18
|
+
|
|
19
|
+
## Introduction
|
|
20
|
+
|
|
21
|
+
Nx targets achieve a specific task, such as loading external config, codegen, type checking, formatting, test execution or enforcing code coverage.
|
|
22
|
+
|
|
23
|
+
Some of these tasks depend on each other, others can be executed in parallel.
|
|
24
|
+
|
|
25
|
+
Nx uses the target's [dependsOn](https://nx.dev/reference/project-configuration#dependson) field to natively manage this, and will execute tasks whenever all dependencies are successfully complete.
|
|
26
|
+
|
|
27
|
+
In practice, this quickly becomes unweildy. In order to properly configure a given task, you have to have full context about all other tasks (including those of your dependencies). You also need to make sure all dependents are updated whenever you add another step to a task.
|
|
28
|
+
|
|
29
|
+
For example, perhaps your code contains a typescript build step [nx-tsc](https://www.npmjs.com/package/nx-tsc), followed by a unit test [mocha](https://mochajs.org/). An initial config will look like:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"targets": {
|
|
34
|
+
"nx-tsc": {
|
|
35
|
+
"dependsOn": ["^nx-tsc"]
|
|
36
|
+
},
|
|
37
|
+
"mocha": {
|
|
38
|
+
"dependsOn": ["nx-tsc"]
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Your mocha task depends on the nx-tsc first, and nx-tsc depends on any of _its_ dependencies to be built first.
|
|
45
|
+
|
|
46
|
+
Now what if you need want to add a step _after_ to your build? Perhaps you want to use your recently built JS code to [generate a JSON config](https://www.npmjs.com/package/nx-populate-files).
|
|
47
|
+
|
|
48
|
+
Now your mocha command needs to update to depend on that populate step, rather than `nx-tsc`. Your `nx-tsc` command _also_ needs to update its dependencies to depend on that task, rather than `nx-tsc`. But only if your dependencies include this populate step!
|
|
49
|
+
|
|
50
|
+
Instead of simply adding this new task, you now need to conditionally manage the dependecones on two other tasks! Imagine how this problem can grow as we begin to support more tasks like type checking, code coverage, or codegen. Each implemented slightly different in each package, especailly if you use more than one language in your monorepo!
|
|
51
|
+
|
|
52
|
+
`nx-lifecycle` is the solution to this problem, by breaking down the solution into two steps:
|
|
53
|
+
|
|
54
|
+
### High level tasks
|
|
55
|
+
|
|
56
|
+
We define abstract workflows that can optionally be used across any and all projects, regardless of language, purpose, or implementation.
|
|
57
|
+
|
|
58
|
+
An example would breaking up general steps around a build/compile step, a formatter/linter, and test execution/coverage. We can fairly safely say that the build step will depend on our dependencies build steps, and test execution will depend on our own projects build step.
|
|
59
|
+
|
|
60
|
+
Then we split up these high level tasks into even simpler tasks.
|
|
61
|
+
An example would be our test suite may contain both unit-tests and functional tests, and then a final step to enforce the resulting code coverage achieves acceptable levels.
|
|
62
|
+
|
|
63
|
+
Note these steps are still abstract, and it is not necessary that every project is capable of implementing every task.
|
|
64
|
+
|
|
65
|
+
### Bind implementions to tasks
|
|
66
|
+
|
|
67
|
+
Now we can bind project specific implementations to each abstract task.
|
|
68
|
+
|
|
69
|
+
In the original example, perhaps we bind our `nx-tsc` target to a `build:run` step, and our `nx-populate-files` step to a `build:post` step.
|
|
70
|
+
|
|
71
|
+
Then our `mocha` target can be bound to a `test:unit` step.
|
|
72
|
+
|
|
73
|
+
You can bind multiple targets to a single step. For example if your code also includes some Java projects, you might also bind your [@nx/gradle](https://nx.dev/nx-api/gradle]) target to `build:run`.
|
|
74
|
+
|
|
75
|
+
### Instrumenting your `nx.json` and `project.json`
|
|
76
|
+
|
|
77
|
+
Now that you configured your abstract tasks and bound implementations, `nx-lifecycle` will update your `nx.json` and relevant `project.json` files will all the `dependsOn` to make your code run efficiently and deterministically!
|
|
78
|
+
|
|
79
|
+
Any time you want to add or remove targets for a project, simply re-run `nx-lifecycle`'s plugin and all dependencies are up to date!
|
|
80
|
+
|
|
81
|
+
## Installation
|
|
82
|
+
|
|
83
|
+
`npm i nx-lifecycle`
|
|
84
|
+
|
|
85
|
+
Register it as a target in a `project.json`:
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"targets": {
|
|
89
|
+
"lifecycle": {
|
|
90
|
+
"executor": "nx-lifecycle:lifecycle",
|
|
91
|
+
"options": {
|
|
92
|
+
"stages": {
|
|
93
|
+
"build": {
|
|
94
|
+
"hooks": ["pre", "run", "post"],
|
|
95
|
+
"dependsOn": ["^build"]
|
|
96
|
+
},
|
|
97
|
+
"test": {
|
|
98
|
+
"hooks": ["run", "report"],
|
|
99
|
+
"dependsOn": ["build"]
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"bindings": {
|
|
103
|
+
"nx-tsc": "build:run",
|
|
104
|
+
"mocha": "test:run"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Note that this target only needs to be run once (not once per project) so it is recommended to put this somewhere like a root `project.json` to own this.
|
|
113
|
+
|
|
114
|
+
## Usage
|
|
115
|
+
|
|
116
|
+
Because the `lifecycle` executor manages target dependencies itself, it probably should not be executed along with targets that are normally executed, like the `build` and `test` targets in the example above.
|
|
117
|
+
|
|
118
|
+
For example, this project's use of `nx-lifecycle` is part of a [local-only project](https://github.com/JacobLey/leyman/blob/main/leyman/main/project.json#L15) that is never deployed anywhere, and exists only to manage the repo itself.
|
|
119
|
+
|
|
120
|
+
`nx-lifecycle` will produce additional targets in your `nx.json` and `project.json`s. These will all be backed by the [noop](https://nx.dev/nx-api/nx/executors/noop) executor, and have no need to invoke caching. In fact, you should never directly manage these commands, their dependencies, or their configurations.
|
|
121
|
+
|
|
122
|
+
You may declare one-off targets that _depend_ on these targets though, that is the whole point! You should able to declare a target depends on the build step, without having to explicitly manage how the build script is implemented.
|
|
123
|
+
|
|
124
|
+
`nx-lifecycle` will use the `nx.json` to assign defaults, and will assign defaults to your targets that are assigned there. However that does not guarantee every target is actually executable by every project. You must declare the target in your `project.json` as well. Do not worry about copying `nx-lifecycle`'s targets over.
|
|
125
|
+
|
|
126
|
+
## Executors
|
|
127
|
+
|
|
128
|
+
### lifecycle
|
|
129
|
+
|
|
130
|
+
Will update all `project.json`s and the `nx.json` with declared stages, and wire up dependencies to your bound targets, ensuring that you can reference your abstract targets, Nx will invoke the required targets in dependency order as necessary.
|
|
131
|
+
|
|
132
|
+
Requires configuration to execute.
|
|
133
|
+
|
|
134
|
+
#### configuration.stages
|
|
135
|
+
|
|
136
|
+
Required. No default value.
|
|
137
|
+
|
|
138
|
+
This is the [high level tasks](#high-level-tasks) logic as described above. Declares the generic targets to be owned by `nx-lifecycle`, which represent workflows with potentially multiple steps.
|
|
139
|
+
|
|
140
|
+
It is an object, where every key is the name of a stage. The value is another object that optionally has two fields:
|
|
141
|
+
|
|
142
|
+
- `steps`
|
|
143
|
+
- A list of "sub-stages" to be executed in order. The resulting name of the target will be delimeted by a colon: `stage:step`.
|
|
144
|
+
- `dependsOn`
|
|
145
|
+
- Accepts the same values as normal Nx [dependsOn](https://nx.dev/reference/project-configuration#dependson). Used to define relationship between stages, and their dependencies.
|
|
146
|
+
|
|
147
|
+
#### configuration.bindings
|
|
148
|
+
|
|
149
|
+
Required. No default value.
|
|
150
|
+
|
|
151
|
+
This is [bind implementions to tasks](#bind-implementions-to-tasks) logic as described above.
|
|
152
|
+
|
|
153
|
+
The value is an object mapping, where the key is the name of your specific target implementations (e.g. `nx-tsc` or `mocha`) and the value is the stage it maps to.
|
|
154
|
+
|
|
155
|
+
__\*\*NOTE\*\*__ If the bound stage has `steps` defined, then you must bind to a specific step. If there are no steps, then you reference the stage directly.
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
{
|
|
159
|
+
"targets": {
|
|
160
|
+
"lifecycle": {
|
|
161
|
+
"executor": "nx-lifecycle:lifecycle",
|
|
162
|
+
"options": {
|
|
163
|
+
"stages": {
|
|
164
|
+
"build": {
|
|
165
|
+
"dependsOn": ["^build"]
|
|
166
|
+
},
|
|
167
|
+
"test": {
|
|
168
|
+
"hooks": ["run", "report"],
|
|
169
|
+
"dependsOn": ["build"]
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
"bindings": {
|
|
173
|
+
"nx-tsc": "build",
|
|
174
|
+
"mocha": "test:run"
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
#### configuration.check
|
|
183
|
+
|
|
184
|
+
`boolean`. Default `true` in CI environments, otherwise `false`.
|
|
185
|
+
|
|
186
|
+
If `true`, the executor will fail if the `nx.json` or any `project.json` is out of sync with what `nx-lifecycle` believes is correct.
|
|
187
|
+
|
|
188
|
+
Useful for ensuring that generated configs are up to date in version control before proceeding with deployments.
|
|
189
|
+
|
|
190
|
+
#### configuration.dryRun
|
|
191
|
+
|
|
192
|
+
`boolean`. Default `false`.
|
|
193
|
+
|
|
194
|
+
If `true`, will do everything except actually write the updated config files.
|
|
195
|
+
|
|
196
|
+
Can still fail if `check` is `true`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { type FilesFormatter } from 'format-file';
|
|
3
|
+
export interface Logger {
|
|
4
|
+
info: (...args: unknown[]) => void;
|
|
5
|
+
error: (...args: unknown[]) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare const readFileIdentifier: import("haywire").HaywireId<typeof readFile, null, null, false, false, false, false>;
|
|
8
|
+
export declare const writeFileIdentifier: import("haywire").HaywireId<typeof writeFile, null, null, false, false, false, false>;
|
|
9
|
+
export declare const formatFileIdentifier: import("haywire").HaywireId<FilesFormatter, null, null, false, false, false, false>;
|
|
10
|
+
export declare const isCiIdentifier: import("haywire").HaywireId<boolean, null, "isCI", false, false, false, false>;
|
|
11
|
+
export declare const loggerIdentifier: import("haywire").HaywireId<Logger, null, null, false, false, false, false>;
|
|
12
|
+
export declare const dependenciesModule: import("haywire").Module<[import("haywire").NonExtendable<typeof readFile | null | undefined, null, null>] | [import("haywire").NonExtendable<typeof readFile | null, null, null>] | [import("haywire").NonExtendable<typeof readFile | undefined, null, null>] | [import("haywire").NonExtendable<typeof readFile, null, null>] | [import("haywire").NonExtendable<typeof writeFile | null | undefined, null, null>] | [import("haywire").NonExtendable<typeof writeFile | null, null, null>] | [import("haywire").NonExtendable<typeof writeFile | undefined, null, null>] | [import("haywire").NonExtendable<typeof writeFile, null, null>] | [import("haywire").NonExtendable<FilesFormatter | null | undefined, null, null>] | [import("haywire").NonExtendable<FilesFormatter | null, null, null>] | [import("haywire").NonExtendable<FilesFormatter | undefined, null, null>] | [import("haywire").NonExtendable<FilesFormatter, null, null>] | [import("haywire").NonExtendable<boolean | null | undefined, null, "isCI">] | [import("haywire").NonExtendable<boolean | null, null, "isCI">] | [import("haywire").NonExtendable<boolean | undefined, null, "isCI">] | [import("haywire").NonExtendable<boolean, null, "isCI">] | [import("haywire").NonExtendable<Logger | null | undefined, null, null>] | [import("haywire").NonExtendable<Logger | null, null, null>] | [import("haywire").NonExtendable<Logger | undefined, null, null>] | [import("haywire").NonExtendable<Logger, null, null>], never, false>;
|
|
13
|
+
//# sourceMappingURL=depedencies.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { isCI } from 'ci-info';
|
|
3
|
+
import { formatFiles } from 'format-file';
|
|
4
|
+
import { bind, createModule, identifier } from 'haywire';
|
|
5
|
+
export const readFileIdentifier = identifier();
|
|
6
|
+
export const writeFileIdentifier = identifier();
|
|
7
|
+
export const formatFileIdentifier = identifier();
|
|
8
|
+
export const isCiIdentifier = identifier().named('isCI');
|
|
9
|
+
export const loggerIdentifier = identifier();
|
|
10
|
+
export const dependenciesModule = createModule(bind(readFileIdentifier).withInstance(readFile)).addBinding(bind(writeFileIdentifier).withInstance(writeFile)).addBinding(bind(formatFileIdentifier).withInstance(formatFiles)).addBinding(bind(isCiIdentifier).withInstance(isCI)).addBinding(bind(loggerIdentifier).withInstance(console));
|
|
11
|
+
//# sourceMappingURL=depedencies.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "default", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return _default;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _commonproxy = require("common-proxy");
|
|
12
|
+
const _default = (0, _commonproxy.commonProxy)(import('./index.js'));
|
|
13
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
declare const _default: (options: Partial<{
|
|
2
|
+
check: boolean;
|
|
3
|
+
dryRun: boolean;
|
|
4
|
+
stages: Record<string, Partial<{
|
|
5
|
+
dependsOn: ((string | (Partial<{
|
|
6
|
+
projects: string | string[];
|
|
7
|
+
}> & Required<Pick<{
|
|
8
|
+
projects: string | string[];
|
|
9
|
+
}, "projects">> & Partial<{
|
|
10
|
+
target: string;
|
|
11
|
+
params: string;
|
|
12
|
+
}> & Required<Pick<{
|
|
13
|
+
target: string;
|
|
14
|
+
params: string;
|
|
15
|
+
}, "target">>) | (Partial<{
|
|
16
|
+
dependencies: boolean;
|
|
17
|
+
}> & Partial<{
|
|
18
|
+
target: string;
|
|
19
|
+
params: string;
|
|
20
|
+
}> & Required<Pick<{
|
|
21
|
+
target: string;
|
|
22
|
+
params: string;
|
|
23
|
+
}, "target">>)) & (string | (Partial<{
|
|
24
|
+
projects: string | string[];
|
|
25
|
+
}> & Required<Pick<{
|
|
26
|
+
projects: string | string[];
|
|
27
|
+
}, "projects">> & Partial<{
|
|
28
|
+
target: string;
|
|
29
|
+
params: string;
|
|
30
|
+
}> & Required<Pick<{
|
|
31
|
+
target: string;
|
|
32
|
+
params: string;
|
|
33
|
+
}, "target">>) | (Partial<{
|
|
34
|
+
dependencies: boolean;
|
|
35
|
+
}> & Partial<{
|
|
36
|
+
target: string;
|
|
37
|
+
params: string;
|
|
38
|
+
}> & Required<Pick<{
|
|
39
|
+
target: string;
|
|
40
|
+
params: string;
|
|
41
|
+
}, "target">>) | undefined))[];
|
|
42
|
+
hooks: string[];
|
|
43
|
+
}>>;
|
|
44
|
+
bindings: Record<string, string>;
|
|
45
|
+
}>, context: import("@nx/devkit").ExecutorContext) => Promise<{
|
|
46
|
+
success: boolean;
|
|
47
|
+
}>;
|
|
48
|
+
export default _default;
|
|
49
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
declare const _default: import("nx-plugin-handler").RawHandler<Partial<{
|
|
2
|
+
check: boolean;
|
|
3
|
+
dryRun: boolean;
|
|
4
|
+
stages: Record<string, Partial<{
|
|
5
|
+
dependsOn: ((string | (Partial<{
|
|
6
|
+
projects: string | string[];
|
|
7
|
+
}> & Required<Pick<{
|
|
8
|
+
projects: string | string[];
|
|
9
|
+
}, "projects">> & Partial<{
|
|
10
|
+
target: string;
|
|
11
|
+
params: string;
|
|
12
|
+
}> & Required<Pick<{
|
|
13
|
+
target: string;
|
|
14
|
+
params: string;
|
|
15
|
+
}, "target">>) | (Partial<{
|
|
16
|
+
dependencies: boolean;
|
|
17
|
+
}> & Partial<{
|
|
18
|
+
target: string;
|
|
19
|
+
params: string;
|
|
20
|
+
}> & Required<Pick<{
|
|
21
|
+
target: string;
|
|
22
|
+
params: string;
|
|
23
|
+
}, "target">>)) & (string | (Partial<{
|
|
24
|
+
projects: string | string[];
|
|
25
|
+
}> & Required<Pick<{
|
|
26
|
+
projects: string | string[];
|
|
27
|
+
}, "projects">> & Partial<{
|
|
28
|
+
target: string;
|
|
29
|
+
params: string;
|
|
30
|
+
}> & Required<Pick<{
|
|
31
|
+
target: string;
|
|
32
|
+
params: string;
|
|
33
|
+
}, "target">>) | (Partial<{
|
|
34
|
+
dependencies: boolean;
|
|
35
|
+
}> & Partial<{
|
|
36
|
+
target: string;
|
|
37
|
+
params: string;
|
|
38
|
+
}> & Required<Pick<{
|
|
39
|
+
target: string;
|
|
40
|
+
params: string;
|
|
41
|
+
}, "target">>) | undefined))[];
|
|
42
|
+
hooks: string[];
|
|
43
|
+
}>>;
|
|
44
|
+
bindings: Record<string, string>;
|
|
45
|
+
}>>;
|
|
46
|
+
export default _default;
|
|
47
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { bind, createContainer, createModule } from 'haywire';
|
|
2
|
+
import { handler } from 'nx-plugin-handler';
|
|
3
|
+
import { isNxJson, isNxJsonIdentifier, isProjectJson, isProjectJsonIdentifier } from '#schemas';
|
|
4
|
+
import { dependenciesModule, formatFileIdentifier, isCiIdentifier, loggerIdentifier, readFileIdentifier, writeFileIdentifier } from './depedencies.js';
|
|
5
|
+
import { Lifecycle } from './lifecycle.js';
|
|
6
|
+
import { Normalizer } from './normalizer.js';
|
|
7
|
+
import { nxAndProjectJsonProcessorIdentifier, processNxAndProjectJsons } from './processor.js';
|
|
8
|
+
const pluginModule = createModule(bind(Lifecycle).withConstructorProvider().withDependencies([
|
|
9
|
+
Normalizer,
|
|
10
|
+
readFileIdentifier,
|
|
11
|
+
writeFileIdentifier,
|
|
12
|
+
formatFileIdentifier,
|
|
13
|
+
nxAndProjectJsonProcessorIdentifier,
|
|
14
|
+
isNxJsonIdentifier,
|
|
15
|
+
isProjectJsonIdentifier,
|
|
16
|
+
loggerIdentifier
|
|
17
|
+
])).addBinding(bind(Normalizer).withConstructorProvider().withDependencies([
|
|
18
|
+
isCiIdentifier
|
|
19
|
+
])).addBinding(bind(nxAndProjectJsonProcessorIdentifier).withInstance(processNxAndProjectJsons)).addBinding(bind(isNxJsonIdentifier).withInstance(isNxJson)).addBinding(bind(isProjectJsonIdentifier).withInstance(isProjectJson));
|
|
20
|
+
const lifecycle = createContainer(dependenciesModule.mergeModule(pluginModule)).get(Lifecycle);
|
|
21
|
+
export default handler(lifecycle.lifecycle.bind(lifecycle));
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type * as fs from 'node:fs/promises';
|
|
2
|
+
import type { FilesFormatter } from 'format-file';
|
|
3
|
+
import type { IsNxJson, IsProjectJson } from '#schemas';
|
|
4
|
+
import type { Logger } from './depedencies.js';
|
|
5
|
+
import type { Normalizer } from './normalizer.js';
|
|
6
|
+
import type { NxAndProjectJsonProcessor } from './processor.js';
|
|
7
|
+
import type { LifecycleOptions } from './schema.js';
|
|
8
|
+
import type { SimpleExecutorContext } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Main logic for lifecycle file management.
|
|
11
|
+
*
|
|
12
|
+
* Loads the `nx.json` + `project.json`s for all projects,
|
|
13
|
+
* calculates the new targets and dependencies,
|
|
14
|
+
* and re-writes files as appropriate.
|
|
15
|
+
*/
|
|
16
|
+
export declare class Lifecycle {
|
|
17
|
+
#private;
|
|
18
|
+
constructor(normalizer: Normalizer, readFile: (typeof fs)['readFile'], writeFile: (typeof fs)['writeFile'], formatFiles: FilesFormatter, processNxAndProjectJsons: NxAndProjectJsonProcessor, isNxJson: IsNxJson, isProjectJson: IsProjectJson, logger: Logger);
|
|
19
|
+
lifecycle(options: LifecycleOptions, context: SimpleExecutorContext): Promise<{
|
|
20
|
+
success: boolean;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { deepEqual } from 'fast-equals';
|
|
2
|
+
export class Lifecycle {
|
|
3
|
+
#normalizer;
|
|
4
|
+
#readFile;
|
|
5
|
+
#writeFile;
|
|
6
|
+
#formatFiles;
|
|
7
|
+
#processNxAndProjectJsons;
|
|
8
|
+
#isNxJson;
|
|
9
|
+
#isProjectJson;
|
|
10
|
+
#logger;
|
|
11
|
+
constructor(normalizer, readFile, writeFile, formatFiles, processNxAndProjectJsons, isNxJson, isProjectJson, logger){
|
|
12
|
+
this.#normalizer = normalizer;
|
|
13
|
+
this.#readFile = readFile;
|
|
14
|
+
this.#writeFile = writeFile;
|
|
15
|
+
this.#formatFiles = formatFiles;
|
|
16
|
+
this.#processNxAndProjectJsons = processNxAndProjectJsons;
|
|
17
|
+
this.#isNxJson = isNxJson;
|
|
18
|
+
this.#isProjectJson = isProjectJson;
|
|
19
|
+
this.#logger = logger;
|
|
20
|
+
}
|
|
21
|
+
async lifecycle(options, context) {
|
|
22
|
+
const normalized = this.#normalizer.normalizeOptions(options, context);
|
|
23
|
+
const { nxJson, projectJsons } = await this.#loadJsonConfigs(normalized);
|
|
24
|
+
const { processedNxJson, processedProjectJsons } = this.#processNxAndProjectJsons({
|
|
25
|
+
nxJson: nxJson.data,
|
|
26
|
+
projectJsons: projectJsons.map(({ data })=>data),
|
|
27
|
+
options: normalized
|
|
28
|
+
});
|
|
29
|
+
await this.#saveJsonConfigs({
|
|
30
|
+
jsons: [
|
|
31
|
+
{
|
|
32
|
+
...nxJson,
|
|
33
|
+
processed: processedNxJson
|
|
34
|
+
},
|
|
35
|
+
...projectJsons.map((projectJson, i)=>({
|
|
36
|
+
...projectJson,
|
|
37
|
+
processed: processedProjectJsons[i]
|
|
38
|
+
}))
|
|
39
|
+
],
|
|
40
|
+
options: normalized
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
success: true
|
|
44
|
+
};
|
|
45
|
+
}
|
|
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
|
+
if (!this.#isNxJson(parsedNxJson)) {
|
|
57
|
+
throw new Error(`Failed to parse nx.json: ${JSON.stringify(this.#isNxJson.errors, null, 2)}`);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
nxJson: {
|
|
61
|
+
name: 'nx.json',
|
|
62
|
+
path: nxJsonPath,
|
|
63
|
+
data: parsedNxJson
|
|
64
|
+
},
|
|
65
|
+
projectJsons: rawProjectJsons.map(({ name, path, rawData })=>{
|
|
66
|
+
const data = JSON.parse(rawData);
|
|
67
|
+
if (!this.#isProjectJson(data)) {
|
|
68
|
+
throw new Error(`Failed to parse ${path}: ${JSON.stringify(this.#isProjectJson.errors, null, 2)}`);
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
name,
|
|
72
|
+
path,
|
|
73
|
+
data
|
|
74
|
+
};
|
|
75
|
+
})
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async #saveJsonConfigs({ jsons, options }) {
|
|
79
|
+
const filesToUpdate = [];
|
|
80
|
+
for (const { path, data, processed } of jsons){
|
|
81
|
+
if (deepEqual(data, processed)) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (options.check) {
|
|
85
|
+
throw new Error(`File ${path} is not up to date`);
|
|
86
|
+
}
|
|
87
|
+
filesToUpdate.push({
|
|
88
|
+
path,
|
|
89
|
+
processed
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
await Promise.all(filesToUpdate.map(async ({ path, processed })=>{
|
|
93
|
+
this.#logger.info(`Updating ${path}`);
|
|
94
|
+
if (options.dryRun) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
return this.#writeFile(path, JSON.stringify(processed), 'utf8');
|
|
98
|
+
}));
|
|
99
|
+
await this.#formatFiles(filesToUpdate.map((file)=>file.path));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { LifecycleOptions } from './schema.js';
|
|
2
|
+
import type { SimpleExecutorContext } from './types.js';
|
|
3
|
+
export interface NormalizedOptions {
|
|
4
|
+
check: boolean;
|
|
5
|
+
dryRun: boolean;
|
|
6
|
+
nxJsonPath: string;
|
|
7
|
+
packageJsonPaths: {
|
|
8
|
+
name: string;
|
|
9
|
+
path: string;
|
|
10
|
+
}[];
|
|
11
|
+
stages: NonNullable<LifecycleOptions['stages']>;
|
|
12
|
+
bindings: NonNullable<LifecycleOptions['bindings']>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Standardizes the options passed to this executor,
|
|
16
|
+
* based on input, reasonable defaults, and project state.
|
|
17
|
+
*/
|
|
18
|
+
export declare class Normalizer {
|
|
19
|
+
#private;
|
|
20
|
+
constructor(isCI: boolean);
|
|
21
|
+
normalizeOptions(options: LifecycleOptions, context: SimpleExecutorContext): NormalizedOptions;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=normalizer.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import Path from 'node:path';
|
|
2
|
+
export class Normalizer {
|
|
3
|
+
#isCI;
|
|
4
|
+
constructor(isCI){
|
|
5
|
+
this.#isCI = isCI;
|
|
6
|
+
}
|
|
7
|
+
normalizeOptions(options, context) {
|
|
8
|
+
return {
|
|
9
|
+
check: options.check ?? this.#isCI,
|
|
10
|
+
dryRun: options.dryRun ?? false,
|
|
11
|
+
nxJsonPath: Path.join(context.root, 'nx.json'),
|
|
12
|
+
packageJsonPaths: Object.values(context.projectsConfigurations.projects).map((projectConfig)=>({
|
|
13
|
+
name: projectConfig.name,
|
|
14
|
+
path: Path.join(context.root, projectConfig.root, 'project.json')
|
|
15
|
+
})),
|
|
16
|
+
stages: options.stages ?? {},
|
|
17
|
+
bindings: options.bindings ?? {}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=normalizer.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type NxJson, type ProjectJson } from '#schemas';
|
|
2
|
+
import type { NormalizedOptions } from './normalizer.js';
|
|
3
|
+
type ProcessorOptions = Pick<NormalizedOptions, 'bindings' | 'stages'>;
|
|
4
|
+
export type NxAndProjectJsonProcessor = ({ nxJson, projectJsons, options, }: {
|
|
5
|
+
nxJson: NxJson;
|
|
6
|
+
projectJsons: ProjectJson[];
|
|
7
|
+
options: ProcessorOptions;
|
|
8
|
+
}) => {
|
|
9
|
+
processedNxJson: NxJson;
|
|
10
|
+
processedProjectJsons: ProjectJson[];
|
|
11
|
+
};
|
|
12
|
+
export declare const nxAndProjectJsonProcessorIdentifier: import("haywire").HaywireId<NxAndProjectJsonProcessor, null, null, false, false, false, false>;
|
|
13
|
+
export declare const processNxAndProjectJsons: NxAndProjectJsonProcessor;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=processor.d.ts.map
|