@gjsify/cli 0.36.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.gjs.mjs +3 -3
- package/lib/bundler-pick.js +61 -87
- package/lib/cli-fail.d.ts +4 -7
- package/lib/cli-fail.js +29 -55
- package/lib/commands/affected.js +161 -286
- package/lib/commands/install.d.ts +63 -66
- package/lib/commands/install.js +372 -536
- package/lib/templates/install.mjs.tmpl +38 -68
- package/lib/types/cli-build-options.d.ts +33 -67
- package/lib/types/command.d.ts +9 -12
- package/lib/types/config-data.d.ts +125 -221
- package/lib/utils/auth-npmrc.d.ts +5 -10
- package/lib/utils/auth-npmrc.js +11 -21
- package/lib/utils/clear-targets.d.ts +8 -11
- package/lib/utils/clear-targets.js +22 -29
- package/lib/utils/copy-targets.d.ts +13 -19
- package/lib/utils/copy-targets.js +31 -44
- package/lib/utils/dep-aggregation.d.ts +7 -15
- package/lib/utils/dep-aggregation.js +9 -22
- package/lib/utils/detect-native-packages.d.ts +83 -116
- package/lib/utils/detect-native-packages.js +111 -149
- package/lib/utils/dir-link.d.ts +29 -48
- package/lib/utils/dir-link.js +33 -56
- package/lib/utils/discover-showcases.d.ts +6 -7
- package/lib/utils/discover-showcases.js +11 -15
- package/lib/utils/dlx-cache.d.ts +29 -38
- package/lib/utils/dlx-cache.js +53 -63
- package/lib/utils/force-exit.d.ts +0 -3
- package/lib/utils/force-exit.js +13 -24
- package/lib/utils/gi-search-path.d.ts +16 -24
- package/lib/utils/gi-search-path.js +54 -82
- package/lib/utils/gjs-bundle-guard.d.ts +5 -13
- package/lib/utils/gjs-bundle-guard.js +35 -57
- package/lib/utils/gjs-source-escape.d.ts +2 -2
- package/lib/utils/gjs-source-escape.js +21 -39
- package/lib/utils/gjsify-shim.d.ts +17 -20
- package/lib/utils/gjsify-shim.js +80 -120
- package/lib/utils/html-entry.d.ts +8 -12
- package/lib/utils/html-entry.js +19 -28
- package/lib/utils/install-backend-native.d.ts +123 -161
- package/lib/utils/install-backend-native.js +536 -737
- package/lib/utils/install-backend.d.ts +42 -57
- package/lib/utils/install-backend.js +16 -21
- package/lib/utils/install-cache-fs.d.ts +10 -15
- package/lib/utils/install-cache-fs.js +18 -26
- package/lib/utils/install-lock.d.ts +5 -6
- package/lib/utils/install-lock.js +46 -65
- package/lib/utils/install-packument-cache.d.ts +27 -33
- package/lib/utils/install-packument-cache.js +44 -57
- package/lib/utils/install-progress.d.ts +2 -9
- package/lib/utils/install-progress.js +14 -39
- package/lib/utils/install-tarball-cache.d.ts +14 -20
- package/lib/utils/install-tarball-cache.js +41 -59
- package/lib/utils/library-output.d.ts +9 -22
- package/lib/utils/library-output.js +19 -37
- package/lib/utils/load-npmrc.d.ts +5 -6
- package/lib/utils/load-npmrc.js +18 -27
- package/lib/utils/node-bundle-guard.d.ts +4 -8
- package/lib/utils/node-bundle-guard.js +21 -35
- package/lib/utils/node-script.js +33 -54
- package/lib/utils/node-version.d.ts +5 -14
- package/lib/utils/node-version.js +15 -24
- package/lib/utils/normalize-bundler-options.d.ts +7 -11
- package/lib/utils/normalize-bundler-options.js +24 -42
- package/lib/utils/npm-oidc.d.ts +23 -37
- package/lib/utils/npm-oidc.js +28 -53
- package/lib/utils/npm-otp-cache.d.ts +4 -4
- package/lib/utils/npm-otp-cache.js +20 -25
- package/lib/utils/npm-otp.d.ts +20 -30
- package/lib/utils/npm-otp.js +32 -51
- package/lib/utils/onboard-probe.d.ts +7 -13
- package/lib/utils/onboard-probe.js +16 -27
- package/lib/utils/parse-spec.d.ts +3 -3
- package/lib/utils/parse-spec.js +4 -5
- package/lib/utils/pkg-json-edit.d.ts +7 -12
- package/lib/utils/pkg-json-edit.js +13 -26
- package/lib/utils/pkg-json.d.ts +11 -18
- package/lib/utils/pkg-json.js +19 -28
- package/lib/utils/prefixed-output.d.ts +3 -4
- package/lib/utils/prefixed-output.js +13 -19
- package/lib/utils/prompt.d.ts +3 -4
- package/lib/utils/prompt.js +14 -18
- package/lib/utils/publish-diagnose.d.ts +11 -16
- package/lib/utils/publish-diagnose.js +15 -28
- package/lib/utils/publish-headers.d.ts +7 -11
- package/lib/utils/publish-headers.js +36 -65
- package/lib/utils/publishable-packages.d.ts +4 -6
- package/lib/utils/publishable-packages.js +14 -18
- package/lib/utils/resolve-gjs-entry.d.ts +5 -8
- package/lib/utils/resolve-gjs-entry.js +10 -27
- package/lib/utils/resolve-npm-package.d.ts +6 -11
- package/lib/utils/resolve-npm-package.js +29 -63
- package/lib/utils/resolve-plugin-by-name.d.ts +11 -19
- package/lib/utils/resolve-plugin-by-name.js +9 -14
- package/lib/utils/run-gjs.d.ts +16 -21
- package/lib/utils/run-gjs.js +31 -56
- package/lib/utils/run-lifecycle-script.d.ts +5 -9
- package/lib/utils/run-lifecycle-script.js +23 -42
- package/lib/utils/run-node.d.ts +21 -33
- package/lib/utils/run-node.js +43 -64
- package/lib/utils/runtimes.d.ts +25 -35
- package/lib/utils/runtimes.js +34 -46
- package/lib/utils/spawn.d.ts +32 -48
- package/lib/utils/spawn.js +51 -84
- package/lib/utils/stdin.d.ts +2 -2
- package/lib/utils/stdin.js +10 -25
- package/lib/utils/suggest.d.ts +6 -7
- package/lib/utils/suggest.js +8 -10
- package/lib/utils/system-gi.d.ts +18 -27
- package/lib/utils/system-gi.js +60 -114
- package/lib/utils/trust-registry.d.ts +4 -8
- package/lib/utils/trust-registry.js +7 -15
- package/lib/utils/win32-command.d.ts +8 -14
- package/lib/utils/win32-command.js +29 -43
- package/lib/utils/workspace-root.js +7 -12
- package/package.json +18 -18
package/lib/utils/spawn.d.ts
CHANGED
|
@@ -2,12 +2,10 @@ import type { ChildProcess } from 'node:child_process';
|
|
|
2
2
|
/**
|
|
3
3
|
* What the calling command does once this spawn settles.
|
|
4
4
|
*
|
|
5
|
-
* - `'exit'` — the command terminates the process itself
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* GJS that MUST NOT leave a main loop armed, so the child is run on the
|
|
10
|
-
* blocking `spawnSync` path instead.
|
|
5
|
+
* - `'exit'` — the command terminates the process itself, which quits the armed GJS
|
|
6
|
+
* main loop, so the streaming async `spawn` is safe on both hosts.
|
|
7
|
+
* - `'return'` — the command returns and lets the CLI unwind. Under GJS that must
|
|
8
|
+
* not leave a main loop armed, so the child runs on the blocking `spawnSync` path.
|
|
11
9
|
*
|
|
12
10
|
* See the module header for the full rationale.
|
|
13
11
|
*/
|
|
@@ -17,59 +15,48 @@ export interface SpawnToCompletionOptions {
|
|
|
17
15
|
completion: SpawnCompletionContract;
|
|
18
16
|
/** Working directory for the child. Defaults to the parent's. */
|
|
19
17
|
cwd?: string;
|
|
20
|
-
/**
|
|
21
|
-
* Environment for the child. Omit to inherit the parent's environment
|
|
22
|
-
* verbatim (what `spawn` does with no `env`).
|
|
23
|
-
*/
|
|
18
|
+
/** Environment for the child. Omit to inherit the parent's verbatim. */
|
|
24
19
|
env?: NodeJS.ProcessEnv;
|
|
25
20
|
/**
|
|
26
|
-
* Seed `FORCE_COLOR=1`
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* stdout is a pipe. Mirrors yarn / npm. Default `false`.
|
|
21
|
+
* Seed `FORCE_COLOR=1` unless `FORCE_COLOR`/`NO_COLOR` is already set, so tools
|
|
22
|
+
* keying on `process.stdout.isTTY` still emit ANSI into a pipe. Mirrors
|
|
23
|
+
* yarn/npm. Default `false`.
|
|
30
24
|
*/
|
|
31
25
|
color?: boolean;
|
|
32
26
|
/**
|
|
33
|
-
* `'inherit'` (default) forwards the parent's stdio
|
|
34
|
-
*
|
|
35
|
-
*
|
|
27
|
+
* `'inherit'` (default) forwards the parent's stdio; `'pipe'` gives the caller
|
|
28
|
+
* `child.stdout`/`child.stderr` via {@link onSpawn}, so it needs
|
|
29
|
+
* `completion: 'exit'`.
|
|
36
30
|
*
|
|
37
|
-
* `'inherit-stderr'` inherits
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* caller that pipes it into `JSON.parse`.
|
|
31
|
+
* `'inherit-stderr'` inherits but routes the child's **stdout to the parent's
|
|
32
|
+
* stderr**, for a parent whose own stdout is machine-readable: `gjsify pack
|
|
33
|
+
* --json` emits JSON there, and interleaved lifecycle-script log lines make it
|
|
34
|
+
* unparseable for anything piping into `JSON.parse`.
|
|
42
35
|
*/
|
|
43
36
|
stdio?: 'inherit' | 'pipe' | 'inherit-stderr';
|
|
44
37
|
/**
|
|
45
|
-
* Run `cmd` through the platform shell
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* Skips the win32 command rewrite by design, per this file's own rule:
|
|
50
|
-
* Node routes a `shell: true` spawn through `%COMSPEC%`, which finds
|
|
51
|
-
* `.cmd` shims on its own, so rewriting the string would corrupt it.
|
|
38
|
+
* Run `cmd` through the platform shell so `&&`, `|` and env-var references work.
|
|
39
|
+
* `cmd` is then a whole command LINE and `args` must be empty — npm/yarn script
|
|
40
|
+
* semantics. Skips the win32 rewrite by design: Node routes it through
|
|
41
|
+
* `%COMSPEC%`, which finds `.cmd` shims itself, so rewriting would corrupt it.
|
|
52
42
|
*/
|
|
53
43
|
shell?: boolean;
|
|
54
44
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* ONLY fires on the streaming path: the blocking `spawnSync` path has no
|
|
60
|
-
* `ChildProcess` handle at all. A site that needs the handle must
|
|
61
|
-
* therefore declare `completion: 'exit'`.
|
|
45
|
+
* Called synchronously with the live `ChildProcess` after a successful async
|
|
46
|
+
* spawn — the seam for kill-tracking registries and piped output forwarders.
|
|
47
|
+
* ONLY fires on the streaming path (the blocking path has no handle), so a site
|
|
48
|
+
* needing the handle must declare `completion: 'exit'`.
|
|
62
49
|
*/
|
|
63
50
|
onSpawn?: (child: ChildProcess) => void;
|
|
64
51
|
/**
|
|
65
|
-
* Map an `ENOENT` spawn failure
|
|
66
|
-
*
|
|
52
|
+
* Map an `ENOENT` spawn failure to a domain error carrying an install hint.
|
|
53
|
+
* Other spawn errors are rejected as-is.
|
|
67
54
|
*/
|
|
68
55
|
notFound?: (err: NodeJS.ErrnoException) => Error;
|
|
69
56
|
/**
|
|
70
|
-
* Capture ceiling for the blocking path
|
|
71
|
-
*
|
|
72
|
-
*
|
|
57
|
+
* Capture ceiling for the blocking path, default 64 MiB: a full-workspace lint
|
|
58
|
+
* with many findings runs well past Node's 1 MB default, and an overflow would
|
|
59
|
+
* silently truncate diagnostics.
|
|
73
60
|
*/
|
|
74
61
|
maxBuffer?: number;
|
|
75
62
|
}
|
|
@@ -82,13 +69,10 @@ export interface SpawnCompletion {
|
|
|
82
69
|
/**
|
|
83
70
|
* Run `cmd args…` to completion and resolve with its exit code and signal.
|
|
84
71
|
*
|
|
85
|
-
* Resolves for ANY child exit — a non-zero code is data, not an error, so each
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* Picks the streaming async path or the blocking captured path per the GJS
|
|
91
|
-
* teardown contract documented at the top of this file.
|
|
72
|
+
* Resolves for ANY child exit — a non-zero code is data, not an error, so each call
|
|
73
|
+
* site keeps its own failure message. Rejects only when the child could not be
|
|
74
|
+
* started (ENOENT mapped through {@link SpawnToCompletionOptions.notFound}). Picks
|
|
75
|
+
* the streaming or blocking path per the teardown contract in the module header.
|
|
92
76
|
*/
|
|
93
77
|
export declare function spawnToCompletion(cmd: string, args: readonly string[], opts: SpawnToCompletionOptions): Promise<SpawnCompletion>;
|
|
94
78
|
/**
|
package/lib/utils/spawn.js
CHANGED
|
@@ -1,33 +1,23 @@
|
|
|
1
1
|
// One `spawn` → `Promise<{ code, signal }>` wrapper for the whole CLI.
|
|
2
2
|
//
|
|
3
|
-
// Six near-identical wrappers used to live in `commands/{foreach,check,
|
|
4
|
-
// workspace,install}.ts`, `flatpak/build.ts` and `utils/oxc-resolve.ts`. They
|
|
5
|
-
// diverged on the completion event (`close` vs `exit`), whether an ENOENT
|
|
6
|
-
// spawn failure got a human hint, whether `FORCE_COLOR` was seeded — and, the
|
|
7
|
-
// dangerous one, on how they handled the GJS teardown contract below.
|
|
8
|
-
//
|
|
9
3
|
// ## The GJS teardown contract — why this helper has a host branch
|
|
10
4
|
//
|
|
11
5
|
// `@gjsify/child_process`'s ASYNC `spawn()` calls `ensureMainLoop()`
|
|
12
|
-
// (`@gjsify/utils`)
|
|
13
|
-
// running GLib main context
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
// `process.exit()` (whose `exitProcess` idle-schedules `quitMainLoop()` +
|
|
6
|
+
// (`@gjsify/utils`) to give the `Gio.Subprocess` `wait_async` exit callback a
|
|
7
|
+
// running GLib main context. That arms a GJS main-loop hook
|
|
8
|
+
// (`GLib.MainLoop.runAsync()` → `setMainLoopHook`) which `eval_module` runs AFTER
|
|
9
|
+
// the entry module's top-level await settles, entering a blocking
|
|
10
|
+
// `g_main_loop_run()` nothing ever quits. GJS has no atexit hook, so only
|
|
11
|
+
// `process.exit()` tears it down (`exitProcess` idle-schedules `quitMainLoop()` +
|
|
19
12
|
// `imports.system.exit()`).
|
|
20
13
|
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
// "the async `spawn`'s exit/close event is not delivered under the bundle".
|
|
27
|
-
// It IS delivered: instrumenting the exit handler shows it firing with the
|
|
28
|
-
// right code; the parent then parks in the armed loop on its way out.
|
|
14
|
+
// So on gjs 1.88 a command that async-spawns a child and then RETURNS NORMALLY
|
|
15
|
+
// parks at 0% CPU AFTER the child has exited and been reaped. The symptom — live
|
|
16
|
+
// parent, no children, blocked in `poll`, `SNl` — is identical to a lost exit
|
|
17
|
+
// callback and gets misdiagnosed as one; the event IS delivered, and the parent
|
|
18
|
+
// then parks in the armed loop on its way out.
|
|
29
19
|
//
|
|
30
|
-
//
|
|
20
|
+
// The branch is therefore NOT "GJS ⇒ spawnSync":
|
|
31
21
|
//
|
|
32
22
|
// | host | caller ends with | path |
|
|
33
23
|
// |------|------------------------|-------------------------------------|
|
|
@@ -35,50 +25,37 @@
|
|
|
35
25
|
// | GJS | `process.exit(…)` | async `spawn` (streaming) |
|
|
36
26
|
// | GJS | a normal `return` | blocking `spawnSync` (captured) |
|
|
37
27
|
//
|
|
38
|
-
// which is why `gjsify foreach
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
// Callers therefore declare their teardown contract via the REQUIRED
|
|
45
|
-
// {@link SpawnToCompletionOptions.completion} field. It is required on
|
|
46
|
-
// purpose: a new call site must state which side of the table it is on rather
|
|
47
|
-
// than silently inherit a default that may be wrong for it.
|
|
28
|
+
// which is why `gjsify foreach`/`workspace`/`check` and `runGjsBundle` run hundreds
|
|
29
|
+
// of async-spawned children under GJS without hanging: each ends its handler with
|
|
30
|
+
// `process.exit(…)`. `gjsify lint` did not, and that was the hang. Hence the
|
|
31
|
+
// REQUIRED {@link SpawnToCompletionOptions.completion} field — a new call site must
|
|
32
|
+
// state which side of the table it is on rather than inherit a wrong default.
|
|
48
33
|
//
|
|
49
|
-
// ## When a raw `node:child_process` spawn is still fine
|
|
34
|
+
// ## When a raw `node:child_process` spawn is still fine
|
|
50
35
|
//
|
|
51
|
-
// This helper
|
|
52
|
-
//
|
|
36
|
+
// This helper also carries the Windows command rewrite (`forWin32`), so the rule is
|
|
37
|
+
// about WHAT is spawned:
|
|
53
38
|
//
|
|
54
|
-
// •
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
// are `.cmd` shims: `spawn('npm')` is ENOENT and `spawn('npm.cmd')` is
|
|
63
|
-
// EINVAL, so a raw spawn cannot work at all.
|
|
39
|
+
// • a real executable (`git`, `gh`, `node`, `gjs`, `flatpak`, `process.execPath`)
|
|
40
|
+
// may use `node:child_process` directly — Windows resolves those via `.exe`.
|
|
41
|
+
// • `shell: true` is fine — Node routes it through `%COMSPEC%`, which finds
|
|
42
|
+
// `.cmd` shims.
|
|
43
|
+
// • a PACKAGE MANAGER or any `node_modules/.bin` entry by BARE NAME (`npm`,
|
|
44
|
+
// `yarn`, `npx`, `tsc`, `oxlint`, a `pm` from `detectPackageManager()`) MUST
|
|
45
|
+
// come through here: on Windows those are `.cmd` shims, where `spawn('npm')` is
|
|
46
|
+
// ENOENT and `spawn('npm.cmd')` is EINVAL, so a raw spawn cannot work at all.
|
|
64
47
|
//
|
|
65
|
-
// The third case is not hypothetical
|
|
66
|
-
//
|
|
67
|
-
// `
|
|
68
|
-
//
|
|
69
|
-
// found on PATH" on a host where npm was on PATH; `commands/onboard.ts` broke
|
|
70
|
-
// the pre-publish build. All three were invisible to Linux and macOS CI.
|
|
48
|
+
// The third case is not hypothetical, and each instance was invisible to Linux and
|
|
49
|
+
// macOS CI: raw spawns left `commands/check.ts` exiting 1 with no diagnostic,
|
|
50
|
+
// `utils/install-backend.ts` reporting "npm not found on PATH" where npm was on
|
|
51
|
+
// PATH, and `commands/onboard.ts` breaking the pre-publish build.
|
|
71
52
|
import { spawn, spawnSync } from 'node:child_process';
|
|
72
53
|
import { existsSync } from 'node:fs';
|
|
73
54
|
import { join } from 'node:path';
|
|
74
55
|
import { isGjs } from '@gjsify/rolldown-plugin-gjsify/runtime';
|
|
75
56
|
import { resolveWin32Command } from './win32-command.js';
|
|
76
|
-
/** Default capture ceiling on the blocking path — 64 MiB. */
|
|
77
57
|
const DEFAULT_MAX_BUFFER = 64 * 1024 * 1024;
|
|
78
|
-
/**
|
|
79
|
-
* Build the child's environment: `undefined` (inherit) unless the caller
|
|
80
|
-
* passed one or asked for colour seeding.
|
|
81
|
-
*/
|
|
58
|
+
/** `undefined` (inherit) unless the caller passed an env or asked for colour. */
|
|
82
59
|
function resolveEnv(opts) {
|
|
83
60
|
if (!opts.color)
|
|
84
61
|
return opts.env;
|
|
@@ -87,12 +64,10 @@ function resolveEnv(opts) {
|
|
|
87
64
|
return { ...base, ...seed };
|
|
88
65
|
}
|
|
89
66
|
/**
|
|
90
|
-
* Rewrite a bare command for Windows
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* streaming one agree; `env` is the child's, because its `PATH` (with
|
|
95
|
-
* `node_modules/.bin` prepended) is what has to be searched.
|
|
67
|
+
* Rewrite a bare command for Windows (see `utils/win32-command.ts`). A no-op on
|
|
68
|
+
* every other platform, and on win32 for anything already carrying a path or
|
|
69
|
+
* extension. Applied to BOTH paths below so the blocking and streaming branches
|
|
70
|
+
* agree; `env` is the CHILD's, because its `PATH` is what must be searched.
|
|
96
71
|
*/
|
|
97
72
|
function forWin32(cmd, args, env) {
|
|
98
73
|
const rewritten = resolveWin32Command(cmd, args, {
|
|
@@ -110,12 +85,8 @@ function mapSpawnError(err, opts) {
|
|
|
110
85
|
return err;
|
|
111
86
|
}
|
|
112
87
|
/**
|
|
113
|
-
* Whether this call has to run on the blocking path
|
|
114
|
-
*
|
|
115
|
-
* Only true under GJS for a caller that returns normally — see the module
|
|
116
|
-
* header's table. On Node the async path is always correct (and keeps the
|
|
117
|
-
* event loop free), and under GJS a caller that exits the process quits the
|
|
118
|
-
* armed main loop itself.
|
|
88
|
+
* Whether this call has to run on the blocking path — only under GJS for a caller
|
|
89
|
+
* that returns normally, per the module header's table.
|
|
119
90
|
*/
|
|
120
91
|
function mustBlock(opts) {
|
|
121
92
|
return isGjs() && opts.completion === 'return';
|
|
@@ -123,13 +94,10 @@ function mustBlock(opts) {
|
|
|
123
94
|
/**
|
|
124
95
|
* Run `cmd args…` to completion and resolve with its exit code and signal.
|
|
125
96
|
*
|
|
126
|
-
* Resolves for ANY child exit — a non-zero code is data, not an error, so each
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* Picks the streaming async path or the blocking captured path per the GJS
|
|
132
|
-
* teardown contract documented at the top of this file.
|
|
97
|
+
* Resolves for ANY child exit — a non-zero code is data, not an error, so each call
|
|
98
|
+
* site keeps its own failure message. Rejects only when the child could not be
|
|
99
|
+
* started (ENOENT mapped through {@link SpawnToCompletionOptions.notFound}). Picks
|
|
100
|
+
* the streaming or blocking path per the teardown contract in the module header.
|
|
133
101
|
*/
|
|
134
102
|
export function spawnToCompletion(cmd, args, opts) {
|
|
135
103
|
const env = resolveEnv(opts);
|
|
@@ -141,9 +109,9 @@ export function spawnToCompletion(cmd, args, opts) {
|
|
|
141
109
|
if (mustBlock(opts)) {
|
|
142
110
|
// `'inherit'` is NOT forwarded through `@gjsify/child_process`'s GJS
|
|
143
111
|
// spawnSync path (it drives a private main context via
|
|
144
|
-
// `communicateWithTimeout`), so the child's diagnostics would be
|
|
145
|
-
//
|
|
146
|
-
//
|
|
112
|
+
// `communicateWithTimeout`), so the child's diagnostics would be silently
|
|
113
|
+
// dropped — capture and re-emit them instead. stdin stays inherited so an
|
|
114
|
+
// interactive child still reads the tty.
|
|
147
115
|
const r = spawnSync(invocation.cmd, invocation.args, {
|
|
148
116
|
stdio: ['inherit', 'pipe', 'pipe'],
|
|
149
117
|
cwd: opts.cwd,
|
|
@@ -154,9 +122,9 @@ export function spawnToCompletion(cmd, args, opts) {
|
|
|
154
122
|
});
|
|
155
123
|
if (r.error)
|
|
156
124
|
return Promise.reject(mapSpawnError(r.error, opts));
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
//
|
|
125
|
+
// Captured stdout goes to the parent's STDERR under `'inherit-stderr'`, the
|
|
126
|
+
// destination the streaming path routes it to, so the two agree on where a
|
|
127
|
+
// lifecycle script's chatter lands.
|
|
160
128
|
const out = opts.stdio === 'inherit-stderr' ? process.stderr : process.stdout;
|
|
161
129
|
if (r.stdout && r.stdout.length > 0)
|
|
162
130
|
out.write(r.stdout);
|
|
@@ -184,9 +152,8 @@ export function spawnToCompletion(cmd, args, opts) {
|
|
|
184
152
|
spawnOpts.windowsVerbatimArguments = true;
|
|
185
153
|
const child = spawn(win.cmd, win.args, spawnOpts);
|
|
186
154
|
opts.onSpawn?.(child);
|
|
187
|
-
// `close` (not `exit`) so piped stdio is fully drained before the
|
|
188
|
-
//
|
|
189
|
-
// back to back, so this is a strict superset of the old `exit` sites.
|
|
155
|
+
// `close` (not `exit`) so piped stdio is fully drained before the caller's
|
|
156
|
+
// flushers run; with `stdio: 'inherit'` the two are emitted back to back.
|
|
190
157
|
child.on('close', (code, signal) => {
|
|
191
158
|
resolvePromise({ code, signal });
|
|
192
159
|
});
|
package/lib/utils/stdin.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Read the whole of stdin as UTF-8.
|
|
2
|
+
* Read the whole of stdin as UTF-8.
|
|
3
3
|
*
|
|
4
4
|
* Synchronous on purpose: the callers are CLI argument-parsing paths that run
|
|
5
5
|
* before any main loop exists, and the async `process.stdin` route carries the
|
|
6
|
-
*
|
|
6
|
+
* resume/pause races documented in `utils/prompt.ts`.
|
|
7
7
|
*/
|
|
8
8
|
export declare function readStdinText(): string;
|
|
9
9
|
/**
|
package/lib/utils/stdin.js
CHANGED
|
@@ -1,37 +1,22 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
// Read ALL of stdin, on Node AND inside the committed GJS bundles.
|
|
3
3
|
//
|
|
4
|
-
// `readFileSync(0, 'utf-8')`
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
4
|
+
// The Node idiom `readFileSync(0, 'utf-8')` means the same thing under GJS
|
|
5
|
+
// because `@gjsify/fs` maps the parent-supplied descriptors 0/1/2 onto the
|
|
6
|
+
// process's own Unix streams (`packages/node/fs/src/std-fd.ts`, reached from
|
|
7
|
+
// `readFileSync` via `sync.ts`).
|
|
8
8
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// THE GAP IS CLOSED AT THE CORE, so the workaround is gone: `@gjsify/fs` maps
|
|
15
|
-
// the parent-supplied descriptors 0/1/2 onto the process's own Unix streams
|
|
16
|
-
// (`packages/node/fs/src/std-fd.ts`, reached from `readFileSync` through
|
|
17
|
-
// `sync.ts`), which makes the Node idiom mean the same thing under GJS. This
|
|
18
|
-
// module used to carry two GJS-only readers for that gap — a
|
|
19
|
-
// `GioUnix.InputStream` chunk loop and a `/dev/stdin` `Gio.File` fallback —
|
|
20
|
-
// plus a runtime branch to choose between them. All three were consumer-side
|
|
21
|
-
// repair for a missing core capability.
|
|
22
|
-
//
|
|
23
|
-
// Anything re-adding a GJS branch HERE is re-adding the workaround. If stdin
|
|
24
|
-
// misbehaves under GJS the defect is in `@gjsify/fs` and belongs there, with a
|
|
25
|
-
// test — that is what made the branch removable in the first place
|
|
26
|
-
// (`packages/node/fs/src/fd-ops.spec.ts` + `tests/e2e/fs-std-fd`, which needs a
|
|
27
|
-
// real pipe and so cannot live in a unit spec).
|
|
9
|
+
// Keep this module runtime-agnostic: a GJS branch here would be consumer-side
|
|
10
|
+
// repair for a core capability. If stdin misbehaves under GJS the defect is in
|
|
11
|
+
// `@gjsify/fs` and belongs there, with a test — `fd-ops.spec.ts` for the fd
|
|
12
|
+
// mapping, `tests/e2e/affected-classifier-bundle` for the real pipe.
|
|
28
13
|
import { readFileSync } from 'node:fs';
|
|
29
14
|
/**
|
|
30
|
-
* Read the whole of stdin as UTF-8.
|
|
15
|
+
* Read the whole of stdin as UTF-8.
|
|
31
16
|
*
|
|
32
17
|
* Synchronous on purpose: the callers are CLI argument-parsing paths that run
|
|
33
18
|
* before any main loop exists, and the async `process.stdin` route carries the
|
|
34
|
-
*
|
|
19
|
+
* resume/pause races documented in `utils/prompt.ts`.
|
|
35
20
|
*/
|
|
36
21
|
export function readStdinText() {
|
|
37
22
|
return readFileSync(0, 'utf-8');
|
package/lib/utils/suggest.d.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Levenshtein edit distance
|
|
3
|
-
*
|
|
4
|
-
* case-insensitive match.
|
|
2
|
+
* Levenshtein edit distance. Case-SENSITIVE — callers lower-case first if they
|
|
3
|
+
* want otherwise.
|
|
5
4
|
*/
|
|
6
5
|
export declare function editDistance(a: string, b: string): number;
|
|
7
6
|
/**
|
|
8
|
-
* The closest candidate to `target
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* The closest candidate to `target`, or `undefined` when none is close enough.
|
|
8
|
+
* The threshold `max(2, floor(len/3))` scales with target length so a short name
|
|
9
|
+
* does not match everything while a long one still tolerates a couple of typos.
|
|
10
|
+
* Comparison is case-insensitive.
|
|
12
11
|
*/
|
|
13
12
|
export declare function suggestClosest(target: string, candidates: readonly string[]): string | undefined;
|
package/lib/utils/suggest.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// off any runtime and safe to pull into a GJS-bundled command.
|
|
1
|
+
// "Did you mean …?" for CLI name typos (unknown workspace, unknown script, …).
|
|
2
|
+
// Kept free of platform imports so it is safe to pull into a GJS-bundled command.
|
|
4
3
|
/**
|
|
5
|
-
* Levenshtein edit distance
|
|
6
|
-
*
|
|
7
|
-
* case-insensitive match.
|
|
4
|
+
* Levenshtein edit distance. Case-SENSITIVE — callers lower-case first if they
|
|
5
|
+
* want otherwise.
|
|
8
6
|
*/
|
|
9
7
|
export function editDistance(a, b) {
|
|
10
8
|
if (a === b)
|
|
@@ -28,10 +26,10 @@ export function editDistance(a, b) {
|
|
|
28
26
|
return prev[b.length];
|
|
29
27
|
}
|
|
30
28
|
/**
|
|
31
|
-
* The closest candidate to `target
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
29
|
+
* The closest candidate to `target`, or `undefined` when none is close enough.
|
|
30
|
+
* The threshold `max(2, floor(len/3))` scales with target length so a short name
|
|
31
|
+
* does not match everything while a long one still tolerates a couple of typos.
|
|
32
|
+
* Comparison is case-insensitive.
|
|
35
33
|
*/
|
|
36
34
|
export function suggestClosest(target, candidates) {
|
|
37
35
|
if (candidates.length === 0)
|
package/lib/utils/system-gi.d.ts
CHANGED
|
@@ -9,22 +9,17 @@ export interface SystemGiOptions {
|
|
|
9
9
|
/** `pkg-config`'s `.pc` search directories; defaults to {@link pkgConfigSearchDirs}. */
|
|
10
10
|
searchDirs?: (env: Record<string, string | undefined>) => string[];
|
|
11
11
|
}
|
|
12
|
-
/**
|
|
13
|
-
* Split an OS search-path variable, dropping empty segments.
|
|
14
|
-
*/
|
|
12
|
+
/** Split an OS search-path variable, dropping empty segments. */
|
|
15
13
|
export declare function splitSearchPath(value: string | undefined, separator?: string): string[];
|
|
16
14
|
/**
|
|
17
15
|
* `pkg-config`'s OWN configured `.pc` search path plus `$PKG_CONFIG_PATH`. Every
|
|
18
16
|
* entry is a `<libdir>/pkgconfig` (or `<datadir>/pkgconfig`) directory, so its
|
|
19
|
-
* parent is a candidate libdir — which is how a prefix nobody hardcoded is
|
|
20
|
-
* found. Measured on the reported host: `pc_path` =
|
|
21
|
-
* `/usr/local/lib/pkgconfig:/usr/local/share/pkgconfig:/usr/lib/pkgconfig:…`,
|
|
22
|
-
* whose first parent is exactly the `/usr/local/lib` that resolves every leaf.
|
|
17
|
+
* parent is a candidate libdir — which is how a prefix nobody hardcoded is found.
|
|
23
18
|
*
|
|
24
|
-
* `spawnSync` reports a missing executable as `result.error`, not by throwing
|
|
25
|
-
* an absent `pkg-config`
|
|
26
|
-
*
|
|
27
|
-
*
|
|
19
|
+
* `spawnSync` reports a missing executable as `result.error`, not by throwing, so
|
|
20
|
+
* an absent `pkg-config` (expected on a runtime-only GTK install) is a checked
|
|
21
|
+
* return value and not an exception path. (`@gjsify/child_process` matches Node
|
|
22
|
+
* here deliberately; see its `spawnSync` note.)
|
|
28
23
|
*/
|
|
29
24
|
export declare function pkgConfigSearchDirs(env: Record<string, string | undefined>): string[];
|
|
30
25
|
/**
|
|
@@ -42,19 +37,16 @@ export declare function pkgConfigSearchDirs(env: Record<string, string | undefin
|
|
|
42
37
|
* Sources 2 and 3 are GUESSES at a prefix, so each must show the
|
|
43
38
|
* `girepository-1.0/` marker before it is believed.
|
|
44
39
|
*
|
|
45
|
-
* Empty off darwin, and that is a statement about LOADERS, not about GI
|
|
46
|
-
* resolves these leaves through `ld.so`'s system-wide
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* NOR re-reads its search path after launch, which is why darwin needs the
|
|
50
|
-
* variable set at launch and nobody else does.
|
|
40
|
+
* Empty off darwin, and that is a statement about LOADERS, not about GI: Linux
|
|
41
|
+
* resolves these leaves through `ld.so`'s system-wide cache (`/etc/ld.so.conf.d`),
|
|
42
|
+
* which a package install populates. dyld is the one loader that neither consults
|
|
43
|
+
* a system-wide config NOR re-reads its search path after launch.
|
|
51
44
|
*
|
|
52
|
-
* win32 is deliberately absent rather than proven unnecessary
|
|
53
|
-
* search path, a Windows GTK distribution puts its `bin` there
|
|
54
|
-
* Windows re-reads that path at every `LoadLibrary` — so if a system
|
|
55
|
-
* turns out not to be on it, the repair is a `PATH` prepend (which
|
|
56
|
-
* {@link buildNativeEnv} already does for the prebuilds), never a second
|
|
57
|
-
* variable. Untested there.
|
|
45
|
+
* win32 is deliberately absent rather than proven unnecessary, and untested:
|
|
46
|
+
* `PATH` is the DLL search path, a Windows GTK distribution puts its `bin` there
|
|
47
|
+
* itself, and Windows re-reads that path at every `LoadLibrary` — so if a system
|
|
48
|
+
* GTK ever turns out not to be on it, the repair is a `PATH` prepend (which
|
|
49
|
+
* {@link buildNativeEnv} already does for the prebuilds), never a second variable.
|
|
58
50
|
*
|
|
59
51
|
* @returns absolute directories, or `[]` when this platform's loader needs none
|
|
60
52
|
*/
|
|
@@ -63,9 +55,8 @@ export declare function systemGiLibraryDirs({ platform, env, existsDir, searchDi
|
|
|
63
55
|
* Whether `current` (a split `DYLD_FALLBACK_LIBRARY_PATH`) already covers every
|
|
64
56
|
* directory in `wanted` — the "nothing to do" test.
|
|
65
57
|
*
|
|
66
|
-
* Kept in the port even though this CLI's consumer does not branch on it:
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* both.
|
|
58
|
+
* Kept in the port even though this CLI's consumer does not branch on it: a mirror
|
|
59
|
+
* that drifts by one export is a mirror nobody can check. `system-gi.spec.ts`
|
|
60
|
+
* pins both.
|
|
70
61
|
*/
|
|
71
62
|
export declare function pathCovers(wanted: readonly string[], current: readonly string[]): boolean;
|