@gjsify/cli 0.52.0 → 0.54.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 +351 -305
- package/lib/actions/build.d.ts +22 -0
- package/lib/actions/build.js +37 -9
- package/lib/bundler-pick.d.ts +72 -1
- package/lib/bundler-pick.js +175 -17
- package/lib/cli-app.js +7 -2
- package/lib/commands/blueprint/index.d.ts +4 -0
- package/lib/commands/blueprint/index.js +15 -0
- package/lib/commands/blueprint/types.d.ts +8 -0
- package/lib/commands/blueprint/types.js +124 -0
- package/lib/commands/devtools.d.ts +21 -0
- package/lib/commands/devtools.js +146 -0
- package/lib/commands/env.d.ts +31 -0
- package/lib/commands/env.js +114 -0
- package/lib/commands/exec.d.ts +10 -0
- package/lib/commands/exec.js +208 -0
- package/lib/commands/foreach.js +5 -97
- package/lib/commands/format.js +26 -6
- package/lib/commands/index.d.ts +5 -0
- package/lib/commands/index.js +5 -0
- package/lib/commands/install.d.ts +2 -2
- package/lib/commands/install.js +5 -5
- package/lib/commands/self-update.js +2 -1
- package/lib/commands/ship.js +51 -8
- package/lib/commands/test.d.ts +18 -9
- package/lib/commands/test.js +56 -11
- package/lib/commands/tsc.js +4 -4
- package/lib/commands/upgrade.d.ts +15 -0
- package/lib/commands/upgrade.js +76 -2
- package/lib/commands/webext/index.d.ts +2 -0
- package/lib/commands/webext/index.js +15 -0
- package/lib/commands/webext/subcommands.d.ts +16 -0
- package/lib/commands/webext/subcommands.js +204 -0
- package/lib/types/config-data.d.ts +1 -1
- package/lib/utils/bin-shim.d.ts +8 -2
- package/lib/utils/bin-shim.js +6 -5
- package/lib/utils/bundle-inputs.d.ts +84 -0
- package/lib/utils/bundle-inputs.js +268 -0
- package/lib/utils/check-system-deps.d.ts +4 -2
- package/lib/utils/check-system-deps.js +21 -26
- package/lib/utils/dev-link.d.ts +9 -6
- package/lib/utils/dev-link.js +71 -19
- package/lib/utils/exec-bin.d.ts +137 -0
- package/lib/utils/exec-bin.js +321 -0
- package/lib/utils/gi-runtime-paths.js +3 -2
- package/lib/utils/gitignore.d.ts +15 -0
- package/lib/utils/gitignore.js +178 -0
- package/lib/utils/gjsify-shim.d.ts +47 -0
- package/lib/utils/gjsify-shim.js +97 -8
- package/lib/utils/install-backend-native.d.ts +19 -1
- package/lib/utils/install-backend-native.js +93 -7
- package/lib/utils/install-lock.js +4 -25
- package/lib/utils/native-skip-scan.d.ts +20 -0
- package/lib/utils/native-skip-scan.js +343 -0
- package/lib/utils/node-script.js +1 -1
- package/lib/utils/oxc-resolve.js +46 -5
- package/lib/utils/process-table.d.ts +22 -0
- package/lib/utils/process-table.js +188 -0
- package/lib/utils/prune-prefix.js +1 -1
- package/lib/utils/run-gjs.d.ts +15 -1
- package/lib/utils/run-gjs.js +16 -7
- package/lib/utils/run-node.d.ts +2 -0
- package/lib/utils/run-node.js +2 -1
- package/lib/utils/runtimes.js +14 -13
- package/lib/utils/ship/app-runtime.d.ts +7 -5
- package/lib/utils/ship/app-runtime.js +11 -8
- package/lib/utils/ship/appimage.d.ts +9 -0
- package/lib/utils/ship/appimage.js +2 -1
- package/lib/utils/ship/deb.d.ts +10 -0
- package/lib/utils/ship/deb.js +7 -0
- package/lib/utils/ship/depends.d.ts +37 -3
- package/lib/utils/ship/depends.js +21 -9
- package/lib/utils/ship/gi-namespaces.d.ts +16 -0
- package/lib/utils/ship/gi-namespaces.js +64 -1
- package/lib/utils/ship/gzip.js +1 -1
- package/lib/utils/ship/icons.d.ts +6 -0
- package/lib/utils/ship/icons.js +6 -5
- package/lib/utils/ship/launcher.js +2 -2
- package/lib/utils/ship/layout.js +25 -2
- package/lib/utils/ship/rpm.d.ts +5 -0
- package/lib/utils/ship/rpm.js +42 -0
- package/lib/utils/ship/settings.js +6 -0
- package/lib/utils/ship/signing.d.ts +7 -2
- package/lib/utils/ship/signing.js +59 -32
- package/lib/utils/ship/stage-manifest.d.ts +14 -0
- package/lib/utils/ship/stage-manifest.js +7 -0
- package/lib/utils/spawn.d.ts +13 -0
- package/lib/utils/spawn.js +10 -5
- package/lib/utils/watch-loop.d.ts +15 -1
- package/lib/utils/watch-loop.js +9 -5
- package/lib/utils/webext/build.d.ts +42 -0
- package/lib/utils/webext/build.js +120 -0
- package/lib/utils/webext/config.d.ts +72 -0
- package/lib/utils/webext/config.js +234 -0
- package/lib/utils/webext/icons.d.ts +24 -0
- package/lib/utils/webext/icons.js +83 -0
- package/lib/utils/webext/launch.d.ts +32 -0
- package/lib/utils/webext/launch.js +77 -0
- package/lib/utils/webext/manifest.d.ts +53 -0
- package/lib/utils/webext/manifest.js +219 -0
- package/lib/utils/webext/pages.d.ts +26 -0
- package/lib/utils/webext/pages.js +61 -0
- package/lib/utils/webext/targets.d.ts +13 -0
- package/lib/utils/webext/targets.js +48 -0
- package/lib/utils/win32-command.d.ts +17 -0
- package/lib/utils/win32-command.js +21 -6
- package/package.json +20 -19
package/lib/actions/build.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ConfigData } from '../types/index.js';
|
|
2
2
|
import type { App } from '@gjsify/rolldown-plugin-gjsify';
|
|
3
3
|
import type { RolldownOutput } from 'rolldown';
|
|
4
|
+
import { type RunBundleOptions } from '../bundler-pick.js';
|
|
4
5
|
export declare class BuildAction {
|
|
5
6
|
readonly configData: ConfigData;
|
|
6
7
|
constructor(configData?: ConfigData);
|
|
@@ -186,11 +187,31 @@ export declare class BuildAction {
|
|
|
186
187
|
*/
|
|
187
188
|
resolveFromToolchain?: boolean;
|
|
188
189
|
}): Promise<string>;
|
|
190
|
+
/**
|
|
191
|
+
* The uncached half of {@link bundleFileForGjsCached}: bundle `inputPath` to
|
|
192
|
+
* `outfile` as one self-contained `--app gjs` module. `gjsify exec` calls it
|
|
193
|
+
* directly because it keys its artifacts by CONTENT (package version, lockfile,
|
|
194
|
+
* CLI version — `utils/exec-bin.ts`) rather than by mtime.
|
|
195
|
+
*/
|
|
196
|
+
static bundleFileForGjs(inputPath: string, outfile: string, opts: {
|
|
197
|
+
verbose?: boolean;
|
|
198
|
+
define?: Record<string, string>;
|
|
199
|
+
preserveDefaultExport?: boolean;
|
|
200
|
+
globals?: string;
|
|
201
|
+
excludeGlobals?: string[];
|
|
202
|
+
/** The running CLI's file, or null — see `resolveFromToolchain` above; the CALLER decides. */
|
|
203
|
+
toolchainAnchor?: string | null;
|
|
204
|
+
/** `silent` drops warnings, never errors — errors reject the build either way. */
|
|
205
|
+
logLevel?: 'silent';
|
|
206
|
+
}): Promise<void>;
|
|
207
|
+
/** The running CLI's own file — the toolchain anchor `gjsify exec` hands {@link bundleFileForGjs}. */
|
|
208
|
+
static runningCliFile(): string | null;
|
|
189
209
|
/** Application mode */
|
|
190
210
|
buildApp(app?: App, opts?: {
|
|
191
211
|
watch?: boolean;
|
|
192
212
|
preserveDefaultExport?: boolean;
|
|
193
213
|
toolchainAnchor?: string;
|
|
214
|
+
onWatchFiles?: RunBundleOptions['onWatchFiles'];
|
|
194
215
|
}): Promise<RolldownOutput[]>;
|
|
195
216
|
/**
|
|
196
217
|
* Drive `rolldown.watch(...)`: rebuild on source change, apply the
|
|
@@ -203,5 +224,6 @@ export declare class BuildAction {
|
|
|
203
224
|
library?: boolean;
|
|
204
225
|
app?: App;
|
|
205
226
|
watch?: boolean;
|
|
227
|
+
onWatchFiles?: RunBundleOptions['onWatchFiles'];
|
|
206
228
|
}): Promise<RolldownOutput[]>;
|
|
207
229
|
}
|
package/lib/actions/build.js
CHANGED
|
@@ -555,6 +555,16 @@ export class BuildAction {
|
|
|
555
555
|
}
|
|
556
556
|
if (opts.verbose)
|
|
557
557
|
console.debug(`[gjsify] bundling ${inputPath} for GJS → ${outfile}`);
|
|
558
|
+
await BuildAction.bundleFileForGjs(inputPath, outfile, { ...opts, toolchainAnchor: anchorFile });
|
|
559
|
+
return outfile;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* The uncached half of {@link bundleFileForGjsCached}: bundle `inputPath` to
|
|
563
|
+
* `outfile` as one self-contained `--app gjs` module. `gjsify exec` calls it
|
|
564
|
+
* directly because it keys its artifacts by CONTENT (package version, lockfile,
|
|
565
|
+
* CLI version — `utils/exec-bin.ts`) rather than by mtime.
|
|
566
|
+
*/
|
|
567
|
+
static async bundleFileForGjs(inputPath, outfile, opts) {
|
|
558
568
|
await mkdir(dirname(outfile), { recursive: true });
|
|
559
569
|
await new BuildAction({
|
|
560
570
|
verbose: opts.verbose,
|
|
@@ -564,15 +574,18 @@ export class BuildAction {
|
|
|
564
574
|
input: inputPath,
|
|
565
575
|
output: { file: outfile },
|
|
566
576
|
...(opts.define ? { transform: { define: opts.define } } : {}),
|
|
577
|
+
...(opts.logLevel ? { logLevel: opts.logLevel } : {}),
|
|
567
578
|
},
|
|
568
579
|
}).buildApp('gjs', {
|
|
569
580
|
preserveDefaultExport: opts.preserveDefaultExport ?? true,
|
|
570
|
-
//
|
|
571
|
-
//
|
|
572
|
-
|
|
573
|
-
...(anchorFile === null ? {} : { toolchainAnchor: anchorFile }),
|
|
581
|
+
// The CALLER decides whether this input is toolchain, because this helper
|
|
582
|
+
// alone cannot tell a config file from a consumer's `node ./src/main.mjs`.
|
|
583
|
+
...(opts.toolchainAnchor ? { toolchainAnchor: opts.toolchainAnchor } : {}),
|
|
574
584
|
});
|
|
575
|
-
|
|
585
|
+
}
|
|
586
|
+
/** The running CLI's own file — the toolchain anchor `gjsify exec` hands {@link bundleFileForGjs}. */
|
|
587
|
+
static runningCliFile() {
|
|
588
|
+
return runningCliFile();
|
|
576
589
|
}
|
|
577
590
|
/** Application mode */
|
|
578
591
|
async buildApp(app = 'gjs', opts = {}) {
|
|
@@ -712,7 +725,13 @@ export class BuildAction {
|
|
|
712
725
|
// unresolvable import would hard-crash the analysis pass.
|
|
713
726
|
// Mirrors the explicit-globals path (resolveGlobalsInject)
|
|
714
727
|
// and resolveUserPlugins above, which already anchor on cwd.
|
|
715
|
-
{
|
|
728
|
+
{
|
|
729
|
+
extraGlobalsList: extras,
|
|
730
|
+
excludeGlobals,
|
|
731
|
+
cwd: process.cwd(),
|
|
732
|
+
// The same fallback the resolver gets, or the gate drops what it would rescue.
|
|
733
|
+
...(opts.toolchainAnchor !== undefined ? { toolchainDir: dirname(opts.toolchainAnchor) } : {}),
|
|
734
|
+
}, bundleToChunks);
|
|
716
735
|
pluginOpts.autoGlobalsInject = injectPath;
|
|
717
736
|
}
|
|
718
737
|
else if (app === 'node' && autoMode) {
|
|
@@ -803,7 +822,7 @@ export class BuildAction {
|
|
|
803
822
|
await this.runWatchLoop(finalOpts, app, outfile, outdir, verbose);
|
|
804
823
|
return [];
|
|
805
824
|
}
|
|
806
|
-
const writeResult = await runBundle(finalOpts);
|
|
825
|
+
const writeResult = await runBundle(finalOpts, { onWatchFiles: opts.onWatchFiles });
|
|
807
826
|
// GJS truncates module source at a raw U+0000 (it is handed to SpiderMonkey as a
|
|
808
827
|
// NUL-terminated C string), so escape any the minifier emitted before anything else
|
|
809
828
|
// touches the file. See utils/gjs-source-escape.ts.
|
|
@@ -920,14 +939,23 @@ export class BuildAction {
|
|
|
920
939
|
}
|
|
921
940
|
await closed;
|
|
922
941
|
}
|
|
923
|
-
async start(buildType = {
|
|
942
|
+
async start(buildType = {
|
|
943
|
+
app: 'gjs',
|
|
944
|
+
}) {
|
|
924
945
|
if (buildType.library) {
|
|
946
|
+
// `onWatchFiles` has no consumer here and is deliberately NOT
|
|
947
|
+
// forwarded: a `--library` build emits a published package, and
|
|
948
|
+
// nothing asks whether it is stale (`gjsify test` is the only
|
|
949
|
+
// freshness check, and it never builds a library).
|
|
925
950
|
if (buildType.watch) {
|
|
926
951
|
throw new Error('gjsify build: --watch is not supported with --library (library mode would emit watcher rebuilds for every produced format; use --app gjs|node|browser instead).');
|
|
927
952
|
}
|
|
928
953
|
return await this.buildLibrary();
|
|
929
954
|
}
|
|
930
|
-
return await this.buildApp(buildType.app, {
|
|
955
|
+
return await this.buildApp(buildType.app, {
|
|
956
|
+
watch: buildType.watch,
|
|
957
|
+
onWatchFiles: buildType.onWatchFiles,
|
|
958
|
+
});
|
|
931
959
|
}
|
|
932
960
|
}
|
|
933
961
|
async function runOneLibraryBuild(args) {
|
package/lib/bundler-pick.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { RolldownOutput, InputOptions, RolldownWatcher } from 'rolldown';
|
|
2
2
|
import type { BundlerOptions } from './types/index.js';
|
|
3
|
+
import { type ResolveNpmPackageOptions } from './utils/resolve-npm-package.js';
|
|
3
4
|
export interface NativePluginContext {
|
|
4
5
|
resolve(specifier: string, importer?: string, opts?: {
|
|
5
6
|
skipSelf?: boolean;
|
|
@@ -8,6 +9,20 @@ export interface NativePluginContext {
|
|
|
8
9
|
id: string;
|
|
9
10
|
external: boolean;
|
|
10
11
|
} | null>;
|
|
12
|
+
/**
|
|
13
|
+
* rolldown's `this.addWatchFile`, collected by the native facade into
|
|
14
|
+
* `BundleResult.watchedFiles` — the native half of what npm rolldown reports
|
|
15
|
+
* as `RolldownBuild.watchFiles`.
|
|
16
|
+
*
|
|
17
|
+
* OPTIONAL here on purpose. The engine is resolved through several anchors
|
|
18
|
+
* and a global-prefix copy can answer first (measured 2026-09-29 on a dev
|
|
19
|
+
* host whose project-local engine was the current one), so a plugin cannot
|
|
20
|
+
* assume the method exists: it is this bridge's documented stance that an
|
|
21
|
+
* unimplemented context method FAILS at hook-call time, and a build must
|
|
22
|
+
* not fail over bookkeeping. `gjsify-css-as-string` therefore
|
|
23
|
+
* feature-detects and warns once.
|
|
24
|
+
*/
|
|
25
|
+
addWatchFile(id: string): void;
|
|
11
26
|
warn(message: string): void;
|
|
12
27
|
error(message: string): never;
|
|
13
28
|
}
|
|
@@ -72,11 +87,67 @@ export declare function bundleToChunks(input: {
|
|
|
72
87
|
* and is responsible for invoking `watcher.close()` on shutdown.
|
|
73
88
|
*/
|
|
74
89
|
export declare function runWatch(finalOpts: BundlerOptions): Promise<RolldownWatcher>;
|
|
90
|
+
/** What one build reported about the files its output depends on. */
|
|
91
|
+
export interface BuildWatchList {
|
|
92
|
+
/**
|
|
93
|
+
* The engine's watch list: npm rolldown's `RolldownBuild.watchFiles` (its
|
|
94
|
+
* module set plus every `this.addWatchFile`), the native facade's
|
|
95
|
+
* `watchedFiles` (the declared half — the native engine reports no modules,
|
|
96
|
+
* hence the two halves rather than either). Read BEFORE `close()`, which is
|
|
97
|
+
* where the npm getter is still alive.
|
|
98
|
+
*/
|
|
99
|
+
files: readonly string[];
|
|
100
|
+
/**
|
|
101
|
+
* `false` when the engine CANNOT report one at all (a `@gjsify/rolldown-native`
|
|
102
|
+
* older than `addWatchFile`). A caller that judges a build's inputs on this
|
|
103
|
+
* list has to refuse to trust it — the file a plugin read itself is then
|
|
104
|
+
* invisible, and a stale bundle would read as current.
|
|
105
|
+
*/
|
|
106
|
+
reportedByEngine: boolean;
|
|
107
|
+
}
|
|
108
|
+
/** Where a build's own input list goes. Absent → the caller does not want it. */
|
|
109
|
+
export interface RunBundleOptions {
|
|
110
|
+
onWatchFiles?: (watch: BuildWatchList) => void;
|
|
111
|
+
}
|
|
75
112
|
/**
|
|
76
113
|
* Run a bundle with the picked engine. Drop-in replacement for the
|
|
77
114
|
* `rolldown(opts).write(opts.output)` flow used directly in build.ts.
|
|
78
115
|
*/
|
|
79
|
-
export declare function runBundle(finalOpts: BundlerOptions): Promise<RolldownOutput>;
|
|
116
|
+
export declare function runBundle(finalOpts: BundlerOptions, options?: RunBundleOptions): Promise<RolldownOutput>;
|
|
117
|
+
/**
|
|
118
|
+
* The file `@gjsify/utils/native-library` resolves to, or THROWS when no anchor
|
|
119
|
+
* reaches it.
|
|
120
|
+
*
|
|
121
|
+
* `resolveNpmPackage` first, the same resolver — and the same `?? createRequire`
|
|
122
|
+
* tail — the ENGINE loader below uses, for the same reason: ONE
|
|
123
|
+
* `createRequire(import.meta.url)` anchor walks exactly one node_modules chain,
|
|
124
|
+
* the one rooted at the BUNDLE. v0.53.0's release `publish-napi` job runs the
|
|
125
|
+
* published bundle from `~/.cache/gjsify/bootstrap/`, which is in no workspace's
|
|
126
|
+
* chain, so the bundle-only anchor found nothing — and since #1901 moved
|
|
127
|
+
* `probeNativeLibrary()` onto `tryLoadNative()`'s SUCCESS path, that is not a
|
|
128
|
+
* degraded diagnostic but NO ENGINE: every `gjsify build` under GJS died with
|
|
129
|
+
* "no usable bundler engine", reported by a diagnostic that resolves the same
|
|
130
|
+
* specifier and so could not name the cause. MEASURED on the release job's own
|
|
131
|
+
* shape: with `@gjsify/utils` FULLY BUILT, anchoring only at the bundle still
|
|
132
|
+
* fails with `Cannot find module "@gjsify/utils/native-library" - not found in
|
|
133
|
+
* any node_modules directory`; anchoring at cwd resolves it. `main.yml` never
|
|
134
|
+
* saw this because it exports `XDG_CACHE_HOME=$PWD/.gjsify-cache` and therefore
|
|
135
|
+
* caches the bundle INSIDE the workspace, where the bundle-only anchor happens
|
|
136
|
+
* to work.
|
|
137
|
+
*
|
|
138
|
+
* Two independent defects, so both are fixed where they live: this anchor, and
|
|
139
|
+
* the `lib/esm` emit it then needs on a cold tree (`@gjsify/utils build:esm`
|
|
140
|
+
* before the first bundler clause, rule 5 of
|
|
141
|
+
* `scripts/check-build-infra-order.mjs`).
|
|
142
|
+
*
|
|
143
|
+
* SCOPE, since it decides what this unblocks: the release job runs `build:infra`
|
|
144
|
+
* with the PUBLISHED bundle, which is fixed code this change cannot reach — so on
|
|
145
|
+
* its own it does NOT unblock v0.53.0. What does is the `XDG_CACHE_HOME` line in
|
|
146
|
+
* release.yml's `publish-napi` job, which puts that bundle inside the workspace
|
|
147
|
+
* where ANY anchor finds this tree. This fix is what stops the next release from
|
|
148
|
+
* depending on that line.
|
|
149
|
+
*/
|
|
150
|
+
export declare function resolveNativeLibraryModule(opts?: ResolveNpmPackageOptions): string;
|
|
80
151
|
export declare function shouldUseNative(): Promise<boolean>;
|
|
81
152
|
/**
|
|
82
153
|
* Walk parent directories from `startDir` (and optionally a second anchor
|
package/lib/bundler-pick.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// synchronous require of a builtin, so the load dies before any `.node` is opened.
|
|
11
11
|
// The distinction is not pedantry — it says an N-API host alone would not be
|
|
12
12
|
// enough; the wrapper has to be bypassed too, which is exactly what
|
|
13
|
-
// `napiNodeAddonPlugin` does for napi-rs packages. See `status/open-todos.md`
|
|
13
|
+
// `napiNodeAddonPlugin` does for napi-rs packages. See `status/open-todos/README.md`
|
|
14
14
|
// § "Can `@gjsify/napi` retire the hand-written `-native` bridges?".
|
|
15
15
|
//
|
|
16
16
|
// `GJSIFY_BUNDLER=native|npm` overrides that: `native` throws when the prebuild is
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
import { promises as fs, existsSync } from 'node:fs';
|
|
31
31
|
import * as path from 'node:path';
|
|
32
32
|
import { createRequire } from 'node:module';
|
|
33
|
-
import { pathToFileURL } from 'node:url';
|
|
33
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
34
34
|
import { resolveNpmPackage } from './utils/resolve-npm-package.js';
|
|
35
35
|
// Static, not dynamic: `diagnoseNativeEngine()` is synchronous and called inside a
|
|
36
36
|
// `throw` expression. Safe because check-system-deps imports only `node:` builtins,
|
|
@@ -80,7 +80,8 @@ async function loadNpmRolldown() {
|
|
|
80
80
|
function diagnoseNativeEngine() {
|
|
81
81
|
let pkgDir = null;
|
|
82
82
|
try {
|
|
83
|
-
|
|
83
|
+
// Not `new URL(...).pathname`: on win32 that is `/C:/...`, percent-encoded, no path.
|
|
84
|
+
const bundleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
84
85
|
pkgDir = findRolldownNativeDir(process.cwd(), bundleDir);
|
|
85
86
|
}
|
|
86
87
|
catch {
|
|
@@ -131,23 +132,39 @@ function diagnoseNativeEngine() {
|
|
|
131
132
|
// reads the same tables `gjsify system-check` does, so the answer cannot drift
|
|
132
133
|
// from the declaration.
|
|
133
134
|
const missing = missingSystemDepsFor('@gjsify/rolldown-native');
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
const names = missing.map((d) => d.name).join(', ');
|
|
136
|
+
const cmd = missing.length > 0 ? buildInstallCommand(detectPackageManager(), missing) : null;
|
|
137
|
+
const installHint = missing.length === 0
|
|
138
|
+
? undefined
|
|
139
|
+
: cmd
|
|
140
|
+
? `Install it:\n ${cmd}`
|
|
141
|
+
: `Install ${names} with your system package manager (this host's manager was not recognised, so no command is suggested rather than a wrong one).`;
|
|
142
|
+
// (0) The measurement could not run at all. First, because everything below
|
|
143
|
+
// reasons from a measurement: with no reading in hand, (2) would assert the
|
|
144
|
+
// prebuild IS on girepository's search path — a positive claim this PR
|
|
145
|
+
// exists to make measurable, so its absence is stated rather than papered over.
|
|
146
|
+
if (_utilsCoreError !== null) {
|
|
147
|
+
parts.push(`\nNOT MEASURED — the native-library probe could not be loaded, so nothing below is a reading of this host:\n ${_utilsCoreError instanceof Error ? _utilsCoreError.message : String(_utilsCoreError)}`);
|
|
148
|
+
}
|
|
149
|
+
// (1) The loader's own answer, when `tryLoadNative()` measured one: the file
|
|
150
|
+
// that would not open and the dependency it named. It outranks (2), which only
|
|
151
|
+
// knows what pkg-config can see — the dependency may be one no table declares.
|
|
152
|
+
if (_nativeLibraryFailure && _utilsCore) {
|
|
153
|
+
parts.push(`\nMEASURED CAUSE — ${new _utilsCore.NativeLibraryLoadError(_nativeLibraryFailure, installHint).message}`);
|
|
154
|
+
}
|
|
155
|
+
else if (missing.length > 0) {
|
|
136
156
|
parts.push(`\nMEASURED CAUSE — a system library the engine's prebuild loads is MISSING: ${names}.\n` +
|
|
137
157
|
'The prebuild is present but its typelib cannot open its backing library, which GJS reports as ' +
|
|
138
158
|
'`Unsupported type void, deriving from fundamental void` — a message that names nothing. ' +
|
|
139
159
|
'This is a system package, not an npm one.');
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
parts.push(`Install it:\n ${cmd}`);
|
|
143
|
-
else
|
|
144
|
-
parts.push(`Install ${names} with your system package manager (this host's manager was not recognised, so no command is suggested rather than a wrong one).`);
|
|
160
|
+
if (installHint)
|
|
161
|
+
parts.push(installHint);
|
|
145
162
|
}
|
|
146
163
|
// (2) No prebuild for this architecture — last because it is least actionable.
|
|
147
164
|
// Read off the set `activateNativePrebuilds()` ACTUALLY put on the search paths:
|
|
148
165
|
// a memoized cached-array read that cannot throw, and it names the per-target
|
|
149
166
|
// sibling package really holding the artifact.
|
|
150
|
-
if (missing.length === 0) {
|
|
167
|
+
if (missing.length === 0 && !_nativeLibraryFailure) {
|
|
151
168
|
const engine = activateNativePrebuilds().find((p) => p.name.startsWith('@gjsify/rolldown-native'));
|
|
152
169
|
parts.push(engine
|
|
153
170
|
? `\nEvery declared system library resolves and ${engine.name}'s prebuild (${engine.prebuildsDir}) IS on ` +
|
|
@@ -255,24 +272,112 @@ export async function runWatch(finalOpts) {
|
|
|
255
272
|
const output = finalOpts.output ?? {};
|
|
256
273
|
return mod.watch({ ...finalOpts, output });
|
|
257
274
|
}
|
|
275
|
+
let _warnedNoWatchList = false;
|
|
276
|
+
function warnNoNativeWatchList() {
|
|
277
|
+
if (_warnedNoWatchList)
|
|
278
|
+
return;
|
|
279
|
+
_warnedNoWatchList = true;
|
|
280
|
+
console.warn('[gjsify] this `@gjsify/rolldown-native` build reports no watch list: it predates ' +
|
|
281
|
+
'`addWatchFile`, so a file a plugin read itself (a stylesheet\u2019s `@import` chain, an ' +
|
|
282
|
+
'inlined `readFileSync`) cannot be declared as a build input. `gjsify test` will rebuild ' +
|
|
283
|
+
'the affected bundles on every run rather than trust a set it cannot see. Upgrade the ' +
|
|
284
|
+
'engine (it ships in the same release train as the CLI).');
|
|
285
|
+
}
|
|
258
286
|
/**
|
|
259
287
|
* Run a bundle with the picked engine. Drop-in replacement for the
|
|
260
288
|
* `rolldown(opts).write(opts.output)` flow used directly in build.ts.
|
|
261
289
|
*/
|
|
262
|
-
export async function runBundle(finalOpts) {
|
|
290
|
+
export async function runBundle(finalOpts, options = {}) {
|
|
263
291
|
if (await shouldUseNative()) {
|
|
264
|
-
return await runNativeBundle(finalOpts);
|
|
292
|
+
return await runNativeBundle(finalOpts, options);
|
|
265
293
|
}
|
|
266
294
|
const rolldown = await loadNpmRolldown();
|
|
267
295
|
const build = await rolldown(finalOpts);
|
|
268
296
|
try {
|
|
269
|
-
|
|
297
|
+
const output = await build.write(finalOpts.output ?? {});
|
|
298
|
+
if (options.onWatchFiles) {
|
|
299
|
+
options.onWatchFiles({ files: await build.watchFiles, reportedByEngine: true });
|
|
300
|
+
}
|
|
301
|
+
return output;
|
|
270
302
|
}
|
|
271
303
|
finally {
|
|
272
304
|
await build.close();
|
|
273
305
|
}
|
|
274
306
|
}
|
|
275
307
|
let _nativeProbe = null;
|
|
308
|
+
/** Why the engine's library would not open, when `tryLoadNative()` measured it. */
|
|
309
|
+
let _nativeLibraryFailure = null;
|
|
310
|
+
let _utilsCore = null;
|
|
311
|
+
/**
|
|
312
|
+
* Why the probe itself could not run, when that is the reason. Kept apart from
|
|
313
|
+
* "there is no engine" on purpose: `tryLoadNative()`'s bare `catch` cannot tell
|
|
314
|
+
* the two apart, and `diagnoseNativeEngine()` then answers a question nobody
|
|
315
|
+
* asked — it reports which system libraries resolve, and says the prebuild IS on
|
|
316
|
+
* girepository's search path, which is a positive claim about a measurement that
|
|
317
|
+
* never happened. This PR exists to make such a claim measurable, so a missing
|
|
318
|
+
* measurement is reported as one.
|
|
319
|
+
*/
|
|
320
|
+
let _utilsCoreError = null;
|
|
321
|
+
/**
|
|
322
|
+
* The file `@gjsify/utils/native-library` resolves to, or THROWS when no anchor
|
|
323
|
+
* reaches it.
|
|
324
|
+
*
|
|
325
|
+
* `resolveNpmPackage` first, the same resolver — and the same `?? createRequire`
|
|
326
|
+
* tail — the ENGINE loader below uses, for the same reason: ONE
|
|
327
|
+
* `createRequire(import.meta.url)` anchor walks exactly one node_modules chain,
|
|
328
|
+
* the one rooted at the BUNDLE. v0.53.0's release `publish-napi` job runs the
|
|
329
|
+
* published bundle from `~/.cache/gjsify/bootstrap/`, which is in no workspace's
|
|
330
|
+
* chain, so the bundle-only anchor found nothing — and since #1901 moved
|
|
331
|
+
* `probeNativeLibrary()` onto `tryLoadNative()`'s SUCCESS path, that is not a
|
|
332
|
+
* degraded diagnostic but NO ENGINE: every `gjsify build` under GJS died with
|
|
333
|
+
* "no usable bundler engine", reported by a diagnostic that resolves the same
|
|
334
|
+
* specifier and so could not name the cause. MEASURED on the release job's own
|
|
335
|
+
* shape: with `@gjsify/utils` FULLY BUILT, anchoring only at the bundle still
|
|
336
|
+
* fails with `Cannot find module "@gjsify/utils/native-library" - not found in
|
|
337
|
+
* any node_modules directory`; anchoring at cwd resolves it. `main.yml` never
|
|
338
|
+
* saw this because it exports `XDG_CACHE_HOME=$PWD/.gjsify-cache` and therefore
|
|
339
|
+
* caches the bundle INSIDE the workspace, where the bundle-only anchor happens
|
|
340
|
+
* to work.
|
|
341
|
+
*
|
|
342
|
+
* Two independent defects, so both are fixed where they live: this anchor, and
|
|
343
|
+
* the `lib/esm` emit it then needs on a cold tree (`@gjsify/utils build:esm`
|
|
344
|
+
* before the first bundler clause, rule 5 of
|
|
345
|
+
* `scripts/check-build-infra-order.mjs`).
|
|
346
|
+
*
|
|
347
|
+
* SCOPE, since it decides what this unblocks: the release job runs `build:infra`
|
|
348
|
+
* with the PUBLISHED bundle, which is fixed code this change cannot reach — so on
|
|
349
|
+
* its own it does NOT unblock v0.53.0. What does is the `XDG_CACHE_HOME` line in
|
|
350
|
+
* release.yml's `publish-napi` job, which puts that bundle inside the workspace
|
|
351
|
+
* where ANY anchor finds this tree. This fix is what stops the next release from
|
|
352
|
+
* depending on that line.
|
|
353
|
+
*/
|
|
354
|
+
export function resolveNativeLibraryModule(opts = {}) {
|
|
355
|
+
const specifier = '@gjsify/utils/native-library';
|
|
356
|
+
return resolveNpmPackage(specifier, opts) ?? createRequire(opts.bundleUrl ?? import.meta.url).resolve(specifier);
|
|
357
|
+
}
|
|
358
|
+
async function utilsCore() {
|
|
359
|
+
if (_utilsCore === null) {
|
|
360
|
+
// `./native-library`, not `./core`: `core` re-exports `main-loop`, which
|
|
361
|
+
// holds a module-level singleton. The GJS bundle inlines utils already,
|
|
362
|
+
// so loading `core` off disk would give the process a SECOND copy of that
|
|
363
|
+
// singleton — inert today because only pure probe functions are read
|
|
364
|
+
// through this edge, and a trap the first time that stops being true.
|
|
365
|
+
//
|
|
366
|
+
// Off disk, so the resolution's ANCHORS matter — see
|
|
367
|
+
// `resolveNativeLibraryModule()` — and on a cold tree the WORKSPACE copy
|
|
368
|
+
// it finds is itself a build output, which is what
|
|
369
|
+
// `@gjsify/utils build:esm` orders (rule 5 of
|
|
370
|
+
// `scripts/check-build-infra-order.mjs`). Two independent preconditions,
|
|
371
|
+
// and v0.53.0's release `publish-napi` job needed both: a bundle-only
|
|
372
|
+
// anchor resolved nothing when the bundle was the published
|
|
373
|
+
// `cli.gjs.mjs` — one loose file in `<cache>/gjsify/bootstrap/`, no
|
|
374
|
+
// `node_modules` above it. So a throw here is "not measured" (see
|
|
375
|
+
// `tryLoadNative`), never "no engine".
|
|
376
|
+
const href = pathToFileURL(resolveNativeLibraryModule({ bundleUrl: import.meta.url })).href;
|
|
377
|
+
_utilsCore = (await import(/* @vite-ignore */ href));
|
|
378
|
+
}
|
|
379
|
+
return _utilsCore;
|
|
380
|
+
}
|
|
276
381
|
export async function shouldUseNative() {
|
|
277
382
|
const env = globalThis.process?.env ?? {};
|
|
278
383
|
const choice = env.GJSIFY_BUNDLER;
|
|
@@ -282,7 +387,8 @@ export async function shouldUseNative() {
|
|
|
282
387
|
if (choice === 'native') {
|
|
283
388
|
const native = await tryLoadNative();
|
|
284
389
|
if (!native) {
|
|
285
|
-
throw new Error('GJSIFY_BUNDLER=native but @gjsify/rolldown-native is not loadable (no prebuild for this architecture, or not running under GJS).'
|
|
390
|
+
throw new Error('GJSIFY_BUNDLER=native but @gjsify/rolldown-native is not loadable (no prebuild for this architecture, or not running under GJS).' +
|
|
391
|
+
(isGjs() ? `\n${diagnoseNativeEngine()}` : ''));
|
|
286
392
|
}
|
|
287
393
|
return true;
|
|
288
394
|
}
|
|
@@ -375,7 +481,7 @@ async function tryLoadNative() {
|
|
|
375
481
|
// limitations and `findWorkspaceRoot`'s dependency on
|
|
376
482
|
// `discoverWorkspaces`, both of which can fail to map a
|
|
377
483
|
// sub-package dir up to the hoisted workspace root.
|
|
378
|
-
const bundleDir = path.dirname(
|
|
484
|
+
const bundleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
379
485
|
const resolvedFromNpm = resolveNpmPackage(specifier, { bundleUrl: import.meta.url });
|
|
380
486
|
const resolvedFromFs = resolvedFromNpm
|
|
381
487
|
? null
|
|
@@ -399,6 +505,44 @@ async function tryLoadNative() {
|
|
|
399
505
|
const mod = (await import(/* @vite-ignore */ target));
|
|
400
506
|
if (!mod.hasNativeRolldown())
|
|
401
507
|
return null;
|
|
508
|
+
// The typelib resolved, which says nothing about its library: that
|
|
509
|
+
// opens at the first class access, and a missing system library
|
|
510
|
+
// (Homebrew json-glib on a Mac) would pass here and fail inside
|
|
511
|
+
// `new BundlerSession()` as the nameless "Unsupported type void" —
|
|
512
|
+
// past `diagnoseNativeEngine()`, which then never runs.
|
|
513
|
+
// Separated from the engine's own failure so that "the engine is
|
|
514
|
+
// absent" and "the measurement could not run" stay distinguishable
|
|
515
|
+
// at the one place that reports them — and only a library the probe
|
|
516
|
+
// MEASURED as broken vetoes the engine. A probe that could not be
|
|
517
|
+
// LOADED does not: the engine's own load already answered twice
|
|
518
|
+
// over (the module imported, `hasNativeRolldown()` resolved its
|
|
519
|
+
// typelib), and under GJS there is no second engine to fall back to
|
|
520
|
+
// — the npm `rolldown` crate is a napi binary — so `null` here
|
|
521
|
+
// turned "nothing was measured" into "there is no bundler".
|
|
522
|
+
//
|
|
523
|
+
// What reaches that verdict is not exotic. `utilsCore()` resolves
|
|
524
|
+
// `@gjsify/utils/native-library` from THIS bundle's own directory,
|
|
525
|
+
// while every other resolution in this function goes through
|
|
526
|
+
// `resolveNpmPackage()` with its cwd/workspace anchors; the bundle
|
|
527
|
+
// that runs a cold `build:infra` is the PUBLISHED `cli.gjs.mjs`,
|
|
528
|
+
// which `install.mjs --fetch-only` caches as one loose file in
|
|
529
|
+
// `<cache>/gjsify/bootstrap/` with no `node_modules` beside it. On
|
|
530
|
+
// the release runner the probe therefore resolved nothing and
|
|
531
|
+
// `@gjsify/napi` did not publish (v0.53.0), and building utils'
|
|
532
|
+
// `lib/esm` first changed nothing — measured, which is why the
|
|
533
|
+
// asymmetry is fixed here and no clause was reordered.
|
|
534
|
+
let surface = null;
|
|
535
|
+
try {
|
|
536
|
+
surface = await utilsCore();
|
|
537
|
+
}
|
|
538
|
+
catch (err) {
|
|
539
|
+
_utilsCoreError = err;
|
|
540
|
+
}
|
|
541
|
+
if (surface !== null) {
|
|
542
|
+
_nativeLibraryFailure = surface.probeNativeLibrary('GjsifyRolldown');
|
|
543
|
+
if (_nativeLibraryFailure)
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
402
546
|
return mod;
|
|
403
547
|
}
|
|
404
548
|
catch {
|
|
@@ -407,7 +551,7 @@ async function tryLoadNative() {
|
|
|
407
551
|
})();
|
|
408
552
|
return _nativeProbe;
|
|
409
553
|
}
|
|
410
|
-
async function runNativeBundle(finalOpts) {
|
|
554
|
+
async function runNativeBundle(finalOpts, options = {}) {
|
|
411
555
|
const native = await tryLoadNative();
|
|
412
556
|
if (!native) {
|
|
413
557
|
throw new Error('@gjsify/rolldown-native not loadable');
|
|
@@ -440,6 +584,20 @@ async function runNativeBundle(finalOpts) {
|
|
|
440
584
|
}
|
|
441
585
|
const result = await native.bundleWithPlugins(bundlerOpts, nativePlugins);
|
|
442
586
|
reportNativeWarnings(result, finalOpts);
|
|
587
|
+
if (options.onWatchFiles) {
|
|
588
|
+
if (result.watchedFiles === undefined) {
|
|
589
|
+
// NOT a silent drop. A version-skewed engine has no `addWatchFile` on
|
|
590
|
+
// its plugin context, so nothing a plugin declares can reach the
|
|
591
|
+
// build's input set — and a caller judging freshness on that set
|
|
592
|
+
// would call a stale bundle current. Say so here, where the engine is
|
|
593
|
+
// in hand, and report `reportedByEngine: false` so the caller refuses.
|
|
594
|
+
warnNoNativeWatchList();
|
|
595
|
+
options.onWatchFiles({ files: [], reportedByEngine: false });
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
options.onWatchFiles({ files: result.watchedFiles, reportedByEngine: true });
|
|
599
|
+
}
|
|
600
|
+
}
|
|
443
601
|
// The native facade returns the BundleOutput shape but doesn't
|
|
444
602
|
// write files — replicate `.write()` here so callers see the same
|
|
445
603
|
// on-disk layout whether they used npm rolldown or native.
|
package/lib/cli-app.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// build`, and `gjsify run build:types → gjsify tsc → gjs tsc.gjs.mjs`), so on
|
|
11
11
|
// CI's few cores the multiplier oversubscribes and the build thrashes. Letting
|
|
12
12
|
// `gjsify run` dispatch the inner `gjsify <cmd>` in-process collapses two gjs
|
|
13
|
-
// into one. See status/open-todos.md.
|
|
13
|
+
// into one. See status/open-todos/README.md.
|
|
14
14
|
//
|
|
15
15
|
// This module MUST NOT execute anything at load time (no top-level
|
|
16
16
|
// `parseAsync`) — `run.ts` imports it, and a top-level run would re-dispatch
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import yargs from 'yargs';
|
|
19
19
|
import { classifyCliFailure } from './cli-fail.js';
|
|
20
20
|
import { cliVersion } from './utils/publish-headers.js';
|
|
21
|
-
import { buildCommand as build, clearCommand as clear, copyCommand as copy, testCommand as test, runCommand as run, infoCommand as info, systemCheckCommand as systemCheck, checkCommand as check, showcaseCommand as showcase, createCommand as create, gresourceCommand as gresource, gettextCommand as gettext, gsettingsCommand as gsettings, flatpakCommand as flatpak, shipCommand as ship, dlxCommand as dlx, installCommand as install, linkCommand as link, unlinkCommand as unlink, foreachCommand as foreach, workspaceCommand as workspace, packCommand as pack, publishCommand as publish, whoamiCommand as whoami, loginCommand as login, logoutCommand as logout, trustCommand as trust, onboardCommand as onboard, selfUpdateCommand as selfUpdate, generateInstallerCommand as generateInstaller, pruneCommand as prune, uninstallCommand as uninstall, formatCommand as format, lintCommand as lint, fixCommand as fix, upgradeCommand as upgrade, barrelsCommand as barrels, tscCommand as tsc, affectedCommand as affected, storybookCommand as storybook, devCommand as dev, debugCommand as debug, browseCommand as browse, } from './commands/index.js';
|
|
21
|
+
import { buildCommand as build, clearCommand as clear, copyCommand as copy, envCommand as env, execCommand as exec, testCommand as test, runCommand as run, infoCommand as info, systemCheckCommand as systemCheck, checkCommand as check, showcaseCommand as showcase, createCommand as create, gresourceCommand as gresource, gettextCommand as gettext, gsettingsCommand as gsettings, flatpakCommand as flatpak, shipCommand as ship, dlxCommand as dlx, installCommand as install, linkCommand as link, unlinkCommand as unlink, foreachCommand as foreach, workspaceCommand as workspace, packCommand as pack, publishCommand as publish, whoamiCommand as whoami, loginCommand as login, logoutCommand as logout, trustCommand as trust, onboardCommand as onboard, selfUpdateCommand as selfUpdate, generateInstallerCommand as generateInstaller, pruneCommand as prune, uninstallCommand as uninstall, formatCommand as format, lintCommand as lint, fixCommand as fix, upgradeCommand as upgrade, barrelsCommand as barrels, tscCommand as tsc, affectedCommand as affected, storybookCommand as storybook, devCommand as dev, debugCommand as debug, devtoolsCommand as devtools, browseCommand as browse, webextCommand as webext, blueprintCommand as blueprint, } from './commands/index.js';
|
|
22
22
|
import { APP_NAME } from './constants.js';
|
|
23
23
|
import { isBun, isDeno, isNode, gjsSystemVersion } from '@gjsify/rolldown-plugin-gjsify/runtime';
|
|
24
24
|
import { ensureGjsifyShimOnPath } from './utils/gjsify-shim.js';
|
|
@@ -98,6 +98,8 @@ export async function runCli(argv) {
|
|
|
98
98
|
.command(build.command, build.description, build.builder, build.handler)
|
|
99
99
|
.command(clear.command, clear.description, clear.builder, clear.handler)
|
|
100
100
|
.command(copy.command, copy.description, copy.builder, copy.handler)
|
|
101
|
+
.command(env.command, env.description, env.builder, env.handler)
|
|
102
|
+
.command(exec.command, exec.description, exec.builder, exec.handler)
|
|
101
103
|
.command(test.command, test.description, test.builder, test.handler)
|
|
102
104
|
.command(run.command, run.description, run.builder, run.handler)
|
|
103
105
|
.command(dlx.command, dlx.description, dlx.builder, dlx.handler)
|
|
@@ -133,7 +135,10 @@ export async function runCli(argv) {
|
|
|
133
135
|
.command(storybook.command, storybook.description, storybook.builder, storybook.handler)
|
|
134
136
|
.command(dev.command, dev.description, dev.builder, dev.handler)
|
|
135
137
|
.command(debug.command, debug.description, debug.builder, debug.handler)
|
|
138
|
+
.command(devtools.command, devtools.description, devtools.builder, devtools.handler)
|
|
136
139
|
.command(browse.command, browse.description, browse.builder, browse.handler)
|
|
140
|
+
.command(webext.command, webext.description, webext.builder, webext.handler)
|
|
141
|
+
.command(blueprint.command, blueprint.description, blueprint.builder, blueprint.handler)
|
|
137
142
|
.demandCommand(1)
|
|
138
143
|
.epilogue(`Running on ${runtimeLabel()}`)
|
|
139
144
|
.help()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// `gjsify blueprint` — yargs subcommand-group dispatcher, the same shape as `flatpak/`.
|
|
2
|
+
//
|
|
3
|
+
// A directory rather than one file because `check-website-cli-reference.mjs` reads every
|
|
4
|
+
// `command:` in a top-level file as a top-level command; a group's subcommands live beside
|
|
5
|
+
// its `index.ts` and are documented as `#### gjsify blueprint <sub>`.
|
|
6
|
+
import { blueprintTypesCommand } from './types.js';
|
|
7
|
+
export { blueprintTypesCommand };
|
|
8
|
+
export const blueprintCommand = {
|
|
9
|
+
command: 'blueprint <subcommand>',
|
|
10
|
+
description: 'Blueprint (.blp) tooling: generate the TypeScript type sidecars a .blp exports (ADR 0088).',
|
|
11
|
+
builder: (yargs) => yargs
|
|
12
|
+
.command(blueprintTypesCommand.command, blueprintTypesCommand.description, blueprintTypesCommand.builder, blueprintTypesCommand.handler)
|
|
13
|
+
.demandCommand(1)
|
|
14
|
+
.strict(),
|
|
15
|
+
};
|