@gjsify/cli 0.26.1 → 0.27.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.
Files changed (55) hide show
  1. package/dist/cli.gjs.mjs +184 -182
  2. package/lib/cli-app.js +3 -1
  3. package/lib/commands/affected.js +1 -1
  4. package/lib/commands/build.js +21 -14
  5. package/lib/commands/check.js +40 -4
  6. package/lib/commands/clear.d.ts +8 -0
  7. package/lib/commands/clear.js +90 -0
  8. package/lib/commands/copy.d.ts +8 -0
  9. package/lib/commands/copy.js +87 -0
  10. package/lib/commands/dlx.js +8 -1
  11. package/lib/commands/index.d.ts +2 -0
  12. package/lib/commands/index.js +2 -0
  13. package/lib/commands/install.d.ts +4 -0
  14. package/lib/commands/install.js +164 -15
  15. package/lib/commands/onboard.js +17 -14
  16. package/lib/commands/publish.js +18 -4
  17. package/lib/commands/storybook.js +5 -1
  18. package/lib/commands/test.js +29 -2
  19. package/lib/commands/uninstall.js +44 -2
  20. package/lib/commands/upgrade.js +1 -4
  21. package/lib/templates/install.mjs.tmpl +110 -19
  22. package/lib/utils/bin-shim.d.ts +73 -0
  23. package/lib/utils/bin-shim.js +97 -2
  24. package/lib/utils/build-cache.d.ts +7 -27
  25. package/lib/utils/build-cache.js +27 -2
  26. package/lib/utils/check-system-deps.d.ts +15 -1
  27. package/lib/utils/check-system-deps.js +126 -2
  28. package/lib/utils/clear-targets.d.ts +30 -0
  29. package/lib/utils/clear-targets.js +114 -0
  30. package/lib/utils/copy-targets.d.ts +41 -0
  31. package/lib/utils/copy-targets.js +145 -0
  32. package/lib/utils/detect-native-packages.d.ts +160 -5
  33. package/lib/utils/detect-native-packages.js +326 -50
  34. package/lib/utils/dir-link.d.ts +34 -6
  35. package/lib/utils/dir-link.js +23 -9
  36. package/lib/utils/install-backend-native.d.ts +109 -1
  37. package/lib/utils/install-backend-native.js +622 -59
  38. package/lib/utils/install-backend.js +17 -15
  39. package/lib/utils/install-global.js +6 -34
  40. package/lib/utils/install-lock.js +1 -1
  41. package/lib/utils/install-packument-cache.d.ts +36 -17
  42. package/lib/utils/install-packument-cache.js +59 -31
  43. package/lib/utils/oxc-resolve.d.ts +27 -1
  44. package/lib/utils/oxc-resolve.js +53 -26
  45. package/lib/utils/pkg-json.d.ts +24 -0
  46. package/lib/utils/pkg-json.js +27 -1
  47. package/lib/utils/platform-check.d.ts +215 -0
  48. package/lib/utils/platform-check.js +377 -0
  49. package/lib/utils/resolve-npm-package.js +9 -2
  50. package/lib/utils/run-gjs.d.ts +1 -1
  51. package/lib/utils/run-gjs.js +19 -3
  52. package/lib/utils/spawn.js +50 -2
  53. package/lib/utils/win32-command.d.ts +54 -0
  54. package/lib/utils/win32-command.js +140 -0
  55. package/package.json +19 -20
@@ -4,12 +4,28 @@ export interface NativePackage {
4
4
  /** Absolute path to the arch-specific prebuilds dir, e.g. "/…/@gjsify/webgl/prebuilds/linux-x64" */
5
5
  prebuildsDir: string;
6
6
  }
7
+ /**
8
+ * Which C library an artifact is built against. npm's own `libc` manifest field
9
+ * uses exactly these two tokens, and it is LINUX-ONLY there — every other OS has
10
+ * one C library, so the axis does not exist off Linux and is `null` there rather
11
+ * than defaulted to something.
12
+ */
13
+ export type HostLibc = 'glibc' | 'musl';
7
14
  /** The host target a resolution is performed for. Defaults to the running process. */
8
15
  export interface HostTarget {
9
16
  /** `process.platform` value — `linux` | `darwin` | `win32` | … */
10
17
  platform?: string;
11
18
  /** `process.arch` value — `x64` | `arm64` | `ppc64` | `s390x` | `riscv64` | … */
12
19
  arch?: string;
20
+ /**
21
+ * Host C library, or `undefined`/`null` when the axis does not apply (any
22
+ * non-Linux OS) or could not be determined. Both non-values mean the same
23
+ * thing to the resolver: behave exactly as before the libc axis existed —
24
+ * never offer a `-musl` directory. That is deliberately the conservative
25
+ * direction: an unsuffixed directory is the DEFAULT build, so a host we
26
+ * cannot classify still resolves what it always did.
27
+ */
28
+ libc?: HostLibc | null;
13
29
  }
14
30
  /**
15
31
  * Environment a GJS process needs so `gi://Gjsify…` imports resolve against
@@ -31,12 +47,78 @@ export interface NativeEnv {
31
47
  [key: string]: string | undefined;
32
48
  }
33
49
  /**
34
- * Canonical `<os>-<arch>` form — the node spelling — so `linux-x86_64` and
35
- * `linux-x64` compare equal. Only the *arch* half is normalised; the OS half
36
- * is always a `process.platform` token (`linux`/`darwin`/`win32`), which has
37
- * no competing spelling.
50
+ * Split a prebuild target into its three axes.
51
+ *
52
+ * TOKEN GRAMMAR: `<os>-<arch>[-musl]`.
53
+ *
54
+ * An UNSUFFIXED token means "the default build". Note what it does NOT mean: it
55
+ * is not a synonym for glibc. For an artifact that links `libc.so.6` the default
56
+ * build IS glibc; for one that links only GLib/GObject/GIO (plus GnuTLS resp.
57
+ * GStreamer) it is libc-AGNOSTIC and loads against whatever libc the host's GLib
58
+ * was built for. The directory name cannot express that difference and does not
59
+ * need to: the package's own `libc` field answers it at install time, and the
60
+ * `prebuild-libc` conformance rule verifies that field against the binaries
61
+ * (`readElfNeeded` — the DT_NEEDED list is where the answer actually lives).
62
+ *
63
+ * DELIBERATELY NOT CHOSEN: renaming every existing `linux-<arch>` directory to
64
+ * `linux-<arch>-gnu`, which is what a symmetric grammar would demand. It would
65
+ * rename ~60 COMMITTED directories, and — per `status/open-todos.md`'s
66
+ * "Nine fixtures re-implement the prebuild-target name instead of importing it"
67
+ * — nine e2e fixtures COMPOSE the token themselves instead of importing it, so
68
+ * the rename would have to be swept by hand in two shapes (the literal path and
69
+ * the computed one), which is exactly how the last vocabulary change missed
70
+ * `tests/e2e/self-host/run.mjs`. The asymmetric grammar buys the same
71
+ * expressiveness for zero renames, and the cost — that "unsuffixed" is not
72
+ * self-describing — is paid by a field that is machine-checked anyway.
73
+ *
74
+ * A `-musl` suffix is only meaningful when the OS half is `linux`: musl targets
75
+ * no other kernel, and npm's own `libc` field is documented as Linux-only. A
76
+ * `darwin-arm64-musl` token is therefore not a target with an odd name, it is a
77
+ * malformed one, and this returns `libc: null` for it so callers report the
78
+ * token rather than half-honouring it.
79
+ */
80
+ export declare function parsePlatformToken(token: string): {
81
+ os: string;
82
+ arch: string;
83
+ libc: HostLibc | null;
84
+ };
85
+ /**
86
+ * Canonical `<os>-<arch>[-musl]` form — the node spelling — so `linux-x86_64`
87
+ * and `linux-x64` compare equal. Only the *arch* half is normalised: the OS half
88
+ * is always a `process.platform` token (`linux`/`darwin`/`win32`), which has no
89
+ * competing spelling, and the libc half has exactly one spelling by
90
+ * construction.
91
+ *
92
+ * A `-musl` suffix on a non-Linux token is preserved verbatim rather than
93
+ * dropped. Silently canonicalising `darwin-arm64-musl` to `darwin-arm64` would
94
+ * make a malformed declaration compare EQUAL to a valid one, so the audit that
95
+ * rejects it would never see it — the same class of bug as folding the legacy
96
+ * uname spelling on a WRITE path.
38
97
  */
39
98
  export declare function canonicalPlatformToken(token: string): string;
99
+ /**
100
+ * The target tokens that describe THIS host, most-specific first — the WRITE-side
101
+ * vocabulary.
102
+ *
103
+ * This is the single definition of the musl preference order, and three callers
104
+ * share it so it cannot drift: the directory probe ({@link prebuildDirCandidates}),
105
+ * the per-platform sibling package probe ({@link platformPackageName}), and
106
+ * `scripts/stage-prebuild.mjs`, which must stage a musl build into
107
+ * `linux-x64-musl/` and never into `linux-x64/`.
108
+ *
109
+ * On a musl host the suffixed token comes FIRST and the unsuffixed one stays as a
110
+ * fallback: an unsuffixed directory is the default build, which for the
111
+ * libc-agnostic bridges (they record no `libc.so.6` at all) genuinely does load
112
+ * on musl. On glibc the `-musl` token is not offered at all — a musl artifact
113
+ * cannot load against glibc, so probing for it could only ever produce a
114
+ * false positive.
115
+ *
116
+ * @param platform `process.platform` value.
117
+ * @param arch `process.arch` value.
118
+ * @param libc host C library; `undefined`/`null` behaves as glibc (see
119
+ * {@link HostTarget.libc} for why that is the safe default).
120
+ */
121
+ export declare function hostPlatformTokens(platform: string, arch: string, libc?: HostLibc | null): string[];
40
122
  /**
41
123
  * The prebuild directory names to probe for a host, most-specific first.
42
124
  *
@@ -65,11 +147,21 @@ export declare function canonicalPlatformToken(token: string): string;
65
147
  * listing — no extra I/O — so tolerance on the READ side costs nothing, while
66
148
  * the single spelling is enforced everywhere a name is WRITTEN.
67
149
  *
150
+ * The LIBC AXIS rides in front of all of that: on a musl host the
151
+ * `<os>-<arch>-musl` token is preferred over `<os>-<arch>`, and on glibc the
152
+ * suffixed token is NEVER offered (a musl artifact cannot load against glibc, so
153
+ * a probe for it can only produce a false positive). The `libc` value is a
154
+ * PARAMETER rather than a `process` read for the same reason `platform` and
155
+ * `arch` are: it is the only way the musl branch gets exercised at all, since CI
156
+ * and every developer machine here run glibc.
157
+ *
68
158
  * @param platform `process.platform` value.
69
159
  * @param arch `process.arch` value.
70
160
  * @param declaredPlatforms `package.json#gjsify.platforms`, when present.
161
+ * @param libc host C library; `undefined`/`null` = behave as before the axis
162
+ * existed.
71
163
  */
72
- export declare function prebuildDirCandidates(platform: string, arch: string, declaredPlatforms?: readonly string[] | undefined): string[];
164
+ export declare function prebuildDirCandidates(platform: string, arch: string, declaredPlatforms?: readonly string[] | undefined, libc?: HostLibc | null): string[];
73
165
  /**
74
166
  * Pick the prebuild directory a host should load, given the directories that
75
167
  * actually exist under `<pkg>/<prebuilds>/`.
@@ -85,7 +177,28 @@ export declare function resolvePrebuildDirName(input: {
85
177
  arch: string;
86
178
  declaredPlatforms?: readonly string[] | undefined;
87
179
  existingDirs: readonly string[];
180
+ libc?: HostLibc | null;
88
181
  }): string | null;
182
+ /**
183
+ * The npm name of the per-platform companion package for a target.
184
+ *
185
+ * `@gjsify/rolldown-native` + `linux-x64` → `@gjsify/rolldown-native-linux-x64`,
186
+ * the pattern `@gjsify/gtk-runtime-darwin-arm64` already ships (and the one
187
+ * napi-rs uses for its `<pkg>-<triple>` siblings, which
188
+ * `napi-node-addon.ts`'s `isNapiRsSibling` already knows).
189
+ *
190
+ * Exported so there is exactly ONE definition of it. The heavy bridges are being
191
+ * split into per-platform packages, and that rollout, the sibling resolution
192
+ * below and the workspace audit all need this string — three independent
193
+ * `` `${name}-${token}` `` template literals would be three places for the
194
+ * separator, the scope handling or the token spelling to drift, and a drifted
195
+ * copy fails as "prebuild not found" in a consumer while every test here stays
196
+ * green.
197
+ *
198
+ * @param baseName the depending package's npm name, scope included.
199
+ * @param token an `<os>-<arch>[-musl]` target.
200
+ */
201
+ export declare function platformPackageName(baseName: string, token: string): string;
89
202
  /**
90
203
  * Walk up the directory tree from `startDir` and merge native packages found
91
204
  * in every `node_modules` encountered.
@@ -103,6 +216,48 @@ export declare function resolvePrebuildDirName(input: {
103
216
  * production call sites take the defaults.
104
217
  */
105
218
  export declare function detectNativePackages(startDir: string, target?: HostTarget): NativePackage[];
219
+ /**
220
+ * Decide a host's C library from independently-gathered facts. PURE.
221
+ *
222
+ * Two probes, because neither is available everywhere this CLI runs:
223
+ *
224
+ * 1. `process.report.getReport().header.glibcVersionRuntime` — present iff the
225
+ * running process is linked against glibc. This is the probe `detect-libc`
226
+ * and node-gyp use, and it is authoritative when it answers. It is a
227
+ * NODE-only API: under the committed GJS bundle `@gjsify/process` has no
228
+ * `report`, so on the runtime this project targets FIRST it never answers.
229
+ * 2. Whether musl's dynamic loader is installed. musl always installs it as
230
+ * `/lib/ld-musl-<arch>.so.1`, which is a fact about the SYSTEM rather than
231
+ * about the running process, so it answers under GJS too.
232
+ *
233
+ * The order matters only for speed; the two cannot disagree on a sane host.
234
+ * When NEITHER answers we return `'glibc'`, and that is a claim about the
235
+ * evidence rather than a guess: probe 2 not finding a musl loader means the host
236
+ * has no musl at all, so `-musl` directories are not what it wants. The failure
237
+ * mode of the opposite default — treating an unclassified host as musl — is
238
+ * probing for suffixed directories on every glibc machine, which is noise with no
239
+ * upside.
240
+ *
241
+ * @param platform `process.platform` value; the axis is Linux-only (as npm's own
242
+ * `libc` field is), so every other OS returns null.
243
+ */
244
+ export declare function resolveHostLibc(input: {
245
+ platform: string;
246
+ glibcVersionRuntime?: string | undefined;
247
+ muslLoaderPresent?: boolean;
248
+ }): HostLibc | null;
249
+ /**
250
+ * Gather the two host facts {@link resolveHostLibc} decides from.
251
+ *
252
+ * BOTH are gathered unconditionally, even though the first one alone usually
253
+ * answers. They are independent observations — one about the running process, one
254
+ * about the system — and a host can genuinely exhibit both (a glibc-linked Node
255
+ * on a distro that also installs musl, e.g. under gcompat). Collecting them
256
+ * separately keeps the decision itself in one pure function a test can pin,
257
+ * instead of encoding the precedence in an if/else chain here where nothing can
258
+ * reach it.
259
+ */
260
+ export declare function detectHostLibc(platform: string): HostLibc | null;
106
261
  /**
107
262
  * The environment-variable name the host's dynamic loader consults for an
108
263
  * additional shared-library search path, plus the separator its search paths