@gjsify/cli 0.8.0 → 0.9.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 +82 -82
- package/lib/actions/build.d.ts +31 -0
- package/lib/actions/build.js +135 -3
- package/lib/bundler-pick.js +6 -9
- package/lib/cli-app.js +2 -1
- package/lib/commands/run.js +2 -6
- package/lib/utils/oxc-resolve.js +3 -4
- package/lib/utils/resolve-plugin-by-name.d.ts +21 -1
- package/lib/utils/resolve-plugin-by-name.js +5 -2
- package/package.json +17 -17
package/lib/actions/build.d.ts
CHANGED
|
@@ -37,9 +37,40 @@ export declare class BuildAction {
|
|
|
37
37
|
* there is no in-bundle plugin, so this hook is the sole injection point.
|
|
38
38
|
*/
|
|
39
39
|
private applyShebang;
|
|
40
|
+
/**
|
|
41
|
+
* GJS plugin loader — bundle the resolved plugin module to a single
|
|
42
|
+
* self-contained ESM file, then import that. Works around GJS's native
|
|
43
|
+
* ESM loader not following `package.json#exports` subpath maps for bare
|
|
44
|
+
* specifiers: Rolldown resolves those subpaths at bundle time, so the
|
|
45
|
+
* emitted file has no unresolvable bare-specifier imports left. Injected
|
|
46
|
+
* as the `loadModule` strategy in `buildApp` only when running under GJS.
|
|
47
|
+
*/
|
|
48
|
+
private loadPluginViaGjsBundle;
|
|
49
|
+
/**
|
|
50
|
+
* Bundle a single plugin entry for `--app gjs` to a cached temp file and
|
|
51
|
+
* return its path. Reuses the full `--app gjs` pipeline (exports-map-aware
|
|
52
|
+
* resolution, `node:`→`@gjsify` aliases, `--globals auto`, single-file
|
|
53
|
+
* output) via a nested `BuildAction`. The nested config carries no
|
|
54
|
+
* `bundler.plugins`, so it never recurses back into plugin resolution.
|
|
55
|
+
*
|
|
56
|
+
* Cached under `node_modules/.cache/gjsify/plugins/`, keyed by the resolved
|
|
57
|
+
* source path. Invalidated when the plugin entry OR the project lockfile is
|
|
58
|
+
* newer than the cached bundle — a dep bump rewrites the lockfile but not
|
|
59
|
+
* the plugin's own entry mtime. The one window neither mtime catches (a
|
|
60
|
+
* transitive dep edited in place) is covered by the `GJSIFY_NO_PLUGIN_CACHE=1`
|
|
61
|
+
* escape hatch. Failures are rethrown with context naming the plugin.
|
|
62
|
+
*/
|
|
63
|
+
private bundlePluginForGjs;
|
|
64
|
+
/**
|
|
65
|
+
* Fresh when the cached bundle is newer than BOTH the plugin entry AND the
|
|
66
|
+
* project's lockfile (the dep-change signal). Missing bundle/source → not
|
|
67
|
+
* fresh (rebuild).
|
|
68
|
+
*/
|
|
69
|
+
private isPluginBundleFresh;
|
|
40
70
|
/** Application mode */
|
|
41
71
|
buildApp(app?: App, opts?: {
|
|
42
72
|
watch?: boolean;
|
|
73
|
+
preserveDefaultExport?: boolean;
|
|
43
74
|
}): Promise<RolldownOutput[]>;
|
|
44
75
|
/**
|
|
45
76
|
* Drive `rolldown.watch(...)`: rebuild on source change, apply the
|
package/lib/actions/build.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { runBundle, runWatch, bundleToChunks } from '../bundler-pick.js';
|
|
2
2
|
import { gjsifyPlugin, textLoaderPlugin, resolveShebangLine, NODE_SHEBANG } from '@gjsify/rolldown-plugin-gjsify';
|
|
3
|
+
import { isGjs } from '@gjsify/rolldown-plugin-gjsify/runtime';
|
|
3
4
|
import { resolveUserPlugins } from '../utils/resolve-plugin-by-name.js';
|
|
4
5
|
import { resolveGlobalsList, writeRegisterInjectFile, detectAutoGlobals } from '@gjsify/rolldown-plugin-gjsify/globals';
|
|
5
6
|
import { pnpPlugin } from '@gjsify/rolldown-plugin-pnp';
|
|
6
|
-
import { dirname, extname } from 'node:path';
|
|
7
|
-
import {
|
|
7
|
+
import { dirname, extname, join } from 'node:path';
|
|
8
|
+
import { pathToFileURL } from 'node:url';
|
|
9
|
+
import { chmod, mkdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
8
10
|
import { normalizeBundlerOptions, mergeBundlerOptions } from '../utils/normalize-bundler-options.js';
|
|
9
11
|
const DEFAULT_GJS_SHEBANG = '#!/usr/bin/env -S gjs -m';
|
|
10
12
|
/**
|
|
@@ -34,6 +36,43 @@ function isUnsafeDefaultOutput(path) {
|
|
|
34
36
|
async function buildPnpPlugin() {
|
|
35
37
|
return pnpPlugin({ issuerUrl: import.meta.url });
|
|
36
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Stable, filesystem-safe short hash of a string (djb2 → 8 hex chars).
|
|
41
|
+
* Used to key a plugin's cached GJS bundle by its resolved source path.
|
|
42
|
+
* Dependency-free on purpose — avoids pulling crypto into this hot path.
|
|
43
|
+
*/
|
|
44
|
+
function shortHash(value) {
|
|
45
|
+
let h = 5381;
|
|
46
|
+
for (let i = 0; i < value.length; i++) {
|
|
47
|
+
h = ((h << 5) + h + value.charCodeAt(i)) >>> 0;
|
|
48
|
+
}
|
|
49
|
+
return h.toString(16).padStart(8, '0');
|
|
50
|
+
}
|
|
51
|
+
function isTruthyEnv(v) {
|
|
52
|
+
return v === '1' || v === 'true' || v === 'yes';
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Lockfiles whose change implies a plugin's *transitive* deps may have moved.
|
|
56
|
+
* A dep bump (`npm/yarn/pnpm/gjsify install`) rewrites one of these but leaves
|
|
57
|
+
* the plugin's own entry-file mtime untouched — so the plugin GJS-bundle cache
|
|
58
|
+
* must invalidate on these too, not only on the entry.
|
|
59
|
+
*/
|
|
60
|
+
const PLUGIN_CACHE_DEP_SIGNALS = ['gjsify-lock.json', 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml'];
|
|
61
|
+
/** Newest mtime (ms) among known lockfiles in `cwd`, or 0 when none exist. */
|
|
62
|
+
async function newestLockfileMtime(cwd) {
|
|
63
|
+
let newest = 0;
|
|
64
|
+
for (const name of PLUGIN_CACHE_DEP_SIGNALS) {
|
|
65
|
+
try {
|
|
66
|
+
const s = await stat(join(cwd, name));
|
|
67
|
+
if (s.mtimeMs > newest)
|
|
68
|
+
newest = s.mtimeMs;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// not present — ignore
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return newest;
|
|
75
|
+
}
|
|
37
76
|
export class BuildAction {
|
|
38
77
|
configData;
|
|
39
78
|
constructor(configData = {}) {
|
|
@@ -179,6 +218,90 @@ export class BuildAction {
|
|
|
179
218
|
if (verbose)
|
|
180
219
|
console.debug(`[gjsify] --shebang: wrote ${line} + chmod 0o755 to ${outfile}`);
|
|
181
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* GJS plugin loader — bundle the resolved plugin module to a single
|
|
223
|
+
* self-contained ESM file, then import that. Works around GJS's native
|
|
224
|
+
* ESM loader not following `package.json#exports` subpath maps for bare
|
|
225
|
+
* specifiers: Rolldown resolves those subpaths at bundle time, so the
|
|
226
|
+
* emitted file has no unresolvable bare-specifier imports left. Injected
|
|
227
|
+
* as the `loadModule` strategy in `buildApp` only when running under GJS.
|
|
228
|
+
*/
|
|
229
|
+
async loadPluginViaGjsBundle(resolvedPath, pluginName, verbose) {
|
|
230
|
+
const outfile = await this.bundlePluginForGjs(resolvedPath, pluginName, verbose);
|
|
231
|
+
try {
|
|
232
|
+
return (await import(pathToFileURL(outfile).href));
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
throw new Error(`gjsify config: failed to import the GJS bundle for plugin "${pluginName}" ` +
|
|
236
|
+
`(bundled to ${outfile}). (${err.message})`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Bundle a single plugin entry for `--app gjs` to a cached temp file and
|
|
241
|
+
* return its path. Reuses the full `--app gjs` pipeline (exports-map-aware
|
|
242
|
+
* resolution, `node:`→`@gjsify` aliases, `--globals auto`, single-file
|
|
243
|
+
* output) via a nested `BuildAction`. The nested config carries no
|
|
244
|
+
* `bundler.plugins`, so it never recurses back into plugin resolution.
|
|
245
|
+
*
|
|
246
|
+
* Cached under `node_modules/.cache/gjsify/plugins/`, keyed by the resolved
|
|
247
|
+
* source path. Invalidated when the plugin entry OR the project lockfile is
|
|
248
|
+
* newer than the cached bundle — a dep bump rewrites the lockfile but not
|
|
249
|
+
* the plugin's own entry mtime. The one window neither mtime catches (a
|
|
250
|
+
* transitive dep edited in place) is covered by the `GJSIFY_NO_PLUGIN_CACHE=1`
|
|
251
|
+
* escape hatch. Failures are rethrown with context naming the plugin.
|
|
252
|
+
*/
|
|
253
|
+
async bundlePluginForGjs(resolvedPath, pluginName, verbose) {
|
|
254
|
+
const cwd = process.cwd();
|
|
255
|
+
const cacheDir = join(cwd, 'node_modules', '.cache', 'gjsify', 'plugins');
|
|
256
|
+
const safeName = pluginName.replace(/[^a-zA-Z0-9._-]/g, '_');
|
|
257
|
+
const outfile = join(cacheDir, `${safeName}-${shortHash(resolvedPath)}.mjs`);
|
|
258
|
+
const cacheDisabled = isTruthyEnv(process.env.GJSIFY_NO_PLUGIN_CACHE);
|
|
259
|
+
if (!cacheDisabled && (await this.isPluginBundleFresh(outfile, resolvedPath, cwd))) {
|
|
260
|
+
if (verbose)
|
|
261
|
+
console.debug(`[gjsify] plugin "${pluginName}": reusing cached GJS bundle ${outfile}`);
|
|
262
|
+
return outfile;
|
|
263
|
+
}
|
|
264
|
+
if (verbose)
|
|
265
|
+
console.debug(`[gjsify] plugin "${pluginName}": bundling for GJS → ${outfile}`);
|
|
266
|
+
try {
|
|
267
|
+
await mkdir(cacheDir, { recursive: true });
|
|
268
|
+
// `--globals auto` stays on so the plugin's own runtime globals
|
|
269
|
+
// (the MDX/unified toolchain touches `document`, etc.) are injected
|
|
270
|
+
// — but that injection wraps the entry with `export * from <entry>`,
|
|
271
|
+
// which drops the `default` export the plugin factory lives on.
|
|
272
|
+
// `preserveDefaultExport` makes the wrapper re-export `default` too,
|
|
273
|
+
// so the bundled plugin imports correctly as a library.
|
|
274
|
+
// shebang is intentionally left unset — the artifact is imported,
|
|
275
|
+
// not executed, so it must NOT carry a `#!` line.
|
|
276
|
+
const pluginBuild = new BuildAction({
|
|
277
|
+
verbose,
|
|
278
|
+
bundler: { input: resolvedPath, output: { file: outfile } },
|
|
279
|
+
});
|
|
280
|
+
await pluginBuild.buildApp('gjs', { preserveDefaultExport: true });
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
throw new Error(`gjsify config: failed to bundle plugin "${pluginName}" for GJS ` +
|
|
284
|
+
`(needed because GJS can't import packages that use exports-map subpaths directly; ` +
|
|
285
|
+
`cache dir ${cacheDir}). (${err.message})`);
|
|
286
|
+
}
|
|
287
|
+
return outfile;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Fresh when the cached bundle is newer than BOTH the plugin entry AND the
|
|
291
|
+
* project's lockfile (the dep-change signal). Missing bundle/source → not
|
|
292
|
+
* fresh (rebuild).
|
|
293
|
+
*/
|
|
294
|
+
async isPluginBundleFresh(outfile, resolvedPath, cwd) {
|
|
295
|
+
try {
|
|
296
|
+
const outStat = await stat(outfile);
|
|
297
|
+
const srcStat = await stat(resolvedPath);
|
|
298
|
+
const depMtime = await newestLockfileMtime(cwd);
|
|
299
|
+
return outStat.mtimeMs >= Math.max(srcStat.mtimeMs, depMtime);
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
182
305
|
/** Application mode */
|
|
183
306
|
async buildApp(app = 'gjs', opts = {}) {
|
|
184
307
|
const { verbose, typescript, exclude, library: pkg, aliases, excludeGlobals } = this.configData;
|
|
@@ -211,6 +334,7 @@ export class BuildAction {
|
|
|
211
334
|
reflection: typescript?.reflection,
|
|
212
335
|
consoleShim,
|
|
213
336
|
...(aliases ? { aliases } : {}),
|
|
337
|
+
...(opts.preserveDefaultExport ? { preserveDefaultExport: true } : {}),
|
|
214
338
|
};
|
|
215
339
|
const { autoMode, extras } = this.parseGlobalsValue(globals);
|
|
216
340
|
const pnp = await buildPnpPlugin();
|
|
@@ -230,7 +354,15 @@ export class BuildAction {
|
|
|
230
354
|
// auto-globals pre-build to avoid claiming the same files via
|
|
231
355
|
// Rolldown's default classifier.
|
|
232
356
|
if (userBundler.plugins?.length) {
|
|
233
|
-
|
|
357
|
+
// Under GJS, plugin modules can't always be imported directly:
|
|
358
|
+
// GJS's native ESM loader doesn't follow `package.json#exports`
|
|
359
|
+
// subpath maps, so a plugin whose source imports an internal
|
|
360
|
+
// subpath (e.g. `@mdx-js/mdx/internal-…`) throws `Module not
|
|
361
|
+
// found` even when the file is on disk. The injected loader
|
|
362
|
+
// first bundles such a plugin to a single self-contained ESM
|
|
363
|
+
// (Rolldown resolves the exports map at bundle time) and imports
|
|
364
|
+
// that instead. On Node the default direct-import path is used.
|
|
365
|
+
const resolved = await resolveUserPlugins(userBundler.plugins, process.cwd(), isGjs() ? { loadModule: (p, name) => this.loadPluginViaGjsBundle(p, name, verbose) } : {});
|
|
234
366
|
userPlugins.push(...resolved);
|
|
235
367
|
}
|
|
236
368
|
// --- Auto mode (with optional extras): iterative multi-pass build ---
|
package/lib/bundler-pick.js
CHANGED
|
@@ -32,6 +32,7 @@ import * as path from 'node:path';
|
|
|
32
32
|
import { createRequire } from 'node:module';
|
|
33
33
|
import { pathToFileURL } from 'node:url';
|
|
34
34
|
import { resolveNpmPackage } from './utils/resolve-npm-package.js';
|
|
35
|
+
import { isGjs } from '@gjsify/rolldown-plugin-gjsify/runtime';
|
|
35
36
|
// npm `rolldown` is a Rust crate with platform-specific prebuilds; loading
|
|
36
37
|
// it eagerly at module init pulls musl-detection code that does
|
|
37
38
|
// `require('node:fs')` synchronously — fine on Node, but fatal under GJS
|
|
@@ -46,8 +47,7 @@ async function loadNpmRolldown() {
|
|
|
46
47
|
// inside the crate's wrapper (e.g. on a truly-cold workspace where
|
|
47
48
|
// `@gjsify/rolldown-native`'s lib/ is not built yet, issue #497).
|
|
48
49
|
// Fail with the actionable diagnosis instead.
|
|
49
|
-
|
|
50
|
-
if (isGjs) {
|
|
50
|
+
if (isGjs()) {
|
|
51
51
|
throw new Error('gjsify build: no usable bundler engine under GJS — `@gjsify/rolldown-native` is not loadable ' +
|
|
52
52
|
'(its JS facade lib/ is not built, or its prebuild typelib is missing from GI_TYPELIB_PATH), ' +
|
|
53
53
|
'and the npm `rolldown` engine is a Rust napi crate that cannot run under GJS. ' +
|
|
@@ -83,8 +83,7 @@ async function loadNpmRolldown() {
|
|
|
83
83
|
* rolldown` even though the package is present under the install dir.
|
|
84
84
|
*/
|
|
85
85
|
function resolveImportTargetForGjs(specifier) {
|
|
86
|
-
|
|
87
|
-
if (!isGjs)
|
|
86
|
+
if (!isGjs())
|
|
88
87
|
return specifier;
|
|
89
88
|
const resolved = resolveNpmPackage(specifier, { bundleUrl: import.meta.url });
|
|
90
89
|
if (resolved)
|
|
@@ -193,8 +192,7 @@ export async function shouldUseNative() {
|
|
|
193
192
|
// GJS: try @gjsify/rolldown-native — it's the only engine
|
|
194
193
|
// that can actually run here (npm rolldown is a Rust
|
|
195
194
|
// crate). Fall back to npm only on Node.
|
|
196
|
-
|
|
197
|
-
if (!isGjs)
|
|
195
|
+
if (!isGjs())
|
|
198
196
|
return false;
|
|
199
197
|
const native = await tryLoadNative();
|
|
200
198
|
return native !== null;
|
|
@@ -236,8 +234,7 @@ async function tryLoadNative() {
|
|
|
236
234
|
if (_nativeProbe)
|
|
237
235
|
return _nativeProbe;
|
|
238
236
|
_nativeProbe = (async () => {
|
|
239
|
-
|
|
240
|
-
if (!isGjs)
|
|
237
|
+
if (!isGjs())
|
|
241
238
|
return null;
|
|
242
239
|
try {
|
|
243
240
|
// Under GJS the ESM loader has no node_modules resolver — a bare
|
|
@@ -256,7 +253,7 @@ async function tryLoadNative() {
|
|
|
256
253
|
// to npm rolldown (which then throws ImportError for `rolldown`).
|
|
257
254
|
const specifier = '@gjsify/rolldown-native';
|
|
258
255
|
let target = specifier;
|
|
259
|
-
if (isGjs) {
|
|
256
|
+
if (isGjs()) {
|
|
260
257
|
// Same multi-anchor resolution as `loadNpmRolldown` —
|
|
261
258
|
// when the bundle is invoked from a cwd outside the
|
|
262
259
|
// install dir, anchoring solely at `import.meta.url`
|
package/lib/cli-app.js
CHANGED
|
@@ -21,6 +21,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
21
21
|
import yargs from 'yargs';
|
|
22
22
|
import { buildCommand as build, 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, dlxCommand as dlx, installCommand as install, foreachCommand as foreach, workspaceCommand as workspace, packCommand as pack, publishCommand as publish, whoamiCommand as whoami, loginCommand as login, logoutCommand as logout, trustCommand as trust, selfUpdateCommand as selfUpdate, generateInstallerCommand as generateInstaller, 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, } from './commands/index.js';
|
|
23
23
|
import { APP_NAME } from './constants.js';
|
|
24
|
+
import { isNode } from '@gjsify/rolldown-plugin-gjsify/runtime';
|
|
24
25
|
// Detect which runtime is executing the CLI (GJS or Node.js).
|
|
25
26
|
// GJS MUST be checked first because @gjsify/process sets
|
|
26
27
|
// `process.versions.node = '20.0.0'` for compatibility — a plain
|
|
@@ -36,7 +37,7 @@ function runtimeLabel() {
|
|
|
36
37
|
catch {
|
|
37
38
|
/* not GJS */
|
|
38
39
|
}
|
|
39
|
-
if (
|
|
40
|
+
if (isNode()) {
|
|
40
41
|
return `Node.js ${process.version}`;
|
|
41
42
|
}
|
|
42
43
|
return 'unknown runtime';
|
package/lib/commands/run.js
CHANGED
|
@@ -19,6 +19,7 @@ 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 { isGjs } from '@gjsify/rolldown-plugin-gjsify/runtime';
|
|
22
23
|
export const runCommand = {
|
|
23
24
|
command: 'run <target> [args..]',
|
|
24
25
|
description: 'Run a script from package.json (yarn-run-style) or a GJS bundle file. If <target> resolves to a file on disk (or has a path-like prefix), it is launched via gjs with LD_LIBRARY_PATH + GI_TYPELIB_PATH set for native packages. Otherwise it is looked up in the current package.json `scripts`.',
|
|
@@ -151,7 +152,7 @@ async function runScript(script, extraArgs) {
|
|
|
151
152
|
// Anything fancier (compound `&&`, pipes, a non-gjsify command) falls
|
|
152
153
|
// through to the shell spawn below. Node keeps spawning (cheap there); this
|
|
153
154
|
// only triggers under GJS, where the nesting actually hurts. See cli-app.ts.
|
|
154
|
-
const inProcArgv =
|
|
155
|
+
const inProcArgv = isGjs() ? gjsifyInProcessArgv(literal, extraArgs) : null;
|
|
155
156
|
if (inProcArgv) {
|
|
156
157
|
// The subcommand runs in our process, so surface the script env on
|
|
157
158
|
// `process.env` (PATH with the workspace .bin dirs, npm_* lifecycle
|
|
@@ -203,11 +204,6 @@ function shellEscape(arg) {
|
|
|
203
204
|
return arg;
|
|
204
205
|
return `'${arg.replace(/'/g, "'\\''")}'`;
|
|
205
206
|
}
|
|
206
|
-
// Detected at CALL time (a module-load const reads `undefined` in the bundled
|
|
207
|
-
// `--app gjs` form). `bundler-pick.ts` uses the same expression.
|
|
208
|
-
function runningUnderGjs() {
|
|
209
|
-
return typeof globalThis.imports?.gi !== 'undefined';
|
|
210
|
-
}
|
|
211
207
|
/**
|
|
212
208
|
* If `literal` is a SINGLE `gjsify <subcommand> …` command with no shell
|
|
213
209
|
* operators / substitutions / unquoted globs, return the argv to feed `runCli`
|
package/lib/utils/oxc-resolve.js
CHANGED
|
@@ -53,6 +53,7 @@ import { createRequire } from 'node:module';
|
|
|
53
53
|
import { pathToFileURL } from 'node:url';
|
|
54
54
|
import { findWorkspaceRoot } from './workspace-root.js';
|
|
55
55
|
import { resolveNpmPackage } from './resolve-npm-package.js';
|
|
56
|
+
import { isGjs } from '@gjsify/rolldown-plugin-gjsify/runtime';
|
|
56
57
|
/**
|
|
57
58
|
* Map Node.js `process.platform` + `arch` (+ musl on Linux) to the
|
|
58
59
|
* napi-rs binding-package suffix oxc uses, e.g. `linux-x64-gnu`,
|
|
@@ -210,8 +211,7 @@ async function tryLoadNativeOxfmt() {
|
|
|
210
211
|
if (_nativeOxfmtProbe)
|
|
211
212
|
return _nativeOxfmtProbe;
|
|
212
213
|
_nativeOxfmtProbe = (async () => {
|
|
213
|
-
|
|
214
|
-
if (!isGjs)
|
|
214
|
+
if (!isGjs())
|
|
215
215
|
return null;
|
|
216
216
|
try {
|
|
217
217
|
const specifier = '@gjsify/oxfmt-native';
|
|
@@ -249,8 +249,7 @@ export async function shouldUseNativeOxfmt() {
|
|
|
249
249
|
}
|
|
250
250
|
return true;
|
|
251
251
|
}
|
|
252
|
-
|
|
253
|
-
if (!isGjs)
|
|
252
|
+
if (!isGjs())
|
|
254
253
|
return false;
|
|
255
254
|
return (await tryLoadNativeOxfmt()) !== null;
|
|
256
255
|
}
|
|
@@ -7,6 +7,26 @@ export interface PluginByName {
|
|
|
7
7
|
/** Options forwarded to the plugin factory. */
|
|
8
8
|
options?: unknown;
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Strategy for turning a resolved plugin file path into its module namespace.
|
|
12
|
+
*
|
|
13
|
+
* On Node this is a plain dynamic `import()`. Under GJS it cannot be — GJS's
|
|
14
|
+
* native ESM loader does not follow npm `package.json#exports` subpath maps
|
|
15
|
+
* for bare specifiers, so importing a plugin whose source does
|
|
16
|
+
* `import … from '@scope/pkg/internal-subpath'` throws
|
|
17
|
+
* `Module not found: @scope/pkg/internal-subpath` even when the file is on
|
|
18
|
+
* disk. The CLI injects a GJS strategy that first bundles the plugin to a
|
|
19
|
+
* single self-contained ESM file (Rolldown resolves the exports map at
|
|
20
|
+
* bundle time), then imports that. See `BuildAction.loadPluginViaGjsBundle`.
|
|
21
|
+
*/
|
|
22
|
+
export type LoadPluginModule = (resolvedPath: string, pluginName: string) => Promise<Record<string, unknown>>;
|
|
23
|
+
export interface ResolveUserPluginsOptions {
|
|
24
|
+
/**
|
|
25
|
+
* Override how a resolved plugin path is turned into its module
|
|
26
|
+
* namespace. Defaults to a direct dynamic `import()` (correct on Node).
|
|
27
|
+
*/
|
|
28
|
+
loadModule?: LoadPluginModule;
|
|
29
|
+
}
|
|
10
30
|
/** Type-guard: a `PluginByName` shape rather than a Rolldown plugin object. */
|
|
11
31
|
export declare function isPluginByName(value: unknown): value is PluginByName;
|
|
12
32
|
/**
|
|
@@ -18,4 +38,4 @@ export declare function isPluginByName(value: unknown): value is PluginByName;
|
|
|
18
38
|
* Throws when a name fails to resolve, when the chosen export is not a
|
|
19
39
|
* function, or when the factory returns nothing.
|
|
20
40
|
*/
|
|
21
|
-
export declare function resolveUserPlugins(plugins: ReadonlyArray<RolldownPluginOption | PluginByName>, projectDir: string): Promise<RolldownPluginOption[]>;
|
|
41
|
+
export declare function resolveUserPlugins(plugins: ReadonlyArray<RolldownPluginOption | PluginByName>, projectDir: string, options?: ResolveUserPluginsOptions): Promise<RolldownPluginOption[]>;
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
import { createRequire } from 'node:module';
|
|
16
16
|
import { join } from 'node:path';
|
|
17
17
|
import { pathToFileURL } from 'node:url';
|
|
18
|
+
/** Default loader: a plain dynamic import of the resolved file URL (Node path). */
|
|
19
|
+
const defaultLoadModule = async (resolvedPath) => (await import(pathToFileURL(resolvedPath).href));
|
|
18
20
|
/** Type-guard: a `PluginByName` shape rather than a Rolldown plugin object. */
|
|
19
21
|
export function isPluginByName(value) {
|
|
20
22
|
return (typeof value === 'object' &&
|
|
@@ -39,7 +41,8 @@ export function isPluginByName(value) {
|
|
|
39
41
|
* Throws when a name fails to resolve, when the chosen export is not a
|
|
40
42
|
* function, or when the factory returns nothing.
|
|
41
43
|
*/
|
|
42
|
-
export async function resolveUserPlugins(plugins, projectDir) {
|
|
44
|
+
export async function resolveUserPlugins(plugins, projectDir, options = {}) {
|
|
45
|
+
const loadModule = options.loadModule ?? defaultLoadModule;
|
|
43
46
|
const requireFromProject = createRequire(join(projectDir, 'package.json'));
|
|
44
47
|
const out = [];
|
|
45
48
|
for (const entry of plugins) {
|
|
@@ -56,7 +59,7 @@ export async function resolveUserPlugins(plugins, projectDir) {
|
|
|
56
59
|
`Add it to your project's dependencies, or pass a Plugin object directly. ` +
|
|
57
60
|
`(${err.message})`);
|
|
58
61
|
}
|
|
59
|
-
const mod = await
|
|
62
|
+
const mod = await loadModule(resolvedPath, entry.name);
|
|
60
63
|
const exportName = entry.export ?? 'default';
|
|
61
64
|
const factory = mod[exportName];
|
|
62
65
|
if (typeof factory !== 'function') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "CLI for Gjsify",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -120,19 +120,19 @@
|
|
|
120
120
|
"cli"
|
|
121
121
|
],
|
|
122
122
|
"dependencies": {
|
|
123
|
-
"@gjsify/buffer": "^0.
|
|
124
|
-
"@gjsify/create-app": "^0.
|
|
125
|
-
"@gjsify/node-globals": "^0.
|
|
126
|
-
"@gjsify/node-polyfills": "^0.
|
|
127
|
-
"@gjsify/npm-registry": "^0.
|
|
128
|
-
"@gjsify/resolve-npm": "^0.
|
|
129
|
-
"@gjsify/rolldown-plugin-gjsify": "^0.
|
|
130
|
-
"@gjsify/rolldown-plugin-pnp": "^0.
|
|
131
|
-
"@gjsify/semver": "^0.
|
|
132
|
-
"@gjsify/tar": "^0.
|
|
133
|
-
"@gjsify/tsc": "^0.
|
|
134
|
-
"@gjsify/web-polyfills": "^0.
|
|
135
|
-
"@gjsify/workspace": "^0.
|
|
123
|
+
"@gjsify/buffer": "^0.9.0",
|
|
124
|
+
"@gjsify/create-app": "^0.9.0",
|
|
125
|
+
"@gjsify/node-globals": "^0.9.0",
|
|
126
|
+
"@gjsify/node-polyfills": "^0.9.0",
|
|
127
|
+
"@gjsify/npm-registry": "^0.9.0",
|
|
128
|
+
"@gjsify/resolve-npm": "^0.9.0",
|
|
129
|
+
"@gjsify/rolldown-plugin-gjsify": "^0.9.0",
|
|
130
|
+
"@gjsify/rolldown-plugin-pnp": "^0.9.0",
|
|
131
|
+
"@gjsify/semver": "^0.9.0",
|
|
132
|
+
"@gjsify/tar": "^0.9.0",
|
|
133
|
+
"@gjsify/tsc": "^0.9.0",
|
|
134
|
+
"@gjsify/web-polyfills": "^0.9.0",
|
|
135
|
+
"@gjsify/workspace": "^0.9.0",
|
|
136
136
|
"cosmiconfig": "^9.0.2",
|
|
137
137
|
"get-tsconfig": "^4.14.0",
|
|
138
138
|
"pkg-types": "^2.3.1",
|
|
@@ -140,13 +140,13 @@
|
|
|
140
140
|
"yargs": "^18.0.0"
|
|
141
141
|
},
|
|
142
142
|
"devDependencies": {
|
|
143
|
-
"@gjsify/unit": "^0.
|
|
143
|
+
"@gjsify/unit": "^0.9.0",
|
|
144
144
|
"@types/yargs": "^17.0.35",
|
|
145
145
|
"typescript": "^6.0.3"
|
|
146
146
|
},
|
|
147
147
|
"peerDependencies": {
|
|
148
|
-
"@gjsify/oxfmt-native": "^0.
|
|
149
|
-
"@gjsify/rolldown-native": "^0.
|
|
148
|
+
"@gjsify/oxfmt-native": "^0.9.0",
|
|
149
|
+
"@gjsify/rolldown-native": "^0.9.0"
|
|
150
150
|
},
|
|
151
151
|
"peerDependenciesMeta": {
|
|
152
152
|
"@gjsify/oxfmt-native": {
|