@gjsify/rolldown-native 0.40.0 → 0.42.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/README.md +1 -1
- package/lib/esm/index.js +1 -1
- package/lib/esm/plugins.js +1 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/plugins.d.ts +23 -0
- package/package.json +6 -6
- package/src/rust/src/session.rs +18 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @gjsify/rolldown-native
|
|
2
2
|
|
|
3
|
-
A native Rust cdylib + Vala/GObject bridge that wraps the Rust `rolldown` bundler and exposes it to GJS via `gi://`. This is the default bundler engine used by `gjsify build` under GJS — npm's `rolldown`
|
|
3
|
+
A native Rust cdylib + Vala/GObject bridge that wraps the Rust `rolldown` bundler and exposes it to GJS via `gi://`. This is the default bundler engine used by `gjsify build` under GJS — npm's `rolldown` does not run under GJS — its JS entry evaluates `createRequire(import.meta.url)` at module scope and then synchronously requires `node:fs` / `node:child_process` for platform detection, which GJS refuses before any `.node` is ever opened — so this bridge is how gjsify bundles without a Node runtime. Includes a complete plugin bridge (`bundleWithPlugins`) for load, transform, resolveId, and render-chunk hooks. Ships prebuilt `.so` + `.typelib` for **Linux**; the source is now platform-neutral and cross-compiles for macOS, but no macOS prebuild is published yet — see [Platform coverage](#platform-coverage).
|
|
4
4
|
|
|
5
5
|
Part of the [gjsify](https://github.com/gjsify/gjsify) project — Node.js and Web APIs for GJS (GNOME JavaScript).
|
|
6
6
|
|
package/lib/esm/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./_virtual/_rolldown/runtime.js";import{bundleWithPlugins as e}from"./plugins.js";let
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{bundleWithPlugins as e,isResolveMiss as t}from"./plugins.js";let n=null,r=!1,i=null;function loadNativeRolldown(){if(r)return n;r=!0;try{let e=globalThis.imports?.gi;if(!e)return i=Error(`imports.gi not available — not running under GJS?`),null;let t=e.GjsifyRolldown;return t?(n=t,n):(i=Error(`GjsifyRolldown typelib not found on GI_TYPELIB_PATH`),null)}catch(e){return i=e instanceof Error?e:Error(String(e)),n=null,null}}function hasNativeRolldown(){return loadNativeRolldown()!==null}function getLoadError(){return i}function getGLib(){let e=globalThis.imports?.gi;if(!e||!e.GLib)throw Error(`@gjsify/rolldown-native: GLib not available (not running under GJS?)`);return e.GLib}function bundle(e){let t=loadNativeRolldown();if(!t)throw Error(`@gjsify/rolldown-native: prebuild not available (${i?.message??`unknown`})`);let n=JSON.stringify(e),r=getGLib().Bytes.new(new TextEncoder().encode(n)),a=new t.Bundler().bundle(r).get_data()??new Uint8Array;return JSON.parse(new TextDecoder().decode(a))}export{bundle,e as bundleWithPlugins,getLoadError,hasNativeRolldown,t as isResolveMiss,loadNativeRolldown};
|
package/lib/esm/plugins.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./_virtual/_rolldown/runtime.js";import{loadNativeRolldown as e}from"./index.js";function getGLib(){let e=globalThis.imports?.gi;if(!e||!e.GLib)throw Error(`@gjsify/rolldown-native: GLib not available (not running under GJS?)`);return e.GLib}const enc=e=>getGLib().Bytes.new(new TextEncoder().encode(e)),dec=e=>new TextDecoder().decode(e.get_data()??new Uint8Array),t=[`load`,`transform`,`resolveId`,`renderChunk`,`banner`,`footer`,`intro`,`outro`,`buildStart`,`buildEnd`,`generateBundle`,`writeBundle`,`closeBundle`];function pluginMeta(e){let n=t.filter(t=>typeof e[t]==`function`),r={name:e.name,hooks:n};return e.idFilter&&(r.idFilter=e.idFilter),r}const n=new Set;function bundleWithPlugins(t,r){let i=e();if(!i)return Promise.reject(Error(`@gjsify/rolldown-native: prebuild not available`));let a=new i.BundlerSession;n.add(a);let o=new Map;a.connect(`context_response`,(e,t,n)=>{let r=o.get(t);if(!r)return;o.delete(t);let i;try{i=JSON.parse(dec(n))}catch(e){r.reject(e instanceof Error?e:Error(String(e)));return}i.error?r.reject(Error(i.error)):i.id?r.resolve({id:i.id,external:i.external??!1}):r.resolve(null)});function makeContext(e){return{resolve(t,n,r){return new Promise((i,s)=>{let c=a.context_resolve(e,enc(JSON.stringify({specifier:t,importer:n,skipSelf:r?.skipSelf??!0,isEntry:r?.isEntry??!1})));if(c===0){s(Error(`@gjsify/rolldown-native: ctx.resolve('${t}') failed — parent req_id ${e} unknown`));return}o.set(c,{resolve:i,reject:s})})},warn(e){a.context_warn(enc(e))},error(e){throw Error(e)}}}a.connect(`hook_requested`,(e,t,n,r,i)=>{dispatchHook(t,n,r,i)});async function dispatchHook(e,t,n,i){let o;try{o=JSON.parse(dec(i))}catch(e){respondError(t,e);return}let s=r[n];if(!s){respondError(t,Error(`@gjsify/rolldown-native: unknown plugin index ${n}`));return}let c=makeContext(t);try{if(e===`transform`&&s.transform){let e=o,n=a.take_request_payload(t);if(n===null)throw Error(`@gjsify/rolldown-native: transform request for ${e.id} missing payload bytes`);let r=dec(n);respondTransform(t,await s.transform.call(c,r,e.id,e.moduleType));return}respondOk(t,await runHook(s,e,o,c))}catch(e){respondError(t,e)}}function respondOk(e,t){let n=t==null?{kind:`skip`}:{kind:`ok`,value:t};a.respond(e,enc(JSON.stringify(n)))}function respondTransform(e,t){if(t==null){a.respond(e,enc(JSON.stringify({kind:`skip`})));return}let n,r;if(typeof t==`string`)n=t;else if(typeof t==`object`){let e=t;n=e.code,r=e.moduleType}if(typeof n!=`string`){a.respond(e,enc(JSON.stringify({kind:`skip`})));return}let i=getGLib().Bytes.new(new TextEncoder().encode(n));if(!a.set_response_payload(e,i)){respondError(e,Error(`@gjsify/rolldown-native: failed to stash transform response payload for reqId ${e}`));return}a.respond(e,enc(JSON.stringify({kind:`ok`,value:r===void 0?{hasCodeBytes:!0}:{hasCodeBytes:!0,moduleType:r}})))}function respondError(e,t){let n=t instanceof Error?t:Error(String(t));a.respond(e,enc(JSON.stringify({kind:`error`,message:n.message,stack:n.stack})))}let closeSession=()=>{n.delete(a);let e=a;try{e.close?.()}catch{}};return new Promise((e,n)=>{a.connect(`completed`,(t,r)=>{try{e(JSON.parse(dec(r)))}catch(e){n(e instanceof Error?e:Error(String(e)))}}),a.connect(`error_occurred`,(e,t)=>{n(Error(t))});try{a.start(enc(JSON.stringify({options:t,plugins:r.map(pluginMeta)})))}catch(e){n(e instanceof Error?e:Error(String(e)))}}).finally(closeSession)}async function runHook(e,t,n,r){switch(t){case`load`:return e.load?normalizeLoadResult(await e.load.call(r,n.id)):null;case`transform`:return null;case`resolveId`:{if(!e.resolveId)return null;let t=n;return normalizeResolveIdResult(await e.resolveId.call(r,t.specifier,t.importer,{isEntry:t.isEntry}))}case`renderChunk`:{if(!e.renderChunk)return null;let t=n;return normalizeRenderChunkResult(await e.renderChunk.call(r,t.code,{fileName:t.fileName,name:t.name,isEntry:t.isEntry}))}case`banner`:case`footer`:case`intro`:case`outro`:{let i=e[t];if(!i)return null;let a=n;return normalizeAddonResult(await i.call(r,{fileName:a.fileName,name:a.name,isEntry:a.isEntry}))}case`buildStart`:case`buildEnd`:case`generateBundle`:case`writeBundle`:case`closeBundle`:{let n=e[t];return n&&await n.call(r),null}default:throw Error(`@gjsify/rolldown-native: unknown hook '${t}'`)}}function normalizeLoadResult(e){return e==null?null:typeof e==`string`?{code:e}:{code:e.code,moduleType:e.moduleType}}function normalizeResolveIdResult(e){return e==null?null:typeof e==`string`?{id:e}:{id:e.id,external:e.external}}function normalizeRenderChunkResult(e){return e==null?null:typeof e==`string`?{code:e}:{code:e.code}}function normalizeAddonResult(e){return e==null?null:typeof e==`string`?e:e.text}export{bundleWithPlugins};
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{loadNativeRolldown as e}from"./index.js";function getGLib(){let e=globalThis.imports?.gi;if(!e||!e.GLib)throw Error(`@gjsify/rolldown-native: GLib not available (not running under GJS?)`);return e.GLib}const enc=e=>getGLib().Bytes.new(new TextEncoder().encode(e)),dec=e=>new TextDecoder().decode(e.get_data()??new Uint8Array),t=[`load`,`transform`,`resolveId`,`renderChunk`,`banner`,`footer`,`intro`,`outro`,`buildStart`,`buildEnd`,`generateBundle`,`writeBundle`,`closeBundle`];function pluginMeta(e){let n=t.filter(t=>typeof e[t]==`function`),r={name:e.name,hooks:n};return e.idFilter&&(r.idFilter=e.idFilter),r}const n=new Set;function isResolveMiss(e){return e.startsWith(`NotFound(`)}function bundleWithPlugins(t,r){let i=e();if(!i)return Promise.reject(Error(`@gjsify/rolldown-native: prebuild not available`));let a=new i.BundlerSession;n.add(a);let o=new Map;a.connect(`context_response`,(e,t,n)=>{let r=o.get(t);if(!r)return;o.delete(t);let i;try{i=JSON.parse(dec(n))}catch(e){r.reject(e instanceof Error?e:Error(String(e)));return}i.error&&isResolveMiss(i.error)?r.resolve(null):i.error?r.reject(Error(i.error)):i.id?r.resolve({id:i.id,external:i.external??!1}):r.resolve(null)});function makeContext(e){return{resolve(t,n,r){return new Promise((i,s)=>{let c=a.context_resolve(e,enc(JSON.stringify({specifier:t,importer:n,skipSelf:r?.skipSelf??!0,isEntry:r?.isEntry??!1})));if(c===0){s(Error(`@gjsify/rolldown-native: ctx.resolve('${t}') failed — parent req_id ${e} unknown`));return}o.set(c,{resolve:i,reject:s})})},warn(e){a.context_warn(enc(e))},error(e){throw Error(e)}}}a.connect(`hook_requested`,(e,t,n,r,i)=>{dispatchHook(t,n,r,i)});async function dispatchHook(e,t,n,i){let o;try{o=JSON.parse(dec(i))}catch(e){respondError(t,e);return}let s=r[n];if(!s){respondError(t,Error(`@gjsify/rolldown-native: unknown plugin index ${n}`));return}let c=makeContext(t);try{if(e===`transform`&&s.transform){let e=o,n=a.take_request_payload(t);if(n===null)throw Error(`@gjsify/rolldown-native: transform request for ${e.id} missing payload bytes`);let r=dec(n);respondTransform(t,await s.transform.call(c,r,e.id,e.moduleType));return}respondOk(t,await runHook(s,e,o,c))}catch(e){respondError(t,e)}}function respondOk(e,t){let n=t==null?{kind:`skip`}:{kind:`ok`,value:t};a.respond(e,enc(JSON.stringify(n)))}function respondTransform(e,t){if(t==null){a.respond(e,enc(JSON.stringify({kind:`skip`})));return}let n,r;if(typeof t==`string`)n=t;else if(typeof t==`object`){let e=t;n=e.code,r=e.moduleType}if(typeof n!=`string`){a.respond(e,enc(JSON.stringify({kind:`skip`})));return}let i=getGLib().Bytes.new(new TextEncoder().encode(n));if(!a.set_response_payload(e,i)){respondError(e,Error(`@gjsify/rolldown-native: failed to stash transform response payload for reqId ${e}`));return}a.respond(e,enc(JSON.stringify({kind:`ok`,value:r===void 0?{hasCodeBytes:!0}:{hasCodeBytes:!0,moduleType:r}})))}function respondError(e,t){let n=t instanceof Error?t:Error(String(t));a.respond(e,enc(JSON.stringify({kind:`error`,message:n.message,stack:n.stack})))}let closeSession=()=>{n.delete(a);let e=a;try{e.close?.()}catch{}};return new Promise((e,n)=>{a.connect(`completed`,(t,r)=>{try{e(JSON.parse(dec(r)))}catch(e){n(e instanceof Error?e:Error(String(e)))}}),a.connect(`error_occurred`,(e,t)=>{n(Error(t))});try{a.start(enc(JSON.stringify({options:t,plugins:r.map(pluginMeta)})))}catch(e){n(e instanceof Error?e:Error(String(e)))}}).finally(closeSession)}async function runHook(e,t,n,r){switch(t){case`load`:return e.load?normalizeLoadResult(await e.load.call(r,n.id)):null;case`transform`:return null;case`resolveId`:{if(!e.resolveId)return null;let t=n;return normalizeResolveIdResult(await e.resolveId.call(r,t.specifier,t.importer,{isEntry:t.isEntry}))}case`renderChunk`:{if(!e.renderChunk)return null;let t=n;return normalizeRenderChunkResult(await e.renderChunk.call(r,t.code,{fileName:t.fileName,name:t.name,isEntry:t.isEntry}))}case`banner`:case`footer`:case`intro`:case`outro`:{let i=e[t];if(!i)return null;let a=n;return normalizeAddonResult(await i.call(r,{fileName:a.fileName,name:a.name,isEntry:a.isEntry}))}case`buildStart`:case`buildEnd`:case`generateBundle`:case`writeBundle`:case`closeBundle`:{let n=e[t];return n&&await n.call(r),null}default:throw Error(`@gjsify/rolldown-native: unknown hook '${t}'`)}}function normalizeLoadResult(e){return e==null?null:typeof e==`string`?{code:e}:{code:e.code,moduleType:e.moduleType}}function normalizeResolveIdResult(e){return e==null?null:typeof e==`string`?{id:e}:{id:e.id,external:e.external}}function normalizeRenderChunkResult(e){return e==null?null:typeof e==`string`?{code:e}:{code:e.code}}function normalizeAddonResult(e){return e==null?null:typeof e==`string`?e:e.text}export{bundleWithPlugins,isResolveMiss};
|
package/lib/types/index.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export interface BundleResult {
|
|
|
50
50
|
warnings: string[];
|
|
51
51
|
output: BundleOutputItem[];
|
|
52
52
|
}
|
|
53
|
-
export { bundleWithPlugins, type NativePlugin, type NativePluginContext, type PluginIdFilter, type LoadHookHandler, type TransformHookHandler, type ResolveIdHookHandler, type RenderChunkHookHandler, type AddonHookHandler, type LifecycleHookHandler, } from './plugins.js';
|
|
53
|
+
export { bundleWithPlugins, isResolveMiss, type NativePlugin, type NativePluginContext, type PluginIdFilter, type LoadHookHandler, type TransformHookHandler, type ResolveIdHookHandler, type RenderChunkHookHandler, type AddonHookHandler, type LifecycleHookHandler, } from './plugins.js';
|
|
54
54
|
/**
|
|
55
55
|
* Convenience facade: serialize options to JSON, call the native
|
|
56
56
|
* bundler, parse the JSON output. Throws if the prebuild is unavailable
|
package/lib/types/plugins.d.ts
CHANGED
|
@@ -97,4 +97,27 @@ export interface NativePlugin {
|
|
|
97
97
|
* Throws if the prebuild isn't loadable. Rejects the returned Promise
|
|
98
98
|
* if the underlying build fails.
|
|
99
99
|
*/
|
|
100
|
+
/**
|
|
101
|
+
* Does a `context_resolve` error string mean "nothing resolved" rather than "the
|
|
102
|
+
* resolve itself failed"?
|
|
103
|
+
*
|
|
104
|
+
* THE TWO ENGINES HAVE TO AGREE, and this is where ours was wrong. `ctx.resolve`
|
|
105
|
+
* is declared (`NativePluginContext.resolve`) to return `null` when the specifier
|
|
106
|
+
* doesn't resolve, because that is what npm `rolldown` does and what every caller
|
|
107
|
+
* in `@gjsify/rolldown-plugin-gjsify` assumes — nine of them, `alias.ts`,
|
|
108
|
+
* `platform-resolve.ts`, `entry-wrapper.ts` and five in `napi-node-addon.ts`,
|
|
109
|
+
* none with a catch, one of them a bare presence probe
|
|
110
|
+
* (`Boolean(await ctx.resolve(…))`). The Rust side maps EVERY `ResolveError`
|
|
111
|
+
* through `format!("{resolve_err:?}")` into `error`, so a plain miss arrived as a
|
|
112
|
+
* REJECTION and those call sites threw instead of taking their not-found branch.
|
|
113
|
+
* Measured on gjs 1.88.1 with the real prebuild: an unresolvable bare specifier
|
|
114
|
+
* rejected with `NotFound("@gjsify/definitely-not-there")`.
|
|
115
|
+
*
|
|
116
|
+
* Only `NotFound` is a miss. `InvalidPackageConfig`, `PackagePathNotExported`,
|
|
117
|
+
* `IOError`, a `BuildError` from another plugin's hook, and the two bridge-internal
|
|
118
|
+
* faults below (malformed payload, unknown parent req_id) are all real failures and
|
|
119
|
+
* keep rejecting — turning those into `null` is how a bridge bug ends up reported
|
|
120
|
+
* as the user's missing dependency.
|
|
121
|
+
*/
|
|
122
|
+
export declare function isResolveMiss(error: string): boolean;
|
|
100
123
|
export declare function bundleWithPlugins(options: BundleOptions, plugins: NativePlugin[]): Promise<BundleResult>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/rolldown-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.42.0",
|
|
4
4
|
"description": "Phase D-2 POC: Vala/GObject + Rust cdylib bridge to rolldown for GJS. Wraps `rolldown::Bundler::generate()` (async, tokio) in a per-call current-thread runtime so JS sees a sync `bundle()` call. JSON-encoded BundlerOptions in, JSON-encoded BundleOutput out — the boundary stays small even though rolldown's option surface is enormous. POC scope: no JS plugins (Phase B), no watch/HMR, no incremental builds. Companion to `@gjsify/lightningcss-native`; together they remove the last two Rust-crate runtime blockers from the gjsify build pipeline (Phase D-3 unblock).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/esm/index.js",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"@girs/gobject-2.0": "^4.1.0"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@gjsify/cli": "^0.
|
|
61
|
+
"@gjsify/cli": "^0.42.0",
|
|
62
62
|
"@types/node": "^25.9.2",
|
|
63
63
|
"typescript": "^6.0.3"
|
|
64
64
|
},
|
|
@@ -74,9 +74,9 @@
|
|
|
74
74
|
},
|
|
75
75
|
"homepage": "https://github.com/gjsify/gjsify/tree/main/packages/infra/rolldown-native#readme",
|
|
76
76
|
"optionalDependencies": {
|
|
77
|
-
"@gjsify/rolldown-native-darwin-arm64": "0.
|
|
78
|
-
"@gjsify/rolldown-native-darwin-x64": "0.
|
|
79
|
-
"@gjsify/rolldown-native-linux-arm64": "0.
|
|
80
|
-
"@gjsify/rolldown-native-linux-x64": "0.
|
|
77
|
+
"@gjsify/rolldown-native-darwin-arm64": "0.42.0",
|
|
78
|
+
"@gjsify/rolldown-native-darwin-x64": "0.42.0",
|
|
79
|
+
"@gjsify/rolldown-native-linux-arm64": "0.42.0",
|
|
80
|
+
"@gjsify/rolldown-native-linux-x64": "0.42.0"
|
|
81
81
|
}
|
|
82
82
|
}
|
package/src/rust/src/session.rs
CHANGED
|
@@ -651,11 +651,28 @@ pub extern "C" fn gjsify_rolldown_session_context_resolve(
|
|
|
651
651
|
external: None,
|
|
652
652
|
error: Some(format!("{resolve_err:?}")),
|
|
653
653
|
},
|
|
654
|
+
// `{e:#}`, NOT `{e}`. This arm carries an `anyhow::Error`, whose
|
|
655
|
+
// OUTERMOST message is never the interesting part — the cause chain
|
|
656
|
+
// underneath is, and that is where a user plugin's own error text
|
|
657
|
+
// lives. `{e}` printed the outer line alone, so a plugin that threw
|
|
658
|
+
// during a NESTED `this.resolve()` reached JS as a bare
|
|
659
|
+
// "plugin `gjsify-alias` threw an error" with the diagnostic that
|
|
660
|
+
// names the specifier, the substitution target and the importer
|
|
661
|
+
// discarded. The `Ok(Err(resolve_err))` arm above has always printed
|
|
662
|
+
// its full form; the two simply disagreed, and this is the arm a
|
|
663
|
+
// plugin failure takes.
|
|
664
|
+
//
|
|
665
|
+
// The alternate form reaches `isResolveMiss` in `../../ts/plugins.ts`,
|
|
666
|
+
// which folds a `context_resolve` error into "nothing there" only when
|
|
667
|
+
// it starts with `NotFound(`. A chain from THIS arm opens with the
|
|
668
|
+
// outer bundler/plugin message, never that — pinned by
|
|
669
|
+
// `@gjsify/cli`'s `bundler-pick.spec.ts`, so the two cannot drift into
|
|
670
|
+
// reporting a real plugin failure as the user's missing dependency.
|
|
654
671
|
Err(e) => ContextResolveResponse {
|
|
655
672
|
child_id,
|
|
656
673
|
id: None,
|
|
657
674
|
external: None,
|
|
658
|
-
error: Some(format!("{e}")),
|
|
675
|
+
error: Some(format!("{e:#}")),
|
|
659
676
|
},
|
|
660
677
|
};
|
|
661
678
|
let _ = shared.context_response_tx.send(resp);
|