@gjsify/cli 0.16.4 → 0.16.6
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 +95 -95
- package/lib/actions/build.d.ts +32 -16
- package/lib/actions/build.js +68 -51
- package/lib/commands/run.d.ts +1 -0
- package/lib/commands/run.js +56 -5
- package/lib/config.js +92 -0
- package/package.json +17 -17
package/lib/actions/build.d.ts
CHANGED
|
@@ -55,26 +55,42 @@ export declare class BuildAction {
|
|
|
55
55
|
*/
|
|
56
56
|
private loadPluginViaGjsBundle;
|
|
57
57
|
/**
|
|
58
|
-
* Bundle a single plugin entry for
|
|
59
|
-
* return its path.
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* `bundler.plugins`, so it never recurses back into plugin resolution.
|
|
63
|
-
*
|
|
64
|
-
* Cached under `node_modules/.cache/gjsify/plugins/`, keyed by the resolved
|
|
65
|
-
* source path. Invalidated when the plugin entry OR the project lockfile is
|
|
66
|
-
* newer than the cached bundle — a dep bump rewrites the lockfile but not
|
|
67
|
-
* the plugin's own entry mtime. The one window neither mtime catches (a
|
|
68
|
-
* transitive dep edited in place) is covered by the `GJSIFY_NO_PLUGIN_CACHE=1`
|
|
69
|
-
* escape hatch. Failures are rethrown with context naming the plugin.
|
|
58
|
+
* Bundle a single plugin entry for GJS via {@link bundleFileForGjsCached} and
|
|
59
|
+
* return its path. Rethrows with context naming the plugin. (GJS's native ESM
|
|
60
|
+
* loader can't resolve a plugin's `package.json#exports` subpaths for a bare
|
|
61
|
+
* specifier; Rolldown resolves them at bundle time.)
|
|
70
62
|
*/
|
|
71
63
|
private bundlePluginForGjs;
|
|
72
64
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
65
|
+
* Bundle a single ESM entry file to a self-contained `--app gjs` module and
|
|
66
|
+
* return its path — the shared "bundle one file for GJS" helper behind both
|
|
67
|
+
* the plugin loader (exports-map subpaths GJS can't resolve for a bare
|
|
68
|
+
* specifier) and the config loader (`node:`/npm imports GJS can't resolve for
|
|
69
|
+
* an external ESM file). Reuses the full `--app gjs` pipeline (exports-map-aware
|
|
70
|
+
* resolution, `node:`→`@gjsify` aliases, `gi://` externalized, `--globals auto`,
|
|
71
|
+
* single-file output).
|
|
72
|
+
*
|
|
73
|
+
* The nested `BuildAction` is constructed with EXPLICIT bundler options, so
|
|
74
|
+
* `buildApp` uses that configData directly and never re-enters `Config.load`
|
|
75
|
+
* — no recursion, even when the input is the very `gjsify.config.js` being
|
|
76
|
+
* loaded. `preserveDefaultExport` re-exports `default` through the side-effect
|
|
77
|
+
* entry wrapper so the file imports as a library; shebang stays unset (the
|
|
78
|
+
* artifact is imported, not executed).
|
|
79
|
+
*
|
|
80
|
+
* Cached under `node_modules/.cache/gjsify/<cacheSubdir>/`, keyed by the source
|
|
81
|
+
* path. Invalidated when the source OR the project lockfile is newer than the
|
|
82
|
+
* cached bundle (a dep bump rewrites the lockfile but not the source mtime).
|
|
83
|
+
* `<noCacheEnv>=1` forces a rebuild (covers the residual window: a transitive
|
|
84
|
+
* dep edited in place, touching neither the source nor a lockfile).
|
|
76
85
|
*/
|
|
77
|
-
|
|
86
|
+
static bundleFileForGjsCached(inputPath: string, opts: {
|
|
87
|
+
cacheSubdir: string;
|
|
88
|
+
label: string;
|
|
89
|
+
verbose?: boolean;
|
|
90
|
+
noCacheEnv?: string;
|
|
91
|
+
/** Extra compile-time `define`s (e.g. rewrite the entry's `import.meta.url`). */
|
|
92
|
+
define?: Record<string, string>;
|
|
93
|
+
}): Promise<string>;
|
|
78
94
|
/** Application mode */
|
|
79
95
|
buildApp(app?: App, opts?: {
|
|
80
96
|
watch?: boolean;
|
package/lib/actions/build.js
CHANGED
|
@@ -75,6 +75,22 @@ async function newestLockfileMtime(cwd) {
|
|
|
75
75
|
}
|
|
76
76
|
return newest;
|
|
77
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Fresh when the cached GJS bundle is newer than BOTH the source file AND the
|
|
80
|
+
* project's lockfile (the dep-change signal). Missing bundle/source → not fresh
|
|
81
|
+
* (rebuild). Shared by the plugin + config GJS-bundle caches.
|
|
82
|
+
*/
|
|
83
|
+
async function isBundleFresh(outfile, sourcePath, cwd) {
|
|
84
|
+
try {
|
|
85
|
+
const outStat = await stat(outfile);
|
|
86
|
+
const srcStat = await stat(sourcePath);
|
|
87
|
+
const depMtime = await newestLockfileMtime(cwd);
|
|
88
|
+
return outStat.mtimeMs >= Math.max(srcStat.mtimeMs, depMtime);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
78
94
|
export class BuildAction {
|
|
79
95
|
configData;
|
|
80
96
|
constructor(configData = {}) {
|
|
@@ -283,70 +299,71 @@ export class BuildAction {
|
|
|
283
299
|
}
|
|
284
300
|
}
|
|
285
301
|
/**
|
|
286
|
-
* Bundle a single plugin entry for
|
|
287
|
-
* return its path.
|
|
288
|
-
*
|
|
289
|
-
*
|
|
290
|
-
* `bundler.plugins`, so it never recurses back into plugin resolution.
|
|
291
|
-
*
|
|
292
|
-
* Cached under `node_modules/.cache/gjsify/plugins/`, keyed by the resolved
|
|
293
|
-
* source path. Invalidated when the plugin entry OR the project lockfile is
|
|
294
|
-
* newer than the cached bundle — a dep bump rewrites the lockfile but not
|
|
295
|
-
* the plugin's own entry mtime. The one window neither mtime catches (a
|
|
296
|
-
* transitive dep edited in place) is covered by the `GJSIFY_NO_PLUGIN_CACHE=1`
|
|
297
|
-
* escape hatch. Failures are rethrown with context naming the plugin.
|
|
302
|
+
* Bundle a single plugin entry for GJS via {@link bundleFileForGjsCached} and
|
|
303
|
+
* return its path. Rethrows with context naming the plugin. (GJS's native ESM
|
|
304
|
+
* loader can't resolve a plugin's `package.json#exports` subpaths for a bare
|
|
305
|
+
* specifier; Rolldown resolves them at bundle time.)
|
|
298
306
|
*/
|
|
299
307
|
async bundlePluginForGjs(resolvedPath, pluginName, verbose) {
|
|
300
|
-
const cwd = process.cwd();
|
|
301
|
-
const cacheDir = join(cwd, 'node_modules', '.cache', 'gjsify', 'plugins');
|
|
302
|
-
const safeName = pluginName.replace(/[^a-zA-Z0-9._-]/g, '_');
|
|
303
|
-
const outfile = join(cacheDir, `${safeName}-${shortHash(resolvedPath)}.mjs`);
|
|
304
|
-
const cacheDisabled = isTruthyEnv(process.env.GJSIFY_NO_PLUGIN_CACHE);
|
|
305
|
-
if (!cacheDisabled && (await this.isPluginBundleFresh(outfile, resolvedPath, cwd))) {
|
|
306
|
-
if (verbose)
|
|
307
|
-
console.debug(`[gjsify] plugin "${pluginName}": reusing cached GJS bundle ${outfile}`);
|
|
308
|
-
return outfile;
|
|
309
|
-
}
|
|
310
|
-
if (verbose)
|
|
311
|
-
console.debug(`[gjsify] plugin "${pluginName}": bundling for GJS → ${outfile}`);
|
|
312
308
|
try {
|
|
313
|
-
await
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
// — but that injection wraps the entry with `export * from <entry>`,
|
|
317
|
-
// which drops the `default` export the plugin factory lives on.
|
|
318
|
-
// `preserveDefaultExport` makes the wrapper re-export `default` too,
|
|
319
|
-
// so the bundled plugin imports correctly as a library.
|
|
320
|
-
// shebang is intentionally left unset — the artifact is imported,
|
|
321
|
-
// not executed, so it must NOT carry a `#!` line.
|
|
322
|
-
const pluginBuild = new BuildAction({
|
|
309
|
+
return await BuildAction.bundleFileForGjsCached(resolvedPath, {
|
|
310
|
+
cacheSubdir: 'plugins',
|
|
311
|
+
label: pluginName,
|
|
323
312
|
verbose,
|
|
324
|
-
|
|
313
|
+
noCacheEnv: 'GJSIFY_NO_PLUGIN_CACHE',
|
|
325
314
|
});
|
|
326
|
-
await pluginBuild.buildApp('gjs', { preserveDefaultExport: true });
|
|
327
315
|
}
|
|
328
316
|
catch (err) {
|
|
329
317
|
throw new Error(`gjsify config: failed to bundle plugin "${pluginName}" for GJS ` +
|
|
330
|
-
`(needed because GJS can't import packages that use exports-map subpaths directly
|
|
331
|
-
`
|
|
318
|
+
`(needed because GJS can't import packages that use exports-map subpaths directly). ` +
|
|
319
|
+
`(${err.message})`);
|
|
332
320
|
}
|
|
333
|
-
return outfile;
|
|
334
321
|
}
|
|
335
322
|
/**
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
323
|
+
* Bundle a single ESM entry file to a self-contained `--app gjs` module and
|
|
324
|
+
* return its path — the shared "bundle one file for GJS" helper behind both
|
|
325
|
+
* the plugin loader (exports-map subpaths GJS can't resolve for a bare
|
|
326
|
+
* specifier) and the config loader (`node:`/npm imports GJS can't resolve for
|
|
327
|
+
* an external ESM file). Reuses the full `--app gjs` pipeline (exports-map-aware
|
|
328
|
+
* resolution, `node:`→`@gjsify` aliases, `gi://` externalized, `--globals auto`,
|
|
329
|
+
* single-file output).
|
|
330
|
+
*
|
|
331
|
+
* The nested `BuildAction` is constructed with EXPLICIT bundler options, so
|
|
332
|
+
* `buildApp` uses that configData directly and never re-enters `Config.load`
|
|
333
|
+
* — no recursion, even when the input is the very `gjsify.config.js` being
|
|
334
|
+
* loaded. `preserveDefaultExport` re-exports `default` through the side-effect
|
|
335
|
+
* entry wrapper so the file imports as a library; shebang stays unset (the
|
|
336
|
+
* artifact is imported, not executed).
|
|
337
|
+
*
|
|
338
|
+
* Cached under `node_modules/.cache/gjsify/<cacheSubdir>/`, keyed by the source
|
|
339
|
+
* path. Invalidated when the source OR the project lockfile is newer than the
|
|
340
|
+
* cached bundle (a dep bump rewrites the lockfile but not the source mtime).
|
|
341
|
+
* `<noCacheEnv>=1` forces a rebuild (covers the residual window: a transitive
|
|
342
|
+
* dep edited in place, touching neither the source nor a lockfile).
|
|
339
343
|
*/
|
|
340
|
-
async
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
344
|
+
static async bundleFileForGjsCached(inputPath, opts) {
|
|
345
|
+
const cwd = process.cwd();
|
|
346
|
+
const cacheDir = join(cwd, 'node_modules', '.cache', 'gjsify', opts.cacheSubdir);
|
|
347
|
+
const safeName = opts.label.replace(/[^a-zA-Z0-9._-]/g, '_');
|
|
348
|
+
const outfile = join(cacheDir, `${safeName}-${shortHash(inputPath)}.mjs`);
|
|
349
|
+
const cacheDisabled = opts.noCacheEnv ? isTruthyEnv(process.env[opts.noCacheEnv]) : false;
|
|
350
|
+
if (!cacheDisabled && (await isBundleFresh(outfile, inputPath, cwd))) {
|
|
351
|
+
if (opts.verbose)
|
|
352
|
+
console.debug(`[gjsify] reusing cached GJS bundle ${outfile}`);
|
|
353
|
+
return outfile;
|
|
349
354
|
}
|
|
355
|
+
if (opts.verbose)
|
|
356
|
+
console.debug(`[gjsify] bundling ${inputPath} for GJS → ${outfile}`);
|
|
357
|
+
await mkdir(cacheDir, { recursive: true });
|
|
358
|
+
await new BuildAction({
|
|
359
|
+
verbose: opts.verbose,
|
|
360
|
+
bundler: {
|
|
361
|
+
input: inputPath,
|
|
362
|
+
output: { file: outfile },
|
|
363
|
+
...(opts.define ? { transform: { define: opts.define } } : {}),
|
|
364
|
+
},
|
|
365
|
+
}).buildApp('gjs', { preserveDefaultExport: true });
|
|
366
|
+
return outfile;
|
|
350
367
|
}
|
|
351
368
|
/** Application mode */
|
|
352
369
|
async buildApp(app = 'gjs', opts = {}) {
|
package/lib/commands/run.d.ts
CHANGED
package/lib/commands/run.js
CHANGED
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
// as a bundle file. Everything else is a script name. Users who want
|
|
15
15
|
// to disambiguate can pass `./<file>` explicitly.
|
|
16
16
|
import { statSync } from 'node:fs';
|
|
17
|
-
import { delimiter, join, resolve } from 'node:path';
|
|
17
|
+
import { basename, delimiter, join, resolve } from 'node:path';
|
|
18
18
|
import { spawn } from 'node:child_process';
|
|
19
19
|
import { runGjsBundle } from '../utils/run-gjs.js';
|
|
20
20
|
import { readPackageJson } from '../utils/pkg-json-edit.js';
|
|
21
21
|
import { findWorkspaceRoot } from '../utils/workspace-root.js';
|
|
22
|
+
import { discoverWorkspaces } from '@gjsify/workspace';
|
|
22
23
|
import { isGjs } from '@gjsify/rolldown-plugin-gjsify/runtime';
|
|
23
24
|
export const runCommand = {
|
|
24
25
|
command: 'run <target> [args..]',
|
|
@@ -34,6 +35,16 @@ export const runCommand = {
|
|
|
34
35
|
type: 'string',
|
|
35
36
|
array: true,
|
|
36
37
|
default: [],
|
|
38
|
+
})
|
|
39
|
+
.option('workspace', {
|
|
40
|
+
// npm/yarn-style `-w <name>`: run <target> as a SCRIPT in the
|
|
41
|
+
// named workspace instead of the current directory. The name
|
|
42
|
+
// matches the package.json `name`, the workspace-relative path
|
|
43
|
+
// (e.g. `cli`), or the directory basename — so both
|
|
44
|
+
// `-w eco-retrofit-cli` and `-w cli` resolve, matching npm.
|
|
45
|
+
alias: 'w',
|
|
46
|
+
type: 'string',
|
|
47
|
+
description: 'Run <target> as a script in the named workspace (by name or path), like `npm run <script> -w <name>`.',
|
|
37
48
|
})
|
|
38
49
|
.parserConfiguration({
|
|
39
50
|
// Preserve `--` as args['--'] so callers can write
|
|
@@ -61,6 +72,12 @@ export const runCommand = {
|
|
|
61
72
|
const positionalArgs = args.args ?? [];
|
|
62
73
|
const doubleDashArgs = (args['--'] ?? []).filter((v) => typeof v === 'string');
|
|
63
74
|
const extraArgs = [...positionalArgs, ...doubleDashArgs];
|
|
75
|
+
// `-w <name>` (npm/yarn parity): run <target> as a script in the named
|
|
76
|
+
// workspace's directory rather than the current one. Never file mode.
|
|
77
|
+
if (typeof args.workspace === 'string' && args.workspace.length > 0) {
|
|
78
|
+
await runScriptInWorkspace(args.workspace, target, extraArgs);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
64
81
|
// Script lookup wins over file detection. Without this, a bare
|
|
65
82
|
// `gjsify run build` would resolve to a file when a `./build`
|
|
66
83
|
// directory exists in cwd (e.g. meson's build dir from a Vala
|
|
@@ -97,6 +114,31 @@ function looksLikeFile(target) {
|
|
|
97
114
|
return false;
|
|
98
115
|
}
|
|
99
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Resolve a workspace by npm/yarn `--workspace` semantics — matching the
|
|
119
|
+
* package.json `name`, the workspace-relative path (`packages/infra/cli` or
|
|
120
|
+
* `cli`), or the directory basename — then run `script` in that workspace's
|
|
121
|
+
* directory. Mirrors `npm run <script> --workspace <name>`.
|
|
122
|
+
*/
|
|
123
|
+
async function runScriptInWorkspace(name, script, extraArgs) {
|
|
124
|
+
const root = findWorkspaceRoot(process.cwd()) ?? process.cwd();
|
|
125
|
+
let workspaces;
|
|
126
|
+
try {
|
|
127
|
+
workspaces = discoverWorkspaces(root);
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
console.error(`gjsify run: could not read workspaces at ${root}: ${err.message}`);
|
|
131
|
+
return process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
const ws = workspaces.find((w) => w.name === name || w.relativeLocation === name || basename(w.location) === name);
|
|
134
|
+
if (!ws) {
|
|
135
|
+
const available = workspaces.map((w) => w.relativeLocation).join(', ') || '<none>';
|
|
136
|
+
console.error(`gjsify run: no workspace "${name}" under ${root} (available: ${available})`);
|
|
137
|
+
// `return process.exit` — a bare exit falls through under GJS (see runScript).
|
|
138
|
+
return process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
await runScript(script, extraArgs, ws.location);
|
|
141
|
+
}
|
|
100
142
|
/**
|
|
101
143
|
* Run a script declared in the current workspace's `package.json#scripts`.
|
|
102
144
|
* Mirrors `yarn run <script>` semantics:
|
|
@@ -107,20 +149,29 @@ function looksLikeFile(target) {
|
|
|
107
149
|
* - executed through `shell: true` so `&&` / `|` / env-var refs work
|
|
108
150
|
* exactly as in package.json scripts (matches npm/yarn)
|
|
109
151
|
*/
|
|
110
|
-
async function runScript(script, extraArgs) {
|
|
111
|
-
const cwd = process.cwd();
|
|
152
|
+
async function runScript(script, extraArgs, cwd = process.cwd()) {
|
|
112
153
|
const pkgPath = join(cwd, 'package.json');
|
|
113
154
|
const pkg = readPackageJson(pkgPath);
|
|
114
155
|
if (!pkg) {
|
|
115
156
|
console.error(`gjsify run: no package.json in ${cwd}`);
|
|
116
|
-
process.exit
|
|
157
|
+
// `return process.exit` — under GJS `process.exit` cannot terminate
|
|
158
|
+
// synchronously (a parked GLib.MainLoop owns the thread): it SCHEDULES
|
|
159
|
+
// the exit and returns, so a bare call falls through. Here that reached
|
|
160
|
+
// `tokenizeSimpleCommand(literal)` with `literal === undefined` →
|
|
161
|
+
// `cannot access property "length"` → the throw raced the scheduled
|
|
162
|
+
// exit into a second `system.exit` → `m_should_exit assertion failed`
|
|
163
|
+
// → core dump. The `return` halts the handler so exactly one exit is
|
|
164
|
+
// scheduled (same discipline as commands/workspace.ts). No-op on Node.
|
|
165
|
+
return process.exit(1);
|
|
117
166
|
}
|
|
118
167
|
const scripts = pkg.scripts ?? {};
|
|
119
168
|
const literal = scripts[script];
|
|
120
169
|
if (typeof literal !== 'string') {
|
|
121
170
|
const available = Object.keys(scripts).join(', ') || '<none>';
|
|
122
171
|
console.error(`gjsify run: no script "${script}" in ${pkgPath} (available: ${available})`);
|
|
123
|
-
process.exit
|
|
172
|
+
// See the `no package.json` branch above — a bare `process.exit` under
|
|
173
|
+
// GJS falls through to `tokenizeSimpleCommand(undefined)` and core-dumps.
|
|
174
|
+
return process.exit(1);
|
|
124
175
|
}
|
|
125
176
|
const monorepoRoot = findWorkspaceRoot(cwd);
|
|
126
177
|
const binDirs = [join(cwd, 'node_modules', '.bin')];
|
package/lib/config.js
CHANGED
|
@@ -1,5 +1,93 @@
|
|
|
1
1
|
import { APP_NAME } from './constants.js';
|
|
2
2
|
import { cosmiconfig } from 'cosmiconfig';
|
|
3
|
+
import { basename } from 'node:path';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { isGjs } from '@gjsify/rolldown-plugin-gjsify/runtime';
|
|
6
|
+
/**
|
|
7
|
+
* Does the failed `import()` of a config look like a MODULE-RESOLUTION failure
|
|
8
|
+
* (the config imports something GJS's ESM loader can't resolve for an external
|
|
9
|
+
* file — `node:` builtins, npm deps) rather than a syntax/eval error in the
|
|
10
|
+
* config body? Only the former is worth retrying by bundling the config.
|
|
11
|
+
*/
|
|
12
|
+
function isModuleResolutionFailure(err) {
|
|
13
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
14
|
+
return /Unsupported URI scheme|\bnode:|Cannot find (module|package)|Module not found|ERR_MODULE_NOT_FOUND|Failed to resolve/i.test(msg);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Load a `gjsify.config.js` that imports `node:`/npm modules under the
|
|
18
|
+
* GJS-bundled CLI by BUNDLING it first (`--app gjs`: `node:`→`@gjsify`, `gi://`
|
|
19
|
+
* externalized, `--globals auto`, one self-contained ESM), then importing that.
|
|
20
|
+
* The Node-free way to run an otherwise-unimportable config under GJS — cached +
|
|
21
|
+
* de-recursed (the nested build uses explicit options, never re-enters config
|
|
22
|
+
* loading; see `BuildAction.bundleFileForGjsCached`).
|
|
23
|
+
*/
|
|
24
|
+
async function loadConfigViaGjsBundle(filepath, verbose) {
|
|
25
|
+
let outfile;
|
|
26
|
+
try {
|
|
27
|
+
// Dynamic import keeps the build machinery out of config.ts's eager
|
|
28
|
+
// module graph — pulled only when a config actually needs bundling.
|
|
29
|
+
const { BuildAction } = await import('./actions/build.js');
|
|
30
|
+
outfile = await BuildAction.bundleFileForGjsCached(filepath, {
|
|
31
|
+
cacheSubdir: 'config',
|
|
32
|
+
label: basename(filepath),
|
|
33
|
+
verbose,
|
|
34
|
+
noCacheEnv: 'GJSIFY_NO_CONFIG_CACHE',
|
|
35
|
+
// The bundle lives in the cache dir, but the config commonly resolves
|
|
36
|
+
// sibling files (e.g. `readFileSync(resolve(dirname(fileURLToPath(
|
|
37
|
+
// import.meta.url)), 'package.json'))`) relative to ITS OWN location.
|
|
38
|
+
// Bake the original config's URL so those reads hit the real dir.
|
|
39
|
+
define: { 'import.meta.url': JSON.stringify(pathToFileURL(filepath).href) },
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (bundleErr) {
|
|
43
|
+
throw new Error(`gjsify: failed to bundle the config ${filepath} for the GJS CLI. ` +
|
|
44
|
+
`Fixes: run the build under Node (\`npx gjsify …\`), keep the config to plain values / ` +
|
|
45
|
+
`\`process.env\`, or move settings into \`package.json#gjsify\`. (${bundleErr.message})`);
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
const mod = (await import(pathToFileURL(outfile).href));
|
|
49
|
+
return mod.default ?? mod;
|
|
50
|
+
}
|
|
51
|
+
catch (importErr) {
|
|
52
|
+
throw new Error(`gjsify: bundled the config ${filepath} for GJS but could not import the result ` +
|
|
53
|
+
`(${outfile}). (${importErr.message})`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Config-file loader for the GJS-bundled CLI (`cli.gjs.mjs`).
|
|
58
|
+
*
|
|
59
|
+
* cosmiconfig's default `.js`/`.mjs`/`.cjs` loader does `await import(href)` and,
|
|
60
|
+
* on failure, falls back to a synchronous CJS `require` — which under GJS crashes
|
|
61
|
+
* deep in the bundled require-shim with an opaque `a.shift is not a function`.
|
|
62
|
+
* The real cause: a `gjsify.config.js` that imports `node:` builtins (e.g.
|
|
63
|
+
* `node:fs` to read `package.json`) can't be resolved by GJS's ESM loader for an
|
|
64
|
+
* externally-imported module (`Unsupported URI scheme for importing: node`).
|
|
65
|
+
*
|
|
66
|
+
* Fast path: plain-value configs `import()` fine under GJS — no bundling. When
|
|
67
|
+
* the import fails on module resolution, bundle the config for GJS and import
|
|
68
|
+
* THAT (so `node:`/npm imports resolve) — so node:-importing configs work
|
|
69
|
+
* Node-free, not just fail cleanly. Genuine syntax/eval errors are rethrown
|
|
70
|
+
* as-is. The Node CLI never takes this path (its `import()` resolves `node:`).
|
|
71
|
+
*/
|
|
72
|
+
const gjsConfigLoader = async (filepath) => {
|
|
73
|
+
try {
|
|
74
|
+
const mod = (await import(pathToFileURL(filepath).href));
|
|
75
|
+
return mod.default ?? mod;
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
if (!isModuleResolutionFailure(err))
|
|
79
|
+
throw err;
|
|
80
|
+
return loadConfigViaGjsBundle(filepath, Boolean(process.env.GJSIFY_DEBUG));
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* On GJS, override cosmiconfig's default JS loaders (whose require fallback
|
|
85
|
+
* crashes) with {@link gjsConfigLoader}. Empty on Node, so cosmiconfig keeps its
|
|
86
|
+
* defaults there — zero behavior change off GJS.
|
|
87
|
+
*/
|
|
88
|
+
const gjsConfigLoaders = isGjs()
|
|
89
|
+
? { '.js': gjsConfigLoader, '.mjs': gjsConfigLoader, '.cjs': gjsConfigLoader }
|
|
90
|
+
: {};
|
|
3
91
|
/** Default cosmiconfig search places for a given module name (matches cosmiconfig defaults). */
|
|
4
92
|
function defaultSearchPlaces(name) {
|
|
5
93
|
return [
|
|
@@ -89,6 +177,10 @@ export class Config {
|
|
|
89
177
|
const fileExplorer = cosmiconfig(APP_NAME, {
|
|
90
178
|
...this.loadOptions,
|
|
91
179
|
searchPlaces: (this.loadOptions.searchPlaces ?? defaultSearchPlaces(APP_NAME)).filter((p) => p !== 'package.json'),
|
|
180
|
+
// GJS: swap the crashing default JS loaders for one that surfaces an
|
|
181
|
+
// actionable error instead of `a.shift is not a function`. User-provided
|
|
182
|
+
// loaders still win. No-op on Node (gjsConfigLoaders is empty there).
|
|
183
|
+
loaders: { ...gjsConfigLoaders, ...this.loadOptions.loaders },
|
|
92
184
|
});
|
|
93
185
|
const fileResult = (await fileExplorer.search(searchFrom));
|
|
94
186
|
const merged = {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/cli",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.6",
|
|
4
4
|
"description": "CLI for Gjsify",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -122,19 +122,19 @@
|
|
|
122
122
|
"cli"
|
|
123
123
|
],
|
|
124
124
|
"dependencies": {
|
|
125
|
-
"@gjsify/buffer": "^0.16.
|
|
126
|
-
"@gjsify/create-app": "^0.16.
|
|
127
|
-
"@gjsify/node-globals": "^0.16.
|
|
128
|
-
"@gjsify/node-polyfills": "^0.16.
|
|
129
|
-
"@gjsify/npm-registry": "^0.16.
|
|
130
|
-
"@gjsify/resolve-npm": "^0.16.
|
|
131
|
-
"@gjsify/rolldown-plugin-gjsify": "^0.16.
|
|
132
|
-
"@gjsify/rolldown-plugin-pnp": "^0.16.
|
|
133
|
-
"@gjsify/semver": "^0.16.
|
|
134
|
-
"@gjsify/tar": "^0.16.
|
|
135
|
-
"@gjsify/tsc": "^0.16.
|
|
136
|
-
"@gjsify/web-polyfills": "^0.16.
|
|
137
|
-
"@gjsify/workspace": "^0.16.
|
|
125
|
+
"@gjsify/buffer": "^0.16.6",
|
|
126
|
+
"@gjsify/create-app": "^0.16.6",
|
|
127
|
+
"@gjsify/node-globals": "^0.16.6",
|
|
128
|
+
"@gjsify/node-polyfills": "^0.16.6",
|
|
129
|
+
"@gjsify/npm-registry": "^0.16.6",
|
|
130
|
+
"@gjsify/resolve-npm": "^0.16.6",
|
|
131
|
+
"@gjsify/rolldown-plugin-gjsify": "^0.16.6",
|
|
132
|
+
"@gjsify/rolldown-plugin-pnp": "^0.16.6",
|
|
133
|
+
"@gjsify/semver": "^0.16.6",
|
|
134
|
+
"@gjsify/tar": "^0.16.6",
|
|
135
|
+
"@gjsify/tsc": "^0.16.6",
|
|
136
|
+
"@gjsify/web-polyfills": "^0.16.6",
|
|
137
|
+
"@gjsify/workspace": "^0.16.6",
|
|
138
138
|
"cosmiconfig": "^9.0.2",
|
|
139
139
|
"get-tsconfig": "^4.14.0",
|
|
140
140
|
"pkg-types": "^2.3.1",
|
|
@@ -142,13 +142,13 @@
|
|
|
142
142
|
"yargs": "^18.0.0"
|
|
143
143
|
},
|
|
144
144
|
"devDependencies": {
|
|
145
|
-
"@gjsify/unit": "^0.16.
|
|
145
|
+
"@gjsify/unit": "^0.16.6",
|
|
146
146
|
"@types/yargs": "^17.0.35",
|
|
147
147
|
"typescript": "^6.0.3"
|
|
148
148
|
},
|
|
149
149
|
"peerDependencies": {
|
|
150
|
-
"@gjsify/oxfmt-native": "^0.16.
|
|
151
|
-
"@gjsify/rolldown-native": "^0.16.
|
|
150
|
+
"@gjsify/oxfmt-native": "^0.16.6",
|
|
151
|
+
"@gjsify/rolldown-native": "^0.16.6"
|
|
152
152
|
},
|
|
153
153
|
"peerDependenciesMeta": {
|
|
154
154
|
"@gjsify/oxfmt-native": {
|