@gjsify/rolldown-plugin-gjsify 0.3.14
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/browser.d.ts +17 -0
- package/lib/app/browser.js +77 -0
- package/lib/app/gjs.d.ts +27 -0
- package/lib/app/gjs.js +211 -0
- package/lib/app/index.d.ts +6 -0
- package/lib/app/index.js +3 -0
- package/lib/app/node.d.ts +17 -0
- package/lib/app/node.js +102 -0
- package/lib/globals.d.ts +4 -0
- package/lib/globals.js +9 -0
- package/lib/index.d.ts +17 -0
- package/lib/index.js +15 -0
- package/lib/library/index.d.ts +2 -0
- package/lib/library/index.js +1 -0
- package/lib/library/lib.d.ts +16 -0
- package/lib/library/lib.js +118 -0
- package/lib/plugin.d.ts +25 -0
- package/lib/plugin.js +67 -0
- package/lib/plugins/alias.d.ts +5 -0
- package/lib/plugins/alias.js +45 -0
- package/lib/plugins/css-as-string.d.ts +2 -0
- package/lib/plugins/css-as-string.js +34 -0
- package/lib/plugins/gjs-imports-empty.d.ts +2 -0
- package/lib/plugins/gjs-imports-empty.js +26 -0
- package/lib/plugins/process-stub.d.ts +28 -0
- package/lib/plugins/process-stub.js +60 -0
- package/lib/plugins/rewrite-node-modules-paths.d.ts +38 -0
- package/lib/plugins/rewrite-node-modules-paths.js +132 -0
- package/lib/plugins/shebang.d.ts +8 -0
- package/lib/plugins/shebang.js +26 -0
- package/lib/shims/console-gjs.d.ts +24 -0
- package/lib/shims/console-gjs.js +24 -0
- package/lib/types/app.d.ts +1 -0
- package/lib/types/app.js +1 -0
- package/lib/types/index.d.ts +3 -0
- package/lib/types/index.js +3 -0
- package/lib/types/plugin-options.d.ts +46 -0
- package/lib/types/plugin-options.js +1 -0
- package/lib/types/resolve-alias-options.d.ts +2 -0
- package/lib/types/resolve-alias-options.js +1 -0
- package/lib/utils/alias.d.ts +12 -0
- package/lib/utils/alias.js +29 -0
- package/lib/utils/auto-globals.d.ts +72 -0
- package/lib/utils/auto-globals.js +193 -0
- package/lib/utils/detect-free-globals.d.ts +18 -0
- package/lib/utils/detect-free-globals.js +268 -0
- package/lib/utils/entry-points.d.ts +2 -0
- package/lib/utils/entry-points.js +38 -0
- package/lib/utils/extension.d.ts +1 -0
- package/lib/utils/extension.js +7 -0
- package/lib/utils/index.d.ts +7 -0
- package/lib/utils/index.js +7 -0
- package/lib/utils/inline-static-reads.d.ts +11 -0
- package/lib/utils/inline-static-reads.js +549 -0
- package/lib/utils/merge.d.ts +2 -0
- package/lib/utils/merge.js +23 -0
- package/lib/utils/scan-globals.d.ts +32 -0
- package/lib/utils/scan-globals.js +85 -0
- package/package.json +68 -0
- package/src/app/browser.ts +102 -0
- package/src/app/gjs.ts +260 -0
- package/src/app/index.ts +6 -0
- package/src/app/node.ts +128 -0
- package/src/globals.ts +11 -0
- package/src/index.ts +32 -0
- package/src/library/index.ts +2 -0
- package/src/library/lib.ts +142 -0
- package/src/plugin.ts +91 -0
- package/src/plugins/alias.ts +53 -0
- package/src/plugins/css-as-string.ts +37 -0
- package/src/plugins/gjs-imports-empty.ts +29 -0
- package/src/plugins/process-stub.ts +91 -0
- package/src/plugins/rewrite-node-modules-paths.ts +169 -0
- package/src/plugins/shebang.ts +33 -0
- package/src/shims/console-gjs.ts +25 -0
- package/src/types/app.ts +1 -0
- package/src/types/index.ts +3 -0
- package/src/types/plugin-options.ts +48 -0
- package/src/types/resolve-alias-options.ts +1 -0
- package/src/utils/alias.ts +46 -0
- package/src/utils/auto-globals.ts +283 -0
- package/src/utils/detect-free-globals.ts +278 -0
- package/src/utils/entry-points.ts +48 -0
- package/src/utils/extension.ts +7 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/inline-static-reads.ts +541 -0
- package/src/utils/merge.ts +22 -0
- package/src/utils/scan-globals.ts +91 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { RolldownOptions, RolldownPluginOption } from 'rolldown';
|
|
2
|
+
import type { PluginOptions } from '../types/plugin-options.js';
|
|
3
|
+
export interface BrowserBuildConfig {
|
|
4
|
+
options: RolldownOptions;
|
|
5
|
+
plugins: RolldownPluginOption[];
|
|
6
|
+
}
|
|
7
|
+
export interface BrowserFactoryInput {
|
|
8
|
+
input?: RolldownOptions['input'];
|
|
9
|
+
output: {
|
|
10
|
+
file?: string;
|
|
11
|
+
dir?: string;
|
|
12
|
+
};
|
|
13
|
+
userExternal?: string[];
|
|
14
|
+
userAliases?: Record<string, string>;
|
|
15
|
+
pluginOptions: PluginOptions;
|
|
16
|
+
}
|
|
17
|
+
export declare const setupForBrowser: (input: BrowserFactoryInput) => Promise<BrowserBuildConfig>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// `--app browser` Rolldown configuration factory.
|
|
2
|
+
//
|
|
3
|
+
// Browser builds redirect `@girs/*` and `gi://*` to an empty virtual module
|
|
4
|
+
// (they appear transitively via `@gjsify/unit` and similar packages with
|
|
5
|
+
// GJS-specific code paths). Standard Node.js → browser polyfill aliases
|
|
6
|
+
// for `process` and `assert` keep `@gjsify/unit`'s top-level imports
|
|
7
|
+
// resolvable in a browser bundle.
|
|
8
|
+
import { aliasPlugin } from '../plugins/alias.js';
|
|
9
|
+
import { deepkitPlugin } from '@gjsify/rolldown-plugin-deepkit';
|
|
10
|
+
import blueprintPlugin from '@gjsify/vite-plugin-blueprint';
|
|
11
|
+
import { globToEntryPoints } from '../utils/entry-points.js';
|
|
12
|
+
import { gjsImportsEmptyPlugin } from '../plugins/gjs-imports-empty.js';
|
|
13
|
+
import { cssAsStringPlugin } from '../plugins/css-as-string.js';
|
|
14
|
+
export const setupForBrowser = async (input) => {
|
|
15
|
+
const userExternal = input.userExternal ?? [];
|
|
16
|
+
const external = [...userExternal];
|
|
17
|
+
const exclude = input.pluginOptions.exclude ?? [];
|
|
18
|
+
const entryPoints = await globToEntryPoints(input.input, exclude);
|
|
19
|
+
// `@gjsify/unit` has `await import('process')` inside a try-catch that
|
|
20
|
+
// is unreachable in browser (typeof document check comes first), but
|
|
21
|
+
// Rolldown still resolves it statically. Map to `@gjsify/empty` so the
|
|
22
|
+
// build succeeds. `assert` → `@gjsify/assert` because `@gjsify/unit`
|
|
23
|
+
// imports `node:assert` at the top level.
|
|
24
|
+
const browserPolyfillAliases = {
|
|
25
|
+
process: '@gjsify/empty',
|
|
26
|
+
'node:process': '@gjsify/empty',
|
|
27
|
+
assert: '@gjsify/assert',
|
|
28
|
+
'node:assert': '@gjsify/assert',
|
|
29
|
+
};
|
|
30
|
+
const aliasMap = {
|
|
31
|
+
...browserPolyfillAliases,
|
|
32
|
+
...(input.pluginOptions.aliases ?? {}),
|
|
33
|
+
...(input.userAliases ?? {}),
|
|
34
|
+
};
|
|
35
|
+
const options = {
|
|
36
|
+
input: entryPoints,
|
|
37
|
+
platform: 'browser',
|
|
38
|
+
external,
|
|
39
|
+
resolve: {
|
|
40
|
+
mainFields: ['browser', 'module', 'main'],
|
|
41
|
+
conditionNames: ['import', 'browser'],
|
|
42
|
+
},
|
|
43
|
+
transform: {
|
|
44
|
+
target: 'esnext',
|
|
45
|
+
define: {
|
|
46
|
+
global: 'globalThis',
|
|
47
|
+
window: 'globalThis',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
output: {
|
|
51
|
+
...input.output,
|
|
52
|
+
format: 'esm',
|
|
53
|
+
minify: false,
|
|
54
|
+
sourcemap: false,
|
|
55
|
+
// Single-bundle output. `codeSplitting: false` replaces the
|
|
56
|
+
// deprecated `inlineDynamicImports: true`.
|
|
57
|
+
codeSplitting: false,
|
|
58
|
+
},
|
|
59
|
+
treeshake: true,
|
|
60
|
+
};
|
|
61
|
+
const plugins = [
|
|
62
|
+
gjsImportsEmptyPlugin(),
|
|
63
|
+
aliasPlugin({ entries: flattenAliases(aliasMap) }),
|
|
64
|
+
blueprintPlugin(),
|
|
65
|
+
deepkitPlugin({ reflection: input.pluginOptions.reflection }),
|
|
66
|
+
cssAsStringPlugin(),
|
|
67
|
+
];
|
|
68
|
+
return { options, plugins };
|
|
69
|
+
};
|
|
70
|
+
function flattenAliases(map) {
|
|
71
|
+
const out = {};
|
|
72
|
+
for (const [from, to] of Object.entries(map)) {
|
|
73
|
+
if (to)
|
|
74
|
+
out[from] = to;
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
package/lib/app/gjs.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { RolldownOptions, RolldownPluginOption } from 'rolldown';
|
|
2
|
+
import type { PluginOptions } from '../types/plugin-options.js';
|
|
3
|
+
/** Resolved Rolldown configuration template + plugins for `--app gjs`. */
|
|
4
|
+
export interface GjsBuildConfig {
|
|
5
|
+
options: RolldownOptions;
|
|
6
|
+
plugins: RolldownPluginOption[];
|
|
7
|
+
}
|
|
8
|
+
export interface GjsFactoryInput {
|
|
9
|
+
/** User entry points after CLI / config merging. */
|
|
10
|
+
input?: RolldownOptions['input'];
|
|
11
|
+
/** Output `file` or `dir` so `import.meta.url` rewriter knows the bundle path. */
|
|
12
|
+
output: {
|
|
13
|
+
file?: string;
|
|
14
|
+
dir?: string;
|
|
15
|
+
};
|
|
16
|
+
/** Caller-supplied externals (`gjsify build --external`). */
|
|
17
|
+
userExternal?: string[];
|
|
18
|
+
/** User-supplied banner string (may contain a leading `#!shebang`). */
|
|
19
|
+
userBanner?: string;
|
|
20
|
+
/** User-supplied resolve.alias overrides. */
|
|
21
|
+
userAliases?: Record<string, string>;
|
|
22
|
+
/** Whether to prepend the `#!/usr/bin/env -S gjs -m` shebang. */
|
|
23
|
+
shebang?: boolean;
|
|
24
|
+
/** Plugin options forwarded to sub-plugins (deepkit, css, …). */
|
|
25
|
+
pluginOptions: PluginOptions;
|
|
26
|
+
}
|
|
27
|
+
export declare const setupForGjs: (input: GjsFactoryInput) => Promise<GjsBuildConfig>;
|
package/lib/app/gjs.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// `--app gjs` Rolldown configuration factory.
|
|
2
|
+
//
|
|
3
|
+
// Mirrors the esbuild predecessor's `setupForGjs` exactly in terms of the
|
|
4
|
+
// effective build behaviour: same externals, same alias map, same target
|
|
5
|
+
// (firefox128 for JS, firefox60 for CSS), same console-shim injection,
|
|
6
|
+
// same process-stub banner, same `random-access-file` fs-backed-fallback.
|
|
7
|
+
//
|
|
8
|
+
// Returns a partial `RolldownOptions` template plus the plugin array the
|
|
9
|
+
// caller should compose with their user-supplied options. Library mode is
|
|
10
|
+
// handled separately by `setupLib`.
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { dirname, resolve } from 'node:path';
|
|
13
|
+
import { aliasPlugin } from '../plugins/alias.js';
|
|
14
|
+
import { deepkitPlugin } from '@gjsify/rolldown-plugin-deepkit';
|
|
15
|
+
import blueprintPlugin from '@gjsify/vite-plugin-blueprint';
|
|
16
|
+
import { getAliasesForGjs } from '../utils/alias.js';
|
|
17
|
+
import { globToEntryPoints } from '../utils/entry-points.js';
|
|
18
|
+
import { nodeModulesPathRewritePlugin, getBundleDirFromOutput, } from '../plugins/rewrite-node-modules-paths.js';
|
|
19
|
+
import { processStubPlugin } from '../plugins/process-stub.js';
|
|
20
|
+
import { cssAsStringPlugin } from '../plugins/css-as-string.js';
|
|
21
|
+
import { shebangPlugin, GJS_SHEBANG } from '../plugins/shebang.js';
|
|
22
|
+
const _shimDir = dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
export const setupForGjs = async (input) => {
|
|
24
|
+
const userExternal = input.userExternal ?? [];
|
|
25
|
+
// Rolldown's `external` array does not support glob patterns the way
|
|
26
|
+
// esbuild's did (`gi://*`). We use a function predicate so any
|
|
27
|
+
// `gi://Foo?version=…` URI matches by prefix and the GJS-built-in
|
|
28
|
+
// string specifiers stay externalised by name.
|
|
29
|
+
const exactExternal = ['cairo', 'gettext', 'system', ...userExternal];
|
|
30
|
+
const external = (id) => {
|
|
31
|
+
if (id.startsWith('gi://'))
|
|
32
|
+
return true;
|
|
33
|
+
if (exactExternal.includes(id))
|
|
34
|
+
return true;
|
|
35
|
+
return false;
|
|
36
|
+
};
|
|
37
|
+
const format = input.pluginOptions.format ?? 'esm';
|
|
38
|
+
const exclude = input.pluginOptions.exclude ?? [];
|
|
39
|
+
const entryPoints = await globToEntryPoints(input.input, exclude);
|
|
40
|
+
const aliasMap = {
|
|
41
|
+
...getAliasesForGjs({ external }),
|
|
42
|
+
...(input.pluginOptions.aliases ?? {}),
|
|
43
|
+
...(input.userAliases ?? {}),
|
|
44
|
+
};
|
|
45
|
+
// The console shim replaces all `console` references with print()/printerr()-
|
|
46
|
+
// based implementations that bypass GLib.log_structured() — no prefix,
|
|
47
|
+
// ANSI codes work. Disabled via `pluginOptions.consoleShim === false`.
|
|
48
|
+
const consoleShimEnabled = input.pluginOptions.consoleShim !== false;
|
|
49
|
+
const consoleShimPath = consoleShimEnabled
|
|
50
|
+
? resolve(_shimDir, '../shims/console-gjs.js')
|
|
51
|
+
: null;
|
|
52
|
+
// The auto-globals inject stub (when present) is side-effect-imported
|
|
53
|
+
// via a virtual entry — its register modules write to globalThis, so
|
|
54
|
+
// the import chain matters but no name binding does. We can't use
|
|
55
|
+
// Rolldown's `inject` for this because the auto-globals invariant
|
|
56
|
+
// forbids source-AST rewrites for global identifiers (false positives
|
|
57
|
+
// from isomorphic guards / bracket access — see AGENTS.md).
|
|
58
|
+
const sideEffectImports = [];
|
|
59
|
+
if (input.pluginOptions.autoGlobalsInject)
|
|
60
|
+
sideEffectImports.push(input.pluginOptions.autoGlobalsInject);
|
|
61
|
+
const virtualEntries = wrapInputWithSideEffects(entryPoints, sideEffectImports);
|
|
62
|
+
const finalInput = virtualEntries.input;
|
|
63
|
+
const options = {
|
|
64
|
+
input: finalInput,
|
|
65
|
+
platform: 'neutral',
|
|
66
|
+
external,
|
|
67
|
+
// 'browser' field is needed so packages like create-hash, create-hmac,
|
|
68
|
+
// randombytes use their pure-JS browser entry instead of index.js
|
|
69
|
+
// (which does require('crypto') and causes circular dependencies via
|
|
70
|
+
// the crypto → @gjsify/crypto alias).
|
|
71
|
+
resolve: {
|
|
72
|
+
mainFields: format === 'esm' ? ['browser', 'module', 'main'] : ['browser', 'main', 'module'],
|
|
73
|
+
// ESM: omit 'require' — packages listing 'require' before 'import'
|
|
74
|
+
// would silently route through their CJS entry.
|
|
75
|
+
conditionNames: format === 'esm' ? ['browser', 'import'] : ['browser', 'require', 'import'],
|
|
76
|
+
},
|
|
77
|
+
transform: {
|
|
78
|
+
// Compile target: GJS 1.86 / SpiderMonkey 128 ≈ firefox128.
|
|
79
|
+
target: 'firefox128',
|
|
80
|
+
define: {
|
|
81
|
+
global: 'globalThis',
|
|
82
|
+
window: 'globalThis',
|
|
83
|
+
'process.env.READABLE_STREAM': '"disable"',
|
|
84
|
+
},
|
|
85
|
+
// Console shim: rewrite bare `console` references to a named
|
|
86
|
+
// import from our shim module. We use Rolldown's `inject`
|
|
87
|
+
// (Oxc-driven, lives under `transform`) because:
|
|
88
|
+
// 1. `globalThis.console` is non-configurable on SpiderMonkey
|
|
89
|
+
// 128 so a register-style global write throws.
|
|
90
|
+
// 2. We're replacing console unconditionally — there's no
|
|
91
|
+
// tree-shake-aware detection concern that motivated the
|
|
92
|
+
// auto-globals invariant.
|
|
93
|
+
...(consoleShimPath ? { inject: { console: [consoleShimPath, 'console'] } } : {}),
|
|
94
|
+
},
|
|
95
|
+
output: {
|
|
96
|
+
...input.output,
|
|
97
|
+
format,
|
|
98
|
+
minify: false,
|
|
99
|
+
sourcemap: false,
|
|
100
|
+
// App builds emit a single bundle file. Disable code-splitting
|
|
101
|
+
// so dynamic imports get inlined and the entire program lands
|
|
102
|
+
// in one chunk that matches `gjsify build --outfile foo.js`.
|
|
103
|
+
// (`codeSplitting: false` replaces the deprecated
|
|
104
|
+
// `inlineDynamicImports: true` in Rolldown ≥ 1.0-rc.18.)
|
|
105
|
+
codeSplitting: false,
|
|
106
|
+
},
|
|
107
|
+
treeshake: true,
|
|
108
|
+
};
|
|
109
|
+
const bundleDir = getBundleDirFromOutput(input.output);
|
|
110
|
+
const plugins = [
|
|
111
|
+
// Virtual-entry plugin runs FIRST so its resolveId/load match the
|
|
112
|
+
// synthetic input ids that `wrapInputWithSideEffects` produces.
|
|
113
|
+
...(virtualEntries.plugin ? [virtualEntries.plugin] : []),
|
|
114
|
+
// random-access-file's 'browser' field maps to a throwing stub; force
|
|
115
|
+
// the fs-backed Node entry. Implemented via the gjsify alias plugin
|
|
116
|
+
// as a direct entry-table override.
|
|
117
|
+
aliasPlugin({
|
|
118
|
+
entries: {
|
|
119
|
+
'random-access-file': 'random-access-file/index.js',
|
|
120
|
+
...flattenAliases(aliasMap),
|
|
121
|
+
},
|
|
122
|
+
}),
|
|
123
|
+
blueprintPlugin(),
|
|
124
|
+
deepkitPlugin({ reflection: input.pluginOptions.reflection }),
|
|
125
|
+
cssAsStringPlugin(),
|
|
126
|
+
nodeModulesPathRewritePlugin({ bundleDir }),
|
|
127
|
+
processStubPlugin({ userBanner: input.userBanner }),
|
|
128
|
+
shebangPlugin({ enabled: input.shebang === true, line: GJS_SHEBANG }),
|
|
129
|
+
];
|
|
130
|
+
return { options, plugins };
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* If there are side-effect imports to land alongside the user's entry,
|
|
134
|
+
* wrap each entry in a virtual module that imports them first then
|
|
135
|
+
* re-exports the entry. Returns the rewritten `input` plus the resolveId/load
|
|
136
|
+
* plugin that resolves the virtual ids.
|
|
137
|
+
*
|
|
138
|
+
* Single-input case: `'src/index.ts'` → `'\0gjsify-entry:src/index.ts'`.
|
|
139
|
+
* Array-input case: each element gets the same wrapper id.
|
|
140
|
+
* Record-input case: values get wrapped, keys preserved.
|
|
141
|
+
*
|
|
142
|
+
* `\0`-prefixed ids are Rollup's convention for synthetic modules — Rolldown
|
|
143
|
+
* recognises and treats them as not-from-disk, skipping the default loader.
|
|
144
|
+
*/
|
|
145
|
+
function wrapInputWithSideEffects(input, sideEffects) {
|
|
146
|
+
if (sideEffects.length === 0 || input === undefined) {
|
|
147
|
+
return { input, plugin: null };
|
|
148
|
+
}
|
|
149
|
+
const userEntries = new Map(); // virtualId → realPath
|
|
150
|
+
const PREFIX = '\0gjsify-entry:';
|
|
151
|
+
function wrap(realPath) {
|
|
152
|
+
const id = PREFIX + realPath;
|
|
153
|
+
userEntries.set(id, realPath);
|
|
154
|
+
return id;
|
|
155
|
+
}
|
|
156
|
+
let wrappedInput;
|
|
157
|
+
if (typeof input === 'string') {
|
|
158
|
+
wrappedInput = wrap(input);
|
|
159
|
+
}
|
|
160
|
+
else if (Array.isArray(input)) {
|
|
161
|
+
wrappedInput = input.map(wrap);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
const out = {};
|
|
165
|
+
for (const [name, path] of Object.entries(input)) {
|
|
166
|
+
out[name] = wrap(path);
|
|
167
|
+
}
|
|
168
|
+
wrappedInput = out;
|
|
169
|
+
}
|
|
170
|
+
const sideEffectImports = sideEffects
|
|
171
|
+
.map((p) => `import ${JSON.stringify(p)};`)
|
|
172
|
+
.join('\n');
|
|
173
|
+
const plugin = {
|
|
174
|
+
name: 'gjsify-virtual-entry',
|
|
175
|
+
async resolveId(source, importer) {
|
|
176
|
+
if (source.startsWith(PREFIX))
|
|
177
|
+
return source;
|
|
178
|
+
return null;
|
|
179
|
+
},
|
|
180
|
+
async load(id) {
|
|
181
|
+
if (!id.startsWith(PREFIX))
|
|
182
|
+
return null;
|
|
183
|
+
const realPath = userEntries.get(id);
|
|
184
|
+
if (!realPath)
|
|
185
|
+
return null;
|
|
186
|
+
// Resolve the user-provided entry path through the full
|
|
187
|
+
// resolver chain so the re-export targets a real on-disk
|
|
188
|
+
// module — otherwise Rolldown treats `src/foo.ts` as a bare
|
|
189
|
+
// specifier and emits it as an external import.
|
|
190
|
+
const resolved = await this.resolve(realPath, undefined, { skipSelf: true });
|
|
191
|
+
const target = resolved?.id ?? realPath;
|
|
192
|
+
return {
|
|
193
|
+
code: `${sideEffectImports}\nexport * from ${JSON.stringify(target)};\n`,
|
|
194
|
+
moduleSideEffects: true,
|
|
195
|
+
};
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
return { input: wrappedInput, plugin };
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Flatten the legacy `Record<string, string>` alias map into the
|
|
202
|
+
* `@rollup/plugin-alias` `entries` array shape, dropping empty values.
|
|
203
|
+
*/
|
|
204
|
+
function flattenAliases(map) {
|
|
205
|
+
const out = {};
|
|
206
|
+
for (const [from, to] of Object.entries(map)) {
|
|
207
|
+
if (to)
|
|
208
|
+
out[from] = to;
|
|
209
|
+
}
|
|
210
|
+
return out;
|
|
211
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { setupForGjs } from './gjs.js';
|
|
2
|
+
export type { GjsBuildConfig, GjsFactoryInput } from './gjs.js';
|
|
3
|
+
export { setupForNode } from './node.js';
|
|
4
|
+
export type { NodeBuildConfig, NodeFactoryInput } from './node.js';
|
|
5
|
+
export { setupForBrowser } from './browser.js';
|
|
6
|
+
export type { BrowserBuildConfig, BrowserFactoryInput } from './browser.js';
|
package/lib/app/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { RolldownOptions, RolldownPluginOption } from 'rolldown';
|
|
2
|
+
import type { PluginOptions } from '../types/plugin-options.js';
|
|
3
|
+
export interface NodeBuildConfig {
|
|
4
|
+
options: RolldownOptions;
|
|
5
|
+
plugins: RolldownPluginOption[];
|
|
6
|
+
}
|
|
7
|
+
export interface NodeFactoryInput {
|
|
8
|
+
input?: RolldownOptions['input'];
|
|
9
|
+
output: {
|
|
10
|
+
file?: string;
|
|
11
|
+
dir?: string;
|
|
12
|
+
};
|
|
13
|
+
userExternal?: string[];
|
|
14
|
+
userAliases?: Record<string, string>;
|
|
15
|
+
pluginOptions: PluginOptions;
|
|
16
|
+
}
|
|
17
|
+
export declare const setupForNode: (input: NodeFactoryInput) => Promise<NodeBuildConfig>;
|
package/lib/app/node.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// `--app node` Rolldown configuration factory.
|
|
2
|
+
//
|
|
3
|
+
// Same external set + alias map as the esbuild predecessor. The
|
|
4
|
+
// `createRequire` banner that esbuild needed for ESM-output CJS interop
|
|
5
|
+
// translates to Rolldown's `output.banner` directly — Rolldown itself does
|
|
6
|
+
// not synthesise a `require()` shim for ESM consumers of bundled CJS code.
|
|
7
|
+
import { aliasPlugin } from '../plugins/alias.js';
|
|
8
|
+
import { deepkitPlugin } from '@gjsify/rolldown-plugin-deepkit';
|
|
9
|
+
import { EXTERNALS_NODE } from '@gjsify/resolve-npm';
|
|
10
|
+
import { getAliasesForNode } from '../utils/alias.js';
|
|
11
|
+
import { globToEntryPoints } from '../utils/entry-points.js';
|
|
12
|
+
import { nodeModulesPathRewritePlugin, getBundleDirFromOutput, } from '../plugins/rewrite-node-modules-paths.js';
|
|
13
|
+
import { cssAsStringPlugin } from '../plugins/css-as-string.js';
|
|
14
|
+
export const setupForNode = async (input) => {
|
|
15
|
+
const userExternal = input.userExternal ?? [];
|
|
16
|
+
// node-datachannel is a native C++ addon that cannot be bundled — its
|
|
17
|
+
// `require('../build/Release/node_datachannel.node')` must resolve at
|
|
18
|
+
// runtime against the real node_modules tree.
|
|
19
|
+
//
|
|
20
|
+
// Note: Rolldown's `external` array does NOT support glob patterns the
|
|
21
|
+
// way esbuild's did (`gi://*`, `@girs/*`). We use a function predicate
|
|
22
|
+
// instead so the gi:// URI scheme and the @girs/ namespace are matched
|
|
23
|
+
// by prefix.
|
|
24
|
+
const exactExternal = [
|
|
25
|
+
...EXTERNALS_NODE,
|
|
26
|
+
'node-datachannel',
|
|
27
|
+
...userExternal,
|
|
28
|
+
];
|
|
29
|
+
const external = (id) => {
|
|
30
|
+
if (id.startsWith('gi://'))
|
|
31
|
+
return true;
|
|
32
|
+
if (id.startsWith('@girs/'))
|
|
33
|
+
return true;
|
|
34
|
+
if (exactExternal.includes(id))
|
|
35
|
+
return true;
|
|
36
|
+
return false;
|
|
37
|
+
};
|
|
38
|
+
const format = input.pluginOptions.format ?? 'esm';
|
|
39
|
+
const exclude = input.pluginOptions.exclude ?? [];
|
|
40
|
+
const entryPoints = await globToEntryPoints(input.input, exclude);
|
|
41
|
+
const aliasMap = {
|
|
42
|
+
...getAliasesForNode({ external }),
|
|
43
|
+
...(input.pluginOptions.aliases ?? {}),
|
|
44
|
+
...(input.userAliases ?? {}),
|
|
45
|
+
};
|
|
46
|
+
const bundleDir = getBundleDirFromOutput(input.output);
|
|
47
|
+
// Rolldown's CJS interop wraps bundled CJS via `__commonJSMin` and
|
|
48
|
+
// routes external Node-builtin `require()` through `__require` —
|
|
49
|
+
// both injected internally. Unlike esbuild we therefore don't need a
|
|
50
|
+
// top-of-bundle `const require = createRequire(...)` shim. Keeping
|
|
51
|
+
// one collides with bundled CJS sources that declare their own
|
|
52
|
+
// `const require = createRequire(...)` (e.g. yargs's ESM platform
|
|
53
|
+
// shim) — `SyntaxError: Identifier 'require' has already been
|
|
54
|
+
// declared`.
|
|
55
|
+
const banner = undefined;
|
|
56
|
+
const options = {
|
|
57
|
+
input: entryPoints,
|
|
58
|
+
platform: 'node',
|
|
59
|
+
external,
|
|
60
|
+
resolve: {
|
|
61
|
+
mainFields: format === 'esm' ? ['module', 'main', 'browser'] : ['main', 'module', 'browser'],
|
|
62
|
+
// CJS-priority conditions for Node bundles. Rolldown uses the first
|
|
63
|
+
// matching key, so including 'import' would route packages like ws
|
|
64
|
+
// v8 (whose exports map lists 'import' before 'require') through
|
|
65
|
+
// their incomplete ESM wrapper.
|
|
66
|
+
conditionNames: format === 'esm' ? ['require', 'node', 'module'] : ['require'],
|
|
67
|
+
},
|
|
68
|
+
transform: {
|
|
69
|
+
target: 'node24',
|
|
70
|
+
define: {
|
|
71
|
+
global: 'globalThis',
|
|
72
|
+
window: 'globalThis',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
output: {
|
|
76
|
+
...input.output,
|
|
77
|
+
format,
|
|
78
|
+
minify: false,
|
|
79
|
+
sourcemap: false,
|
|
80
|
+
banner,
|
|
81
|
+
// Single-bundle output. `codeSplitting: false` replaces the
|
|
82
|
+
// deprecated `inlineDynamicImports: true`.
|
|
83
|
+
codeSplitting: false,
|
|
84
|
+
},
|
|
85
|
+
treeshake: true,
|
|
86
|
+
};
|
|
87
|
+
const plugins = [
|
|
88
|
+
aliasPlugin({ entries: flattenAliases(aliasMap) }),
|
|
89
|
+
deepkitPlugin({ reflection: input.pluginOptions.reflection }),
|
|
90
|
+
cssAsStringPlugin(),
|
|
91
|
+
nodeModulesPathRewritePlugin({ bundleDir }),
|
|
92
|
+
];
|
|
93
|
+
return { options, plugins };
|
|
94
|
+
};
|
|
95
|
+
function flattenAliases(map) {
|
|
96
|
+
const out = {};
|
|
97
|
+
for (const [from, to] of Object.entries(map)) {
|
|
98
|
+
if (to)
|
|
99
|
+
out[from] = to;
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
package/lib/globals.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { resolveGlobalsList, writeRegisterInjectFile } from './utils/scan-globals.js';
|
|
2
|
+
export { detectFreeGlobals } from './utils/detect-free-globals.js';
|
|
3
|
+
export { detectAutoGlobals } from './utils/auto-globals.js';
|
|
4
|
+
export type { AutoGlobalsResult, DetectAutoGlobalsOptions, AnalysisOptions } from './utils/auto-globals.js';
|
package/lib/globals.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Public subpath export for the `--globals` CLI support.
|
|
2
|
+
//
|
|
3
|
+
// Consumed by `@gjsify/cli` to resolve the user's explicit `--globals` list
|
|
4
|
+
// (or auto-detect via the iterative multi-pass build) and write the inject
|
|
5
|
+
// stub that the plugin picks up via its `autoGlobalsInject` option. See the
|
|
6
|
+
// "Tree-shakeable Globals" section in AGENTS.md for the architecture.
|
|
7
|
+
export { resolveGlobalsList, writeRegisterInjectFile } from './utils/scan-globals.js';
|
|
8
|
+
export { detectFreeGlobals } from './utils/detect-free-globals.js';
|
|
9
|
+
export { detectAutoGlobals } from './utils/auto-globals.js';
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export * from './types/index.js';
|
|
2
|
+
export * from './utils/index.js';
|
|
3
|
+
export * from './app/index.js';
|
|
4
|
+
export * from './library/index.js';
|
|
5
|
+
export { REWRITE_FILTER, getBundleDirFromOutput, rewriteContents, shouldRewrite, nodeModulesPathRewritePlugin, } from './plugins/rewrite-node-modules-paths.js';
|
|
6
|
+
export type { NodeModulesPathRewriteOptions, RewriteResult, } from './plugins/rewrite-node-modules-paths.js';
|
|
7
|
+
export { processStubPlugin, GJS_PROCESS_STUB, composeBanner } from './plugins/process-stub.js';
|
|
8
|
+
export type { ProcessStubPluginOptions } from './plugins/process-stub.js';
|
|
9
|
+
export { cssAsStringPlugin } from './plugins/css-as-string.js';
|
|
10
|
+
export { shebangPlugin, GJS_SHEBANG } from './plugins/shebang.js';
|
|
11
|
+
export type { ShebangPluginOptions } from './plugins/shebang.js';
|
|
12
|
+
export { gjsImportsEmptyPlugin } from './plugins/gjs-imports-empty.js';
|
|
13
|
+
export * from './plugin.js';
|
|
14
|
+
import { gjsifyPlugin } from './plugin.js';
|
|
15
|
+
export { gjsifyPlugin };
|
|
16
|
+
export default gjsifyPlugin;
|
|
17
|
+
export * from '@gjsify/resolve-npm';
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Public re-exports for `@gjsify/rolldown-plugin-gjsify`.
|
|
2
|
+
export * from './types/index.js';
|
|
3
|
+
export * from './utils/index.js';
|
|
4
|
+
export * from './app/index.js';
|
|
5
|
+
export * from './library/index.js';
|
|
6
|
+
export { REWRITE_FILTER, getBundleDirFromOutput, rewriteContents, shouldRewrite, nodeModulesPathRewritePlugin, } from './plugins/rewrite-node-modules-paths.js';
|
|
7
|
+
export { processStubPlugin, GJS_PROCESS_STUB, composeBanner } from './plugins/process-stub.js';
|
|
8
|
+
export { cssAsStringPlugin } from './plugins/css-as-string.js';
|
|
9
|
+
export { shebangPlugin, GJS_SHEBANG } from './plugins/shebang.js';
|
|
10
|
+
export { gjsImportsEmptyPlugin } from './plugins/gjs-imports-empty.js';
|
|
11
|
+
export * from './plugin.js';
|
|
12
|
+
import { gjsifyPlugin } from './plugin.js';
|
|
13
|
+
export { gjsifyPlugin };
|
|
14
|
+
export default gjsifyPlugin;
|
|
15
|
+
export * from '@gjsify/resolve-npm';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { setupLib } from './lib.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RolldownOptions, RolldownPluginOption } from 'rolldown';
|
|
2
|
+
import type { PluginOptions } from '../types/plugin-options.js';
|
|
3
|
+
export interface LibBuildConfig {
|
|
4
|
+
options: RolldownOptions;
|
|
5
|
+
plugins: RolldownPluginOption[];
|
|
6
|
+
}
|
|
7
|
+
export interface LibFactoryInput {
|
|
8
|
+
input?: RolldownOptions['input'];
|
|
9
|
+
output: {
|
|
10
|
+
file?: string;
|
|
11
|
+
dir?: string;
|
|
12
|
+
};
|
|
13
|
+
userAliases?: Record<string, string>;
|
|
14
|
+
pluginOptions: PluginOptions;
|
|
15
|
+
}
|
|
16
|
+
export declare const setupLib: (input: LibFactoryInput) => Promise<LibBuildConfig>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// Library mode — multi-entry, unbundled output for republication on npm.
|
|
2
|
+
//
|
|
3
|
+
// Equivalent to esbuild's `bundle: false`: every input file is emitted
|
|
4
|
+
// 1:1 with its imports preserved as-is (resolved by Rolldown). The user
|
|
5
|
+
// alias map is applied so `node:fs` → `fs/promises` style remappings still
|
|
6
|
+
// work, and the JS extension table mirrors the esbuild predecessor.
|
|
7
|
+
//
|
|
8
|
+
// Targeting `esnext` and `platform: 'neutral'` matches the original
|
|
9
|
+
// library config: consumers downstream (downstream apps using `gjsify
|
|
10
|
+
// build --app gjs|node|browser`) re-bundle and apply their own target
|
|
11
|
+
// lowering. Library output stays maximally portable.
|
|
12
|
+
import { aliasPlugin } from '../plugins/alias.js';
|
|
13
|
+
import { cssAsStringPlugin } from '../plugins/css-as-string.js';
|
|
14
|
+
import { globToEntryPoints } from '../utils/entry-points.js';
|
|
15
|
+
export const setupLib = async (input) => {
|
|
16
|
+
// Derive output format from `library: 'esm' | 'cjs'` when the caller
|
|
17
|
+
// didn't pass `format` explicitly. The library type and the emitted
|
|
18
|
+
// module format are inseparable: a CJS-library build that emits ESM
|
|
19
|
+
// (or vice versa) is broken by definition.
|
|
20
|
+
const format = input.pluginOptions.format ?? input.pluginOptions.library ?? 'esm';
|
|
21
|
+
const exclude = input.pluginOptions.exclude ?? [];
|
|
22
|
+
const entryPoints = await globToEntryPoints(input.input, exclude);
|
|
23
|
+
// Derive `preserveModulesRoot` from the common ancestor of every
|
|
24
|
+
// resolved entry. Workspaces use various roots (`src/`, `src/ts/`,
|
|
25
|
+
// `lib/`); without stripping the right one, Rolldown emits paths
|
|
26
|
+
// like `lib/esm/<root>/<file>.js` instead of `lib/esm/<file>.js`,
|
|
27
|
+
// which doesn't match the package.json `exports` map.
|
|
28
|
+
const preserveModulesRoot = computeCommonRoot(entryPoints);
|
|
29
|
+
const aliasMap = {
|
|
30
|
+
...(input.pluginOptions.aliases ?? {}),
|
|
31
|
+
...(input.userAliases ?? {}),
|
|
32
|
+
};
|
|
33
|
+
// Library mode keeps all third-party / workspace imports as-is so the
|
|
34
|
+
// emitted package re-exports its dep tree by reference. Rolldown's
|
|
35
|
+
// default behaviour would inline workspace packages into the output
|
|
36
|
+
// directory; we mark anything not starting with `./` or `/` as external.
|
|
37
|
+
const external = (id) => {
|
|
38
|
+
if (id.startsWith('./') || id.startsWith('../') || id.startsWith('/'))
|
|
39
|
+
return false;
|
|
40
|
+
return true;
|
|
41
|
+
};
|
|
42
|
+
const options = {
|
|
43
|
+
input: entryPoints,
|
|
44
|
+
platform: 'neutral',
|
|
45
|
+
external,
|
|
46
|
+
resolve: {
|
|
47
|
+
mainFields: format === 'esm' ? ['module', 'main'] : ['main'],
|
|
48
|
+
conditionNames: format === 'esm' ? ['module', 'import'] : ['require'],
|
|
49
|
+
},
|
|
50
|
+
transform: { target: 'esnext' },
|
|
51
|
+
output: {
|
|
52
|
+
...input.output,
|
|
53
|
+
format,
|
|
54
|
+
// Library mode = preserve module structure (multi-file output,
|
|
55
|
+
// imports resolved but not bundled).
|
|
56
|
+
preserveModules: true,
|
|
57
|
+
// Strip the source root from the emitted paths. Without this,
|
|
58
|
+
// Rolldown keeps the full project-relative path. The root is
|
|
59
|
+
// computed from the common ancestor of resolved entries.
|
|
60
|
+
preserveModulesRoot,
|
|
61
|
+
minify: false,
|
|
62
|
+
sourcemap: false,
|
|
63
|
+
},
|
|
64
|
+
treeshake: false,
|
|
65
|
+
};
|
|
66
|
+
const plugins = [
|
|
67
|
+
aliasPlugin({ entries: flattenAliases(aliasMap) }),
|
|
68
|
+
// Rolldown removed experimental CSS bundling — `.css` files would
|
|
69
|
+
// error at the bundler level. Library-mode packages that bundle
|
|
70
|
+
// CSS as a string (e.g. `@gjsify/adwaita-fonts/index.css`) need
|
|
71
|
+
// the same `load` hook the app factories install. The result is a
|
|
72
|
+
// tiny JS module re-exporting the CSS source, which preserveModules
|
|
73
|
+
// emits 1:1.
|
|
74
|
+
cssAsStringPlugin(),
|
|
75
|
+
];
|
|
76
|
+
return { options, plugins };
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Compute the common-ancestor directory of a set of entry paths so
|
|
80
|
+
* Rolldown's `preserveModulesRoot` strips the right prefix from emitted
|
|
81
|
+
* file paths. Falls back to `'src'` when there are no entries or the
|
|
82
|
+
* entries don't share a meaningful prefix.
|
|
83
|
+
*/
|
|
84
|
+
function computeCommonRoot(entries) {
|
|
85
|
+
const paths = entries === undefined
|
|
86
|
+
? []
|
|
87
|
+
: typeof entries === 'string'
|
|
88
|
+
? [entries]
|
|
89
|
+
: Array.isArray(entries)
|
|
90
|
+
? entries
|
|
91
|
+
: Object.values(entries);
|
|
92
|
+
if (paths.length === 0)
|
|
93
|
+
return 'src';
|
|
94
|
+
const split = paths.map((p) => p.split('/').filter(Boolean));
|
|
95
|
+
const head = split[0];
|
|
96
|
+
let i = 0;
|
|
97
|
+
for (; i < head.length; i++) {
|
|
98
|
+
const seg = head[i];
|
|
99
|
+
if (!split.every((parts) => parts[i] === seg))
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
if (i === 0)
|
|
103
|
+
return 'src';
|
|
104
|
+
// Drop the basename if the common prefix points at a single file.
|
|
105
|
+
const commonParts = head.slice(0, i);
|
|
106
|
+
// Heuristic: treat the prefix as a directory when at least one path
|
|
107
|
+
// has more segments after it.
|
|
108
|
+
const hasMoreAfter = split.some((parts) => parts.length > i);
|
|
109
|
+
return hasMoreAfter ? commonParts.join('/') : commonParts.slice(0, -1).join('/') || 'src';
|
|
110
|
+
}
|
|
111
|
+
function flattenAliases(map) {
|
|
112
|
+
const out = {};
|
|
113
|
+
for (const [from, to] of Object.entries(map)) {
|
|
114
|
+
if (to)
|
|
115
|
+
out[from] = to;
|
|
116
|
+
}
|
|
117
|
+
return out;
|
|
118
|
+
}
|