rman 1.3.0 → 2.0.0-beta.3
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/README.md +65 -31
- package/builtins/platforms/node/augmentation/rmanrc.augmentation.d.ts +83 -0
- package/builtins/platforms/node/augmentation/system-info.augmentation.d.ts +26 -0
- package/builtins/platforms/node/augmentation/system-info.augmentation.js +79 -0
- package/builtins/platforms/node/commands/ci.command.d.ts +133 -0
- package/builtins/platforms/node/commands/ci.command.js +60 -0
- package/builtins/platforms/node/commands/clean.command.d.ts +185 -0
- package/builtins/platforms/node/commands/clean.command.js +74 -0
- package/builtins/platforms/node/index.d.ts +8 -0
- package/builtins/platforms/node/index.js +8 -0
- package/builtins/platforms/node/node-config.interface.d.ts +76 -0
- package/builtins/platforms/node/node-config.interface.js +7 -0
- package/builtins/platforms/node/node-manifest.provider.d.ts +68 -0
- package/builtins/platforms/node/node-manifest.provider.js +125 -0
- package/builtins/platforms/node/node.platform.d.ts +53 -0
- package/builtins/platforms/node/node.platform.js +134 -0
- package/builtins/platforms/node/services/ci.service.d.ts +47 -0
- package/builtins/platforms/node/services/ci.service.js +213 -0
- package/builtins/platforms/node/services/clean.service.d.ts +53 -0
- package/builtins/platforms/node/services/clean.service.js +237 -0
- package/builtins/platforms/node/services/publish.service.d.ts +114 -0
- package/builtins/platforms/node/services/publish.service.js +371 -0
- package/builtins/platforms/node/services/version-plan.service.d.ts +44 -0
- package/builtins/platforms/node/services/version-plan.service.js +58 -0
- package/builtins/platforms/node/utils/workspace-range.d.ts +26 -0
- package/builtins/platforms/node/utils/workspace-range.js +28 -0
- package/builtins/presets/index.d.ts +15 -0
- package/builtins/presets/index.js +57 -0
- package/builtins/presets/node.d.ts +16 -0
- package/builtins/presets/node.js +57 -0
- package/{services → builtins/publish-targets/docker}/docker-publish.service.d.ts +52 -31
- package/{services → builtins/publish-targets/docker}/docker-publish.service.js +49 -26
- package/builtins/publish-targets/docker/docker.target.d.ts +54 -0
- package/builtins/publish-targets/docker/docker.target.js +40 -0
- package/builtins/publish-targets/docker/index.d.ts +2 -0
- package/builtins/publish-targets/docker/index.js +2 -0
- package/builtins/publish-targets/npm/index.d.ts +2 -0
- package/builtins/publish-targets/npm/index.js +2 -0
- package/builtins/publish-targets/npm/npm-publish-target.d.ts +73 -0
- package/builtins/publish-targets/npm/npm-publish-target.js +95 -0
- package/builtins/publish-targets/npm/npm-view.d.ts +48 -0
- package/builtins/publish-targets/npm/npm-view.js +71 -0
- package/cli.d.ts +8 -0
- package/cli.js +228 -93
- package/commands/build.command.d.ts +177 -3
- package/commands/build.command.js +20 -10
- package/commands/changelog.command.d.ts +296 -3
- package/commands/changelog.command.js +127 -43
- package/commands/config.command.d.ts +44 -3
- package/commands/config.command.js +53 -24
- package/commands/diff.command.d.ts +37 -3
- package/commands/diff.command.js +25 -16
- package/commands/exec.command.d.ts +193 -3
- package/commands/exec.command.js +60 -58
- package/commands/github-release.command.d.ts +154 -3
- package/commands/github-release.command.js +67 -37
- package/commands/import.command.d.ts +36 -3
- package/commands/import.command.js +28 -20
- package/commands/info.command.d.ts +35 -7
- package/commands/info.command.js +36 -30
- package/commands/lint.command.d.ts +176 -0
- package/commands/lint.command.js +34 -0
- package/commands/list.command.d.ts +163 -3
- package/commands/list.command.js +109 -71
- package/commands/publish.command.d.ts +231 -0
- package/commands/publish.command.js +304 -0
- package/commands/run.command.d.ts +186 -6
- package/commands/run.command.js +26 -72
- package/commands/test.command.d.ts +173 -3
- package/commands/test.command.js +16 -10
- package/commands/version.command.d.ts +330 -3
- package/commands/version.command.js +292 -78
- package/commands.d.ts +32 -0
- package/commands.js +28 -0
- package/constants.js +1 -1
- package/core/application.d.ts +116 -0
- package/core/application.js +160 -0
- package/core/classes/package.d.ts +219 -0
- package/core/classes/package.js +214 -0
- package/core/classes/registry.d.ts +30 -0
- package/core/classes/registry.js +47 -0
- package/core/{repository.d.ts → classes/repository.d.ts} +69 -20
- package/core/{repository.js → classes/repository.js} +195 -75
- package/core/classes/service.d.ts +49 -0
- package/core/classes/service.js +40 -0
- package/core/{version-scheme.d.ts → classes/version-scheme.d.ts} +23 -1
- package/core/{version-scheme.js → classes/version-scheme.js} +29 -1
- package/core/classes/workspace.d.ts +275 -0
- package/core/classes/workspace.js +537 -0
- package/core/command-builder.d.ts +14 -0
- package/core/command-builder.js +94 -0
- package/core/config/config-file-scope.d.ts +107 -0
- package/core/config/config-file-scope.js +226 -0
- package/core/config/config-interpolator.d.ts +164 -0
- package/core/config/config-interpolator.js +577 -0
- package/core/config/config-paths.d.ts +65 -0
- package/core/config/config-paths.js +78 -0
- package/core/config/config-reader.d.ts +192 -0
- package/core/config/config-reader.js +727 -0
- package/core/{merge-config.d.ts → config/merge-config.d.ts} +33 -34
- package/core/config/merge-config.js +260 -0
- package/core/{resolve-target.d.ts → config/resolve-config-target.d.ts} +1 -1
- package/core/{resolve-target.js → config/resolve-config-target.js} +1 -1
- package/core/{custom-command.d.ts → interfaces/custom-command.d.ts} +46 -10
- package/core/{custom-command.js → interfaces/custom-command.js} +44 -17
- package/core/{manifest.d.ts → interfaces/manifest.d.ts} +49 -27
- package/core/interfaces/manifest.js +132 -0
- package/core/interfaces/plugin.d.ts +174 -0
- package/core/interfaces/plugin.js +124 -0
- package/core/interfaces/publish-target.d.ts +136 -0
- package/core/interfaces/publish-target.js +43 -0
- package/core/{run-step.d.ts → interfaces/run-step.d.ts} +4 -4
- package/core/interfaces/run-step.js +1 -0
- package/core/plugin-loader.d.ts +25 -0
- package/core/plugin-loader.js +53 -0
- package/index.d.ts +122 -24
- package/index.js +66 -16
- package/interfaces/config-scope.interface.d.ts +223 -0
- package/interfaces/config-scope.interface.js +1 -0
- package/interfaces/rman-config.interface.d.ts +821 -203
- package/interfaces/rman-config.interface.js +78 -1
- package/package.json +3 -1
- package/services/change-hash.service.d.ts +42 -8
- package/services/change-hash.service.js +92 -13
- package/services/changelog.service.d.ts +91 -52
- package/services/changelog.service.js +460 -74
- package/services/config-service.d.ts +8 -0
- package/services/config-service.js +3 -0
- package/services/exec.service.d.ts +23 -12
- package/services/exec.service.js +14 -9
- package/services/github-release.service.d.ts +45 -34
- package/services/github-release.service.js +15 -12
- package/services/import.service.d.ts +25 -14
- package/services/import.service.js +9 -5
- package/services/list.service.d.ts +67 -13
- package/services/list.service.js +62 -15
- package/services/run.service.d.ts +35 -22
- package/services/run.service.js +316 -233
- package/services/system-info.d.ts +1 -1
- package/services/version-plan.service.d.ts +35 -8
- package/services/version-plan.service.js +96 -36
- package/services/version.service.d.ts +34 -14
- package/services/version.service.js +35 -16
- package/services.d.ts +1 -1
- package/services.js +1 -1
- package/utils/bin-path.d.ts +6 -7
- package/utils/bin-path.js +7 -18
- package/utils/branch-guard.d.ts +30 -1
- package/utils/branch-guard.js +31 -0
- package/utils/color-yaml.d.ts +8 -0
- package/utils/color-yaml.js +86 -0
- package/utils/exec.d.ts +10 -0
- package/utils/exec.js +1 -1
- package/utils/git.d.ts +26 -0
- package/utils/git.js +65 -0
- package/utils/logger.d.ts +2 -2
- package/utils/logger.js +1 -1
- package/utils/package-filter.d.ts +135 -8
- package/utils/package-filter.js +204 -16
- package/utils/printable-config.d.ts +9 -1
- package/utils/printable-config.js +46 -1
- package/utils/progress-panel.d.ts +9 -0
- package/utils/progress-panel.js +23 -1
- package/utils/release-version.d.ts +1 -1
- package/utils/run-bin.d.ts +10 -0
- package/utils/run-bin.js +1 -1
- package/utils/run-options.d.ts +97 -0
- package/utils/run-options.js +81 -0
- package/utils/version-group.d.ts +31 -0
- package/utils/version-group.js +43 -0
- package/utils/version-stamp.d.ts +1 -1
- package/utils/version-stamp.js +1 -1
- package/commands/changed.command.d.ts +0 -3
- package/commands/changed.command.js +0 -29
- package/core/config.d.ts +0 -409
- package/core/config.js +0 -939
- package/core/extends-config.d.ts +0 -27
- package/core/extends-config.js +0 -89
- package/core/manifest.js +0 -150
- package/core/merge-config.js +0 -219
- package/core/package.d.ts +0 -83
- package/core/package.js +0 -106
- package/core/plugin.d.ts +0 -112
- package/core/plugin.js +0 -189
- package/core/workspace.d.ts +0 -68
- package/core/workspace.js +0 -83
- /package/{core/run-step.js → builtins/platforms/node/augmentation/rmanrc.augmentation.js} +0 -0
package/services/run.service.js
CHANGED
|
@@ -3,221 +3,31 @@ import { inspect } from 'node:util';
|
|
|
3
3
|
import colors from 'ansi-colors';
|
|
4
4
|
import { tokenize } from 'fast-tokenizer';
|
|
5
5
|
import { Task } from 'power-tasks';
|
|
6
|
+
import { Service } from '../core/classes/service.js';
|
|
6
7
|
import { exec } from '../utils/exec.js';
|
|
7
8
|
import { LOG_LEVELS, Logger, resolveRootLogLevel } from '../utils/logger.js';
|
|
8
9
|
import { filterPackages } from '../utils/package-filter.js';
|
|
9
10
|
import { ProgressPanel } from '../utils/progress-panel.js';
|
|
10
11
|
import { runBin } from '../utils/run-bin.js';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (stepSources.includes(source))
|
|
24
|
-
return;
|
|
25
|
-
stepSources.push(source);
|
|
26
|
-
}
|
|
27
|
-
RunService.addStepSource = addStepSource;
|
|
28
|
-
/** For tests, which would otherwise leak a source into every later case in the process. */
|
|
29
|
-
function clearStepSources() {
|
|
30
|
-
stepSources.length = 0;
|
|
31
|
-
}
|
|
32
|
-
RunService.clearStepSources = clearStepSources;
|
|
33
|
-
/**
|
|
34
|
-
* What the *package itself* declares for the lifecycle `script`, from the contributed sources
|
|
35
|
-
* alone - no `.rmanrc` involved. `undefined` when it declares nothing.
|
|
36
|
-
*
|
|
37
|
-
* Exported because `run` is not the only lifecycle rman wraps: `version` runs hooks around the
|
|
38
|
-
* version write, and npm spells those `preversion`/`version`/`postversion` in `package.json` -
|
|
39
|
-
* which is the same `pre<script>`/`<script>`/`post<script>` shape a step source already answers.
|
|
40
|
-
* So `VersionService` asks here for `'version'` instead of reading `manifest.raw.scripts` itself,
|
|
41
|
-
* and npm's version lifecycle keeps working with no second seam and no extra line in any plugin.
|
|
42
|
-
* A plugin for another ecosystem gets its own lifecycle hooks the moment it contributes steps.
|
|
43
|
-
*/
|
|
44
|
-
function contributedSlots(pkg, script) {
|
|
45
|
-
return firstContributed(pkg, script);
|
|
46
|
-
}
|
|
47
|
-
RunService.contributedSlots = contributedSlots;
|
|
48
|
-
/**
|
|
49
|
-
* Runs one slot of a lifecycle belonging to some operation other than `run` itself - `version`'s
|
|
50
|
-
* hooks around the version write are the only one so far.
|
|
51
|
-
*
|
|
52
|
-
* **Here rather than in `VersionService`, because the rule it applies is this service's**: the
|
|
53
|
-
* package's own declaration for `script` wins over the caller's `fallback`, slot by slot, exactly
|
|
54
|
-
* as `getScriptSteps` decides it for `run`. Kept in two places that rule would drift, and one of
|
|
55
|
-
* the copies would sit in the file that writes versions - which now runs no command of its own at
|
|
56
|
-
* all.
|
|
57
|
-
*
|
|
58
|
-
* `fallback` is the caller's own configured step(s), **already evaluated**: `version`'s three
|
|
59
|
-
* paths are in `DEFERRED_PATHS` precisely because only the caller can bind
|
|
60
|
-
* `${{ pkg.targetVersion }}`, so interpolating here would either be too early or need a scope this
|
|
61
|
-
* service has no business holding.
|
|
62
|
-
*
|
|
63
|
-
* **A list, not one joined string.** `VersionService` used to `join(' && ')` an array into a
|
|
64
|
-
* single shell line, which a function step cannot be part of - and which quietly changed the
|
|
65
|
-
* semantics of the shell case too, since `cd x && y` in one process is not the same as two.
|
|
66
|
-
*/
|
|
67
|
-
async function runLifecycleSlot(pkg, script, slot, fallback) {
|
|
68
|
-
const own = contributedSlots(pkg, script)?.[slot] ?? [];
|
|
69
|
-
const values = own.length ? own : (fallback ?? []);
|
|
70
|
-
for (const value of values) {
|
|
71
|
-
if (typeof value === 'function') {
|
|
72
|
-
await value(createStepContext(pkg, pkg.dirname));
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
await exec(value, { cwd: pkg.dirname, stdio: 'inherit' });
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
RunService.runLifecycleSlot = runLifecycleSlot;
|
|
79
|
-
function getConfig(pkg, script) {
|
|
80
|
-
const runCfg = pkg.config?.run;
|
|
81
|
-
const cfg = runCfg && typeof runCfg === 'object' ? runCfg[script] : undefined;
|
|
82
|
-
/** The bare-value shorthand. A function is `typeof 'function'`, not `'object'`, so without
|
|
83
|
-
* naming it here `run: { build: myFn }` fell through to the `{}` below - the long form would
|
|
84
|
-
* have worked and the short one silently done nothing, an arbitrary difference. */
|
|
85
|
-
if (typeof cfg === 'string' || typeof cfg === 'function' || Array.isArray(cfg))
|
|
86
|
-
return { exec: cfg };
|
|
87
|
-
return cfg && typeof cfg === 'object' ? cfg : {};
|
|
88
|
-
}
|
|
89
|
-
RunService.getConfig = getConfig;
|
|
90
|
-
/**
|
|
91
|
-
* The context a function step or `if` is handed - see [`RunStepContext`](../core/run-step.ts).
|
|
92
|
-
*
|
|
93
|
-
* `runBin` and `logger` are bound to *this run* rather than left to be imported, which is the
|
|
94
|
-
* whole reason they are handed over: an imported `runBin` knows neither the cwd nor the resolved
|
|
95
|
-
* log level.
|
|
96
|
-
*/
|
|
97
|
-
/**
|
|
98
|
-
* A `run.<script>.before`/`.exec`/`.after` (or `version.<slot>`) value: one step, or several to
|
|
99
|
-
* run in sequence. A shell command or a function, and a list may mix them.
|
|
100
|
-
*
|
|
101
|
-
* **Anything else throws, naming the path.** It used to `return []`, which meant a value rman did
|
|
102
|
-
* not recognize was dropped with no trace: writing a function here - the obvious guess, and now
|
|
103
|
-
* the supported form - produced `1 succeeded, 0 failed` with the step never run (measured). A
|
|
104
|
-
* configuration mistake has to be loud; silence here reads as success.
|
|
105
|
-
*
|
|
106
|
-
* Exported, and the only implementation: `VersionService` used to carry a second one that behaved
|
|
107
|
-
* differently, which is how `version.<slot>` came to join its array with `' && '`.
|
|
108
|
-
*/
|
|
109
|
-
function normalizeScriptValue(value, at) {
|
|
110
|
-
const items = Array.isArray(value) ? value : [value];
|
|
111
|
-
const steps = [];
|
|
112
|
-
for (let i = 0; i < items.length; i++) {
|
|
113
|
-
const item = items[i];
|
|
114
|
-
/** An empty string and an absent value are both "nothing here", which is how a `"[*]"` block
|
|
115
|
-
* declaring a slot some packages don't use has always behaved. */
|
|
116
|
-
if (item === undefined || item === null || item === '')
|
|
117
|
-
continue;
|
|
118
|
-
if (typeof item === 'string' || typeof item === 'function') {
|
|
119
|
-
steps.push(item);
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
const where = Array.isArray(value) ? `${at}[${i}]` : at;
|
|
123
|
-
throw new Error(`"${where}" must be a shell command or a function, but it is ${describeValue(item)}.\n` +
|
|
124
|
-
` A list of either (or both) runs them in sequence.`);
|
|
125
|
-
}
|
|
126
|
-
return steps;
|
|
127
|
-
}
|
|
128
|
-
RunService.normalizeScriptValue = normalizeScriptValue;
|
|
129
|
-
function createStepContext(pkg, cwd) {
|
|
130
|
-
const logLevel = resolveRootLogLevel(pkg.repository);
|
|
131
|
-
return {
|
|
132
|
-
pkg,
|
|
133
|
-
repository: pkg.repository,
|
|
134
|
-
cwd,
|
|
135
|
-
runBin: (bin, argv, opts) => runBin(bin, argv, { cwd, logLevel, ...opts }),
|
|
136
|
-
logger: new Logger(logLevel),
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
RunService.createStepContext = createStepContext;
|
|
140
|
-
function resolveEnvPlaceholders(value) {
|
|
141
|
-
return value.replace(/\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_, name) => process.env[name] ?? '');
|
|
142
|
-
}
|
|
143
|
-
/** Recursive-descent parser over `tokenizeIf`'s output: expr := or ; or := and ('or' and)* ;
|
|
144
|
-
* and := unary ('and' unary)* ; unary := 'not' unary | GROUP | NAME ['=' VALUE] */
|
|
145
|
-
function parseIfExpr(raw) {
|
|
146
|
-
if (typeof raw !== 'string' || !raw.trim())
|
|
147
|
-
return undefined;
|
|
148
|
-
const tokens = tokenizeIf(raw);
|
|
149
|
-
if (!tokens.length)
|
|
150
|
-
return undefined;
|
|
151
|
-
let i = 0;
|
|
152
|
-
const peek = () => tokens[i];
|
|
153
|
-
const next = () => tokens[i++];
|
|
154
|
-
function parseUnary() {
|
|
155
|
-
if (peek()?.toLowerCase() === 'not') {
|
|
156
|
-
next();
|
|
157
|
-
return { kind: 'not', node: parseUnary() };
|
|
158
|
-
}
|
|
159
|
-
const tok = next() ?? '';
|
|
160
|
-
/** A token containing whitespace can only be a collapsed `(...)` group - plain
|
|
161
|
-
* atom/operator tokens never do, since they're themselves split on whitespace. */
|
|
162
|
-
if (/\s/.test(tok)) {
|
|
163
|
-
const inner = parseIfExpr(tok);
|
|
164
|
-
if (!inner)
|
|
165
|
-
throw new Error(`Empty group in "if" expression: "${raw}"`);
|
|
166
|
-
return inner;
|
|
167
|
-
}
|
|
168
|
-
let value;
|
|
169
|
-
if (peek() === '=') {
|
|
170
|
-
next();
|
|
171
|
-
value = resolveEnvPlaceholders(next() ?? '');
|
|
172
|
-
}
|
|
173
|
-
return { kind: 'atom', name: tok, value };
|
|
174
|
-
}
|
|
175
|
-
function parseAnd() {
|
|
176
|
-
let node = parseUnary();
|
|
177
|
-
while (peek()?.toLowerCase() === 'and') {
|
|
178
|
-
next();
|
|
179
|
-
node = { kind: 'and', left: node, right: parseUnary() };
|
|
180
|
-
}
|
|
181
|
-
return node;
|
|
182
|
-
}
|
|
183
|
-
function parseOr() {
|
|
184
|
-
let node = parseAnd();
|
|
185
|
-
while (peek()?.toLowerCase() === 'or') {
|
|
186
|
-
next();
|
|
187
|
-
node = { kind: 'or', left: node, right: parseAnd() };
|
|
188
|
-
}
|
|
189
|
-
return node;
|
|
190
|
-
}
|
|
191
|
-
return parseOr();
|
|
192
|
-
}
|
|
193
|
-
RunService.parseIfExpr = parseIfExpr;
|
|
194
|
-
/** Evaluates a parsed `if` expression for one package. `statusCache` avoids repeat `git` calls
|
|
195
|
-
* for the same reference hash across packages/scripts in a single run. */
|
|
196
|
-
async function evaluateIf(repository, pkg, node, statusCache) {
|
|
197
|
-
if (node.kind === 'and') {
|
|
198
|
-
if (!(await evaluateIf(repository, pkg, node.left, statusCache)))
|
|
199
|
-
return false;
|
|
200
|
-
return evaluateIf(repository, pkg, node.right, statusCache);
|
|
201
|
-
}
|
|
202
|
-
if (node.kind === 'or') {
|
|
203
|
-
if (await evaluateIf(repository, pkg, node.left, statusCache))
|
|
204
|
-
return true;
|
|
205
|
-
return evaluateIf(repository, pkg, node.right, statusCache);
|
|
206
|
-
}
|
|
207
|
-
if (node.kind === 'not') {
|
|
208
|
-
return !(await evaluateIf(repository, pkg, node.node, statusCache));
|
|
209
|
-
}
|
|
210
|
-
return evaluateIfAtom(repository, pkg, node.name, node.value, statusCache);
|
|
211
|
-
}
|
|
212
|
-
RunService.evaluateIf = evaluateIf;
|
|
213
|
-
async function runScript(repository, script, options = {}) {
|
|
12
|
+
/**
|
|
13
|
+
* A service class - see `ListService` for the shape and `Service` for the three measured
|
|
14
|
+
* consequences a namespace had.
|
|
15
|
+
*
|
|
16
|
+
* **Only `runScript` became a method**, because only it takes a repository. `getConfig`,
|
|
17
|
+
* `parseIfExpr`, `normalizeScriptValue` and the rest take a `Package` or a raw value and stay
|
|
18
|
+
* functions on the namespace below - the same rule that leaves `ChangeHashService` a namespace
|
|
19
|
+
* entirely. Declared before the namespace, which TypeScript requires for the merge.
|
|
20
|
+
*/
|
|
21
|
+
export class RunService extends Service {
|
|
22
|
+
async runScript(script, options = {}) {
|
|
23
|
+
const repository = this.repository;
|
|
214
24
|
const commandName = options.commandName || 'run';
|
|
215
|
-
const rootCfg = getConfig(repository.rootPackage, script);
|
|
25
|
+
const rootCfg = RunService.getConfig(repository.rootPackage, script);
|
|
216
26
|
const logLevelDefault = resolveRootLogLevel(repository);
|
|
217
27
|
/** Standing inside a single package's own directory scopes the run to just that package
|
|
218
|
-
* (and drops the root bookend below) unless `--root` asks for the whole repository anyway -
|
|
28
|
+
* (and drops the root bookend below) unless `--from-root` asks for the whole repository anyway -
|
|
219
29
|
* a no-op when already at the root, or outside any known package. */
|
|
220
|
-
const cwdScope = options.
|
|
30
|
+
const cwdScope = options.fromRoot ? undefined : repository.currentPackage;
|
|
221
31
|
/** Global fallback for topo - individual packages can still override their own linking below,
|
|
222
32
|
* but the initial sort (topological vs alphabetical) has to be decided for the whole list at once. */
|
|
223
33
|
const topo = resolveBool(options.topo, repository.rootPackage, script, 'topo', true);
|
|
@@ -251,6 +61,12 @@ export var RunService;
|
|
|
251
61
|
for (let i = 0; i < steps.length; i++) {
|
|
252
62
|
const step = steps[i];
|
|
253
63
|
ctx.currentStep = step.name;
|
|
64
|
+
/** The command for a shell step, the function's own name for a JS one - and a bare
|
|
65
|
+
* marker for an anonymous function, which is still worth saying: the slot label alone
|
|
66
|
+
* does not distinguish a JS step from a shell one. `step.label` is not used directly
|
|
67
|
+
* because for a function it already folds the slot in (`before (js)`), which the row
|
|
68
|
+
* prints two characters to its left. */
|
|
69
|
+
ctx.currentCommand = step.command ?? (step.run?.name ? `${step.run.name}()` : '(js function)');
|
|
254
70
|
ctx.stepIndex = i;
|
|
255
71
|
if (panel.enabled) {
|
|
256
72
|
const onLine = (line) => {
|
|
@@ -260,7 +76,7 @@ export var RunService;
|
|
|
260
76
|
if (step.run)
|
|
261
77
|
await runFunctionStep(step.run, pkg, cwd, onLine);
|
|
262
78
|
else
|
|
263
|
-
await exec(step.command, { cwd, stdio: 'pipe', onLine });
|
|
79
|
+
await exec(step.command, { cwd, stdio: 'pipe', onLine, app: pkg.repository.app });
|
|
264
80
|
}
|
|
265
81
|
else {
|
|
266
82
|
/** Match the classic rman output: raw command output streams straight through
|
|
@@ -274,10 +90,23 @@ export var RunService;
|
|
|
274
90
|
if (step.run)
|
|
275
91
|
await runFunctionStep(step.run, pkg, cwd);
|
|
276
92
|
else
|
|
277
|
-
await exec(step.command, { cwd, stdio: 'inherit' });
|
|
93
|
+
await exec(step.command, { cwd, stdio: 'inherit', app: pkg.repository.app });
|
|
278
94
|
}
|
|
279
95
|
catch (e) {
|
|
280
|
-
|
|
96
|
+
/**
|
|
97
|
+
* **Normalized to an `Error`, because a *falsy* throw was indistinguishable from no
|
|
98
|
+
* failure at all.** This was `let stepError: any` with `if (stepError) throw
|
|
99
|
+
* stepError` below, so a step doing `throw undefined` - legal JavaScript, and what a
|
|
100
|
+
* rejected promise carrying nothing gives you - left `stepError` falsy: the step
|
|
101
|
+
* line printed **success**, nothing was rethrown, and the run exited 0. Found by the
|
|
102
|
+
* spec written for the message-reporting fix above, which is the only reason it is
|
|
103
|
+
* not still there. A step that fails while reporting success is the one outcome this
|
|
104
|
+
* slot exists to rule out.
|
|
105
|
+
*
|
|
106
|
+
* Only the panel-off path had it: with the panel on there is no local catch, and the
|
|
107
|
+
* outer one runs whatever was thrown.
|
|
108
|
+
*/
|
|
109
|
+
stepError = e instanceof Error ? e : new Error(messageOf(e));
|
|
281
110
|
}
|
|
282
111
|
printLegacyStepLine(commandName, pkgLabel, step, Date.now() - stepStart, pkgLogLevel, stepError);
|
|
283
112
|
if (stepError)
|
|
@@ -331,7 +160,7 @@ export var RunService;
|
|
|
331
160
|
}
|
|
332
161
|
const stepsByPackage = new Map();
|
|
333
162
|
for (const pkg of packages) {
|
|
334
|
-
const pkgCfg = getConfig(pkg, script);
|
|
163
|
+
const pkgCfg = RunService.getConfig(pkg, script);
|
|
335
164
|
if (pkgCfg.skip === true)
|
|
336
165
|
continue;
|
|
337
166
|
if (!(await passesIf(repository, pkg, pkgCfg.if, pkg.dirname, ifStatusCache)))
|
|
@@ -431,7 +260,191 @@ export var RunService;
|
|
|
431
260
|
throw err;
|
|
432
261
|
}
|
|
433
262
|
}
|
|
434
|
-
|
|
263
|
+
}
|
|
264
|
+
(function (RunService) {
|
|
265
|
+
/**
|
|
266
|
+
* What the *package itself* declares for the lifecycle `script`, from the contributed sources
|
|
267
|
+
* alone - no `.rmanrc` involved. `undefined` when it declares nothing.
|
|
268
|
+
*
|
|
269
|
+
* Exported because `run` is not the only lifecycle rman wraps: `version` runs hooks around the
|
|
270
|
+
* version write, and npm spells those `preversion`/`version`/`postversion` in `package.json` -
|
|
271
|
+
* which is the same `pre<script>`/`<script>`/`post<script>` shape a step source already answers.
|
|
272
|
+
* So `VersionService` asks here for `'version'` instead of reading `manifest.raw.scripts` itself,
|
|
273
|
+
* and npm's version lifecycle keeps working with no second seam and no extra line in any plugin.
|
|
274
|
+
* A plugin for another ecosystem gets its own lifecycle hooks the moment it contributes steps.
|
|
275
|
+
*/
|
|
276
|
+
function contributedSlots(pkg, script) {
|
|
277
|
+
return contributedSlotsFor(pkg, script);
|
|
278
|
+
}
|
|
279
|
+
RunService.contributedSlots = contributedSlots;
|
|
280
|
+
/**
|
|
281
|
+
* Runs one slot of a lifecycle belonging to some operation other than `run` itself - `version`'s
|
|
282
|
+
* hooks around the version write are the only one so far.
|
|
283
|
+
*
|
|
284
|
+
* **Here rather than in `VersionService`, because the rule it applies is this service's**: how a
|
|
285
|
+
* package's own declaration for `script` and the caller's `fallback` combine, slot by slot -
|
|
286
|
+
* `slotValues`, the same function `getScriptSteps` decides it with. Kept in two places that rule
|
|
287
|
+
* would drift, and one of the copies would sit in the file that writes versions, which now runs
|
|
288
|
+
* no command of its own at all.
|
|
289
|
+
*
|
|
290
|
+
* So npm's `preversion` does **not** replace a repository's `.rmanrc version.before` any more;
|
|
291
|
+
* both run, the config's first. Only `version.exec` is a single answer, and there the package's
|
|
292
|
+
* own still wins.
|
|
293
|
+
*
|
|
294
|
+
* `fallback` is the caller's own configured step(s), **already evaluated**: `version`'s three
|
|
295
|
+
* paths are in `DEFERRED_PATHS` precisely because only the caller can bind
|
|
296
|
+
* `${{ pkg.targetVersion }}`, so interpolating here would either be too early or need a scope this
|
|
297
|
+
* service has no business holding.
|
|
298
|
+
*
|
|
299
|
+
* **A list, not one joined string.** `VersionService` used to `join(' && ')` an array into a
|
|
300
|
+
* single shell line, which a function step cannot be part of - and which quietly changed the
|
|
301
|
+
* semantics of the shell case too, since `cd x && y` in one process is not the same as two.
|
|
302
|
+
*/
|
|
303
|
+
async function runLifecycleSlot(pkg, script, slot, fallback) {
|
|
304
|
+
const values = slotValues(slot, contributedSlots(pkg, script)?.[slot] ?? [], fallback ?? [], false);
|
|
305
|
+
for (const value of values) {
|
|
306
|
+
if (typeof value === 'function') {
|
|
307
|
+
await value(createStepContext(pkg, pkg.dirname));
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
await exec(value, { cwd: pkg.dirname, stdio: 'inherit', app: pkg.repository.app });
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
RunService.runLifecycleSlot = runLifecycleSlot;
|
|
314
|
+
function getConfig(pkg, script) {
|
|
315
|
+
const runCfg = pkg.config?.run;
|
|
316
|
+
const cfg = runCfg && typeof runCfg === 'object' ? runCfg[script] : undefined;
|
|
317
|
+
/** The bare-value shorthand. A function is `typeof 'function'`, not `'object'`, so without
|
|
318
|
+
* naming it here `run: { build: myFn }` fell through to the `{}` below - the long form would
|
|
319
|
+
* have worked and the short one silently done nothing, an arbitrary difference. */
|
|
320
|
+
if (typeof cfg === 'string' || typeof cfg === 'function' || Array.isArray(cfg))
|
|
321
|
+
return { exec: cfg };
|
|
322
|
+
return cfg && typeof cfg === 'object' ? cfg : {};
|
|
323
|
+
}
|
|
324
|
+
RunService.getConfig = getConfig;
|
|
325
|
+
/**
|
|
326
|
+
* The context a function step or `if` is handed - see [`RunStepContext`](../core/run-step.ts).
|
|
327
|
+
*
|
|
328
|
+
* `runBin` and `logger` are bound to *this run* rather than left to be imported, which is the
|
|
329
|
+
* whole reason they are handed over: an imported `runBin` knows neither the cwd nor the resolved
|
|
330
|
+
* log level.
|
|
331
|
+
*/
|
|
332
|
+
/**
|
|
333
|
+
* A `run.<script>.before`/`.exec`/`.after` (or `version.<slot>`) value: one step, or several to
|
|
334
|
+
* run in sequence. A shell command or a function, and a list may mix them.
|
|
335
|
+
*
|
|
336
|
+
* **Anything else throws, naming the path.** It used to `return []`, which meant a value rman did
|
|
337
|
+
* not recognize was dropped with no trace: writing a function here - the obvious guess, and now
|
|
338
|
+
* the supported form - produced `1 succeeded, 0 failed` with the step never run (measured). A
|
|
339
|
+
* configuration mistake has to be loud; silence here reads as success.
|
|
340
|
+
*
|
|
341
|
+
* Exported, and the only implementation: `VersionService` used to carry a second one that behaved
|
|
342
|
+
* differently, which is how `version.<slot>` came to join its array with `' && '`.
|
|
343
|
+
*/
|
|
344
|
+
function normalizeScriptValue(value, at) {
|
|
345
|
+
const items = Array.isArray(value) ? value : [value];
|
|
346
|
+
const steps = [];
|
|
347
|
+
for (let i = 0; i < items.length; i++) {
|
|
348
|
+
const item = items[i];
|
|
349
|
+
/** An empty string and an absent value are both "nothing here", which is how a `"[*]"` block
|
|
350
|
+
* declaring a slot some packages don't use has always behaved. */
|
|
351
|
+
if (item === undefined || item === null || item === '')
|
|
352
|
+
continue;
|
|
353
|
+
if (typeof item === 'string' || typeof item === 'function') {
|
|
354
|
+
steps.push(item);
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
const where = Array.isArray(value) ? `${at}[${i}]` : at;
|
|
358
|
+
throw new Error(`"${where}" must be a shell command or a function, but it is ${describeValue(item)}.\n` +
|
|
359
|
+
` A list of either (or both) runs them in sequence.`);
|
|
360
|
+
}
|
|
361
|
+
return steps;
|
|
362
|
+
}
|
|
363
|
+
RunService.normalizeScriptValue = normalizeScriptValue;
|
|
364
|
+
function createStepContext(pkg, cwd) {
|
|
365
|
+
const logLevel = resolveRootLogLevel(pkg.repository);
|
|
366
|
+
return {
|
|
367
|
+
pkg,
|
|
368
|
+
repository: pkg.repository,
|
|
369
|
+
cwd,
|
|
370
|
+
runBin: (bin, argv, opts) => runBin(bin, argv, { cwd, logLevel, app: pkg.repository.app, ...opts }),
|
|
371
|
+
logger: new Logger(logLevel),
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
RunService.createStepContext = createStepContext;
|
|
375
|
+
function resolveEnvPlaceholders(value) {
|
|
376
|
+
return value.replace(/\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_, name) => process.env[name] ?? '');
|
|
377
|
+
}
|
|
378
|
+
/** Recursive-descent parser over `tokenizeIf`'s output: expr := or ; or := and ('or' and)* ;
|
|
379
|
+
* and := unary ('and' unary)* ; unary := 'not' unary | GROUP | NAME ['=' VALUE] */
|
|
380
|
+
function parseIfExpr(raw) {
|
|
381
|
+
if (typeof raw !== 'string' || !raw.trim())
|
|
382
|
+
return undefined;
|
|
383
|
+
const tokens = tokenizeIf(raw);
|
|
384
|
+
if (!tokens.length)
|
|
385
|
+
return undefined;
|
|
386
|
+
let i = 0;
|
|
387
|
+
const peek = () => tokens[i];
|
|
388
|
+
const next = () => tokens[i++];
|
|
389
|
+
function parseUnary() {
|
|
390
|
+
if (peek()?.toLowerCase() === 'not') {
|
|
391
|
+
next();
|
|
392
|
+
return { kind: 'not', node: parseUnary() };
|
|
393
|
+
}
|
|
394
|
+
const tok = next() ?? '';
|
|
395
|
+
/** A token containing whitespace can only be a collapsed `(...)` group - plain
|
|
396
|
+
* atom/operator tokens never do, since they're themselves split on whitespace. */
|
|
397
|
+
if (/\s/.test(tok)) {
|
|
398
|
+
const inner = parseIfExpr(tok);
|
|
399
|
+
if (!inner)
|
|
400
|
+
throw new Error(`Empty group in "if" expression: "${raw}"`);
|
|
401
|
+
return inner;
|
|
402
|
+
}
|
|
403
|
+
let value;
|
|
404
|
+
if (peek() === '=') {
|
|
405
|
+
next();
|
|
406
|
+
value = resolveEnvPlaceholders(next() ?? '');
|
|
407
|
+
}
|
|
408
|
+
return { kind: 'atom', name: tok, value };
|
|
409
|
+
}
|
|
410
|
+
function parseAnd() {
|
|
411
|
+
let node = parseUnary();
|
|
412
|
+
while (peek()?.toLowerCase() === 'and') {
|
|
413
|
+
next();
|
|
414
|
+
node = { kind: 'and', left: node, right: parseUnary() };
|
|
415
|
+
}
|
|
416
|
+
return node;
|
|
417
|
+
}
|
|
418
|
+
function parseOr() {
|
|
419
|
+
let node = parseAnd();
|
|
420
|
+
while (peek()?.toLowerCase() === 'or') {
|
|
421
|
+
next();
|
|
422
|
+
node = { kind: 'or', left: node, right: parseAnd() };
|
|
423
|
+
}
|
|
424
|
+
return node;
|
|
425
|
+
}
|
|
426
|
+
return parseOr();
|
|
427
|
+
}
|
|
428
|
+
RunService.parseIfExpr = parseIfExpr;
|
|
429
|
+
/** Evaluates a parsed `if` expression for one package. `statusCache` avoids repeat `git` calls
|
|
430
|
+
* for the same reference hash across packages/scripts in a single run. */
|
|
431
|
+
async function evaluateIf(repository, pkg, node, statusCache) {
|
|
432
|
+
if (node.kind === 'and') {
|
|
433
|
+
if (!(await evaluateIf(repository, pkg, node.left, statusCache)))
|
|
434
|
+
return false;
|
|
435
|
+
return evaluateIf(repository, pkg, node.right, statusCache);
|
|
436
|
+
}
|
|
437
|
+
if (node.kind === 'or') {
|
|
438
|
+
if (await evaluateIf(repository, pkg, node.left, statusCache))
|
|
439
|
+
return true;
|
|
440
|
+
return evaluateIf(repository, pkg, node.right, statusCache);
|
|
441
|
+
}
|
|
442
|
+
if (node.kind === 'not') {
|
|
443
|
+
return !(await evaluateIf(repository, pkg, node.node, statusCache));
|
|
444
|
+
}
|
|
445
|
+
return evaluateIfAtom(repository, pkg, node.name, node.value, statusCache);
|
|
446
|
+
}
|
|
447
|
+
RunService.evaluateIf = evaluateIf;
|
|
435
448
|
})(RunService || (RunService = {}));
|
|
436
449
|
/**
|
|
437
450
|
* Classic one-line-per-step log ("info build pkg ┆ step success ┆ command (123 ms)"),
|
|
@@ -483,11 +496,30 @@ function toStep(slot, value) {
|
|
|
483
496
|
*
|
|
484
497
|
* Restored in a `finally`, because a step that throws must not leave the rest of the run writing
|
|
485
498
|
* into a log nobody reads.
|
|
499
|
+
*
|
|
500
|
+
* **Its failure message is reported here, because nothing else does it.** A shell step's reason
|
|
501
|
+
* reaches the user on its own - the output streams through `onLine` or straight to the terminal,
|
|
502
|
+
* and `exec` names the command and its exit code. A function step has neither: it fails by
|
|
503
|
+
* throwing, and the throw goes to the outer `catch` that marks the package failed and rethrows an
|
|
504
|
+
* error the CLI treats as already-logged. Measured on a real config whose build step threw a
|
|
505
|
+
* carefully worded message about a missing `tsconfig.json`: the run printed
|
|
506
|
+
* `error build pkg-forgot ┆ exec failed ┆ buildWithTsc` and exited 1, and the message appeared
|
|
507
|
+
* nowhere at all - so the one thing that said what to do was the one thing dropped.
|
|
508
|
+
*
|
|
509
|
+
* Written where a shell step's output goes, so it needs no second channel: through `onLine` with
|
|
510
|
+
* the panel on (the step's own log, which is what the panel shows for a failed item), and to
|
|
511
|
+
* stderr with it off, ahead of the `failed` line - the order a shell step already produces.
|
|
486
512
|
*/
|
|
487
513
|
async function runFunctionStep(run, pkg, cwd, onLine) {
|
|
488
514
|
const context = RunService.createStepContext(pkg, cwd);
|
|
489
515
|
if (!onLine) {
|
|
490
|
-
|
|
516
|
+
try {
|
|
517
|
+
await run(context);
|
|
518
|
+
}
|
|
519
|
+
catch (e) {
|
|
520
|
+
console.error(colors.red(messageOf(e)));
|
|
521
|
+
throw e;
|
|
522
|
+
}
|
|
491
523
|
return;
|
|
492
524
|
}
|
|
493
525
|
const console_ = globalThis.console;
|
|
@@ -504,11 +536,24 @@ async function runFunctionStep(run, pkg, cwd, onLine) {
|
|
|
504
536
|
try {
|
|
505
537
|
await run(context);
|
|
506
538
|
}
|
|
539
|
+
catch (e) {
|
|
540
|
+
/** Through the patched `console` deliberately - `onLine` is still installed at this point, so
|
|
541
|
+
* the message lands in this step's log rather than over the panel it is drawn inside. */
|
|
542
|
+
for (const line of messageOf(e).split('\n'))
|
|
543
|
+
onLine(line);
|
|
544
|
+
throw e;
|
|
545
|
+
}
|
|
507
546
|
finally {
|
|
508
547
|
for (const method of CAPTURED_CONSOLE)
|
|
509
548
|
console_[method] = original[method];
|
|
510
549
|
}
|
|
511
550
|
}
|
|
551
|
+
/** What a thrown value has to say for itself. A step may throw anything, and `String(undefined)`
|
|
552
|
+
* reading as `undefined` in a run log is worse than saying nothing was said. */
|
|
553
|
+
function messageOf(error) {
|
|
554
|
+
const message = error instanceof Error ? error.message : typeof error === 'string' ? error : '';
|
|
555
|
+
return message.trim() || `the step threw ${inspect(error)}`;
|
|
556
|
+
}
|
|
512
557
|
const CAPTURED_CONSOLE = ['log', 'info', 'warn', 'error', 'debug'];
|
|
513
558
|
function format(args) {
|
|
514
559
|
return args.map(arg => (typeof arg === 'string' ? arg : inspect(arg))).join(' ');
|
|
@@ -539,17 +584,19 @@ async function passesIf(repository, pkg, raw, cwd, statusCache) {
|
|
|
539
584
|
*
|
|
540
585
|
* 1. `run.<script>.override: true` -> the config, always. The escape hatch for "ignore what the
|
|
541
586
|
* package says it does".
|
|
542
|
-
* 2. a **contributed** source - `package.json#scripts` via `rman-node
|
|
543
|
-
* because
|
|
544
|
-
*
|
|
545
|
-
*
|
|
546
|
-
*
|
|
547
|
-
* 4.
|
|
587
|
+
* 2. for `exec`, a **contributed** source - `package.json#scripts` via `rman-node` - wins over the
|
|
588
|
+
* config, because the two are one answer to one question and the more specific statement wins.
|
|
589
|
+
* 3. for `before`/`after`, the two **compose**: they are hooks at a point rather than answers, so
|
|
590
|
+
* the config's bracket the package's own (`config.before -> prebuild -> build -> postbuild ->
|
|
591
|
+
* config.after`). See `slotValues` for the measurement behind that.
|
|
592
|
+
* 4. the config's own `before`/`exec`/`after` alone, letting a package run a script it never
|
|
593
|
+
* declared.
|
|
594
|
+
* 5. nothing - and an empty result is what tells `runScript` this package defines no such script.
|
|
548
595
|
*/
|
|
549
596
|
function getScriptSteps(pkg, script) {
|
|
550
597
|
const cfg = RunService.getConfig(pkg, script);
|
|
551
598
|
const override = cfg.override === true;
|
|
552
|
-
const contributed =
|
|
599
|
+
const contributed = contributedSlotsFor(pkg, script);
|
|
553
600
|
const fromConfig = {
|
|
554
601
|
before: RunService.normalizeScriptValue(cfg.before, `run.${script}.before`),
|
|
555
602
|
exec: RunService.normalizeScriptValue(cfg.exec, `run.${script}.exec`),
|
|
@@ -557,27 +604,63 @@ function getScriptSteps(pkg, script) {
|
|
|
557
604
|
};
|
|
558
605
|
const steps = [];
|
|
559
606
|
for (const slot of SCRIPT_SLOTS) {
|
|
560
|
-
const
|
|
561
|
-
const configured = fromConfig[slot] ?? [];
|
|
562
|
-
const values = override ? (configured.length ? configured : own) : own.length ? own : configured;
|
|
563
|
-
for (const value of values)
|
|
607
|
+
for (const value of slotValues(slot, contributed?.[slot] ?? [], fromConfig[slot] ?? [], override)) {
|
|
564
608
|
steps.push(toStep(slot, value));
|
|
609
|
+
}
|
|
565
610
|
}
|
|
566
611
|
return steps;
|
|
567
612
|
}
|
|
568
|
-
/**
|
|
569
|
-
*
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
613
|
+
/**
|
|
614
|
+
* The steps for one slot, from the package's own declaration and the config's, in the order they
|
|
615
|
+
* run.
|
|
616
|
+
*
|
|
617
|
+
* **`exec` replaces; `before` and `after` compose.** They are not the same kind of key, and
|
|
618
|
+
* treating them alike is what this function exists to stop:
|
|
619
|
+
*
|
|
620
|
+
* - `exec` is *one answer to one question* - what this script does. A package declaring
|
|
621
|
+
* `"build": "tsc -b"` and a config declaring `exec: 'tsc -b'` are the same build stated twice,
|
|
622
|
+
* so the more specific statement wins and the other is dropped.
|
|
623
|
+
* - `before`/`after` are *hooks at a point*, and two of them at one point both belong. npm's
|
|
624
|
+
* `prebuild` means "immediately before my own build"; `run.build.before` means "before this
|
|
625
|
+
* script, for every package". Neither answers the other.
|
|
626
|
+
*
|
|
627
|
+
* **Measured, on a two-line repository**: a root declaring
|
|
628
|
+
* `"[*]": { run: { build: { before: 'echo CONFIG-BEFORE', after: 'echo CONFIG-AFTER' } } }` and a
|
|
629
|
+
* package declaring `prebuild`/`build`/`postbuild` ran the package's three and **neither of the
|
|
630
|
+
* root's** - while the identical repository with `prebuild` deleted ran both. So adding an
|
|
631
|
+
* unrelated codegen hook to one package silently cancelled a repo-wide `rman clean`, with a stale
|
|
632
|
+
* build directory as the only symptom.
|
|
633
|
+
*
|
|
634
|
+
* **The config brackets the package's own**, which is what the wider statement means and the shape
|
|
635
|
+
* a bookend already has here: `config.before -> prebuild -> build -> postbuild -> config.after`.
|
|
636
|
+
*
|
|
637
|
+
* `override: true` is unchanged - it is the escape hatch for "ignore what the package says it
|
|
638
|
+
* does", so the config replaces rather than composes, per slot, falling back to the package's own
|
|
639
|
+
* where the config says nothing about that slot.
|
|
640
|
+
*/
|
|
641
|
+
function slotValues(slot, own, configured, override) {
|
|
642
|
+
if (override)
|
|
643
|
+
return configured.length ? configured : own;
|
|
644
|
+
if (slot === 'exec')
|
|
645
|
+
return own.length ? own : configured;
|
|
646
|
+
return slot === 'before' ? [...configured, ...own] : [...own, ...configured];
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* What this package's own technology says it declares for `script`.
|
|
650
|
+
*
|
|
651
|
+
* **Its own, not every registered one.** This walked all the contributed sources and took the first
|
|
652
|
+
* that answered, which in a polyglot repository meant npm's `package.json#scripts` reader was handed
|
|
653
|
+
* a Cargo package and asked whether it declared `build` - its first line is
|
|
654
|
+
* `pkg.manifest.raw?.scripts`, so it was reading a manifest another technology produced. The package
|
|
655
|
+
* already knows which technology claimed it; asking anyone else was only ever a way of finding that
|
|
656
|
+
* out again.
|
|
657
|
+
*/
|
|
658
|
+
function contributedSlotsFor(pkg, script) {
|
|
659
|
+
const slots = pkg.platform.getRunSteps?.(pkg, script);
|
|
660
|
+
return slots && (slots.before?.length || slots.exec?.length || slots.after?.length) ? slots : undefined;
|
|
577
661
|
}
|
|
578
662
|
/** In execution order - `before`, the script itself, then `after`. */
|
|
579
663
|
const SCRIPT_SLOTS = ['before', 'exec', 'after'];
|
|
580
|
-
const stepSources = [];
|
|
581
664
|
/**
|
|
582
665
|
* Splits on whitespace, but a `(...)` group collapses to a single token holding its inner text
|
|
583
666
|
* (parens stripped, inner whitespace kept) - `evaluate` below re-tokenizes any token that still
|