@gjsify/rolldown-plugin-gjsify 0.3.14 → 0.3.16
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 +7 -2
- package/lib/app/gjs.js +15 -6
- package/lib/index.d.ts +3 -1
- package/lib/index.js +2 -1
- package/lib/plugin.d.ts +7 -2
- package/lib/plugins/css-as-string.d.ts +18 -1
- package/lib/plugins/css-as-string.js +25 -6
- package/lib/plugins/shebang.d.ts +22 -0
- package/lib/plugins/shebang.js +38 -0
- package/lib/plugins/text-loader.d.ts +10 -0
- package/lib/plugins/text-loader.js +41 -0
- package/package.json +5 -5
- package/src/app/gjs.ts +22 -8
- package/src/index.ts +3 -1
- package/src/plugin.ts +7 -2
- package/src/plugins/css-as-string.ts +47 -6
- package/src/plugins/shebang.ts +36 -0
- package/src/plugins/text-loader.ts +54 -0
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
|
-
/**
|
|
23
|
-
|
|
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
|
-
// (
|
|
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,
|
|
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
|
|
79
|
-
target: '
|
|
78
|
+
// Compile target: GJS 1.86 / SpiderMonkey 140 ≈ firefox140.
|
|
79
|
+
target: 'firefox140',
|
|
80
80
|
define: {
|
|
81
81
|
global: 'globalThis',
|
|
82
82
|
window: 'globalThis',
|
|
@@ -122,10 +122,19 @@ export const setupForGjs = async (input) => {
|
|
|
122
122
|
}),
|
|
123
123
|
blueprintPlugin(),
|
|
124
124
|
deepkitPlugin({ reflection: input.pluginOptions.reflection }),
|
|
125
|
-
|
|
125
|
+
// GTK4's CSS engine is much older than browser engines — its
|
|
126
|
+
// parser predates nesting + many modern selectors. Targeting
|
|
127
|
+
// `firefox: 60 << 16` makes lightningcss flatten the source
|
|
128
|
+
// into the subset GTK4 understands.
|
|
129
|
+
cssAsStringPlugin({ targets: { firefox: 60 << 16 } }),
|
|
126
130
|
nodeModulesPathRewritePlugin({ bundleDir }),
|
|
127
131
|
processStubPlugin({ userBanner: input.userBanner }),
|
|
128
|
-
|
|
132
|
+
// resolveShebangLine returns null when disabled (false/undefined) and
|
|
133
|
+
// the resolved line otherwise — also handles `${env:…}` expansion.
|
|
134
|
+
(() => {
|
|
135
|
+
const line = resolveShebangLine(input.shebang);
|
|
136
|
+
return shebangPlugin({ enabled: line !== null, line: line ?? undefined });
|
|
137
|
+
})(),
|
|
129
138
|
];
|
|
130
139
|
return { options, plugins };
|
|
131
140
|
};
|
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 {
|
|
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 {
|
|
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
|
-
/**
|
|
17
|
-
|
|
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
|
|
@@ -1,2 +1,19 @@
|
|
|
1
1
|
import type { Plugin } from 'rolldown';
|
|
2
|
-
export
|
|
2
|
+
export interface CssAsStringOptions {
|
|
3
|
+
/**
|
|
4
|
+
* lightningcss browser targets passed to `bundleAsync`. When set,
|
|
5
|
+
* nesting + modern syntax are lowered for the given engines. The
|
|
6
|
+
* GJS orchestrator defaults this to `{ firefox: 60 << 16 }` to
|
|
7
|
+
* match the GTK4 CSS parser. Omit or leave undefined to skip
|
|
8
|
+
* lowering (output stays as-authored except for `@import` inlining).
|
|
9
|
+
*/
|
|
10
|
+
targets?: import('lightningcss').Targets;
|
|
11
|
+
/**
|
|
12
|
+
* When true (default), `@import` statements are resolved by
|
|
13
|
+
* lightningcss `bundleAsync`. Set false to fall back to a plain
|
|
14
|
+
* `readFile` — useful only when you want to keep `@import` strings
|
|
15
|
+
* verbatim in the bundled JS (rare).
|
|
16
|
+
*/
|
|
17
|
+
bundle?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function cssAsStringPlugin(options?: CssAsStringOptions): Plugin;
|
|
@@ -12,18 +12,27 @@
|
|
|
12
12
|
//
|
|
13
13
|
// — the canonical pattern for `Gtk.CssProvider` under GJS.
|
|
14
14
|
//
|
|
15
|
-
// `@import` resolution
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
15
|
+
// `@import` resolution + nesting/modern-syntax lowering are handled via
|
|
16
|
+
// lightningcss `bundleAsync`. The defaults work for the common case
|
|
17
|
+
// (resolve `@import`s, no targeting); the `--app gjs` orchestrator passes
|
|
18
|
+
// `targets: { firefox: 60 << 16 }` so nesting + modern selectors get
|
|
19
|
+
// flattened to GTK4-CSS-engine-compatible output. Targeting is opt-in —
|
|
20
|
+
// a missing `targets` keeps the source pristine.
|
|
21
|
+
//
|
|
22
|
+
// `lightningcss` is a regular dependency of this package; the plugin
|
|
23
|
+
// imports it lazily so missing-arch installs surface the underlying
|
|
24
|
+
// load error instead of crashing every gjsify build.
|
|
19
25
|
import { readFile } from 'node:fs/promises';
|
|
20
|
-
export function cssAsStringPlugin() {
|
|
26
|
+
export function cssAsStringPlugin(options = {}) {
|
|
27
|
+
const { targets, bundle = true } = options;
|
|
21
28
|
return {
|
|
22
29
|
name: 'gjsify-css-as-string',
|
|
23
30
|
load: {
|
|
24
31
|
filter: { id: /\.css$/ },
|
|
25
32
|
async handler(id) {
|
|
26
|
-
const code =
|
|
33
|
+
const code = bundle
|
|
34
|
+
? new TextDecoder('utf-8').decode(await loadAndBundleCss(id, targets))
|
|
35
|
+
: await readFile(id, 'utf8');
|
|
27
36
|
return {
|
|
28
37
|
code: `export default ${JSON.stringify(code)};`,
|
|
29
38
|
moduleType: 'js',
|
|
@@ -32,3 +41,13 @@ export function cssAsStringPlugin() {
|
|
|
32
41
|
},
|
|
33
42
|
};
|
|
34
43
|
}
|
|
44
|
+
async function loadAndBundleCss(filename, targets) {
|
|
45
|
+
const { bundleAsync } = await import('lightningcss');
|
|
46
|
+
const result = await bundleAsync({
|
|
47
|
+
filename,
|
|
48
|
+
targets,
|
|
49
|
+
minify: false,
|
|
50
|
+
errorRecovery: true,
|
|
51
|
+
});
|
|
52
|
+
return result.code;
|
|
53
|
+
}
|
package/lib/plugins/shebang.d.ts
CHANGED
|
@@ -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;
|
package/lib/plugins/shebang.js
CHANGED
|
@@ -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.
|
|
3
|
+
"version": "0.3.16",
|
|
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.
|
|
46
|
-
"@gjsify/rolldown-plugin-deepkit": "^0.3.
|
|
47
|
-
"@gjsify/rolldown-plugin-pnp": "^0.3.
|
|
48
|
-
"@gjsify/vite-plugin-blueprint": "^0.3.
|
|
45
|
+
"@gjsify/resolve-npm": "^0.3.16",
|
|
46
|
+
"@gjsify/rolldown-plugin-deepkit": "^0.3.16",
|
|
47
|
+
"@gjsify/rolldown-plugin-pnp": "^0.3.16",
|
|
48
|
+
"@gjsify/vite-plugin-blueprint": "^0.3.16",
|
|
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
|
-
// (
|
|
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,
|
|
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
|
-
/**
|
|
51
|
-
|
|
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
|
|
115
|
-
target: '
|
|
119
|
+
// Compile target: GJS 1.86 / SpiderMonkey 140 ≈ firefox140.
|
|
120
|
+
target: 'firefox140',
|
|
116
121
|
define: {
|
|
117
122
|
global: 'globalThis',
|
|
118
123
|
window: 'globalThis',
|
|
@@ -160,10 +165,19 @@ export const setupForGjs = async (input: GjsFactoryInput): Promise<GjsBuildConfi
|
|
|
160
165
|
}),
|
|
161
166
|
blueprintPlugin() as RolldownPluginOption,
|
|
162
167
|
deepkitPlugin({ reflection: input.pluginOptions.reflection }),
|
|
163
|
-
|
|
168
|
+
// GTK4's CSS engine is much older than browser engines — its
|
|
169
|
+
// parser predates nesting + many modern selectors. Targeting
|
|
170
|
+
// `firefox: 60 << 16` makes lightningcss flatten the source
|
|
171
|
+
// into the subset GTK4 understands.
|
|
172
|
+
cssAsStringPlugin({ targets: { firefox: 60 << 16 } }),
|
|
164
173
|
nodeModulesPathRewritePlugin({ bundleDir }),
|
|
165
174
|
processStubPlugin({ userBanner: input.userBanner }),
|
|
166
|
-
|
|
175
|
+
// resolveShebangLine returns null when disabled (false/undefined) and
|
|
176
|
+
// the resolved line otherwise — also handles `${env:…}` expansion.
|
|
177
|
+
(() => {
|
|
178
|
+
const line = resolveShebangLine(input.shebang);
|
|
179
|
+
return shebangPlugin({ enabled: line !== null, line: line ?? undefined });
|
|
180
|
+
})(),
|
|
167
181
|
];
|
|
168
182
|
|
|
169
183
|
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 {
|
|
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
|
-
/**
|
|
31
|
-
|
|
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
|
/**
|
|
@@ -12,21 +12,48 @@
|
|
|
12
12
|
//
|
|
13
13
|
// — the canonical pattern for `Gtk.CssProvider` under GJS.
|
|
14
14
|
//
|
|
15
|
-
// `@import` resolution
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
15
|
+
// `@import` resolution + nesting/modern-syntax lowering are handled via
|
|
16
|
+
// lightningcss `bundleAsync`. The defaults work for the common case
|
|
17
|
+
// (resolve `@import`s, no targeting); the `--app gjs` orchestrator passes
|
|
18
|
+
// `targets: { firefox: 60 << 16 }` so nesting + modern selectors get
|
|
19
|
+
// flattened to GTK4-CSS-engine-compatible output. Targeting is opt-in —
|
|
20
|
+
// a missing `targets` keeps the source pristine.
|
|
21
|
+
//
|
|
22
|
+
// `lightningcss` is a regular dependency of this package; the plugin
|
|
23
|
+
// imports it lazily so missing-arch installs surface the underlying
|
|
24
|
+
// load error instead of crashing every gjsify build.
|
|
19
25
|
|
|
20
26
|
import { readFile } from 'node:fs/promises';
|
|
21
27
|
import type { Plugin } from 'rolldown';
|
|
22
28
|
|
|
23
|
-
export
|
|
29
|
+
export interface CssAsStringOptions {
|
|
30
|
+
/**
|
|
31
|
+
* lightningcss browser targets passed to `bundleAsync`. When set,
|
|
32
|
+
* nesting + modern syntax are lowered for the given engines. The
|
|
33
|
+
* GJS orchestrator defaults this to `{ firefox: 60 << 16 }` to
|
|
34
|
+
* match the GTK4 CSS parser. Omit or leave undefined to skip
|
|
35
|
+
* lowering (output stays as-authored except for `@import` inlining).
|
|
36
|
+
*/
|
|
37
|
+
targets?: import('lightningcss').Targets;
|
|
38
|
+
/**
|
|
39
|
+
* When true (default), `@import` statements are resolved by
|
|
40
|
+
* lightningcss `bundleAsync`. Set false to fall back to a plain
|
|
41
|
+
* `readFile` — useful only when you want to keep `@import` strings
|
|
42
|
+
* verbatim in the bundled JS (rare).
|
|
43
|
+
*/
|
|
44
|
+
bundle?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function cssAsStringPlugin(options: CssAsStringOptions = {}): Plugin {
|
|
48
|
+
const { targets, bundle = true } = options;
|
|
24
49
|
return {
|
|
25
50
|
name: 'gjsify-css-as-string',
|
|
26
51
|
load: {
|
|
27
52
|
filter: { id: /\.css$/ },
|
|
28
53
|
async handler(id: string) {
|
|
29
|
-
const code =
|
|
54
|
+
const code = bundle
|
|
55
|
+
? new TextDecoder('utf-8').decode(await loadAndBundleCss(id, targets))
|
|
56
|
+
: await readFile(id, 'utf8');
|
|
30
57
|
return {
|
|
31
58
|
code: `export default ${JSON.stringify(code)};`,
|
|
32
59
|
moduleType: 'js' as const,
|
|
@@ -35,3 +62,17 @@ export function cssAsStringPlugin(): Plugin {
|
|
|
35
62
|
},
|
|
36
63
|
};
|
|
37
64
|
}
|
|
65
|
+
|
|
66
|
+
async function loadAndBundleCss(
|
|
67
|
+
filename: string,
|
|
68
|
+
targets: import('lightningcss').Targets | undefined,
|
|
69
|
+
): Promise<Uint8Array> {
|
|
70
|
+
const { bundleAsync } = await import('lightningcss');
|
|
71
|
+
const result = await bundleAsync({
|
|
72
|
+
filename,
|
|
73
|
+
targets,
|
|
74
|
+
minify: false,
|
|
75
|
+
errorRecovery: true,
|
|
76
|
+
});
|
|
77
|
+
return result.code;
|
|
78
|
+
}
|
package/src/plugins/shebang.ts
CHANGED
|
@@ -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
|
+
}
|