@gjsify/rolldown-plugin-gjsify 0.38.0 → 0.39.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/lib/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export * from './library/index.js';
5
5
  export { REWRITE_FILTER, extractPackageSpec, getBundleDirFromOutput, rewriteContents, shouldRewrite, nodeModulesPathRewritePlugin, } from './plugins/rewrite-node-modules-paths.js';
6
6
  export type { NodeModulesPathRewriteOptions, RewriteResult } from './plugins/rewrite-node-modules-paths.js';
7
7
  export { processStubPlugin, GJS_PROCESS_STUB, composeBanner } from './plugins/process-stub.js';
8
+ export { giRuntimePathsStub } from './plugins/gi-runtime-paths.js';
8
9
  export type { ProcessStubPluginOptions } from './plugins/process-stub.js';
9
10
  export { cssAsStringPlugin } from './plugins/css-as-string.js';
10
11
  export { textLoaderPlugin } from './plugins/text-loader.js';
package/lib/index.js CHANGED
@@ -5,6 +5,7 @@ export * from './app/index.js';
5
5
  export * from './library/index.js';
6
6
  export { REWRITE_FILTER, extractPackageSpec, getBundleDirFromOutput, rewriteContents, shouldRewrite, nodeModulesPathRewritePlugin, } from './plugins/rewrite-node-modules-paths.js';
7
7
  export { processStubPlugin, GJS_PROCESS_STUB, composeBanner } from './plugins/process-stub.js';
8
+ export { giRuntimePathsStub } from './plugins/gi-runtime-paths.js';
8
9
  export { cssAsStringPlugin } from './plugins/css-as-string.js';
9
10
  export { textLoaderPlugin } from './plugins/text-loader.js';
10
11
  export { shebangPlugin, GJS_SHEBANG, NODE_SHEBANG, expandEnvTemplate, resolveShebangLine } from './plugins/shebang.js';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The byte-1 prologue that prepends `dirs` to the default repository's typelib
3
+ * search path and library path. Empty list → `''`: a no-op prologue in every
4
+ * bundle is a guard that can never fire, and a banner that is not there cannot
5
+ * drift.
6
+ *
7
+ * @param dirs directories RELATIVE to the program's own directory, `/`-separated —
8
+ * an absolute path baked at build time is the BUILD machine's
9
+ */
10
+ export declare function giRuntimePathsStub(dirs: readonly string[]): string;
@@ -0,0 +1,37 @@
1
+ // Tell GI where a bundle's own typelibs and their backing libraries are — from
2
+ // INSIDE the process, so a `--app gjs` bundle needs no launcher and no environment.
3
+ // Why it exists, what it does NOT cover (a dylib reached through another dylib's
4
+ // link closure never passes through GI, so only `@rpath`/`$ORIGIN` reaches it) and
5
+ // the macOS measurements behind it: `status/open-todos.md` § "A globally installed
6
+ // GJS launcher still cannot load a system GTK on macOS", ADR 0023 § 4.
7
+ /**
8
+ * The byte-1 prologue that prepends `dirs` to the default repository's typelib
9
+ * search path and library path. Empty list → `''`: a no-op prologue in every
10
+ * bundle is a guard that can never fire, and a banner that is not there cannot
11
+ * drift.
12
+ *
13
+ * @param dirs directories RELATIVE to the program's own directory, `/`-separated —
14
+ * an absolute path baked at build time is the BUILD machine's
15
+ */
16
+ export function giRuntimePathsStub(dirs) {
17
+ if (dirs.length === 0)
18
+ return '';
19
+ const list = dirs.map((d) => JSON.stringify(d)).join(',');
20
+ // Four rules, each pinned with its incident by `gi-runtime-paths-banner.spec.ts`:
21
+ // every ambient global through `globalThis.` (a bare `imports` at byte 1 binds to
22
+ // a bundled module's own top-level `const imports`, still in its TDZ); guarded
23
+ // probes, not try/catch (no call here has a throw path, so a catch would hide a
24
+ // real absence); one line (the banner precedes source-map-aware machinery); both
25
+ // prepends (search path alone finds the typelib, then fails the dlopen).
26
+ return (`(function(){var i=globalThis.imports;if(!i||!i.gi||!i.system)return;` +
27
+ `var R=i.gi.GIRepository;if(!R||!R.Repository||!R.Repository.dup_default)return;` +
28
+ `var r=R.Repository.dup_default();if(!r||!r.prepend_search_path||!r.prepend_library_path)return;` +
29
+ `var p=i.system.programPath||i.system.programInvocationName||'';` +
30
+ // Both separators: the program path is the HOST's, and on win32 a `/`-only
31
+ // cut matches nothing (same rule as `lastPathSeparatorIndex`, #1143).
32
+ `var m=/^(.*)[\\/\\\\][^\\/\\\\]*$/.exec(p);if(!m)return;` +
33
+ // Absolute stays absolute: joining a system libdir under the program dir names nothing.
34
+ `var b=m[1];for(var d of [${list}]){var f=/^([A-Za-z]:[\\/\\\\]|[\\/\\\\])/.test(d)?d:b+'/'+d;` +
35
+ `r.prepend_search_path(f);r.prepend_library_path(f)}` +
36
+ `})();`);
37
+ }
@@ -1,26 +1,12 @@
1
1
  import type { Plugin } from 'rolldown';
2
2
  export declare const GJS_PROCESS_STUB: string;
3
3
  /**
4
- * Compose the GJS process stub with the user-supplied banner so the result
5
- * is valid syntax for `gjs -m`. A leading `#!shebang` line in the user
6
- * banner is hoisted to byte 0 of the output. Any `#` character that appears
7
- * anywhere except byte 0 is a fatal SyntaxError under SpiderMonkey 128+ —
8
- * putting our process stub before the user's shebang would break the bundle.
9
- *
10
- * Output shape:
11
- * [#!shebang\n][<process-stub>\n<rest-of-user-banner>]
12
- *
13
- * Either side of the bracket may be empty; the result is always concatenated
14
- * without leading whitespace.
4
+ * Compose the GJS process stub with the user-supplied banner so the result is
5
+ * valid syntax for `gjs -m`, hoisting a leading `#!shebang` to byte 0: a `#`
6
+ * anywhere except byte 0 is a fatal SyntaxError under SpiderMonkey 128+, so
7
+ * putting the stub before the user's shebang would break the bundle.
15
8
  */
16
9
  export declare function composeBanner(stub: string, userBanner: string): string;
17
- /**
18
- * Build a Rolldown plugin that injects the GJS process stub as a chunk
19
- * banner. Runs with `enforce: 'post'`-equivalent ordering so the stub
20
- * lands *after* any user `output.banner` value, except when the user
21
- * banner starts with a `#!shebang` line — which is hoisted to byte 0
22
- * by `composeBanner`.
23
- */
24
10
  export interface ProcessStubPluginOptions {
25
11
  /** User-supplied banner string. May contain a leading `#!shebang`. */
26
12
  userBanner?: string;
@@ -29,5 +15,14 @@ export interface ProcessStubPluginOptions {
29
15
  * ESM-only — set by the orchestrator when `format === 'esm'`.
30
16
  */
31
17
  captureBundleUrl?: boolean;
18
+ /**
19
+ * Directories, RELATIVE to the program's own directory, holding typelibs and
20
+ * their backing libraries. Emitted as a byte-1 prologue that hands them to GI at
21
+ * runtime, so the bundle needs no `GI_TYPELIB_PATH` / loader-path environment and
22
+ * therefore no launcher (see `gi-runtime-paths.ts` for the measurement).
23
+ * Empty or unset emits nothing.
24
+ */
25
+ giRuntimePaths?: readonly string[];
32
26
  }
27
+ /** Inject the byte-1 banner as a chunk banner, after any user `output.banner`. */
33
28
  export declare function processStubPlugin(options?: ProcessStubPluginOptions): Plugin;
@@ -1,5 +1,6 @@
1
1
  import { BUNDLE_URL_BANNER } from './bundle-url-banner.js';
2
2
  import { GJS_WELLKNOWN_SYMBOLS_STUB } from './wellknown-symbols-banner.js';
3
+ import { giRuntimePathsStub } from './gi-runtime-paths.js';
3
4
  // Every GJS ambient global is reached through `globalThis.` — NEVER as a bare
4
5
  // identifier. The banner shares the module's top-level scope with the bundled
5
6
  // code, so a bare `imports` binds to any top-level `const imports` a bundled
@@ -7,29 +8,22 @@ import { GJS_WELLKNOWN_SYMBOLS_STUB } from './wellknown-symbols-banner.js';
7
8
  // in its temporal dead zone: `ReferenceError: can't access lexical declaration
8
9
  // 'imports' before initialization`, thrown at load, before a single line of
9
10
  // program code runs. `@girs/gjs`'s `gjs.js` declares exactly that
10
- // (`const imports = globalThis.imports || {}`), so the failure is real and
11
- // arrives whenever tree-shaking happens to retain that module. It was found
12
- // via `@gjsify/web-streams`, whose GJS test leg died the moment `@gjsify/utils`
13
- // became more shakeable — but nothing about it is specific to either package.
11
+ // (`const imports = globalThis.imports || {}`), so the failure arrives whenever
12
+ // tree-shaking happens to retain that module.
14
13
  export const GJS_PROCESS_STUB = 'if(typeof globalThis.process==="undefined"){' +
15
14
  'const _s=globalThis.imports.system,_G=globalThis.imports.gi.GLib;' +
16
- // process.hrtime needs a `.bigint` property attached to the function
17
- // itself (Node API shape: `process.hrtime.bigint()` — used by execa,
18
- // perf-tracking libs, …). Build it as a named local so we can
19
- // attach the property before stashing it on the stub object.
15
+ // `process.hrtime.bigint()` is the shape execa and perf-tracking libs reach
16
+ // for, so `.bigint` has to hang off the function itself.
20
17
  'const _h=t=>t?[0,0]:[0,0];_h.bigint=()=>0n;' +
21
- // `platform`/`arch`, answered LAZILY by the same `uname -sm` probe
22
- // `@gjsify/process` uses (`packages/node/process/src/internal/uname.ts`),
23
- // with `@gjsify/utils`' `platform-names.ts` mapping tables inlined minimally
24
- // — a banner runs before the module system exists and cannot import the
25
- // canonical ones. Those two are the source of truth; keep this in step or a
26
- // bundle answers differently depending on whether it pulled
27
- // `@gjsify/process` in. These used to be the literals `"linux"`/`"x64"`,
28
- // which is a WRONG answer on two of three OSes rather than a missing one.
29
- // Lazy, so the cost falls only on a bundle that reads the field AND never
30
- // loads `@gjsify/process` (whose register replaces this object): nothing is
31
- // spawned at load. Windows is answered from the environment — `uname` is not
32
- // on a native Windows PATH, and the env answer is exact.
18
+ // `platform`/`arch` from the same `uname -sm` probe `@gjsify/process` uses
19
+ // (`packages/node/process/src/internal/uname.ts`) plus `@gjsify/utils`'
20
+ // `platform-names.ts` tables, inlined minimally because a banner runs before
21
+ // the module system exists. Those two are the source of truth; keep this in
22
+ // step or a bundle answers differently depending on whether it pulled
23
+ // `@gjsify/process` in. These used to be the literals `"linux"`/`"x64"` — a
24
+ // WRONG answer on two of three OSes rather than a missing one. Lazy, so
25
+ // nothing is spawned at load. Windows is answered from the environment:
26
+ // `uname` is not on a native Windows PATH, and the env answer is exact.
33
27
  'let _pc;const _p=()=>{if(_pc)return _pc;_pc={platform:"linux",arch:"x64"};' +
34
28
  'try{' +
35
29
  'if(_G.getenv("OS")==="Windows_NT"||_G.getenv("SystemRoot")){' +
@@ -65,17 +59,10 @@ export const GJS_PROCESS_STUB = 'if(typeof globalThis.process==="undefined"){' +
65
59
  '};' +
66
60
  '}';
67
61
  /**
68
- * Compose the GJS process stub with the user-supplied banner so the result
69
- * is valid syntax for `gjs -m`. A leading `#!shebang` line in the user
70
- * banner is hoisted to byte 0 of the output. Any `#` character that appears
71
- * anywhere except byte 0 is a fatal SyntaxError under SpiderMonkey 128+ —
72
- * putting our process stub before the user's shebang would break the bundle.
73
- *
74
- * Output shape:
75
- * [#!shebang\n][<process-stub>\n<rest-of-user-banner>]
76
- *
77
- * Either side of the bracket may be empty; the result is always concatenated
78
- * without leading whitespace.
62
+ * Compose the GJS process stub with the user-supplied banner so the result is
63
+ * valid syntax for `gjs -m`, hoisting a leading `#!shebang` to byte 0: a `#`
64
+ * anywhere except byte 0 is a fatal SyntaxError under SpiderMonkey 128+, so
65
+ * putting the stub before the user's shebang would break the bundle.
79
66
  */
80
67
  export function composeBanner(stub, userBanner) {
81
68
  if (!userBanner)
@@ -88,13 +75,17 @@ export function composeBanner(stub, userBanner) {
88
75
  const rest = userBanner.slice(shebang.length);
89
76
  return shebang + stub + (rest ? '\n' + rest : '');
90
77
  }
78
+ /** Inject the byte-1 banner as a chunk banner, after any user `output.banner`. */
91
79
  export function processStubPlugin(options = {}) {
92
- // The anchor capture must precede the process stub so it runs at the very
93
- // top of the chunk (where `import.meta.url` is the bundle's own URL). The
94
- // well-known-symbols polyfill must also run before any module init, so it
95
- // joins the byte-1 banner. All three pieces are single-line (no source-map
96
- // drift) and idempotent.
97
- const stub = (options.captureBundleUrl ? BUNDLE_URL_BANNER : '') + GJS_WELLKNOWN_SYMBOLS_STUB + GJS_PROCESS_STUB;
80
+ // Order is load-bearing: the anchor capture first, because `import.meta.url`
81
+ // is the bundle's own URL only at the very top of the chunk; the GI path
82
+ // prologue last, because it reads `imports.system`, which the process stub
83
+ // neither provides nor disturbs. Everything here runs before module init and
84
+ // is single-line and idempotent.
85
+ const stub = (options.captureBundleUrl ? BUNDLE_URL_BANNER : '') +
86
+ GJS_WELLKNOWN_SYMBOLS_STUB +
87
+ GJS_PROCESS_STUB +
88
+ giRuntimePathsStub(options.giRuntimePaths ?? []);
98
89
  const banner = composeBanner(stub, options.userBanner ?? '');
99
90
  return {
100
91
  name: 'gjsify-process-stub',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/rolldown-plugin-gjsify",
3
- "version": "0.38.0",
3
+ "version": "0.39.0",
4
4
  "description": "Rolldown / Rollup / Vite plugin orchestrator for GJS, Node, and Browser targets",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -55,11 +55,11 @@
55
55
  ],
56
56
  "license": "MIT",
57
57
  "dependencies": {
58
- "@gjsify/console": "^0.38.0",
59
- "@gjsify/resolve-npm": "^0.38.0",
60
- "@gjsify/rolldown-plugin-deepkit": "^0.38.0",
61
- "@gjsify/rolldown-plugin-pnp": "^0.38.0",
62
- "@gjsify/vite-plugin-blueprint": "^0.38.0",
58
+ "@gjsify/console": "^0.39.0",
59
+ "@gjsify/resolve-npm": "^0.39.0",
60
+ "@gjsify/rolldown-plugin-deepkit": "^0.39.0",
61
+ "@gjsify/rolldown-plugin-pnp": "^0.39.0",
62
+ "@gjsify/vite-plugin-blueprint": "^0.39.0",
63
63
  "@rollup/pluginutils": "^5.4.0",
64
64
  "acorn": "^8.17.0",
65
65
  "acorn-walk": "^8.3.5",
@@ -68,7 +68,7 @@
68
68
  "sass": "^1.101.0"
69
69
  },
70
70
  "peerDependencies": {
71
- "@gjsify/lightningcss-native": "^0.38.0",
71
+ "@gjsify/lightningcss-native": "^0.39.0",
72
72
  "rolldown": "^1.1.4"
73
73
  },
74
74
  "peerDependenciesMeta": {