@gjsify/rolldown-plugin-gjsify 0.3.14 → 0.3.15

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/app/gjs.d.ts CHANGED
@@ -19,8 +19,13 @@ export interface GjsFactoryInput {
19
19
  userBanner?: string;
20
20
  /** User-supplied resolve.alias overrides. */
21
21
  userAliases?: Record<string, string>;
22
- /** Whether to prepend the `#!/usr/bin/env -S gjs -m` shebang. */
23
- shebang?: boolean;
22
+ /**
23
+ * Shebang to prepend to the output bundle.
24
+ * `true` → default `#!/usr/bin/env -S gjs -m`
25
+ * `false` → no shebang
26
+ * `"…"` → custom line, supports `${env:NAME[:-default]}` placeholders
27
+ */
28
+ shebang?: boolean | string;
24
29
  /** Plugin options forwarded to sub-plugins (deepkit, css, …). */
25
30
  pluginOptions: PluginOptions;
26
31
  }
package/lib/app/gjs.js CHANGED
@@ -2,7 +2,7 @@
2
2
  //
3
3
  // Mirrors the esbuild predecessor's `setupForGjs` exactly in terms of the
4
4
  // effective build behaviour: same externals, same alias map, same target
5
- // (firefox128 for JS, firefox60 for CSS), same console-shim injection,
5
+ // (firefox140 for JS, firefox60 for CSS), same console-shim injection,
6
6
  // same process-stub banner, same `random-access-file` fs-backed-fallback.
7
7
  //
8
8
  // Returns a partial `RolldownOptions` template plus the plugin array the
@@ -18,7 +18,7 @@ import { globToEntryPoints } from '../utils/entry-points.js';
18
18
  import { nodeModulesPathRewritePlugin, getBundleDirFromOutput, } from '../plugins/rewrite-node-modules-paths.js';
19
19
  import { processStubPlugin } from '../plugins/process-stub.js';
20
20
  import { cssAsStringPlugin } from '../plugins/css-as-string.js';
21
- import { shebangPlugin, GJS_SHEBANG } from '../plugins/shebang.js';
21
+ import { shebangPlugin, resolveShebangLine } from '../plugins/shebang.js';
22
22
  const _shimDir = dirname(fileURLToPath(import.meta.url));
23
23
  export const setupForGjs = async (input) => {
24
24
  const userExternal = input.userExternal ?? [];
@@ -75,8 +75,8 @@ export const setupForGjs = async (input) => {
75
75
  conditionNames: format === 'esm' ? ['browser', 'import'] : ['browser', 'require', 'import'],
76
76
  },
77
77
  transform: {
78
- // Compile target: GJS 1.86 / SpiderMonkey 128firefox128.
79
- target: 'firefox128',
78
+ // Compile target: GJS 1.86 / SpiderMonkey 140firefox140.
79
+ target: 'firefox140',
80
80
  define: {
81
81
  global: 'globalThis',
82
82
  window: 'globalThis',
@@ -125,7 +125,12 @@ export const setupForGjs = async (input) => {
125
125
  cssAsStringPlugin(),
126
126
  nodeModulesPathRewritePlugin({ bundleDir }),
127
127
  processStubPlugin({ userBanner: input.userBanner }),
128
- shebangPlugin({ enabled: input.shebang === true, line: GJS_SHEBANG }),
128
+ // resolveShebangLine returns null when disabled (false/undefined) and
129
+ // the resolved line otherwise — also handles `${env:…}` expansion.
130
+ (() => {
131
+ const line = resolveShebangLine(input.shebang);
132
+ return shebangPlugin({ enabled: line !== null, line: line ?? undefined });
133
+ })(),
129
134
  ];
130
135
  return { options, plugins };
131
136
  };
package/lib/index.d.ts CHANGED
@@ -7,7 +7,9 @@ export type { NodeModulesPathRewriteOptions, RewriteResult, } from './plugins/re
7
7
  export { processStubPlugin, GJS_PROCESS_STUB, composeBanner } from './plugins/process-stub.js';
8
8
  export type { ProcessStubPluginOptions } from './plugins/process-stub.js';
9
9
  export { cssAsStringPlugin } from './plugins/css-as-string.js';
10
- export { shebangPlugin, GJS_SHEBANG } from './plugins/shebang.js';
10
+ export { textLoaderPlugin } from './plugins/text-loader.js';
11
+ export type { TextLoaderPluginOptions } from './plugins/text-loader.js';
12
+ export { shebangPlugin, GJS_SHEBANG, expandEnvTemplate, resolveShebangLine } from './plugins/shebang.js';
11
13
  export type { ShebangPluginOptions } from './plugins/shebang.js';
12
14
  export { gjsImportsEmptyPlugin } from './plugins/gjs-imports-empty.js';
13
15
  export * from './plugin.js';
package/lib/index.js CHANGED
@@ -6,7 +6,8 @@ export * from './library/index.js';
6
6
  export { REWRITE_FILTER, 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
8
  export { cssAsStringPlugin } from './plugins/css-as-string.js';
9
- export { shebangPlugin, GJS_SHEBANG } from './plugins/shebang.js';
9
+ export { textLoaderPlugin } from './plugins/text-loader.js';
10
+ export { shebangPlugin, GJS_SHEBANG, expandEnvTemplate, resolveShebangLine } from './plugins/shebang.js';
10
11
  export { gjsImportsEmptyPlugin } from './plugins/gjs-imports-empty.js';
11
12
  export * from './plugin.js';
12
13
  import { gjsifyPlugin } from './plugin.js';
package/lib/plugin.d.ts CHANGED
@@ -13,8 +13,13 @@ export interface GjsifyPluginInput {
13
13
  userExternal?: string[];
14
14
  userBanner?: string;
15
15
  userAliases?: Record<string, string>;
16
- /** Whether to prepend `#!/usr/bin/env -S gjs -m` to the GJS bundle. */
17
- shebang?: boolean;
16
+ /**
17
+ * Shebang to prepend to the GJS bundle.
18
+ * `true` → default `#!/usr/bin/env -S gjs -m`
19
+ * `false` → no shebang
20
+ * `"…"` → custom line, supports `${env:NAME[:-default]}` placeholders
21
+ */
22
+ shebang?: boolean | string;
18
23
  }
19
24
  /**
20
25
  * Build the Rolldown configuration template + plugin array for the given
@@ -6,3 +6,25 @@ export interface ShebangPluginOptions {
6
6
  line?: string;
7
7
  }
8
8
  export declare function shebangPlugin(options?: ShebangPluginOptions): Plugin | null;
9
+ /**
10
+ * Expand `${env:NAME}` and `${env:NAME:-default}` placeholders against
11
+ * `process.env`. Missing without default → `''`. Used to let the shebang
12
+ * config field reference build-time env vars (e.g. `GJS_CONSOLE` set by
13
+ * meson-driven Flatpak builds where the GJS interpreter lives at
14
+ * `/usr/bin/gjs-console`).
15
+ */
16
+ export declare function expandEnvTemplate(input: string, env?: Record<string, string | undefined>): string;
17
+ /**
18
+ * Normalize the user-facing `shebang` config value into the literal line
19
+ * that should be prepended to the bundle (without trailing newline), or
20
+ * `null` when shebang injection is disabled.
21
+ *
22
+ * `true` → default GJS shebang
23
+ * `false|undefined` → null (disabled)
24
+ * `"…"` → string with `${env:NAME[:-default]}` expanded
25
+ *
26
+ * If the resolved string does not start with `#!`, it is prefixed
27
+ * automatically so users can write `"shebang": "/usr/bin/gjs -m"` instead
28
+ * of `"#!/usr/bin/gjs -m"`.
29
+ */
30
+ export declare function resolveShebangLine(value: boolean | string | undefined): string | null;
@@ -24,3 +24,41 @@ export function shebangPlugin(options = {}) {
24
24
  },
25
25
  };
26
26
  }
27
+ /**
28
+ * Expand `${env:NAME}` and `${env:NAME:-default}` placeholders against
29
+ * `process.env`. Missing without default → `''`. Used to let the shebang
30
+ * config field reference build-time env vars (e.g. `GJS_CONSOLE` set by
31
+ * meson-driven Flatpak builds where the GJS interpreter lives at
32
+ * `/usr/bin/gjs-console`).
33
+ */
34
+ export function expandEnvTemplate(input, env = process.env) {
35
+ return input.replace(/\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}/g, (_match, name, fallback) => {
36
+ const value = env[name];
37
+ if (value !== undefined && value !== '')
38
+ return value;
39
+ return fallback ?? '';
40
+ });
41
+ }
42
+ /**
43
+ * Normalize the user-facing `shebang` config value into the literal line
44
+ * that should be prepended to the bundle (without trailing newline), or
45
+ * `null` when shebang injection is disabled.
46
+ *
47
+ * `true` → default GJS shebang
48
+ * `false|undefined` → null (disabled)
49
+ * `"…"` → string with `${env:NAME[:-default]}` expanded
50
+ *
51
+ * If the resolved string does not start with `#!`, it is prefixed
52
+ * automatically so users can write `"shebang": "/usr/bin/gjs -m"` instead
53
+ * of `"#!/usr/bin/gjs -m"`.
54
+ */
55
+ export function resolveShebangLine(value) {
56
+ if (value === undefined || value === false)
57
+ return null;
58
+ if (value === true)
59
+ return GJS_SHEBANG;
60
+ const expanded = expandEnvTemplate(value);
61
+ if (!expanded.trim())
62
+ return null;
63
+ return expanded.startsWith('#!') ? expanded : '#!' + expanded;
64
+ }
@@ -0,0 +1,10 @@
1
+ import type { Plugin } from 'rolldown';
2
+ export interface TextLoaderPluginOptions {
3
+ /**
4
+ * Map of file extension (with leading `.`) → loader kind. Currently only
5
+ * `'text'` is implemented; the field is shaped this way to leave room
6
+ * for `'json'` / `'binary'` later without a config break.
7
+ */
8
+ loaders?: Record<string, 'text'>;
9
+ }
10
+ export declare function textLoaderPlugin(options?: TextLoaderPluginOptions): Plugin | null;
@@ -0,0 +1,41 @@
1
+ // Generic "load file as JS string default export" plugin.
2
+ //
3
+ // Mirrors `css-as-string` but lets the user opt-in arbitrary extensions
4
+ // through `bundler.loaders` config, e.g.:
5
+ //
6
+ // "bundler": { "loaders": { ".ui": "text", ".asm": "text" } }
7
+ //
8
+ // — replaces the esbuild `loader: { '.ui': 'text' }` shorthand from the
9
+ // pre-Rolldown era. Rolldown does not classify unknown extensions as text
10
+ // by default; without a hook it tries to parse them as JS and fails.
11
+ import { readFile } from 'node:fs/promises';
12
+ export function textLoaderPlugin(options = {}) {
13
+ const exts = Object.entries(options.loaders ?? {})
14
+ .filter(([, kind]) => kind === 'text')
15
+ .map(([ext]) => ext);
16
+ if (exts.length === 0)
17
+ return null;
18
+ // Build a single regex matching any of the configured extensions:
19
+ // ['.ui', '.asm'] → /\.(ui|asm)$/
20
+ const escaped = exts.map((e) => e.replace(/^\./, '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
21
+ const filter = new RegExp(`\\.(?:${escaped.join('|')})$`);
22
+ // Use the function-form `load(id)` (Rollup-compatible) rather than the
23
+ // newer `load: { filter, handler }` shape. The newer shape was observed
24
+ // not to claim unknown-extension files reliably under Rolldown rc.18 —
25
+ // Rolldown's parser ran BEFORE the filtered handler fired and rejected
26
+ // `.ui`/`.asm` content as invalid JS/JSX. Using the function form (same
27
+ // as `@gjsify/vite-plugin-blueprint`'s `.blp` hook) intercepts during
28
+ // module-load lookup and works under both Vite and Rolldown.
29
+ return {
30
+ name: 'gjsify-text-loader',
31
+ async load(id) {
32
+ if (!filter.test(id))
33
+ return null;
34
+ const code = await readFile(id, 'utf8');
35
+ return {
36
+ code: `export default ${JSON.stringify(code)};`,
37
+ moduleType: 'js',
38
+ };
39
+ },
40
+ };
41
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/rolldown-plugin-gjsify",
3
- "version": "0.3.14",
3
+ "version": "0.3.15",
4
4
  "description": "Rolldown / Rollup / Vite plugin orchestrator for GJS, Node, and Browser targets",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -42,10 +42,10 @@
42
42
  ],
43
43
  "license": "MIT",
44
44
  "dependencies": {
45
- "@gjsify/resolve-npm": "^0.3.14",
46
- "@gjsify/rolldown-plugin-deepkit": "^0.3.14",
47
- "@gjsify/rolldown-plugin-pnp": "^0.3.14",
48
- "@gjsify/vite-plugin-blueprint": "^0.3.14",
45
+ "@gjsify/resolve-npm": "^0.3.15",
46
+ "@gjsify/rolldown-plugin-deepkit": "^0.3.15",
47
+ "@gjsify/rolldown-plugin-pnp": "^0.3.15",
48
+ "@gjsify/vite-plugin-blueprint": "^0.3.15",
49
49
  "@rollup/pluginutils": "^5.1.4",
50
50
  "acorn": "^8.14.0",
51
51
  "acorn-walk": "^8.3.4",
package/src/app/gjs.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  //
3
3
  // Mirrors the esbuild predecessor's `setupForGjs` exactly in terms of the
4
4
  // effective build behaviour: same externals, same alias map, same target
5
- // (firefox128 for JS, firefox60 for CSS), same console-shim injection,
5
+ // (firefox140 for JS, firefox60 for CSS), same console-shim injection,
6
6
  // same process-stub banner, same `random-access-file` fs-backed-fallback.
7
7
  //
8
8
  // Returns a partial `RolldownOptions` template plus the plugin array the
@@ -26,7 +26,7 @@ import {
26
26
  } from '../plugins/rewrite-node-modules-paths.js';
27
27
  import { processStubPlugin } from '../plugins/process-stub.js';
28
28
  import { cssAsStringPlugin } from '../plugins/css-as-string.js';
29
- import { shebangPlugin, GJS_SHEBANG } from '../plugins/shebang.js';
29
+ import { shebangPlugin, resolveShebangLine } from '../plugins/shebang.js';
30
30
 
31
31
  const _shimDir = dirname(fileURLToPath(import.meta.url));
32
32
 
@@ -47,8 +47,13 @@ export interface GjsFactoryInput {
47
47
  userBanner?: string;
48
48
  /** User-supplied resolve.alias overrides. */
49
49
  userAliases?: Record<string, string>;
50
- /** Whether to prepend the `#!/usr/bin/env -S gjs -m` shebang. */
51
- shebang?: boolean;
50
+ /**
51
+ * Shebang to prepend to the output bundle.
52
+ * `true` → default `#!/usr/bin/env -S gjs -m`
53
+ * `false` → no shebang
54
+ * `"…"` → custom line, supports `${env:NAME[:-default]}` placeholders
55
+ */
56
+ shebang?: boolean | string;
52
57
  /** Plugin options forwarded to sub-plugins (deepkit, css, …). */
53
58
  pluginOptions: PluginOptions;
54
59
  }
@@ -111,8 +116,8 @@ export const setupForGjs = async (input: GjsFactoryInput): Promise<GjsBuildConfi
111
116
  conditionNames: format === 'esm' ? ['browser', 'import'] : ['browser', 'require', 'import'],
112
117
  },
113
118
  transform: {
114
- // Compile target: GJS 1.86 / SpiderMonkey 128firefox128.
115
- target: 'firefox128',
119
+ // Compile target: GJS 1.86 / SpiderMonkey 140firefox140.
120
+ target: 'firefox140',
116
121
  define: {
117
122
  global: 'globalThis',
118
123
  window: 'globalThis',
@@ -163,7 +168,12 @@ export const setupForGjs = async (input: GjsFactoryInput): Promise<GjsBuildConfi
163
168
  cssAsStringPlugin(),
164
169
  nodeModulesPathRewritePlugin({ bundleDir }),
165
170
  processStubPlugin({ userBanner: input.userBanner }),
166
- shebangPlugin({ enabled: input.shebang === true, line: GJS_SHEBANG }),
171
+ // resolveShebangLine returns null when disabled (false/undefined) and
172
+ // the resolved line otherwise — also handles `${env:…}` expansion.
173
+ (() => {
174
+ const line = resolveShebangLine(input.shebang);
175
+ return shebangPlugin({ enabled: line !== null, line: line ?? undefined });
176
+ })(),
167
177
  ];
168
178
 
169
179
  return { options, plugins };
package/src/index.ts CHANGED
@@ -20,7 +20,9 @@ export type {
20
20
  export { processStubPlugin, GJS_PROCESS_STUB, composeBanner } from './plugins/process-stub.js';
21
21
  export type { ProcessStubPluginOptions } from './plugins/process-stub.js';
22
22
  export { cssAsStringPlugin } from './plugins/css-as-string.js';
23
- export { shebangPlugin, GJS_SHEBANG } from './plugins/shebang.js';
23
+ export { textLoaderPlugin } from './plugins/text-loader.js';
24
+ export type { TextLoaderPluginOptions } from './plugins/text-loader.js';
25
+ export { shebangPlugin, GJS_SHEBANG, expandEnvTemplate, resolveShebangLine } from './plugins/shebang.js';
24
26
  export type { ShebangPluginOptions } from './plugins/shebang.js';
25
27
  export { gjsImportsEmptyPlugin } from './plugins/gjs-imports-empty.js';
26
28
 
package/src/plugin.ts CHANGED
@@ -27,8 +27,13 @@ export interface GjsifyPluginInput {
27
27
  userExternal?: string[];
28
28
  userBanner?: string;
29
29
  userAliases?: Record<string, string>;
30
- /** Whether to prepend `#!/usr/bin/env -S gjs -m` to the GJS bundle. */
31
- shebang?: boolean;
30
+ /**
31
+ * Shebang to prepend to the GJS bundle.
32
+ * `true` → default `#!/usr/bin/env -S gjs -m`
33
+ * `false` → no shebang
34
+ * `"…"` → custom line, supports `${env:NAME[:-default]}` placeholders
35
+ */
36
+ shebang?: boolean | string;
32
37
  }
33
38
 
34
39
  /**
@@ -31,3 +31,39 @@ export function shebangPlugin(options: ShebangPluginOptions = {}): Plugin | null
31
31
  },
32
32
  };
33
33
  }
34
+
35
+ /**
36
+ * Expand `${env:NAME}` and `${env:NAME:-default}` placeholders against
37
+ * `process.env`. Missing without default → `''`. Used to let the shebang
38
+ * config field reference build-time env vars (e.g. `GJS_CONSOLE` set by
39
+ * meson-driven Flatpak builds where the GJS interpreter lives at
40
+ * `/usr/bin/gjs-console`).
41
+ */
42
+ export function expandEnvTemplate(input: string, env: Record<string, string | undefined> = process.env): string {
43
+ return input.replace(/\$\{env:([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}/g, (_match, name: string, fallback?: string) => {
44
+ const value = env[name];
45
+ if (value !== undefined && value !== '') return value;
46
+ return fallback ?? '';
47
+ });
48
+ }
49
+
50
+ /**
51
+ * Normalize the user-facing `shebang` config value into the literal line
52
+ * that should be prepended to the bundle (without trailing newline), or
53
+ * `null` when shebang injection is disabled.
54
+ *
55
+ * `true` → default GJS shebang
56
+ * `false|undefined` → null (disabled)
57
+ * `"…"` → string with `${env:NAME[:-default]}` expanded
58
+ *
59
+ * If the resolved string does not start with `#!`, it is prefixed
60
+ * automatically so users can write `"shebang": "/usr/bin/gjs -m"` instead
61
+ * of `"#!/usr/bin/gjs -m"`.
62
+ */
63
+ export function resolveShebangLine(value: boolean | string | undefined): string | null {
64
+ if (value === undefined || value === false) return null;
65
+ if (value === true) return GJS_SHEBANG;
66
+ const expanded = expandEnvTemplate(value);
67
+ if (!expanded.trim()) return null;
68
+ return expanded.startsWith('#!') ? expanded : '#!' + expanded;
69
+ }
@@ -0,0 +1,54 @@
1
+ // Generic "load file as JS string default export" plugin.
2
+ //
3
+ // Mirrors `css-as-string` but lets the user opt-in arbitrary extensions
4
+ // through `bundler.loaders` config, e.g.:
5
+ //
6
+ // "bundler": { "loaders": { ".ui": "text", ".asm": "text" } }
7
+ //
8
+ // — replaces the esbuild `loader: { '.ui': 'text' }` shorthand from the
9
+ // pre-Rolldown era. Rolldown does not classify unknown extensions as text
10
+ // by default; without a hook it tries to parse them as JS and fails.
11
+
12
+ import { readFile } from 'node:fs/promises';
13
+ import type { Plugin } from 'rolldown';
14
+
15
+ export interface TextLoaderPluginOptions {
16
+ /**
17
+ * Map of file extension (with leading `.`) → loader kind. Currently only
18
+ * `'text'` is implemented; the field is shaped this way to leave room
19
+ * for `'json'` / `'binary'` later without a config break.
20
+ */
21
+ loaders?: Record<string, 'text'>;
22
+ }
23
+
24
+ export function textLoaderPlugin(options: TextLoaderPluginOptions = {}): Plugin | null {
25
+ const exts = Object.entries(options.loaders ?? {})
26
+ .filter(([, kind]) => kind === 'text')
27
+ .map(([ext]) => ext);
28
+
29
+ if (exts.length === 0) return null;
30
+
31
+ // Build a single regex matching any of the configured extensions:
32
+ // ['.ui', '.asm'] → /\.(ui|asm)$/
33
+ const escaped = exts.map((e) => e.replace(/^\./, '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
34
+ const filter = new RegExp(`\\.(?:${escaped.join('|')})$`);
35
+
36
+ // Use the function-form `load(id)` (Rollup-compatible) rather than the
37
+ // newer `load: { filter, handler }` shape. The newer shape was observed
38
+ // not to claim unknown-extension files reliably under Rolldown rc.18 —
39
+ // Rolldown's parser ran BEFORE the filtered handler fired and rejected
40
+ // `.ui`/`.asm` content as invalid JS/JSX. Using the function form (same
41
+ // as `@gjsify/vite-plugin-blueprint`'s `.blp` hook) intercepts during
42
+ // module-load lookup and works under both Vite and Rolldown.
43
+ return {
44
+ name: 'gjsify-text-loader',
45
+ async load(id: string) {
46
+ if (!filter.test(id)) return null;
47
+ const code = await readFile(id, 'utf8');
48
+ return {
49
+ code: `export default ${JSON.stringify(code)};`,
50
+ moduleType: 'js' as const,
51
+ };
52
+ },
53
+ };
54
+ }