@omega.js/desktop 0.53.0 → 0.54.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.
- package/README.md +38 -38
- package/dist/cli-run.js +4 -1
- package/dist/cli.js +2 -2
- package/dist/commands/cdp/client.js +1 -1
- package/dist/commands/cdp.js +1 -1
- package/dist/commands/clean.js +2 -3
- package/dist/commands/dev.js +25 -0
- package/dist/commands/lib/ensure-target.js +12 -17
- package/dist/commands/lib/migrate.js +17 -0
- package/dist/commands/logs.js +1 -1
- package/dist/commands/release.js +1 -1
- package/dist/commands/test.js +4 -4
- package/dist/commands/update.js +5 -4
- package/dist/defaults/.github/workflows/build.yml +18 -18
- package/dist/defaults/_.gitignore +0 -2
- package/dist/defaults/_mas/README.md +3 -3
- package/dist/defaults/config/certs/README.md +1 -1
- package/dist/defaults/config/omega.json5 +36 -36
- package/dist/defaults/docs/README.md +3 -3
- package/dist/defaults/gulpfile.js +1 -1
- package/dist/defaults/hooks/build/post.js +1 -1
- package/dist/defaults/hooks/build/pre.js +1 -1
- package/dist/defaults/hooks/notarize/post.js +2 -2
- package/dist/defaults/hooks/release/post.js +1 -1
- package/dist/defaults/hooks/release/pre.js +1 -1
- package/dist/defaults/src/assets/scss/pages/about.scss +1 -1
- package/dist/defaults/src/assets/scss/pages/main.scss +1 -1
- package/dist/defaults/src/assets/scss/pages/settings.scss +1 -1
- package/dist/defaults/src/integrations/context-menu/index.js +11 -11
- package/dist/defaults/src/integrations/menu/index.js +5 -5
- package/dist/defaults/src/integrations/tray/index.js +9 -9
- package/dist/defaults/src/main.js +2 -2
- package/dist/defaults/src/preload.js +1 -1
- package/dist/defaults/test/README.md +3 -3
- package/dist/defaults/test/_init.js +1 -1
- package/dist/gulp/tasks/audit.js +5 -8
- package/dist/lib/restart-manager/index.js +1 -1
- package/dist/lib/restart-manager/install.js +1 -1
- package/dist/lib/restart-manager/protocol.js +1 -1
- package/dist/main.js +4 -3
- package/dist/preload.js +1 -1
- package/dist/test/suites/build/audit.test.js +20 -7
- package/dist/test/suites/build/build-workflow-jobs.test.js +2 -2
- package/dist/test/suites/build/cli.test.js +28 -0
- package/dist/test/suites/build/defaults-em-dash.test.js +22 -0
- package/dist/test/suites/build/defaults-scaffold.test.js +19 -5
- package/dist/test/suites/build/deploy-direct.test.js +7 -5
- package/dist/test/suites/build/deploy-dispatch.test.js +2 -1
- package/dist/test/suites/build/deploy-hook.test.js +4 -2
- package/dist/test/suites/build/dev-verb.test.js +67 -0
- package/dist/test/suites/build/ensure-target.test.js +11 -3
- package/dist/test/suites/build/merge-line-files.test.js +6 -6
- package/dist/test/suites/build/migrate.test.js +29 -0
- package/dist/test/suites/build/project-scripts-deps.test.js +6 -10
- package/dist/test/suites/build/runner-env-write.test.js +73 -0
- package/dist/test/suites/build/runner.test.js +9 -8
- package/dist/test/suites/build/setup-scripts.test.js +27 -0
- package/dist/test/suites/build/validate-config.test.js +13 -2
- package/dist/test/suites/build/verb-logs.test.js +20 -0
- package/dist/test/suites/renderer/window-desktop-surface.test.js +1 -1
- package/dist/utils/build-pipeline.js +4 -4
- package/dist/utils/runner-env.js +13 -28
- package/dist/vendor/config/company.js +46 -14
- package/dist/vendor/config/defaults.js +30 -7
- package/dist/vendor/config/edit.js +25 -3
- package/dist/vendor/config/env-delivery.js +1 -1
- package/dist/vendor/config/env-schema.js +3 -6
- package/dist/vendor/config/env.js +34 -22
- package/dist/vendor/config/index.js +13 -17
- package/dist/vendor/config/load.js +15 -7
- package/dist/vendor/config/repo.js +10 -27
- package/dist/vendor/config/schema-client.js +64 -0
- package/dist/vendor/config/schema-cloud.js +38 -0
- package/dist/vendor/config/schema-manager.js +118 -0
- package/dist/vendor/config/schema-overrides.js +68 -0
- package/dist/vendor/config/schema.js +99 -152
- package/dist/vendor/config/validate.js +97 -77
- package/dist/vendor/devkit/agents-md.js +233 -0
- package/dist/vendor/devkit/attach-log-file.js +15 -1
- package/dist/vendor/devkit/ci-workflows.js +30 -30
- package/dist/vendor/devkit/cli-router.js +13 -7
- package/dist/vendor/devkit/defaults-engine.js +9 -43
- package/dist/vendor/devkit/deploy-snapshot.js +44 -9
- package/dist/vendor/devkit/env-lines.js +183 -0
- package/dist/vendor/devkit/local.js +62 -10
- package/dist/vendor/devkit/lockfile.js +32 -13
- package/dist/vendor/devkit/logger.js +7 -2
- package/dist/vendor/devkit/merge-line-files.js +219 -176
- package/dist/vendor/devkit/omega-bin.js +208 -111
- package/dist/vendor/devkit/preludes/docs-sync.js +52 -0
- package/dist/vendor/devkit/preludes/index.js +1 -0
- package/dist/vendor/devkit/target-picker.js +45 -0
- package/dist/vendor/devkit/test/dashed-files.js +37 -0
- package/dist/vendor/devkit/test/run-verb-under-tee.js +71 -0
- package/dist/vendor/devkit/update.js +15 -15
- package/dist/vendor/devkit/verb-scripts.js +40 -0
- package/dist/vendor/devkit/verbs.js +170 -0
- package/package.json +18 -24
- package/dist/commands/install.js +0 -37
- package/dist/defaults/AGENTS.md +0 -119
- package/dist/defaults/CLAUDE.md +0 -1
- package/dist/vendor/config/env-retired.js +0 -137
- package/dist/vendor/config/retired-keys.js +0 -635
- package/docs/analytics.md +0 -140
- package/docs/app-state.md +0 -92
- package/docs/audit.md +0 -69
- package/docs/auth.md +0 -284
- package/docs/auto-updater.md +0 -243
- package/docs/boot-sequence.md +0 -44
- package/docs/build-system.md +0 -169
- package/docs/cdp-debugging.md +0 -169
- package/docs/common-mistakes.md +0 -21
- package/docs/config-schema.md +0 -120
- package/docs/context-menu.md +0 -112
- package/docs/context.md +0 -81
- package/docs/css.md +0 -84
- package/docs/deep-link.md +0 -186
- package/docs/environment-detection.md +0 -112
- package/docs/fontawesome.md +0 -109
- package/docs/hooks.md +0 -89
- package/docs/icons.md +0 -79
- package/docs/index.md +0 -328
- package/docs/installer-options.md +0 -165
- package/docs/ipc.md +0 -61
- package/docs/lib-modules.md +0 -53
- package/docs/logging.md +0 -227
- package/docs/menu.md +0 -160
- package/docs/releasing.md +0 -239
- package/docs/remote-config.md +0 -118
- package/docs/remote-scripts.md +0 -144
- package/docs/restart-manager.md +0 -144
- package/docs/runner.md +0 -290
- package/docs/sentry.md +0 -97
- package/docs/shared/agent-docs.md +0 -89
- package/docs/shared/analytics.md +0 -612
- package/docs/shared/brands.md +0 -57
- package/docs/shared/breaking-changes.md +0 -917
- package/docs/shared/config.md +0 -1948
- package/docs/shared/deploys.md +0 -341
- package/docs/shared/icons.md +0 -219
- package/docs/shared/local-dev.md +0 -167
- package/docs/shared/logging.md +0 -205
- package/docs/shared/monitoring.md +0 -167
- package/docs/shared/publishing.md +0 -187
- package/docs/shared/rulings.md +0 -34
- package/docs/shared/testing.md +0 -147
- package/docs/shared/theming.md +0 -629
- package/docs/shared/translation.md +0 -342
- package/docs/shared/updates.md +0 -61
- package/docs/signing.md +0 -293
- package/docs/startup.md +0 -142
- package/docs/storage.md +0 -59
- package/docs/templating.md +0 -101
- package/docs/test-boot-layer.md +0 -157
- package/docs/test-framework.md +0 -362
- package/docs/themes.md +0 -149
- package/docs/tooltips.md +0 -99
- package/docs/tray.md +0 -164
- package/docs/usage.md +0 -58
- package/docs/verts.md +0 -62
- package/docs/windows.md +0 -149
|
@@ -1,38 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* omega-bin
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* THAT context's CLI:
|
|
10
|
-
*
|
|
11
|
-
* - an OMEGA PACKAGE'S OWN ROOT (package.json `name` is `@omega.js/*`) →
|
|
12
|
-
* that package's own CLI, or a refusal when it ships none. Checked FIRST,
|
|
13
|
-
* ahead of the dependency rule: a framework devDepends on other frameworks
|
|
14
|
-
* (extension and desktop on @omega.js/web for vendorAssets, the manager on
|
|
15
|
-
* @omega.js/backend), and reading those as a target made `omega test` in
|
|
16
|
-
* packages/extension run WEB's CLI, which scaffolded a web target into the
|
|
17
|
-
* package ([#757](https://github.com/Omega-JS-Stack/omega/issues/757))
|
|
18
|
-
* - a TARGET (nearest package.json declaring a framework, including the
|
|
19
|
-
* backend's functions/ layout) → that framework's CLI
|
|
20
|
-
* - a BRAND ROOT (config/omega.json5 with no framework declared nearer)
|
|
21
|
-
* → @omega.js/manager's CLI, so `omega test` at a brand root fans out
|
|
22
|
-
* over the brand's targets instead of guessing one framework
|
|
23
|
-
*
|
|
24
|
-
* Contract: every dispatch target exposes `exports['./cli']` → a module with
|
|
25
|
-
* `run()` that parses process.argv itself. Framework bin shims call
|
|
26
|
-
* `run({ hostName, hostRun })` — hostRun executes the host's own CLI via a
|
|
27
|
-
* RELATIVE require (vendor-safe), so the host never resolves itself by name.
|
|
28
|
-
*
|
|
29
|
-
* This module stays stdlib-only (fs/path/module): it is vendored into every
|
|
30
|
-
* framework dist and must never assume another package is installed.
|
|
2
|
+
* omega-bin: the context-aware dispatcher behind every framework's `omega`/`omg`/`mgr`
|
|
3
|
+
* bin. npm links one arbitrary winner of those bins, so this resolves the ROOT that owns
|
|
4
|
+
* the cwd (a brand root, a standalone target, the monorepo root) and runs THAT root's CLI.
|
|
5
|
+
* Below a root, a verb runs in place only inside a target of one of its owner frameworks;
|
|
6
|
+
* any other verb refuses and prints the root form. Stdlib plus its data
|
|
7
|
+
* siblings (verbs.js, target-picker.js): it is vendored into every framework dist.
|
|
8
|
+
* The contract: docs/devkit/index.md.
|
|
31
9
|
*/
|
|
32
10
|
|
|
33
11
|
const fs = require('fs');
|
|
34
12
|
const path = require('path');
|
|
35
13
|
const { createRequire } = require('module');
|
|
14
|
+
const { spawnSync } = require('child_process');
|
|
15
|
+
const { VERBS, TARGET_ORDER, tokensOf, findVerb } = require('./verbs.js');
|
|
16
|
+
const { PICKER_FLAG, takePicker } = require('./target-picker.js');
|
|
36
17
|
|
|
37
18
|
const FRAMEWORKS = [
|
|
38
19
|
'@omega.js/web',
|
|
@@ -55,24 +36,10 @@ const OMEGA_SCOPE = '@omega.js/';
|
|
|
55
36
|
// the canonical list ([#307](https://github.com/Omega-JS-Stack/omega/issues/307)).
|
|
56
37
|
const TARGET_SUBDIRS = ['functions', 'dist'];
|
|
57
38
|
|
|
58
|
-
// The
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
// `omega deploy` at a workspace root scaffolded a whole desktop target into it
|
|
63
|
-
// ([#699](https://github.com/Omega-JS-Stack/omega/issues/699)).
|
|
64
|
-
//
|
|
65
|
-
// Tokens, not command names: a flag-style alias (`omega --deploy`, `omega -v`)
|
|
66
|
-
// selects a verb too, so every spelling of an allowed verb is listed —
|
|
67
|
-
// onboard/help/version across the router frameworks and the manager, plus
|
|
68
|
-
// backend's `cwd` and desktop's `logs`, the two read-only reporters.
|
|
69
|
-
const CONTEXTLESS_VERBS = new Set([
|
|
70
|
-
'onboard', 'create', 'new', '-o', '--onboard',
|
|
71
|
-
'help', 'h', '-h', '--help',
|
|
72
|
-
'version', 'v', '-v', '--version',
|
|
73
|
-
'cwd',
|
|
74
|
-
'logs', 'log', '--logs', 'logs:read', 'logs:tail', 'logs:stream',
|
|
75
|
-
]);
|
|
39
|
+
// The verbs that run with NO target context, every spelling (a flag-style alias
|
|
40
|
+
// selects a verb too): the registry's contextless rows. Any other verb run where
|
|
41
|
+
// no target exists would scaffold one into the cwd.
|
|
42
|
+
const CONTEXTLESS_VERBS = new Set(VERBS.filter((entry) => entry.scope === 'contextless').flatMap(tokensOf));
|
|
76
43
|
|
|
77
44
|
// The signing box's verbs. A box that hosts the Windows EV signing runner is a
|
|
78
45
|
// MACHINE, not a project: `omega runner` and `omega sign-windows` read their
|
|
@@ -82,7 +49,7 @@ const CONTEXTLESS_VERBS = new Set([
|
|
|
82
49
|
// desktop resolved from the cwd (a monorepo checkout, a brand root) — and
|
|
83
50
|
// refuse naming the install when it is nowhere. They scaffold nothing
|
|
84
51
|
// ([#337](https://github.com/Omega-JS-Stack/omega/issues/337)).
|
|
85
|
-
const BOX_VERBS = new Set(
|
|
52
|
+
const BOX_VERBS = new Set(VERBS.filter((entry) => entry.scope === 'box').map((entry) => entry.name));
|
|
86
53
|
const DESKTOP = '@omega.js/desktop';
|
|
87
54
|
|
|
88
55
|
/**
|
|
@@ -156,47 +123,51 @@ function isBrandRoot(dir) {
|
|
|
156
123
|
}
|
|
157
124
|
|
|
158
125
|
/**
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
126
|
+
* Is `dir` the OMEGA monorepo root? Stdlib twin of devkit local.js's
|
|
127
|
+
* isMonorepoRoot, duplicated on purpose: the dispatcher cannot require local.js.
|
|
128
|
+
*/
|
|
129
|
+
function isMonorepoRoot(dir) {
|
|
130
|
+
const pkg = readPackage(dir);
|
|
131
|
+
return !!pkg && pkg.name === 'omega' && fs.existsSync(path.join(dir, 'packages', 'devkit', 'package.json'));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Walk up from startDir to the nearest dispatch context: a dir declaring a
|
|
136
|
+
* framework (a target), then a brand root, then the monorepo root. An `@omega.js/*`
|
|
137
|
+
* package's own root is never a target (its devDependencies on sibling frameworks
|
|
138
|
+
* would read as one), so the walk passes it and keeps the nearest for the monorepo's
|
|
139
|
+
* root form. The walk stops at the nearest `.git`, that directory still checked.
|
|
173
140
|
*
|
|
174
|
-
* @returns {{ kind: '
|
|
175
|
-
* | { kind: '
|
|
176
|
-
* | { kind: '
|
|
177
|
-
* (self) / where the framework dep is declared (framework) / the brand root
|
|
178
|
-
* (brand)
|
|
141
|
+
* @returns {{ kind: 'framework', name: string, dir: string }
|
|
142
|
+
* | { kind: 'brand', dir: string }
|
|
143
|
+
* | { kind: 'monorepo', dir: string, package?: { name: string, dir: string } } | null}
|
|
179
144
|
*/
|
|
180
145
|
function findTarget(startDir) {
|
|
181
146
|
let dir = path.resolve(startDir);
|
|
147
|
+
let omegaPackage = null;
|
|
182
148
|
while (true) {
|
|
183
149
|
const pkg = readPackage(dir);
|
|
150
|
+
const isOmegaPackage = !!pkg && typeof pkg.name === 'string' && pkg.name.startsWith(OMEGA_SCOPE);
|
|
151
|
+
|
|
152
|
+
if (isOmegaPackage) {
|
|
153
|
+
omegaPackage = omegaPackage || { name: pkg.name, dir };
|
|
154
|
+
} else {
|
|
155
|
+
const matches = frameworksOf(pkg);
|
|
156
|
+
if (matches.length > 1) {
|
|
157
|
+
// Targets are one-framework-per-target by design: a multi-framework
|
|
158
|
+
// manifest dispatches by FRAMEWORKS order, which must never be silent.
|
|
159
|
+
console.error(`omega: ${dir} declares ${matches.length} frameworks (${matches.join(', ')}): dispatching ${matches[0]}`);
|
|
160
|
+
}
|
|
161
|
+
const own = matches[0] || null;
|
|
162
|
+
if (own) return { kind: 'framework', name: own, dir };
|
|
184
163
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const matches = frameworksOf(pkg);
|
|
190
|
-
if (matches.length > 1) {
|
|
191
|
-
// Targets are one-framework-per-target by design — a multi-framework
|
|
192
|
-
// manifest dispatches by FRAMEWORKS order, which must never be silent.
|
|
193
|
-
console.error(`omega: ${dir} declares ${matches.length} frameworks (${matches.join(', ')}) — dispatching ${matches[0]}`);
|
|
164
|
+
if (isBrandRoot(dir)) {
|
|
165
|
+
return { kind: 'brand', dir };
|
|
166
|
+
}
|
|
194
167
|
}
|
|
195
|
-
const own = matches[0] || null;
|
|
196
|
-
if (own) return { kind: 'framework', name: own, dir };
|
|
197
168
|
|
|
198
|
-
if (
|
|
199
|
-
return { kind: '
|
|
169
|
+
if (isMonorepoRoot(dir)) {
|
|
170
|
+
return omegaPackage ? { kind: 'monorepo', dir, package: omegaPackage } : { kind: 'monorepo', dir };
|
|
200
171
|
}
|
|
201
172
|
|
|
202
173
|
// Repo boundary — stop here rather than statting out through the host
|
|
@@ -209,6 +180,54 @@ function findTarget(startDir) {
|
|
|
209
180
|
}
|
|
210
181
|
}
|
|
211
182
|
|
|
183
|
+
/**
|
|
184
|
+
* The root a context runs its verbs from, what kind of root it is, and the
|
|
185
|
+
* `--target=` word that picks the cwd's target or package there (null when the
|
|
186
|
+
* cwd sits in none).
|
|
187
|
+
*
|
|
188
|
+
* @returns {{ dir: string, kind: 'brand'|'target'|'monorepo', target: string|null }}
|
|
189
|
+
*/
|
|
190
|
+
function rootOf(context, cwd) {
|
|
191
|
+
if (context.kind === 'framework') {
|
|
192
|
+
const targetsDir = path.dirname(context.dir);
|
|
193
|
+
const brandRoot = path.dirname(targetsDir);
|
|
194
|
+
// A target under a brand's targets/ runs from the brand root; a standalone target is its own root
|
|
195
|
+
if (path.basename(targetsDir) === 'targets' && isBrandRoot(brandRoot)) {
|
|
196
|
+
return { dir: brandRoot, kind: 'brand', target: path.basename(context.dir) };
|
|
197
|
+
}
|
|
198
|
+
return { dir: context.dir, kind: 'target', target: null };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (context.kind === 'monorepo') {
|
|
202
|
+
return { dir: context.dir, kind: 'monorepo', target: context.package ? path.basename(context.package.dir) : null };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// A brand: a dir under targets/ with no framework dep is still a target (a custom one)
|
|
206
|
+
const [top, name] = path.relative(context.dir, cwd).split(path.sep);
|
|
207
|
+
return { dir: context.dir, kind: 'brand', target: top === 'targets' && name ? name : null };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Does the verb this token selects run in place below its root? */
|
|
211
|
+
function ownsInPlace(context, token) {
|
|
212
|
+
// Only in a TARGET of one of its owner frameworks (a brand target or a standalone project); framework source is never a target
|
|
213
|
+
if (context.kind !== 'framework') return false;
|
|
214
|
+
// Every row the token selects counts: `serve` is web's dev alias and the backend's own verb
|
|
215
|
+
return VERBS.some((row) => tokensOf(row).includes(token) && row.owners.includes(context.name));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Refuse a verb run below its root, printing the one command that runs it. */
|
|
219
|
+
function refuseOutsideRoot(verb, argv, root, row) {
|
|
220
|
+
const rest = argv.filter((arg, index) => index !== argv.indexOf(verb));
|
|
221
|
+
// A brand-wide verb (only the manager owns it) runs once for the whole brand, so it picks no target
|
|
222
|
+
const brandWide = root.kind === 'brand' && !!row && row.owners.every((owner) => owner === MANAGER);
|
|
223
|
+
const picker = root.target && !brandWide ? ` --${PICKER_FLAG}=${root.target}` : '';
|
|
224
|
+
const form = root.kind === 'monorepo'
|
|
225
|
+
? monorepoForm(root.dir, verb, rest, root.target)
|
|
226
|
+
: [`npx omega ${verb}${picker}`, ...rest].join(' ');
|
|
227
|
+
console.error(`omega: refusing to run "${verb}" in ${process.cwd()}: this verb runs at the ${root.kind} root. Run: cd ${root.dir} && ${form}`);
|
|
228
|
+
process.exit(1);
|
|
229
|
+
}
|
|
230
|
+
|
|
212
231
|
/**
|
|
213
232
|
* Resolve a dispatch target's ./cli from where it is declared.
|
|
214
233
|
* @returns {{ cliPath: string } | { error: Error }} — callers decide whether an
|
|
@@ -249,20 +268,117 @@ function verbOf(argv) {
|
|
|
249
268
|
return argv.find((arg) => !arg.startsWith('-')) || argv[0] || null;
|
|
250
269
|
}
|
|
251
270
|
|
|
252
|
-
|
|
253
|
-
|
|
271
|
+
/**
|
|
272
|
+
* The framework packages, in the registry's target order: their CLI's `test` runs
|
|
273
|
+
* their own suite. The manager's `test` is the brand fan-out, so its suite is `npm test`.
|
|
274
|
+
*/
|
|
275
|
+
function monorepoPackages(root) {
|
|
276
|
+
const packagesDir = path.join(root, 'packages');
|
|
277
|
+
const rank = (dirName) => (TARGET_ORDER.includes(dirName) ? TARGET_ORDER.indexOf(dirName) : TARGET_ORDER.length);
|
|
278
|
+
return fs.readdirSync(packagesDir)
|
|
279
|
+
.map((dirName) => ({ dirName, dir: path.join(packagesDir, dirName), manifest: readPackage(path.join(packagesDir, dirName)) }))
|
|
280
|
+
.filter((pkg) => pkg.manifest && FRAMEWORKS.includes(pkg.manifest.name) && pkg.manifest.bin && pkg.manifest.bin.omega)
|
|
281
|
+
.sort((a, b) => rank(a.dirName) - rank(b.dirName) || a.dirName.localeCompare(b.dirName));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* The ONE command the monorepo root accepts for a verb, shared by the root's own
|
|
286
|
+
* refusal and the in-package refusal so the two cannot drift: `test` picks a
|
|
287
|
+
* framework package (any other package's suite is its own workspace's npm test),
|
|
288
|
+
* `dev` is the root watch, and every other verb is a workspace script.
|
|
289
|
+
*
|
|
290
|
+
* @param {string} root - The monorepo root.
|
|
291
|
+
* @param {string} verb - The verb as typed.
|
|
292
|
+
* @param {string[]} rest - The args after the verb.
|
|
293
|
+
* @param {string|null} pkg - The package dir name the cwd sits in, if any.
|
|
294
|
+
* @returns {string}
|
|
295
|
+
*/
|
|
296
|
+
function monorepoForm(root, verb, rest, pkg) {
|
|
297
|
+
const entry = findVerb(verb);
|
|
298
|
+
if (entry && entry.name === 'test') {
|
|
299
|
+
if (pkg && !monorepoPackages(root).some((candidate) => candidate.dirName === pkg)) return `npm test --workspace packages/${pkg}`;
|
|
300
|
+
return [`npx omega test --${PICKER_FLAG}=${pkg || '<package>'}`, ...rest].join(' ');
|
|
301
|
+
}
|
|
302
|
+
if (entry && entry.name === 'dev') return 'npm start';
|
|
303
|
+
return `npm run ${entry ? entry.name : verb} --workspaces --if-present`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* The monorepo root: `test` runs each package `--target=` picks through its own
|
|
308
|
+
* CLI file, never the dispatcher (which refuses inside a package), in the package
|
|
309
|
+
* dir, stopping at the first failure; every other verb is a root npm script and
|
|
310
|
+
* refuses naming it.
|
|
311
|
+
*/
|
|
312
|
+
function runMonorepoRoot({ root, argv, verb, spawn }) {
|
|
313
|
+
const entry = findVerb(verb);
|
|
314
|
+
if (!entry || entry.name !== 'test') {
|
|
315
|
+
console.error(`omega: the monorepo root runs only \`npx omega test --${PICKER_FLAG}=<package>\`; for "${verb}" run the root npm script: ${monorepoForm(root, verb, [])}`);
|
|
316
|
+
process.exit(1);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const packages = monorepoPackages(root);
|
|
320
|
+
const known = packages.map((pkg) => pkg.dirName).join(', ');
|
|
321
|
+
const { tokens, rest } = takePicker(argv.filter((arg, index) => index !== argv.indexOf(verb)));
|
|
322
|
+
|
|
323
|
+
// The monorepo has no "all" default: the whole battery is `npm test`
|
|
324
|
+
if (tokens.length === 0) {
|
|
325
|
+
console.error(`omega: pick the packages to test: npx omega test --${PICKER_FLAG}=<package>[,...] [scope], e.g. npx omega test --${PICKER_FLAG}=web framework:. The packages are ${known}; the whole battery is npm test.`);
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const matches = (pkg, token) => token === pkg.dirName || token === pkg.manifest.name;
|
|
330
|
+
const unknown = tokens.filter((token) => !packages.some((pkg) => matches(pkg, token)));
|
|
331
|
+
if (unknown.length > 0) {
|
|
332
|
+
console.error(`omega: unknown --${PICKER_FLAG} ${unknown.map((token) => `"${token}"`).join(', ')}: the monorepo's packages are ${known}. Nothing ran.`);
|
|
333
|
+
process.exit(1);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
for (const pkg of packages.filter((candidate) => tokens.some((token) => matches(candidate, token)))) {
|
|
337
|
+
const { cliPath, error } = tryResolveCli(pkg.manifest.name, pkg.dir);
|
|
338
|
+
if (!cliPath) {
|
|
339
|
+
console.error(`omega: ${pkg.manifest.name}'s './cli' did not resolve (${error.message}; an unbuilt dist? run its prepare). Nothing ran for it.`);
|
|
340
|
+
process.exitCode = 1;
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
console.error(`omega: ${pkg.manifest.name}: omega test ${rest.join(' ')}`.trimEnd());
|
|
345
|
+
const result = spawn(process.execPath, [cliPath, 'test', ...rest], {
|
|
346
|
+
cwd: pkg.dir,
|
|
347
|
+
stdio: 'inherit',
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// A child killed by a signal (or never spawned) has no status, and failed all the same
|
|
351
|
+
const code = typeof result.status === 'number' ? result.status : 1;
|
|
352
|
+
if (code !== 0) {
|
|
353
|
+
process.exitCode = code;
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async function run({ hostName, hostRun, argv = process.argv.slice(2), spawn = spawnSync }) {
|
|
360
|
+
const cwd = process.cwd();
|
|
361
|
+
const target = findTarget(cwd);
|
|
362
|
+
const verb = verbOf(argv);
|
|
363
|
+
// A contextless or box verb has no root to run from, and a bare `omega` prints help
|
|
364
|
+
const rootless = verb === null || CONTEXTLESS_VERBS.has(verb) || boxVerbOf(argv) !== null;
|
|
365
|
+
|
|
366
|
+
if (target && !rootless) {
|
|
367
|
+
const root = rootOf(target, cwd);
|
|
368
|
+
const row = findVerb(verb);
|
|
369
|
+
if (cwd !== root.dir && !ownsInPlace(target, verb)) refuseOutsideRoot(verb, argv, root, row);
|
|
370
|
+
}
|
|
254
371
|
|
|
255
372
|
// No context — the bootstrap case (a verb run in a fresh directory has
|
|
256
373
|
// no framework dep yet, by definition). Run the INSTALLED @omega.js/manager
|
|
257
374
|
// when one resolves, else the HOST framework's CLI exactly like the
|
|
258
375
|
// pre-dispatcher bins did, and say which one so a hoist-winner at a brand
|
|
259
376
|
// root is never a silent mystery.
|
|
260
|
-
if (!target) {
|
|
377
|
+
if (!target || (target.kind === 'monorepo' && rootless)) {
|
|
261
378
|
// …but ONLY for a verb that cannot write. The fallback hands a mutating verb
|
|
262
379
|
// to whichever framework won npm's bin link, and that verb scaffolds its own
|
|
263
380
|
// target into the cwd — a directory that owns no target is never where that
|
|
264
381
|
// should land (#699). Twin of ensure-target's refusal (devkit scaffold-guard.js).
|
|
265
|
-
const verb = verbOf(argv);
|
|
266
382
|
const boxVerb = boxVerbOf(argv);
|
|
267
383
|
if (boxVerb) {
|
|
268
384
|
if (hostName === DESKTOP) return hostRun();
|
|
@@ -276,7 +392,7 @@ async function run({ hostName, hostRun, argv = process.argv.slice(2) }) {
|
|
|
276
392
|
}
|
|
277
393
|
if (verb && !CONTEXTLESS_VERBS.has(verb)) {
|
|
278
394
|
console.error(`omega: refusing to run "${verb}" — ${process.cwd()} is not inside an OMEGA target (no framework dependency and no config/omega.json5 above it). Nothing was scaffolded.`);
|
|
279
|
-
console.error('Run it from a target
|
|
395
|
+
console.error('Run it from a root (a brand root or a standalone target), or `npx omega onboard` to create one here. Without a target, only onboard (create, new), help, version, cwd and logs run; the signing box\'s runner and sign-windows run through @omega.js/desktop.');
|
|
280
396
|
process.exit(1);
|
|
281
397
|
}
|
|
282
398
|
|
|
@@ -298,27 +414,8 @@ async function run({ hostName, hostRun, argv = process.argv.slice(2) }) {
|
|
|
298
414
|
return hostRun();
|
|
299
415
|
}
|
|
300
416
|
|
|
301
|
-
//
|
|
302
|
-
|
|
303
|
-
// target: nothing here may reach the host fallback, whose ensure-target would
|
|
304
|
-
// scaffold a consumer project into the framework source tree (#757).
|
|
305
|
-
if (target.kind === 'self') {
|
|
306
|
-
if (target.name === hostName) return hostRun();
|
|
307
|
-
|
|
308
|
-
const { cliPath, error } = tryResolveCli(target.name, target.dir);
|
|
309
|
-
if (!cliPath) {
|
|
310
|
-
// Two different facts share one refusal: a package that exports no
|
|
311
|
-
// `./cli` at all (config, devkit, ...) versus one that declares it but
|
|
312
|
-
// cannot resolve it right now (an unbuilt dist, a fresh clone before
|
|
313
|
-
// `npm install`). Name the one that applies.
|
|
314
|
-
const reason = error && error.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED'
|
|
315
|
-
? 'which ships no CLI'
|
|
316
|
-
: `whose './cli' did not resolve (${error ? error.message : 'unknown'}; an unbuilt dist? run its prepare)`;
|
|
317
|
-
console.error(`omega: refusing to run — ${target.dir} is the ${target.name} package itself, ${reason}, and an OMEGA package is never a target. Nothing was scaffolded.`);
|
|
318
|
-
process.exit(1);
|
|
319
|
-
}
|
|
320
|
-
return require(cliPath).run();
|
|
321
|
-
}
|
|
417
|
+
// Below the monorepo root every verb refused above, so this is the root, which runs `test` over its packages
|
|
418
|
+
if (target.kind === 'monorepo') return runMonorepoRoot({ root: target.dir, argv, verb, spawn });
|
|
322
419
|
|
|
323
420
|
// A brand root — the manager owns brand-level commands (`omega test` fans
|
|
324
421
|
// out over targets/*). Resolve it from the brand root and hand over.
|
|
@@ -338,23 +435,23 @@ async function run({ hostName, hostRun, argv = process.argv.slice(2) }) {
|
|
|
338
435
|
if (hostName === MANAGER) {
|
|
339
436
|
console.error(`omega: brand-shaped directory at ${target.dir} with no installed ${MANAGER} — running the bundled ${hostName} (run npm install to use the pinned version)`);
|
|
340
437
|
} else {
|
|
341
|
-
console.error(`omega: brand-shaped directory at ${target.dir} but ${MANAGER} is not installed
|
|
438
|
+
console.error(`omega: brand-shaped directory at ${target.dir} but ${MANAGER} is not installed, running ${hostName} instead (install ${MANAGER} at the brand root if this really is a brand)`);
|
|
342
439
|
}
|
|
343
440
|
return hostRun();
|
|
344
441
|
}
|
|
345
442
|
return require(cliPath).run();
|
|
346
443
|
}
|
|
347
444
|
|
|
348
|
-
// The bin that won npm's .bin link belongs to this target's framework
|
|
445
|
+
// The bin that won npm's .bin link belongs to this target's framework: run it directly.
|
|
349
446
|
if (target.name === hostName) {
|
|
350
447
|
return hostRun();
|
|
351
448
|
}
|
|
352
449
|
|
|
353
|
-
// The target belongs to a DIFFERENT framework
|
|
450
|
+
// The target belongs to a DIFFERENT framework: resolve its CLI from where the
|
|
354
451
|
// dependency is declared and hand over.
|
|
355
452
|
const cliPath = resolveCli(target.name, target.dir,
|
|
356
|
-
'Is the framework installed? Try npm install, or `
|
|
453
|
+
'Is the framework installed? Try npm install, or `npx omega i local` at the brand root for a monorepo link.');
|
|
357
454
|
return require(cliPath).run();
|
|
358
455
|
}
|
|
359
456
|
|
|
360
|
-
module.exports = { run, findTarget, isBrandRoot, verbOf, isBoxVerbArgv, TARGET_SUBDIRS, CONTEXTLESS_VERBS, BOX_VERBS, FRAMEWORKS, MANAGER };
|
|
457
|
+
module.exports = { run, findTarget, isBrandRoot, isMonorepoRoot, verbOf, isBoxVerbArgv, TARGET_SUBDIRS, CONTEXTLESS_VERBS, BOX_VERBS, FRAMEWORKS, MANAGER };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The docs sync: a brand whose @omega.js/manager is a local checkout gets that
|
|
3
|
+
* manager's docs/ synced from the monorepo's docs/ before every verb, so the
|
|
4
|
+
* brand's agents read the live map through the manager's AGENTS.md.
|
|
5
|
+
*
|
|
6
|
+
* A published manager ships the docs of its own version and is left alone, as
|
|
7
|
+
* is an invocation outside a brand. The sync writes only files whose content
|
|
8
|
+
* changed, so a boot with nothing to do reads the tree and writes nothing.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const { resolvePackageRealDir, isLocalCheckout, isMonorepoRoot } = require('../local.js');
|
|
13
|
+
|
|
14
|
+
const MANAGER = '@omega.js/manager';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {object} context - The prelude context.
|
|
18
|
+
* @param {string|null} context.brandRoot - The brand root of the invocation.
|
|
19
|
+
* @param {function} [context.log] - Injectable line printer (tests).
|
|
20
|
+
* @returns {{ synced: boolean, reason?: string, written?: string[], removed?: string[] }}
|
|
21
|
+
*/
|
|
22
|
+
function run(context = {}) {
|
|
23
|
+
const { brandRoot } = context;
|
|
24
|
+
const log = context.log || console.log;
|
|
25
|
+
if (!brandRoot) {
|
|
26
|
+
return { synced: false, reason: 'no-brand' };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const managerDir = resolvePackageRealDir(MANAGER, brandRoot);
|
|
30
|
+
if (!managerDir) {
|
|
31
|
+
return { synced: false, reason: 'no-manager' };
|
|
32
|
+
}
|
|
33
|
+
// A checkout lives at <monorepo>/packages/manager, the layout every link points at.
|
|
34
|
+
const monorepoRoot = path.dirname(path.dirname(managerDir));
|
|
35
|
+
if (!isLocalCheckout(managerDir) || !isMonorepoRoot(monorepoRoot)) {
|
|
36
|
+
return { synced: false, reason: 'published' };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// The checkout's own docs lane, so the sync matches the monorepo's code.
|
|
40
|
+
const { syncDocs } = require(path.join(monorepoRoot, 'packages', 'devkit', 'tools', 'vendor-docs.js'));
|
|
41
|
+
const { written, removed } = syncDocs({ monorepoRoot, packageDir: managerDir });
|
|
42
|
+
if (written.length + removed.length > 0) {
|
|
43
|
+
log(`omega: synced the linked manager's docs from the monorepo (${written.length} written, ${removed.length} removed)`);
|
|
44
|
+
}
|
|
45
|
+
return { synced: true, written, removed };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
name: 'docs-sync',
|
|
50
|
+
verbs: 'all',
|
|
51
|
+
run,
|
|
52
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* target-picker: the ONE spelling and parse of the root picker, `--target=<a,b>`.
|
|
3
|
+
* A brand root picks targets with it and the monorepo root picks packages with
|
|
4
|
+
* it, so the flag name and its comma list can never drift between the two.
|
|
5
|
+
* Stdlib-free: the dispatcher that requires it is vendored into every framework dist.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// The flag that spells the picker on every root verb
|
|
9
|
+
const PICKER_FLAG = 'target';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A comma list of target tokens, trimmed and emptied of blanks.
|
|
13
|
+
*
|
|
14
|
+
* @param {string} [value] - the raw flag value
|
|
15
|
+
* @returns {string[]}
|
|
16
|
+
*/
|
|
17
|
+
function parseTargetTokens(value) {
|
|
18
|
+
return String(value || '').split(',').map((part) => part.trim()).filter(Boolean);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Split the picker (`--target=a,b` or `--target a,b`) out of raw args, keeping
|
|
23
|
+
* every other arg verbatim and in order: those are the ones that forward.
|
|
24
|
+
*
|
|
25
|
+
* @param {string[]} args - raw args, as typed
|
|
26
|
+
* @returns {{ tokens: string[], rest: string[] }}
|
|
27
|
+
*/
|
|
28
|
+
function takePicker(args) {
|
|
29
|
+
const tokens = [];
|
|
30
|
+
const rest = [];
|
|
31
|
+
for (let index = 0; index < args.length; index++) {
|
|
32
|
+
const arg = args[index];
|
|
33
|
+
if (arg.startsWith(`--${PICKER_FLAG}=`)) {
|
|
34
|
+
tokens.push(...parseTargetTokens(arg.slice(PICKER_FLAG.length + 3)));
|
|
35
|
+
} else if (arg === `--${PICKER_FLAG}`) {
|
|
36
|
+
tokens.push(...parseTargetTokens(args[index + 1]));
|
|
37
|
+
index++;
|
|
38
|
+
} else {
|
|
39
|
+
rest.push(arg);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { tokens, rest };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = { PICKER_FLAG, parseTargetTokens, takePicker };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The files under a tree that carry an em dash (U+2014).
|
|
3
|
+
*
|
|
4
|
+
* Every file a framework copies into a consumer's repo meets that repo's own
|
|
5
|
+
* commit guard, which may refuse the character, so each emitted tree pins
|
|
6
|
+
* itself with an equality on `dashedFiles(ROOT)` against `[]`.
|
|
7
|
+
*
|
|
8
|
+
* A walk that finds almost nothing means a wrong root, and a pin over an empty
|
|
9
|
+
* list would pass vacuously, so that walk throws instead. Node builtins only:
|
|
10
|
+
* the backend vendors this file as a raw asset, with no specifier rewrite.
|
|
11
|
+
*/
|
|
12
|
+
const fs = require('node:fs');
|
|
13
|
+
const path = require('node:path');
|
|
14
|
+
|
|
15
|
+
// Fewer files than this under a root means the walk missed its tree.
|
|
16
|
+
const MIN_FILES = 6;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {string} root - Absolute directory to walk
|
|
20
|
+
* @returns {string[]} Root-relative paths of the files carrying U+2014, sorted
|
|
21
|
+
*/
|
|
22
|
+
function dashedFiles(root) {
|
|
23
|
+
const files = fs.readdirSync(root, { recursive: true })
|
|
24
|
+
.map((entry) => path.join(root, entry))
|
|
25
|
+
.filter((file) => fs.statSync(file).isFile());
|
|
26
|
+
|
|
27
|
+
if (files.length < MIN_FILES) {
|
|
28
|
+
throw new Error(`dashedFiles: the walk of ${root} found ${files.length} files, so the pin would pass vacuously`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return files
|
|
32
|
+
.filter((file) => fs.readFileSync(file, 'utf8').includes('\u2014'))
|
|
33
|
+
.map((file) => path.relative(root, file))
|
|
34
|
+
.sort();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = { dashedFiles };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run a framework's `omega test` verb in process under an outer log tee, the
|
|
3
|
+
* one harness the desktop and extension verb-logs suites share.
|
|
4
|
+
*
|
|
5
|
+
* The verb's attach is DECLINED (CI set for the call) so it pushes no layer of
|
|
6
|
+
* its own; the result says whether the tee underneath still receives after
|
|
7
|
+
* the verb returns, which is what a blind detach would break.
|
|
8
|
+
*/
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
const Module = require('module');
|
|
13
|
+
const attachLogFile = require('../attach-log-file.js');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {object} options
|
|
17
|
+
* @param {string} options.commands - The framework's commands dir (holds test.js and lib/ensure-target.js)
|
|
18
|
+
* @param {string} options.prefix - The tmpdir prefix for the empty consumer dir
|
|
19
|
+
* @param {string[]} options.positionals - The verb's `_` after `test`
|
|
20
|
+
* @returns {Promise<{ outer: string, exitCodes: number[] }>} The outer log and any process.exit codes
|
|
21
|
+
*/
|
|
22
|
+
async function runTestVerbUnderOuterTee({ commands, prefix, positionals }) {
|
|
23
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
24
|
+
fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({ name: 'test-verb-fixture' }));
|
|
25
|
+
const outerPath = path.join(dir, 'outer.log');
|
|
26
|
+
|
|
27
|
+
// ensureTarget scaffolds a real target; the verb destructures it at load.
|
|
28
|
+
const ensurePath = require.resolve(path.join(commands, 'lib', 'ensure-target.js'));
|
|
29
|
+
const cachedEnsure = require.cache[ensurePath];
|
|
30
|
+
const stub = new Module(ensurePath);
|
|
31
|
+
stub.filename = ensurePath;
|
|
32
|
+
stub.loaded = true;
|
|
33
|
+
stub.exports = { ensureTarget: async () => {} };
|
|
34
|
+
require.cache[ensurePath] = stub;
|
|
35
|
+
const verbPath = require.resolve(path.join(commands, 'test.js'));
|
|
36
|
+
const cachedVerb = require.cache[verbPath];
|
|
37
|
+
delete require.cache[verbPath];
|
|
38
|
+
|
|
39
|
+
const prior = { cwd: process.cwd(), exitCode: process.exitCode, exit: process.exit, log: console.log, CI: process.env.CI };
|
|
40
|
+
const exitCodes = [];
|
|
41
|
+
const restoreTee = attachLogFile.mark();
|
|
42
|
+
attachLogFile(outerPath, { env: {} });
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
process.chdir(dir);
|
|
46
|
+
console.log = () => {};
|
|
47
|
+
// Stubbed only because the green path ends the process.
|
|
48
|
+
process.exit = (code) => { exitCodes.push(code); };
|
|
49
|
+
process.env.CI = 'true';
|
|
50
|
+
await require(verbPath)({ _: ['test', ...positionals] });
|
|
51
|
+
process.stdout.write('after the verb\n');
|
|
52
|
+
} finally {
|
|
53
|
+
if (prior.CI === undefined) delete process.env.CI;
|
|
54
|
+
else process.env.CI = prior.CI;
|
|
55
|
+
process.exit = prior.exit;
|
|
56
|
+
console.log = prior.log;
|
|
57
|
+
process.exitCode = prior.exitCode;
|
|
58
|
+
process.chdir(prior.cwd);
|
|
59
|
+
restoreTee();
|
|
60
|
+
if (cachedEnsure) require.cache[ensurePath] = cachedEnsure;
|
|
61
|
+
else delete require.cache[ensurePath];
|
|
62
|
+
if (cachedVerb) require.cache[verbPath] = cachedVerb;
|
|
63
|
+
else delete require.cache[verbPath];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const outer = fs.readFileSync(outerPath, 'utf8');
|
|
67
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
68
|
+
return { outer, exitCodes };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = { runTestVerbUnderOuterTee };
|