@gjsify/cli 0.24.1 → 0.25.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/dist/cli.gjs.mjs +190 -184
- package/lib/actions/build.d.ts +13 -0
- package/lib/actions/build.js +57 -11
- package/lib/commands/dlx.d.ts +15 -0
- package/lib/commands/dlx.js +3 -2
- package/lib/commands/install.d.ts +1 -1
- package/lib/commands/install.js +10 -19
- package/lib/commands/showcase.js +58 -7
- package/lib/utils/bin-shim.d.ts +49 -0
- package/lib/utils/bin-shim.js +105 -0
- package/lib/utils/install-backend-native.js +0 -1
- package/lib/utils/install-global.d.ts +0 -16
- package/lib/utils/install-global.js +19 -36
- package/lib/utils/node-bundle-guard.d.ts +16 -0
- package/lib/utils/node-bundle-guard.js +69 -0
- package/lib/utils/normalize-bundler-options.js +0 -2
- package/package.json +17 -17
package/lib/actions/build.d.ts
CHANGED
|
@@ -60,6 +60,19 @@ export declare class BuildAction {
|
|
|
60
60
|
* text scan cannot tell from a statement.
|
|
61
61
|
*/
|
|
62
62
|
private assertGjsOutputLoadable;
|
|
63
|
+
/**
|
|
64
|
+
* Post-bundle gate for `--app node`: throw when the emitted bundle still
|
|
65
|
+
* references GJS ambient globals (`print`/`imports`/…) although the
|
|
66
|
+
* pre-build detection decided no `@gjsify/node-gi/globals` shim was needed
|
|
67
|
+
* — see `utils/node-bundle-guard.ts` for the full rationale.
|
|
68
|
+
*
|
|
69
|
+
* Unlike the `--app gjs` gate, the oracle here CANNOT be the module graph:
|
|
70
|
+
* an ambient global is a free identifier, not an import, so nothing about it
|
|
71
|
+
* appears in a chunk's `imports` list. It is the same acorn pass the
|
|
72
|
+
* detector runs, re-applied to what was actually written — a post-condition
|
|
73
|
+
* on the prediction, not a second opinion.
|
|
74
|
+
*/
|
|
75
|
+
private assertNodeOutputGlobals;
|
|
63
76
|
/**
|
|
64
77
|
* Post-bundle step for a `--app browser` HTML entry: write the processed
|
|
65
78
|
* `index.html` beside the JS bundle, with the entry `<script>`'s `src`
|
package/lib/actions/build.js
CHANGED
|
@@ -11,6 +11,7 @@ import { normalizeBundlerOptions, mergeBundlerOptions } from '../utils/normalize
|
|
|
11
11
|
import { inputSourceDirs, isOutdirInsideSource, libraryOutputLeakError } from '../utils/library-output.js';
|
|
12
12
|
import { detectHtmlEntry, parseHtmlEntry, emitBrowserHtml, htmlOutPathFor } from '../utils/html-entry.js';
|
|
13
13
|
import { assertGjsBundleLoadable } from '../utils/gjs-bundle-guard.js';
|
|
14
|
+
import { assertNodeBundleGlobalsShimmed } from '../utils/node-bundle-guard.js';
|
|
14
15
|
const DEFAULT_GJS_SHEBANG = '#!/usr/bin/env -S gjs -m';
|
|
15
16
|
/**
|
|
16
17
|
* `true` when `path` points at a location that's unsafe to use as a build
|
|
@@ -291,6 +292,31 @@ export class BuildAction {
|
|
|
291
292
|
const chunks = (result.output ?? []).filter((item) => item.type === 'chunk');
|
|
292
293
|
assertGjsBundleLoadable(chunks, outfile ?? outdir ?? 'the GJS bundle');
|
|
293
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* Post-bundle gate for `--app node`: throw when the emitted bundle still
|
|
297
|
+
* references GJS ambient globals (`print`/`imports`/…) although the
|
|
298
|
+
* pre-build detection decided no `@gjsify/node-gi/globals` shim was needed
|
|
299
|
+
* — see `utils/node-bundle-guard.ts` for the full rationale.
|
|
300
|
+
*
|
|
301
|
+
* Unlike the `--app gjs` gate, the oracle here CANNOT be the module graph:
|
|
302
|
+
* an ambient global is a free identifier, not an import, so nothing about it
|
|
303
|
+
* appears in a chunk's `imports` list. It is the same acorn pass the
|
|
304
|
+
* detector runs, re-applied to what was actually written — a post-condition
|
|
305
|
+
* on the prediction, not a second opinion.
|
|
306
|
+
*/
|
|
307
|
+
assertNodeOutputGlobals(result, outfile, outdir) {
|
|
308
|
+
const label = outfile ?? outdir ?? 'the node bundle';
|
|
309
|
+
for (const item of result.output ?? []) {
|
|
310
|
+
if (item.type !== 'chunk')
|
|
311
|
+
continue;
|
|
312
|
+
// `code` is present on a written chunk for both engines; skip
|
|
313
|
+
// defensively rather than fail a build over a missing field.
|
|
314
|
+
const code = item.code;
|
|
315
|
+
if (typeof code !== 'string' || code.length === 0)
|
|
316
|
+
continue;
|
|
317
|
+
assertNodeBundleGlobalsShimmed(code, item.fileName ? `${label} (${item.fileName})` : label);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
294
320
|
/**
|
|
295
321
|
* Post-bundle step for a `--app browser` HTML entry: write the processed
|
|
296
322
|
* `index.html` beside the JS bundle, with the entry `<script>`'s `src`
|
|
@@ -530,6 +556,30 @@ export class BuildAction {
|
|
|
530
556
|
// detection must run BEFORE the final build so the entry can be
|
|
531
557
|
// wrapped; nothing is injected when no globals are referenced (the
|
|
532
558
|
// eager-native-load regression guard).
|
|
559
|
+
//
|
|
560
|
+
// Explicit extras on a node build (`--globals auto,dom,…`) are the
|
|
561
|
+
// reverse bridge's DOM-surface request: inject the SAME register
|
|
562
|
+
// modules the gjs target would for those identifiers, so a genuine
|
|
563
|
+
// GJS source (an Excalibur/WebGLBridge app) gets document /
|
|
564
|
+
// HTMLCanvasElement / matchMedia / … on Node via node-gi. The
|
|
565
|
+
// orchestrator lifts the `@gjsify/empty` register routing only when
|
|
566
|
+
// this inject stub is present (see app/node.ts) — plain `auto`
|
|
567
|
+
// node builds are byte-unchanged.
|
|
568
|
+
//
|
|
569
|
+
// The stub is resolved BEFORE the detection pass, and that ordering
|
|
570
|
+
// is load-bearing: it is one of the two signals `isGjsSourceBuild`
|
|
571
|
+
// reads, so it decides whether `/register` subpaths resolve for real
|
|
572
|
+
// or route to `@gjsify/empty` and whether `@girs/*` bodies survive.
|
|
573
|
+
// Resolving it afterwards made the analysis bundle a DIFFERENT
|
|
574
|
+
// module graph from the one actually emitted — every GJS ambient
|
|
575
|
+
// global reachable only THROUGH a register module was invisible, so
|
|
576
|
+
// the shim was not injected and the emitted bundle threw
|
|
577
|
+
// `ReferenceError: imports is not defined` at runtime (the
|
|
578
|
+
// excalibur-jelly-jumper showcase on node/bun, via
|
|
579
|
+
// `@gjsify/canvas2d-core`'s `_toDataURL` and `@gjsify/utils`).
|
|
580
|
+
if (extras) {
|
|
581
|
+
pluginOpts.autoGlobalsInject = await this.resolveGlobalsInject(app, extras, verbose);
|
|
582
|
+
}
|
|
533
583
|
const gjsifyPluginFactory = async (opts) => {
|
|
534
584
|
const cfg = await gjsifyPlugin({
|
|
535
585
|
input: userBundler.input,
|
|
@@ -550,17 +600,6 @@ export class BuildAction {
|
|
|
550
600
|
}, pluginOpts, gjsifyPluginFactory, bundleToChunks);
|
|
551
601
|
if (needsGiGlobals)
|
|
552
602
|
pluginOpts.nodeGiGlobalsInject = true;
|
|
553
|
-
// Explicit extras on a node build (`--globals auto,dom,…`) are the
|
|
554
|
-
// reverse bridge's DOM-surface request: inject the SAME register
|
|
555
|
-
// modules the gjs target would for those identifiers, so a genuine
|
|
556
|
-
// GJS source (an Excalibur/WebGLBridge app) gets document /
|
|
557
|
-
// HTMLCanvasElement / matchMedia / … on Node via node-gi. The
|
|
558
|
-
// orchestrator lifts the `@gjsify/empty` register routing only when
|
|
559
|
-
// this inject stub is present (see app/node.ts) — plain `auto`
|
|
560
|
-
// node builds are byte-unchanged.
|
|
561
|
-
if (extras) {
|
|
562
|
-
pluginOpts.autoGlobalsInject = await this.resolveGlobalsInject(app, extras, verbose);
|
|
563
|
-
}
|
|
564
603
|
}
|
|
565
604
|
else if (extras) {
|
|
566
605
|
pluginOpts.autoGlobalsInject = await this.resolveGlobalsInject(app, extras, verbose);
|
|
@@ -596,6 +635,13 @@ export class BuildAction {
|
|
|
596
635
|
if (app === 'gjs') {
|
|
597
636
|
this.assertGjsOutputLoadable(writeResult, outfile, outdir);
|
|
598
637
|
}
|
|
638
|
+
// Refuse to hand back a `--app node` bundle that reaches for GJS ambient
|
|
639
|
+
// globals nothing seeds. Only meaningful when the shim was NOT injected:
|
|
640
|
+
// with it, the globals are installed and the references are correct.
|
|
641
|
+
// `--globals none` opts out of the mechanism, so it opts out of the gate.
|
|
642
|
+
if (app === 'node' && autoMode && !pluginOpts.nodeGiGlobalsInject) {
|
|
643
|
+
this.assertNodeOutputGlobals(writeResult, outfile, outdir);
|
|
644
|
+
}
|
|
599
645
|
// Browser HTML entry: emit the processed `index.html` beside the bundle
|
|
600
646
|
// (engine-portable post-step — the native rolldown engine has no
|
|
601
647
|
// `emitFile`, so this can't live in a plugin). `outfile` is guaranteed
|
package/lib/commands/dlx.d.ts
CHANGED
|
@@ -16,6 +16,21 @@ export interface EnsureOpts {
|
|
|
16
16
|
registry?: string;
|
|
17
17
|
cacheMaxAge: number;
|
|
18
18
|
frozen: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Extra specs to install into the SAME cache tree as the requested package.
|
|
21
|
+
*
|
|
22
|
+
* A dlx tree is the package plus its own `dependencies`, which is right for
|
|
23
|
+
* the GJS path — the showcase's `--app gjs` bundle needs nothing else. The
|
|
24
|
+
* `--app node` bundle does: its `gi://` imports resolve through
|
|
25
|
+
* `@gjsify/node-gi`, which is a build-time concern for the showcase (a
|
|
26
|
+
* devDependency) but a RUNTIME one for whoever launches it off GJS. Nobody
|
|
27
|
+
* can act on the resulting "add @gjsify/node-gi as a dependency" error —
|
|
28
|
+
* the tree belongs to the cache, not to the user — so the launcher that
|
|
29
|
+
* knows the runtime supplies it.
|
|
30
|
+
*
|
|
31
|
+
* Part of the cache key, so the gjs and node trees never share an entry.
|
|
32
|
+
*/
|
|
33
|
+
extraSpecs?: readonly string[];
|
|
19
34
|
}
|
|
20
35
|
/**
|
|
21
36
|
* Resolve (installing + caching when remote) the on-disk directory of a package
|
package/lib/commands/dlx.js
CHANGED
|
@@ -106,7 +106,8 @@ export async function ensurePkgDir(parsed, opts) {
|
|
|
106
106
|
if (parsed.kind === 'local') {
|
|
107
107
|
return { pkgDir: parsed.path, cachedPkgName: null };
|
|
108
108
|
}
|
|
109
|
-
const
|
|
109
|
+
const specs = [parsed.spec, ...(opts.extraSpecs ?? [])];
|
|
110
|
+
const cacheKey = createCacheKey({ packages: specs });
|
|
110
111
|
const cacheDir = cacheDirFor(cacheKey);
|
|
111
112
|
const cached = opts.cacheMaxAge > 0 ? getValidCachedPkg(cacheDir, opts.cacheMaxAge) : undefined;
|
|
112
113
|
if (cached) {
|
|
@@ -124,7 +125,7 @@ export async function ensurePkgDir(parsed, opts) {
|
|
|
124
125
|
const progress = makeProgressReporter({ enabled: !opts.verbose });
|
|
125
126
|
await installPackages({
|
|
126
127
|
prefix: prepareDir,
|
|
127
|
-
specs
|
|
128
|
+
specs,
|
|
128
129
|
verbose: opts.verbose,
|
|
129
130
|
registry: opts.registry,
|
|
130
131
|
// Cache-prepare dirs are scoped per cache key, so writing a lockfile
|
|
@@ -14,7 +14,7 @@ interface InstallOptions {
|
|
|
14
14
|
timeout: number;
|
|
15
15
|
}
|
|
16
16
|
export declare const installCommand: Command<unknown, InstallOptions>;
|
|
17
|
-
export declare function buildBinShim(wsLocation: string, nodeTarget?: string, gjsTarget?: string, nativePrebuildDirs?: string[], platform?: string): string;
|
|
17
|
+
export declare function buildBinShim(wsLocation: string, nodeTarget?: string, gjsTarget?: string, nativePrebuildDirs?: string[], platform?: string, scanRoot?: string): string;
|
|
18
18
|
/**
|
|
19
19
|
* `.cmd` / `.ps1` companions for {@link buildBinShim}.
|
|
20
20
|
*
|
package/lib/commands/install.js
CHANGED
|
@@ -34,8 +34,8 @@ import { spawnSync } from 'node:child_process';
|
|
|
34
34
|
import { gjsExit } from '@gjsify/rolldown-plugin-gjsify/runtime';
|
|
35
35
|
import { discoverWorkspaces } from '@gjsify/workspace';
|
|
36
36
|
import { buildInstallCommand, detectPackageManager, runMinimalChecks } from '../utils/check-system-deps.js';
|
|
37
|
-
import { detectNativePackages
|
|
38
|
-
import { buildLauncherShims } from '../utils/bin-shim.js';
|
|
37
|
+
import { detectNativePackages } from '../utils/detect-native-packages.js';
|
|
38
|
+
import { buildLauncherShims, buildNativeEnvPreamble } from '../utils/bin-shim.js';
|
|
39
39
|
import { installPackages, makeProgressReporter } from '../utils/install-backend.js';
|
|
40
40
|
import { atomicWriteStrict } from '../utils/install-cache-fs.js';
|
|
41
41
|
import { acquireInstallLock } from '../utils/install-lock.js';
|
|
@@ -957,9 +957,7 @@ function writeWorkspaceBinShims(cwd, workspaces) {
|
|
|
957
957
|
/* fine */
|
|
958
958
|
}
|
|
959
959
|
}
|
|
960
|
-
writeFileSync(linkPath, buildBinShim(ws.location, nodeTarget, gjsTarget, nativePrebuildDirs), {
|
|
961
|
-
mode: 0o755,
|
|
962
|
-
});
|
|
960
|
+
writeFileSync(linkPath, buildBinShim(ws.location, nodeTarget, gjsTarget, nativePrebuildDirs, process.platform, cwd), { mode: 0o755 });
|
|
963
961
|
chmodSync(linkPath, 0o755);
|
|
964
962
|
// Windows executes neither an extension-less file nor a `#!` line —
|
|
965
963
|
// the sh shim above is only reachable from git-bash/MSYS/WSL. Write
|
|
@@ -1045,26 +1043,19 @@ function scopedOverrideFor(ws, depName, extracted) {
|
|
|
1045
1043
|
}
|
|
1046
1044
|
return undefined;
|
|
1047
1045
|
}
|
|
1048
|
-
export function buildBinShim(wsLocation, nodeTarget, gjsTarget, nativePrebuildDirs = [], platform = process.platform) {
|
|
1046
|
+
export function buildBinShim(wsLocation, nodeTarget, gjsTarget, nativePrebuildDirs = [], platform = process.platform, scanRoot = wsLocation) {
|
|
1049
1047
|
const nodeAbs = nodeTarget ? join(wsLocation, nodeTarget) : null;
|
|
1050
1048
|
const gjsAbs = gjsTarget ? join(wsLocation, gjsTarget) : null;
|
|
1051
1049
|
// GJS-only env preamble — Node ignores GI_TYPELIB_PATH so we scope the
|
|
1052
1050
|
// export to the gjs branch, keeping the shim minimal when no native pkgs
|
|
1053
1051
|
// exist or only the Node bin is in play.
|
|
1054
1052
|
//
|
|
1055
|
-
//
|
|
1056
|
-
//
|
|
1057
|
-
//
|
|
1058
|
-
//
|
|
1059
|
-
|
|
1060
|
-
const gjsPreamble = nativePrebuildDirs
|
|
1061
|
-
? ''
|
|
1062
|
-
: (() => {
|
|
1063
|
-
const joined = `'${nativePrebuildDirs.join(':').replace(/'/g, `'\\''`)}'`;
|
|
1064
|
-
return (`GI_TYPELIB_PATH=${joined}\${GI_TYPELIB_PATH:+":$GI_TYPELIB_PATH"}\n` +
|
|
1065
|
-
`${libVar}=${joined}\${${libVar}:+":$${libVar}"}\n` +
|
|
1066
|
-
`export GI_TYPELIB_PATH ${libVar}\n`);
|
|
1067
|
-
})();
|
|
1053
|
+
// `scanRoot` is the INSTALL prefix (the tree holding `node_modules/.bin`),
|
|
1054
|
+
// not the workspace package dir: the preamble globs its `node_modules` at
|
|
1055
|
+
// launch time instead of embedding what was installed the day the shim was
|
|
1056
|
+
// written, so `gjsify install <some>-native` afterwards is picked up without
|
|
1057
|
+
// a re-link. See `buildNativeEnvPreamble`.
|
|
1058
|
+
const gjsPreamble = buildNativeEnvPreamble(scanRoot, nativePrebuildDirs, { platform });
|
|
1068
1059
|
if (nodeAbs && gjsAbs) {
|
|
1069
1060
|
// GJS-FIRST: prefer the committed Node-free GJS bundle when `gjs` is on
|
|
1070
1061
|
// PATH AND the bundle exists; fall back to the Node entry otherwise (a
|
package/lib/commands/showcase.js
CHANGED
|
@@ -18,6 +18,27 @@ function readCliVersion() {
|
|
|
18
18
|
return undefined;
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* `[gjsify <version>] ` for the banner — the CLI naming ITSELF.
|
|
23
|
+
*
|
|
24
|
+
* `showcase` pins the showcase package to the CLI's own version, so a stale CLI
|
|
25
|
+
* silently runs a stale showcase, and the only version in the output was the
|
|
26
|
+
* showcase's. That is not a hypothetical: every runner that caches an unpinned
|
|
27
|
+
* bin reuses whatever it resolved once (`npx @gjsify/cli` → a 0.4.x `_npx`
|
|
28
|
+
* entry; `deno run npm:@gjsify/cli` → whatever sits in `DENO_DIR`), so the same
|
|
29
|
+
* command produced 0.24.1 under bunx, 0.4.25 under npx and 0.23.0 under deno on
|
|
30
|
+
* ONE machine on ONE day — and the two old ones failed in ways that read as
|
|
31
|
+
* gjsify bugs. gjsify cannot flush somebody else's cache, but it can stop the
|
|
32
|
+
* output from hiding which gjsify is talking.
|
|
33
|
+
*/
|
|
34
|
+
function runningAs(cliVersion) {
|
|
35
|
+
return cliVersion ? `[gjsify ${cliVersion}] ` : '';
|
|
36
|
+
}
|
|
37
|
+
/** Pinned invocations for the three runners that cache an unpinned bin. */
|
|
38
|
+
const PIN_HINT = 'A package runner reuses a CACHED copy of an unpinned bin. Force the current one:\n' +
|
|
39
|
+
' npx @gjsify/cli@latest showcase <name>\n' +
|
|
40
|
+
' bunx @gjsify/cli@latest showcase <name>\n' +
|
|
41
|
+
' deno run -A --reload npm:@gjsify/cli@latest showcase <name>';
|
|
21
42
|
export const showcaseCommand = {
|
|
22
43
|
command: 'showcase [name]',
|
|
23
44
|
description: 'List or run curated gjsify showcase applications.',
|
|
@@ -146,7 +167,7 @@ export const showcaseCommand = {
|
|
|
146
167
|
// newer CLI assumes (the `@gjsify/http-soup-bridge` regression
|
|
147
168
|
// reported against `@gjsify/cli@0.3.17`).
|
|
148
169
|
const dlxSpec = cliVersion ? `${showcase.packageName}@${cliVersion}` : showcase.packageName;
|
|
149
|
-
console.log(`Running showcase: ${showcase.name} (via gjsify dlx ${dlxSpec})\n`);
|
|
170
|
+
console.log(`Running showcase: ${showcase.name} ${runningAs(cliVersion)}(via gjsify dlx ${dlxSpec})\n`);
|
|
150
171
|
// Dispatch `dlx` IN-PROCESS (not `spawn(process.execPath, [cliBin, …])`):
|
|
151
172
|
// under the committed GJS bundle `process.execPath` is `gjs` and
|
|
152
173
|
// `../index.js` is the Node entry, so the spawn ran `gjs cli.gjs.mjs
|
|
@@ -169,7 +190,7 @@ export const showcaseCommand = {
|
|
|
169
190
|
* in-tree build), falling back to `gjsify dlx`'s install-into-cache for a
|
|
170
191
|
* published showcase. Returns the directory containing the package.json.
|
|
171
192
|
*/
|
|
172
|
-
async function resolveShowcaseDir(showcase, cliVersion) {
|
|
193
|
+
async function resolveShowcaseDir(showcase, cliVersion, extraSpecs = []) {
|
|
173
194
|
// Local-first: a workspace showcase resolves via its own package name.
|
|
174
195
|
try {
|
|
175
196
|
const req = createRequire(import.meta.url);
|
|
@@ -185,6 +206,7 @@ async function resolveShowcaseDir(showcase, cliVersion) {
|
|
|
185
206
|
verbose: false,
|
|
186
207
|
cacheMaxAge: 60 * 24 * 7,
|
|
187
208
|
frozen: false,
|
|
209
|
+
extraSpecs,
|
|
188
210
|
});
|
|
189
211
|
return pkgDir;
|
|
190
212
|
}
|
|
@@ -197,7 +219,16 @@ async function resolveShowcaseDir(showcase, cliVersion) {
|
|
|
197
219
|
async function runShowcaseOnRuntime(showcase, runtime, cliVersion) {
|
|
198
220
|
let pkgDir;
|
|
199
221
|
try {
|
|
200
|
-
|
|
222
|
+
// `@gjsify/node-gi` is the `gi://` bridge the `--app node` bundle
|
|
223
|
+
// resolves at RUNTIME. Showcases carry it as a devDependency (it is a
|
|
224
|
+
// build input for them), so a dlx tree — the package plus its own
|
|
225
|
+
// `dependencies` — does not contain it and the launch died with "add
|
|
226
|
+
// @gjsify/node-gi as a dependency": advice the user cannot act on,
|
|
227
|
+
// since the tree is the CLI's cache. The launcher knows the runtime, so
|
|
228
|
+
// it supplies the bridge. Pinned to the CLI's version like the showcase
|
|
229
|
+
// itself, so the bridge and the bundle move together.
|
|
230
|
+
const nodeGiSpec = cliVersion ? `@gjsify/node-gi@${cliVersion}` : '@gjsify/node-gi';
|
|
231
|
+
pkgDir = await resolveShowcaseDir(showcase, cliVersion, [nodeGiSpec]);
|
|
201
232
|
}
|
|
202
233
|
catch (err) {
|
|
203
234
|
console.error(`Could not resolve showcase "${showcase.name}": ${err.message}`);
|
|
@@ -222,10 +253,30 @@ async function runShowcaseOnRuntime(showcase, runtime, cliVersion) {
|
|
|
222
253
|
entry = resolveNodeEntry(pkgDir);
|
|
223
254
|
}
|
|
224
255
|
catch (err) {
|
|
225
|
-
|
|
256
|
+
// The showcase promised this runtime and did not ship the bundle. Two
|
|
257
|
+
// causes, and the user can only act on one of them, so name both: the
|
|
258
|
+
// package version is genuinely broken (fixed by a newer one), or THIS
|
|
259
|
+
// CLI is a cached old copy that pinned a genuinely broken old showcase.
|
|
260
|
+
const version = typeof pkg.name === 'string' ? `${pkg.name}@${cliVersion ?? '?'}` : showcase.packageName;
|
|
261
|
+
console.error(`Cannot run showcase "${showcase.name}" on ${runtime}: ${err.message}\n\n` +
|
|
262
|
+
` ${version} declares --runtime ${runtime} but ships no \`--app node\` bundle, so that\n` +
|
|
263
|
+
` version of the showcase cannot run there. You are running gjsify ${cliVersion ?? '(unknown)'},\n` +
|
|
264
|
+
` and \`showcase\` pins the showcase to the CLI's own version.\n\n ${PIN_HINT}\n\n` +
|
|
265
|
+
` Or run it on GJS, which every showcase ships: gjsify showcase ${showcase.name} --runtime gjs`);
|
|
266
|
+
return process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
console.log(`Running showcase: ${showcase.name} ${runningAs(cliVersion)}on ${runtime} (${entry})\n`);
|
|
269
|
+
// Terminal call — exit on success. Failures are reported here rather than
|
|
270
|
+
// thrown: an exception escaping the handler makes yargs dump the command's
|
|
271
|
+
// full `--help` above the message, which buries it.
|
|
272
|
+
try {
|
|
273
|
+
await runRuntimeBundle(runtime, entry, [], { exitOnSuccess: true });
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
console.error(`${err.message}\n\n` +
|
|
277
|
+
` The showcase and the \`@gjsify/node-gi\` bridge it needs are both installed by THIS CLI\n` +
|
|
278
|
+
` (gjsify ${cliVersion ?? '(unknown)'}) into its own cache, so a missing bridge points at the CLI\n` +
|
|
279
|
+
` rather than at anything in your project.\n\n ${PIN_HINT}`);
|
|
226
280
|
return process.exit(1);
|
|
227
281
|
}
|
|
228
|
-
console.log(`Running showcase: ${showcase.name} on ${runtime} (${entry})\n`);
|
|
229
|
-
// Terminal call — exit on success.
|
|
230
|
-
await runRuntimeBundle(runtime, entry, [], { exitOnSuccess: true });
|
|
231
282
|
}
|
package/lib/utils/bin-shim.d.ts
CHANGED
|
@@ -64,3 +64,52 @@ export declare function buildLauncherShims(opts: {
|
|
|
64
64
|
cmd: string;
|
|
65
65
|
ps1: string;
|
|
66
66
|
};
|
|
67
|
+
/**
|
|
68
|
+
* The `sh` preamble a GJS launcher needs so `imports.gi.X` resolves against the
|
|
69
|
+
* `@gjsify/*` native prebuilds installed next to it.
|
|
70
|
+
*
|
|
71
|
+
* WHY IT SCANS AT RUN TIME INSTEAD OF BAKING A LIST
|
|
72
|
+
*
|
|
73
|
+
* The typelib/library lookup happens inside the GJS runtime, before a single
|
|
74
|
+
* line of the CLI bundle runs, so the env MUST be set by the launcher — the CLI
|
|
75
|
+
* cannot repair it from the inside. The launcher used to bake the directories
|
|
76
|
+
* `detectNativePackages()` found at INSTALL time, which makes the env a SNAPSHOT
|
|
77
|
+
* of one moment:
|
|
78
|
+
*
|
|
79
|
+
* - a native package installed into the same prefix LATER (`gjsify install -g
|
|
80
|
+
* @gjsify/<x>-native`, a project `gjsify install` that adds one) is invisible
|
|
81
|
+
* until something happens to rewrite the launcher;
|
|
82
|
+
* - and if the scan comes back empty for any reason, the launcher is written
|
|
83
|
+
* with NO preamble at all and nothing says so. The failure then surfaces
|
|
84
|
+
* much later and somewhere else entirely — `gjsify build` dying with "no
|
|
85
|
+
* usable bundler engine under GJS" in an unrelated project, which reads as a
|
|
86
|
+
* broken install rather than a stale launcher. That is what shipped in
|
|
87
|
+
* v0.24.1: a `~/.local/bin/gjsify` with a bare `exec gjs -m …` line while
|
|
88
|
+
* `@gjsify/rolldown-native` sat correctly installed in the prefix.
|
|
89
|
+
*
|
|
90
|
+
* Deriving the env from disk on every launch removes both: the launcher states
|
|
91
|
+
* WHERE to look rather than WHAT was there, so it cannot go stale, and an empty
|
|
92
|
+
* result is a fact about the current prefix instead of a silently frozen one.
|
|
93
|
+
* The cost is a handful of shell globs per launch (they are builtins, and a
|
|
94
|
+
* pattern that matches nothing stays literal and is rejected by `[ -d ]`).
|
|
95
|
+
*
|
|
96
|
+
* `bakedDirs` covers what a run-time scan of `scanRoot` structurally cannot see:
|
|
97
|
+
* `detectNativePackages()` walks UP the directory chain, so in a hoisted layout
|
|
98
|
+
* a prebuild may live in an ancestor's `node_modules`. Those are still embedded
|
|
99
|
+
* verbatim; only the primary root is dynamic. Duplicates are harmless (both
|
|
100
|
+
* variables are search paths).
|
|
101
|
+
*
|
|
102
|
+
* Windows is deliberately excluded: there the `sh` file is only reachable from
|
|
103
|
+
* git-bash/MSYS, the real launchers are the `.cmd`/`.ps1` companions built by
|
|
104
|
+
* {@link buildLauncherShims} from the same baked list, and `PATH` doubles as the
|
|
105
|
+
* DLL search path with `;` separators. It keeps the snapshot behaviour.
|
|
106
|
+
*
|
|
107
|
+
* @param scanRoot Directory whose `node_modules` is globbed at launch time.
|
|
108
|
+
* @param bakedDirs Prebuild dirs found at write time; entries outside `scanRoot`
|
|
109
|
+
* are embedded (entries inside it are dropped — the scan finds them).
|
|
110
|
+
* @returns the preamble, or `''` when there is nothing to export.
|
|
111
|
+
*/
|
|
112
|
+
export declare function buildNativeEnvPreamble(scanRoot: string, bakedDirs?: readonly string[], target?: {
|
|
113
|
+
platform?: string;
|
|
114
|
+
arch?: string;
|
|
115
|
+
}): string;
|
package/lib/utils/bin-shim.js
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
// All three re-derive the interpreter from the target's shebang, so a bin
|
|
25
25
|
// declaring `#!/usr/bin/env node` and one declaring `#!/usr/bin/env -S gjs -m`
|
|
26
26
|
// both work without the caller knowing which it is.
|
|
27
|
+
import { libraryPathVar, prebuildDirCandidates } from './detect-native-packages.js';
|
|
27
28
|
// Matches `#!<prog> <args>` and `#!/usr/bin/env [-S] [K=V ...] <prog> <args>`.
|
|
28
29
|
// Verbatim from cmd-shim's `shebangExpr`.
|
|
29
30
|
const SHEBANG_RE = /^#!\s*(?:\/usr\/bin\/env\s+(?:-S\s+)?((?:[^ \t=]+=[^ \t=]+\s+)*))?([^ \t]+)(.*)$/;
|
|
@@ -244,3 +245,107 @@ export function buildLauncherShims(opts) {
|
|
|
244
245
|
'exit $LASTEXITCODE\n';
|
|
245
246
|
return { cmd, ps1 };
|
|
246
247
|
}
|
|
248
|
+
/** POSIX `sh` single-quoting — the one place the escape lives. */
|
|
249
|
+
function shQuote(s) {
|
|
250
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* The `sh` preamble a GJS launcher needs so `imports.gi.X` resolves against the
|
|
254
|
+
* `@gjsify/*` native prebuilds installed next to it.
|
|
255
|
+
*
|
|
256
|
+
* WHY IT SCANS AT RUN TIME INSTEAD OF BAKING A LIST
|
|
257
|
+
*
|
|
258
|
+
* The typelib/library lookup happens inside the GJS runtime, before a single
|
|
259
|
+
* line of the CLI bundle runs, so the env MUST be set by the launcher — the CLI
|
|
260
|
+
* cannot repair it from the inside. The launcher used to bake the directories
|
|
261
|
+
* `detectNativePackages()` found at INSTALL time, which makes the env a SNAPSHOT
|
|
262
|
+
* of one moment:
|
|
263
|
+
*
|
|
264
|
+
* - a native package installed into the same prefix LATER (`gjsify install -g
|
|
265
|
+
* @gjsify/<x>-native`, a project `gjsify install` that adds one) is invisible
|
|
266
|
+
* until something happens to rewrite the launcher;
|
|
267
|
+
* - and if the scan comes back empty for any reason, the launcher is written
|
|
268
|
+
* with NO preamble at all and nothing says so. The failure then surfaces
|
|
269
|
+
* much later and somewhere else entirely — `gjsify build` dying with "no
|
|
270
|
+
* usable bundler engine under GJS" in an unrelated project, which reads as a
|
|
271
|
+
* broken install rather than a stale launcher. That is what shipped in
|
|
272
|
+
* v0.24.1: a `~/.local/bin/gjsify` with a bare `exec gjs -m …` line while
|
|
273
|
+
* `@gjsify/rolldown-native` sat correctly installed in the prefix.
|
|
274
|
+
*
|
|
275
|
+
* Deriving the env from disk on every launch removes both: the launcher states
|
|
276
|
+
* WHERE to look rather than WHAT was there, so it cannot go stale, and an empty
|
|
277
|
+
* result is a fact about the current prefix instead of a silently frozen one.
|
|
278
|
+
* The cost is a handful of shell globs per launch (they are builtins, and a
|
|
279
|
+
* pattern that matches nothing stays literal and is rejected by `[ -d ]`).
|
|
280
|
+
*
|
|
281
|
+
* `bakedDirs` covers what a run-time scan of `scanRoot` structurally cannot see:
|
|
282
|
+
* `detectNativePackages()` walks UP the directory chain, so in a hoisted layout
|
|
283
|
+
* a prebuild may live in an ancestor's `node_modules`. Those are still embedded
|
|
284
|
+
* verbatim; only the primary root is dynamic. Duplicates are harmless (both
|
|
285
|
+
* variables are search paths).
|
|
286
|
+
*
|
|
287
|
+
* Windows is deliberately excluded: there the `sh` file is only reachable from
|
|
288
|
+
* git-bash/MSYS, the real launchers are the `.cmd`/`.ps1` companions built by
|
|
289
|
+
* {@link buildLauncherShims} from the same baked list, and `PATH` doubles as the
|
|
290
|
+
* DLL search path with `;` separators. It keeps the snapshot behaviour.
|
|
291
|
+
*
|
|
292
|
+
* @param scanRoot Directory whose `node_modules` is globbed at launch time.
|
|
293
|
+
* @param bakedDirs Prebuild dirs found at write time; entries outside `scanRoot`
|
|
294
|
+
* are embedded (entries inside it are dropped — the scan finds them).
|
|
295
|
+
* @returns the preamble, or `''` when there is nothing to export.
|
|
296
|
+
*/
|
|
297
|
+
export function buildNativeEnvPreamble(scanRoot, bakedDirs = [], target = {}) {
|
|
298
|
+
const platform = target.platform ?? process.platform;
|
|
299
|
+
const arch = target.arch ?? process.arch;
|
|
300
|
+
const { name: libVar, separator } = libraryPathVar(platform);
|
|
301
|
+
if (platform === 'win32') {
|
|
302
|
+
if (bakedDirs.length === 0)
|
|
303
|
+
return '';
|
|
304
|
+
const joined = shQuote(bakedDirs.join(separator));
|
|
305
|
+
return (`GI_TYPELIB_PATH=${joined}\${GI_TYPELIB_PATH:+"${separator}$GI_TYPELIB_PATH"}\n` +
|
|
306
|
+
`${libVar}=${joined}\${${libVar}:+"${separator}$${libVar}"}\n` +
|
|
307
|
+
`export GI_TYPELIB_PATH ${libVar}\n`);
|
|
308
|
+
}
|
|
309
|
+
// Only the ancestors' hits need embedding — anything under `scanRoot` is
|
|
310
|
+
// what the loop below finds, and re-embedding it would reintroduce the
|
|
311
|
+
// snapshot for exactly the packages the scan exists to keep current.
|
|
312
|
+
const nmRoot = `${scanRoot.replace(/\/+$/, '')}/node_modules/`;
|
|
313
|
+
const external = bakedDirs.filter((d) => !d.startsWith(nmRoot));
|
|
314
|
+
// The same `<os>-<arch>` spellings `resolvePrebuildDirName()` probes, in the
|
|
315
|
+
// same order: the canonical `${platform}-${arch}` plus the legacy uname one
|
|
316
|
+
// a tarball published before the rename still ships. Passing no declared
|
|
317
|
+
// platforms is correct here — the shell cannot read a package.json, and a
|
|
318
|
+
// package declaring ONLY a non-canonical spelling of this host has not
|
|
319
|
+
// existed since the audit made that state impossible.
|
|
320
|
+
const candidates = prebuildDirCandidates(platform, arch);
|
|
321
|
+
const patterns = [];
|
|
322
|
+
for (const candidate of candidates) {
|
|
323
|
+
// Scoped (`@gjsify/x`) and unscoped packages, matching `scanNodeModules`.
|
|
324
|
+
patterns.push(`${shQuote(scanRoot)}/node_modules/@*/*/prebuilds/${candidate}`);
|
|
325
|
+
patterns.push(`${shQuote(scanRoot)}/node_modules/*/prebuilds/${candidate}`);
|
|
326
|
+
}
|
|
327
|
+
const seed = external.length > 0 ? shQuote(external.join(separator)) : `''`;
|
|
328
|
+
// A directory match is not enough: `prebuilds/<os>-<arch>/` is ALSO the
|
|
329
|
+
// prebuildify convention, so a plain glob sweeps in `bare-fs`, `bare-os`
|
|
330
|
+
// and friends — packages with no typelib at all, which `detectNativePackages`
|
|
331
|
+
// never returned because it keyed on `gjsify.prebuilds`. Onto
|
|
332
|
+
// GI_TYPELIB_PATH that is merely noise; onto the LOADER path it is a
|
|
333
|
+
// directory of foreign shared objects ahead of the system ones. The
|
|
334
|
+
// `.typelib` probe restores the old precision from the filesystem alone —
|
|
335
|
+
// and it is exactly what the artifact invariant already guarantees every
|
|
336
|
+
// gjsify GI bridge ships (a library AND the typelib without which
|
|
337
|
+
// GI_TYPELIB_PATH resolves nothing).
|
|
338
|
+
return (`gjsify_np=${seed}\n` +
|
|
339
|
+
`for gjsify_d in ${patterns.join(' ')}; do\n` +
|
|
340
|
+
` [ -d "$gjsify_d" ] || continue\n` +
|
|
341
|
+
` for gjsify_t in "$gjsify_d"/*.typelib; do\n` +
|
|
342
|
+
` if [ -f "$gjsify_t" ]; then gjsify_np="\${gjsify_np:+$gjsify_np${separator}}$gjsify_d"; break; fi\n` +
|
|
343
|
+
` done\n` +
|
|
344
|
+
`done\n` +
|
|
345
|
+
`if [ -n "$gjsify_np" ]; then\n` +
|
|
346
|
+
` GI_TYPELIB_PATH="$gjsify_np\${GI_TYPELIB_PATH:+${separator}$GI_TYPELIB_PATH}"\n` +
|
|
347
|
+
` ${libVar}="$gjsify_np\${${libVar}:+${separator}$${libVar}}"\n` +
|
|
348
|
+
` export GI_TYPELIB_PATH ${libVar}\n` +
|
|
349
|
+
`fi\n` +
|
|
350
|
+
`unset gjsify_np gjsify_d gjsify_t\n`);
|
|
351
|
+
}
|
|
@@ -571,7 +571,6 @@ function findVisible(requesterPath, name, byPath) {
|
|
|
571
571
|
candidates.push(`${requesterPath}/node_modules/${name}`);
|
|
572
572
|
// Walk up: strip the last `/node_modules/<pkg>` segment and try again.
|
|
573
573
|
let p = requesterPath;
|
|
574
|
-
// eslint-disable-next-line no-constant-condition
|
|
575
574
|
while (true) {
|
|
576
575
|
// Find the deepest `/node_modules/<pkg>` in `p`, strip it.
|
|
577
576
|
const idx = p.lastIndexOf('/node_modules/');
|
|
@@ -86,22 +86,6 @@ export interface LinkedBin {
|
|
|
86
86
|
* simplicity and so we don't need to discriminate by runtime here.
|
|
87
87
|
*/
|
|
88
88
|
export declare function linkGlobalBins(packageNames: string[], layout: GlobalLayout): LinkedBin[];
|
|
89
|
-
/**
|
|
90
|
-
* Build the POSIX-sh `export` lines that prepend the given prebuild
|
|
91
|
-
* directories to GI_TYPELIB_PATH and the host's shared-library search path.
|
|
92
|
-
* Any pre-existing value inherited from the user's environment is preserved as
|
|
93
|
-
* a suffix so user-installed typelibs/libraries still resolve.
|
|
94
|
-
*
|
|
95
|
-
* The library variable is host-dependent — `LD_LIBRARY_PATH` on Linux,
|
|
96
|
-
* `DYLD_LIBRARY_PATH` on macOS (dyld never reads the ELF one), so a launcher
|
|
97
|
-
* written on a Mac used to export a variable nothing consults, leaving every
|
|
98
|
-
* `darwin-arm64` prebuild unloadable. `platform` is a parameter so both
|
|
99
|
-
* branches are unit-testable from either host.
|
|
100
|
-
*
|
|
101
|
-
* Returns the empty string when no prebuilds were found — avoids emitting an
|
|
102
|
-
* inert assignment in the launcher.
|
|
103
|
-
*/
|
|
104
|
-
export declare function buildLauncherEnvPreamble(prebuildsDirs: string[], platform?: string): string;
|
|
105
89
|
/** Returns `true` if `binDir` is on the user's PATH. */
|
|
106
90
|
export declare function binDirOnPath(binDir: string): boolean;
|
|
107
91
|
/**
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
import * as fs from 'node:fs';
|
|
24
24
|
import * as os from 'node:os';
|
|
25
25
|
import * as path from 'node:path';
|
|
26
|
-
import { detectNativePackages
|
|
27
|
-
import { buildLauncherShims, parseShebang } from './bin-shim.js';
|
|
26
|
+
import { detectNativePackages } from './detect-native-packages.js';
|
|
27
|
+
import { buildLauncherShims, buildNativeEnvPreamble, parseShebang } from './bin-shim.js';
|
|
28
28
|
/**
|
|
29
29
|
* Compute the canonical global install layout for the current user. Honours
|
|
30
30
|
* `XDG_DATA_HOME` (per the XDG Base Directory Spec) plus
|
|
@@ -143,18 +143,25 @@ export async function installGjsEnginePackages(prefix, version, opts = {}) {
|
|
|
143
143
|
export function linkGlobalBins(packageNames, layout) {
|
|
144
144
|
fs.mkdirSync(layout.binDir, { recursive: true });
|
|
145
145
|
const created = [];
|
|
146
|
-
//
|
|
147
|
-
// + shared libs)
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
//
|
|
146
|
+
// The launcher exports GI_TYPELIB_PATH / <loader path> for the @gjsify/*
|
|
147
|
+
// native prebuilds (Vala/GObject typelibs + shared libs) installed in this
|
|
148
|
+
// prefix, so `imports.gi.X` resolves at CLI startup — required for the
|
|
149
|
+
// bundler engine `@gjsify/rolldown-native` (without which `gjsify build`
|
|
150
|
+
// hard-fails under GJS) and for e.g. `@gjsify/terminal-native`, without
|
|
151
|
+
// which isTTY / columns / colors fall back to conservative defaults.
|
|
152
152
|
//
|
|
153
|
-
// `runGjsBundle()` does the same dance for `gjsify run <bundle>` at
|
|
154
|
-
//
|
|
155
|
-
//
|
|
153
|
+
// `runGjsBundle()` does the same dance for `gjsify run <bundle>` at runtime;
|
|
154
|
+
// here it has to be the launcher, because the typelib lookup happens inside
|
|
155
|
+
// the GJS runtime before the bundle's first line runs.
|
|
156
|
+
//
|
|
157
|
+
// The preamble SCANS the prefix at launch time rather than embedding what
|
|
158
|
+
// was there at install time — see `buildNativeEnvPreamble` for why a
|
|
159
|
+
// snapshot is the wrong shape. `nativePrebuildDirs` is still computed: the
|
|
160
|
+
// Windows `.cmd`/`.ps1` shims take the baked list, and a hit in an
|
|
161
|
+
// ANCESTOR's node_modules (which the walk finds and a single-root scan
|
|
162
|
+
// cannot) is embedded verbatim.
|
|
156
163
|
const nativePrebuildDirs = detectNativePackages(layout.prefix).map((p) => p.prebuildsDir);
|
|
157
|
-
const envPreamble =
|
|
164
|
+
const envPreamble = buildNativeEnvPreamble(layout.prefix, nativePrebuildDirs);
|
|
158
165
|
for (const pkgName of packageNames) {
|
|
159
166
|
const pkgDir = path.join(layout.prefix, 'node_modules', pkgName);
|
|
160
167
|
const pkgJsonPath = path.join(pkgDir, 'package.json');
|
|
@@ -237,30 +244,6 @@ export function linkGlobalBins(packageNames, layout) {
|
|
|
237
244
|
function shQuote(s) {
|
|
238
245
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
239
246
|
}
|
|
240
|
-
/**
|
|
241
|
-
* Build the POSIX-sh `export` lines that prepend the given prebuild
|
|
242
|
-
* directories to GI_TYPELIB_PATH and the host's shared-library search path.
|
|
243
|
-
* Any pre-existing value inherited from the user's environment is preserved as
|
|
244
|
-
* a suffix so user-installed typelibs/libraries still resolve.
|
|
245
|
-
*
|
|
246
|
-
* The library variable is host-dependent — `LD_LIBRARY_PATH` on Linux,
|
|
247
|
-
* `DYLD_LIBRARY_PATH` on macOS (dyld never reads the ELF one), so a launcher
|
|
248
|
-
* written on a Mac used to export a variable nothing consults, leaving every
|
|
249
|
-
* `darwin-arm64` prebuild unloadable. `platform` is a parameter so both
|
|
250
|
-
* branches are unit-testable from either host.
|
|
251
|
-
*
|
|
252
|
-
* Returns the empty string when no prebuilds were found — avoids emitting an
|
|
253
|
-
* inert assignment in the launcher.
|
|
254
|
-
*/
|
|
255
|
-
export function buildLauncherEnvPreamble(prebuildsDirs, platform = process.platform) {
|
|
256
|
-
if (prebuildsDirs.length === 0)
|
|
257
|
-
return '';
|
|
258
|
-
const { name: libVar } = libraryPathVar(platform);
|
|
259
|
-
const joined = shQuote(prebuildsDirs.join(':'));
|
|
260
|
-
return (`GI_TYPELIB_PATH=${joined}\${GI_TYPELIB_PATH:+":$GI_TYPELIB_PATH"}\n` +
|
|
261
|
-
`${libVar}=${joined}\${${libVar}:+":$${libVar}"}\n` +
|
|
262
|
-
`export GI_TYPELIB_PATH ${libVar}\n`);
|
|
263
|
-
}
|
|
264
247
|
/** Parse a bin file's `#!` line; `null` when unreadable or not a shebang. */
|
|
265
248
|
function readShebangOf(file) {
|
|
266
249
|
try {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The GJS ambient globals referenced as BARE identifiers in `code`, sorted.
|
|
3
|
+
*
|
|
4
|
+
* Empty for a bundle that only uses the `globalThis.imports` isomorphic-guard
|
|
5
|
+
* shape — that form is a runtime probe, not a dependency, and the detector
|
|
6
|
+
* ignores it for exactly that reason.
|
|
7
|
+
*/
|
|
8
|
+
export declare function findUnshimmedGjsGlobals(code: string): string[];
|
|
9
|
+
/**
|
|
10
|
+
* Throw when a `--app node` bundle references GJS ambient globals but was built
|
|
11
|
+
* without the `@gjsify/node-gi/globals` shim.
|
|
12
|
+
*
|
|
13
|
+
* @param code the emitted bundle source
|
|
14
|
+
* @param outfile path of the bundle, for the error message
|
|
15
|
+
*/
|
|
16
|
+
export declare function assertNodeBundleGlobalsShimmed(code: string, outfile: string): void;
|