@gjsify/rolldown-plugin-gjsify 0.4.0 → 0.4.3
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/package.json +71 -67
- package/src/app/browser.ts +0 -101
- package/src/app/gjs.ts +0 -334
- package/src/app/index.ts +0 -6
- package/src/app/node.ts +0 -127
- package/src/globals.ts +0 -11
- package/src/index.ts +0 -34
- package/src/library/index.ts +0 -2
- package/src/library/lib.ts +0 -141
- package/src/plugin.ts +0 -96
- package/src/plugins/alias.ts +0 -61
- package/src/plugins/css-as-string.ts +0 -189
- package/src/plugins/gjs-imports-empty.ts +0 -29
- package/src/plugins/process-stub.ts +0 -91
- package/src/plugins/rewrite-node-modules-paths.ts +0 -169
- package/src/plugins/shebang.ts +0 -93
- package/src/plugins/text-loader.ts +0 -54
- package/src/shims/console-gjs.ts +0 -25
- package/src/shims/unicorn-magic.ts +0 -75
- package/src/types/app.ts +0 -1
- package/src/types/index.ts +0 -3
- package/src/types/plugin-options.ts +0 -48
- package/src/types/resolve-alias-options.ts +0 -1
- package/src/utils/alias.ts +0 -46
- package/src/utils/auto-globals.ts +0 -321
- package/src/utils/detect-free-globals.ts +0 -284
- package/src/utils/entry-points.ts +0 -48
- package/src/utils/extension.ts +0 -7
- package/src/utils/index.ts +0 -7
- package/src/utils/inline-static-reads.ts +0 -541
- package/src/utils/merge.ts +0 -22
- package/src/utils/scan-globals.ts +0 -91
- package/tsconfig.json +0 -16
package/src/app/node.ts
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
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
|
-
|
|
8
|
-
import { aliasPlugin } from '../plugins/alias.js';
|
|
9
|
-
import type { RolldownOptions, RolldownPluginOption } from 'rolldown';
|
|
10
|
-
|
|
11
|
-
import { deepkitPlugin } from '@gjsify/rolldown-plugin-deepkit';
|
|
12
|
-
import { EXTERNALS_NODE } from '@gjsify/resolve-npm';
|
|
13
|
-
|
|
14
|
-
import type { PluginOptions } from '../types/plugin-options.js';
|
|
15
|
-
import { getAliasesForNode } from '../utils/alias.js';
|
|
16
|
-
import { globToEntryPoints } from '../utils/entry-points.js';
|
|
17
|
-
import {
|
|
18
|
-
nodeModulesPathRewritePlugin,
|
|
19
|
-
getBundleDirFromOutput,
|
|
20
|
-
} from '../plugins/rewrite-node-modules-paths.js';
|
|
21
|
-
import { cssAsStringPlugin } from '../plugins/css-as-string.js';
|
|
22
|
-
|
|
23
|
-
export interface NodeBuildConfig {
|
|
24
|
-
options: RolldownOptions;
|
|
25
|
-
plugins: RolldownPluginOption[];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface NodeFactoryInput {
|
|
29
|
-
input?: RolldownOptions['input'];
|
|
30
|
-
output: { file?: string; dir?: string };
|
|
31
|
-
userExternal?: string[];
|
|
32
|
-
userAliases?: Record<string, string>;
|
|
33
|
-
pluginOptions: PluginOptions;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export const setupForNode = async (input: NodeFactoryInput): Promise<NodeBuildConfig> => {
|
|
37
|
-
const userExternal = input.userExternal ?? [];
|
|
38
|
-
// node-datachannel is a native C++ addon that cannot be bundled — its
|
|
39
|
-
// `require('../build/Release/node_datachannel.node')` must resolve at
|
|
40
|
-
// runtime against the real node_modules tree.
|
|
41
|
-
//
|
|
42
|
-
// Note: Rolldown's `external` array does NOT support glob patterns the
|
|
43
|
-
// way esbuild's did (`gi://*`, `@girs/*`). We use a function predicate
|
|
44
|
-
// instead so the gi:// URI scheme and the @girs/ namespace are matched
|
|
45
|
-
// by prefix.
|
|
46
|
-
const exactExternal = [
|
|
47
|
-
...EXTERNALS_NODE as string[],
|
|
48
|
-
'node-datachannel',
|
|
49
|
-
...userExternal,
|
|
50
|
-
];
|
|
51
|
-
const external = (id: string): boolean => {
|
|
52
|
-
if (id.startsWith('gi://')) return true;
|
|
53
|
-
if (id.startsWith('@girs/')) return true;
|
|
54
|
-
if (exactExternal.includes(id)) return true;
|
|
55
|
-
return false;
|
|
56
|
-
};
|
|
57
|
-
const format = input.pluginOptions.format ?? 'esm';
|
|
58
|
-
|
|
59
|
-
const exclude = input.pluginOptions.exclude ?? [];
|
|
60
|
-
const entryPoints = await globToEntryPoints(input.input, exclude);
|
|
61
|
-
|
|
62
|
-
const aliasMap = {
|
|
63
|
-
...getAliasesForNode({ external }),
|
|
64
|
-
...(input.pluginOptions.aliases ?? {}),
|
|
65
|
-
...(input.userAliases ?? {}),
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const bundleDir = getBundleDirFromOutput(input.output);
|
|
69
|
-
|
|
70
|
-
// Rolldown's CJS interop wraps bundled CJS via `__commonJSMin` and
|
|
71
|
-
// routes external Node-builtin `require()` through `__require` —
|
|
72
|
-
// both injected internally. Unlike esbuild we therefore don't need a
|
|
73
|
-
// top-of-bundle `const require = createRequire(...)` shim. Keeping
|
|
74
|
-
// one collides with bundled CJS sources that declare their own
|
|
75
|
-
// `const require = createRequire(...)` (e.g. yargs's ESM platform
|
|
76
|
-
// shim) — `SyntaxError: Identifier 'require' has already been
|
|
77
|
-
// declared`.
|
|
78
|
-
const banner: string | undefined = undefined;
|
|
79
|
-
|
|
80
|
-
const options: RolldownOptions = {
|
|
81
|
-
input: entryPoints,
|
|
82
|
-
platform: 'node',
|
|
83
|
-
external,
|
|
84
|
-
resolve: {
|
|
85
|
-
mainFields: format === 'esm' ? ['module', 'main', 'browser'] : ['main', 'module', 'browser'],
|
|
86
|
-
// CJS-priority conditions for Node bundles. Rolldown uses the first
|
|
87
|
-
// matching key, so including 'import' would route packages like ws
|
|
88
|
-
// v8 (whose exports map lists 'import' before 'require') through
|
|
89
|
-
// their incomplete ESM wrapper.
|
|
90
|
-
conditionNames: format === 'esm' ? ['require', 'node', 'module'] : ['require'],
|
|
91
|
-
},
|
|
92
|
-
transform: {
|
|
93
|
-
target: 'node24',
|
|
94
|
-
define: {
|
|
95
|
-
global: 'globalThis',
|
|
96
|
-
window: 'globalThis',
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
output: {
|
|
100
|
-
...input.output,
|
|
101
|
-
format,
|
|
102
|
-
sourcemap: false,
|
|
103
|
-
banner,
|
|
104
|
-
// Single-bundle output. `codeSplitting: false` replaces the
|
|
105
|
-
// deprecated `inlineDynamicImports: true`.
|
|
106
|
-
codeSplitting: false,
|
|
107
|
-
},
|
|
108
|
-
treeshake: true,
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
const plugins: RolldownPluginOption[] = [
|
|
112
|
-
aliasPlugin({ entries: flattenAliases(aliasMap) }),
|
|
113
|
-
deepkitPlugin({ reflection: input.pluginOptions.reflection }),
|
|
114
|
-
cssAsStringPlugin(),
|
|
115
|
-
nodeModulesPathRewritePlugin({ bundleDir }),
|
|
116
|
-
];
|
|
117
|
-
|
|
118
|
-
return { options, plugins };
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
function flattenAliases(map: Record<string, string>): Record<string, string> {
|
|
122
|
-
const out: Record<string, string> = {};
|
|
123
|
-
for (const [from, to] of Object.entries(map)) {
|
|
124
|
-
if (to) out[from] = to;
|
|
125
|
-
}
|
|
126
|
-
return out;
|
|
127
|
-
}
|
package/src/globals.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
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
|
-
|
|
8
|
-
export { resolveGlobalsList, writeRegisterInjectFile } from './utils/scan-globals.js';
|
|
9
|
-
export { detectFreeGlobals } from './utils/detect-free-globals.js';
|
|
10
|
-
export { detectAutoGlobals } from './utils/auto-globals.js';
|
|
11
|
-
export type { AutoGlobalsResult, DetectAutoGlobalsOptions, AnalysisOptions } from './utils/auto-globals.js';
|
package/src/index.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// Public re-exports for `@gjsify/rolldown-plugin-gjsify`.
|
|
2
|
-
|
|
3
|
-
export * from './types/index.js';
|
|
4
|
-
export * from './utils/index.js';
|
|
5
|
-
export * from './app/index.js';
|
|
6
|
-
export * from './library/index.js';
|
|
7
|
-
|
|
8
|
-
export {
|
|
9
|
-
REWRITE_FILTER,
|
|
10
|
-
getBundleDirFromOutput,
|
|
11
|
-
rewriteContents,
|
|
12
|
-
shouldRewrite,
|
|
13
|
-
nodeModulesPathRewritePlugin,
|
|
14
|
-
} from './plugins/rewrite-node-modules-paths.js';
|
|
15
|
-
export type {
|
|
16
|
-
NodeModulesPathRewriteOptions,
|
|
17
|
-
RewriteResult,
|
|
18
|
-
} from './plugins/rewrite-node-modules-paths.js';
|
|
19
|
-
|
|
20
|
-
export { processStubPlugin, GJS_PROCESS_STUB, composeBanner } from './plugins/process-stub.js';
|
|
21
|
-
export type { ProcessStubPluginOptions } from './plugins/process-stub.js';
|
|
22
|
-
export { cssAsStringPlugin } from './plugins/css-as-string.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';
|
|
26
|
-
export type { ShebangPluginOptions } from './plugins/shebang.js';
|
|
27
|
-
export { gjsImportsEmptyPlugin } from './plugins/gjs-imports-empty.js';
|
|
28
|
-
|
|
29
|
-
export * from './plugin.js';
|
|
30
|
-
import { gjsifyPlugin } from './plugin.js';
|
|
31
|
-
export { gjsifyPlugin };
|
|
32
|
-
export default gjsifyPlugin;
|
|
33
|
-
|
|
34
|
-
export * from '@gjsify/resolve-npm';
|
package/src/library/index.ts
DELETED
package/src/library/lib.ts
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
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
|
-
|
|
13
|
-
import { aliasPlugin } from '../plugins/alias.js';
|
|
14
|
-
import { cssAsStringPlugin } from '../plugins/css-as-string.js';
|
|
15
|
-
import type { RolldownOptions, RolldownPluginOption } from 'rolldown';
|
|
16
|
-
|
|
17
|
-
import type { PluginOptions } from '../types/plugin-options.js';
|
|
18
|
-
import { globToEntryPoints } from '../utils/entry-points.js';
|
|
19
|
-
|
|
20
|
-
export interface LibBuildConfig {
|
|
21
|
-
options: RolldownOptions;
|
|
22
|
-
plugins: RolldownPluginOption[];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface LibFactoryInput {
|
|
26
|
-
input?: RolldownOptions['input'];
|
|
27
|
-
output: { file?: string; dir?: string };
|
|
28
|
-
userAliases?: Record<string, string>;
|
|
29
|
-
pluginOptions: PluginOptions;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export const setupLib = async (input: LibFactoryInput): Promise<LibBuildConfig> => {
|
|
33
|
-
// Derive output format from `library: 'esm' | 'cjs'` when the caller
|
|
34
|
-
// didn't pass `format` explicitly. The library type and the emitted
|
|
35
|
-
// module format are inseparable: a CJS-library build that emits ESM
|
|
36
|
-
// (or vice versa) is broken by definition.
|
|
37
|
-
const format = input.pluginOptions.format ?? input.pluginOptions.library ?? 'esm';
|
|
38
|
-
|
|
39
|
-
const exclude = input.pluginOptions.exclude ?? [];
|
|
40
|
-
const entryPoints = await globToEntryPoints(input.input, exclude);
|
|
41
|
-
|
|
42
|
-
// Derive `preserveModulesRoot` from the common ancestor of every
|
|
43
|
-
// resolved entry. Workspaces use various roots (`src/`, `src/ts/`,
|
|
44
|
-
// `lib/`); without stripping the right one, Rolldown emits paths
|
|
45
|
-
// like `lib/esm/<root>/<file>.js` instead of `lib/esm/<file>.js`,
|
|
46
|
-
// which doesn't match the package.json `exports` map.
|
|
47
|
-
const preserveModulesRoot = computeCommonRoot(entryPoints);
|
|
48
|
-
|
|
49
|
-
const aliasMap = {
|
|
50
|
-
...(input.pluginOptions.aliases ?? {}),
|
|
51
|
-
...(input.userAliases ?? {}),
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
// Library mode keeps all third-party / workspace imports as-is so the
|
|
55
|
-
// emitted package re-exports its dep tree by reference. Rolldown's
|
|
56
|
-
// default behaviour would inline workspace packages into the output
|
|
57
|
-
// directory; we mark anything not starting with `./` or `/` as external.
|
|
58
|
-
const external = (id: string): boolean => {
|
|
59
|
-
if (id.startsWith('./') || id.startsWith('../') || id.startsWith('/')) return false;
|
|
60
|
-
return true;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const options: RolldownOptions = {
|
|
64
|
-
input: entryPoints,
|
|
65
|
-
platform: 'neutral',
|
|
66
|
-
external,
|
|
67
|
-
resolve: {
|
|
68
|
-
mainFields: format === 'esm' ? ['module', 'main'] : ['main'],
|
|
69
|
-
conditionNames: format === 'esm' ? ['module', 'import'] : ['require'],
|
|
70
|
-
},
|
|
71
|
-
transform: { target: 'esnext' },
|
|
72
|
-
output: {
|
|
73
|
-
...input.output,
|
|
74
|
-
format,
|
|
75
|
-
// Library mode = preserve module structure (multi-file output,
|
|
76
|
-
// imports resolved but not bundled).
|
|
77
|
-
preserveModules: true,
|
|
78
|
-
// Strip the source root from the emitted paths. Without this,
|
|
79
|
-
// Rolldown keeps the full project-relative path. The root is
|
|
80
|
-
// computed from the common ancestor of resolved entries.
|
|
81
|
-
preserveModulesRoot,
|
|
82
|
-
sourcemap: false,
|
|
83
|
-
},
|
|
84
|
-
treeshake: false,
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const plugins: RolldownPluginOption[] = [
|
|
88
|
-
aliasPlugin({ entries: flattenAliases(aliasMap) }),
|
|
89
|
-
// Rolldown removed experimental CSS bundling — `.css` files would
|
|
90
|
-
// error at the bundler level. Library-mode packages that bundle
|
|
91
|
-
// CSS as a string (e.g. `@gjsify/adwaita-fonts/index.css`) need
|
|
92
|
-
// the same `load` hook the app factories install. The result is a
|
|
93
|
-
// tiny JS module re-exporting the CSS source, which preserveModules
|
|
94
|
-
// emits 1:1.
|
|
95
|
-
cssAsStringPlugin(),
|
|
96
|
-
];
|
|
97
|
-
|
|
98
|
-
return { options, plugins };
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Compute the common-ancestor directory of a set of entry paths so
|
|
103
|
-
* Rolldown's `preserveModulesRoot` strips the right prefix from emitted
|
|
104
|
-
* file paths. Falls back to `'src'` when there are no entries or the
|
|
105
|
-
* entries don't share a meaningful prefix.
|
|
106
|
-
*/
|
|
107
|
-
function computeCommonRoot(
|
|
108
|
-
entries: ReturnType<typeof globToEntryPoints> extends Promise<infer T> ? T : never,
|
|
109
|
-
): string {
|
|
110
|
-
const paths: string[] = entries === undefined
|
|
111
|
-
? []
|
|
112
|
-
: typeof entries === 'string'
|
|
113
|
-
? [entries]
|
|
114
|
-
: Array.isArray(entries)
|
|
115
|
-
? entries
|
|
116
|
-
: Object.values(entries);
|
|
117
|
-
if (paths.length === 0) return 'src';
|
|
118
|
-
|
|
119
|
-
const split = paths.map((p) => p.split('/').filter(Boolean));
|
|
120
|
-
const head = split[0];
|
|
121
|
-
let i = 0;
|
|
122
|
-
for (; i < head.length; i++) {
|
|
123
|
-
const seg = head[i];
|
|
124
|
-
if (!split.every((parts) => parts[i] === seg)) break;
|
|
125
|
-
}
|
|
126
|
-
if (i === 0) return 'src';
|
|
127
|
-
// Drop the basename if the common prefix points at a single file.
|
|
128
|
-
const commonParts = head.slice(0, i);
|
|
129
|
-
// Heuristic: treat the prefix as a directory when at least one path
|
|
130
|
-
// has more segments after it.
|
|
131
|
-
const hasMoreAfter = split.some((parts) => parts.length > i);
|
|
132
|
-
return hasMoreAfter ? commonParts.join('/') : commonParts.slice(0, -1).join('/') || 'src';
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function flattenAliases(map: Record<string, string>): Record<string, string> {
|
|
136
|
-
const out: Record<string, string> = {};
|
|
137
|
-
for (const [from, to] of Object.entries(map)) {
|
|
138
|
-
if (to) out[from] = to;
|
|
139
|
-
}
|
|
140
|
-
return out;
|
|
141
|
-
}
|
package/src/plugin.ts
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
// `gjsifyPlugin` — orchestrator entry point.
|
|
2
|
-
//
|
|
3
|
-
// Picks the platform-specific factory based on `pluginOptions.app`
|
|
4
|
-
// (or `library` for library mode) and returns the resolved Rolldown
|
|
5
|
-
// configuration. Unlike the esbuild predecessor, this is NOT a single
|
|
6
|
-
// `Plugin` object — it returns a *config bundle* the caller composes into
|
|
7
|
-
// `rolldown(opts)`, because Rolldown does not have esbuild's `setup(build)`
|
|
8
|
-
// hook through which a single plugin can mutate `build.initialOptions`.
|
|
9
|
-
//
|
|
10
|
-
// The CLI consumer (`@gjsify/cli`) calls `gjsifyPlugin(...)` to get back
|
|
11
|
-
// `{ options, plugins }`, then calls `rolldown({ ...options, plugins:
|
|
12
|
-
// [...userPlugins, ...plugins] })`.
|
|
13
|
-
|
|
14
|
-
import type { RolldownOptions, RolldownPluginOption } from 'rolldown';
|
|
15
|
-
import type { PluginOptions } from './types/plugin-options.js';
|
|
16
|
-
import { setupForGjs, setupForNode, setupForBrowser } from './app/index.js';
|
|
17
|
-
import { setupLib } from './library/index.js';
|
|
18
|
-
|
|
19
|
-
export interface GjsifyConfig {
|
|
20
|
-
options: RolldownOptions;
|
|
21
|
-
plugins: RolldownPluginOption[];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface GjsifyPluginInput {
|
|
25
|
-
input?: RolldownOptions['input'];
|
|
26
|
-
output: { file?: string; dir?: string };
|
|
27
|
-
userExternal?: string[];
|
|
28
|
-
userBanner?: string;
|
|
29
|
-
userAliases?: Record<string, string>;
|
|
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;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Build the Rolldown configuration template + plugin array for the given
|
|
41
|
-
* pluginOptions. The caller composes the returned `options.plugins` with
|
|
42
|
-
* its own user plugins and passes the merged options to `rolldown(...)`.
|
|
43
|
-
*/
|
|
44
|
-
export const gjsifyPlugin = async (
|
|
45
|
-
input: GjsifyPluginInput,
|
|
46
|
-
pluginOptions: PluginOptions = {},
|
|
47
|
-
): Promise<GjsifyConfig> => {
|
|
48
|
-
if (pluginOptions.library) {
|
|
49
|
-
switch (pluginOptions.library) {
|
|
50
|
-
case 'esm':
|
|
51
|
-
case 'cjs':
|
|
52
|
-
return await setupLib({
|
|
53
|
-
input: input.input,
|
|
54
|
-
output: input.output,
|
|
55
|
-
userAliases: input.userAliases,
|
|
56
|
-
pluginOptions,
|
|
57
|
-
});
|
|
58
|
-
default:
|
|
59
|
-
throw new TypeError('Unknown library type: ' + pluginOptions.library);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const app = pluginOptions.app ?? 'gjs';
|
|
64
|
-
switch (app) {
|
|
65
|
-
case 'gjs':
|
|
66
|
-
return await setupForGjs({
|
|
67
|
-
input: input.input,
|
|
68
|
-
output: input.output,
|
|
69
|
-
userExternal: input.userExternal,
|
|
70
|
-
userBanner: input.userBanner,
|
|
71
|
-
userAliases: input.userAliases,
|
|
72
|
-
shebang: input.shebang,
|
|
73
|
-
pluginOptions,
|
|
74
|
-
});
|
|
75
|
-
case 'node':
|
|
76
|
-
return await setupForNode({
|
|
77
|
-
input: input.input,
|
|
78
|
-
output: input.output,
|
|
79
|
-
userExternal: input.userExternal,
|
|
80
|
-
userAliases: input.userAliases,
|
|
81
|
-
pluginOptions,
|
|
82
|
-
});
|
|
83
|
-
case 'browser':
|
|
84
|
-
return await setupForBrowser({
|
|
85
|
-
input: input.input,
|
|
86
|
-
output: input.output,
|
|
87
|
-
userExternal: input.userExternal,
|
|
88
|
-
userAliases: input.userAliases,
|
|
89
|
-
pluginOptions,
|
|
90
|
-
});
|
|
91
|
-
default:
|
|
92
|
-
throw new TypeError('Unknown app platform: ' + app);
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
export default gjsifyPlugin;
|
package/src/plugins/alias.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
// Custom alias plugin — `@rollup/plugin-alias` returns the rewritten
|
|
2
|
-
// specifier as the resolved id, then leaves Rolldown's default resolver to
|
|
3
|
-
// load it. That fails for workspace package targets (`@gjsify/crypto` etc.)
|
|
4
|
-
// when the importer's package.json doesn't list them as direct deps —
|
|
5
|
-
// Rolldown rejects packages that aren't declared in the importer's manifest
|
|
6
|
-
// even when they exist in a hoisted workspace `node_modules`.
|
|
7
|
-
//
|
|
8
|
-
// This plugin instead calls `this.resolve(target, importer, { skipSelf: true })`
|
|
9
|
-
// which goes through the full plugin chain (including any subsequent resolvers)
|
|
10
|
-
// and resolves to a real file path the default loader can read.
|
|
11
|
-
//
|
|
12
|
-
// Behaviour preserved from the esbuild predecessor's `aliasPlugin`:
|
|
13
|
-
// - exact string match (no prefix-aware semantics needed at this layer)
|
|
14
|
-
// - `node:<name>` specifiers map to the same target as `<name>`
|
|
15
|
-
// (handled in the alias-builder helpers, not here).
|
|
16
|
-
//
|
|
17
|
-
// `extraOptions.kind` is forwarded to `this.resolve()` so package.json
|
|
18
|
-
// `exports` conditions ("import" / "require") match the original call site.
|
|
19
|
-
// Without this, a CJS `require('stream')` in a bundled npm package would
|
|
20
|
-
// resolve through the "import" condition (Rolldown's default), bypassing the
|
|
21
|
-
// `cjs-compat.cjs` shim that unwraps named-export ESM modules to their
|
|
22
|
-
// constructor — breaking `util.inherits(Child, Stream)` patterns.
|
|
23
|
-
|
|
24
|
-
import type { Plugin } from 'rolldown';
|
|
25
|
-
|
|
26
|
-
export interface AliasPluginOptions {
|
|
27
|
-
entries: Record<string, string>;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function aliasPlugin(options: AliasPluginOptions): Plugin {
|
|
31
|
-
const entries = options.entries;
|
|
32
|
-
const keys = Object.keys(entries);
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
name: 'gjsify-alias',
|
|
36
|
-
resolveId: {
|
|
37
|
-
order: 'pre' as const,
|
|
38
|
-
async handler(source, importer, extraOptions) {
|
|
39
|
-
if (!Object.prototype.hasOwnProperty.call(entries, source)) {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
const target = entries[source];
|
|
43
|
-
// Self-reference guard: if a user maps a specifier to itself
|
|
44
|
-
// (rare but legal), skip to avoid infinite loops.
|
|
45
|
-
if (target === source) return null;
|
|
46
|
-
|
|
47
|
-
const resolved = await this.resolve(target, importer, {
|
|
48
|
-
skipSelf: true,
|
|
49
|
-
kind: extraOptions?.kind,
|
|
50
|
-
});
|
|
51
|
-
if (resolved !== null) {
|
|
52
|
-
return resolved;
|
|
53
|
-
}
|
|
54
|
-
// Fall through to other resolvers if we couldn't load it
|
|
55
|
-
// ourselves. `null` from a `pre`-order resolveId lets the
|
|
56
|
-
// default chain continue.
|
|
57
|
-
return null;
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
}
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
// Wrap `.css` imports as JS string default exports.
|
|
2
|
-
//
|
|
3
|
-
// Rolldown's experimental CSS bundling was removed (see
|
|
4
|
-
// https://github.com/rolldown/rolldown/issues/4271) — when the bundler
|
|
5
|
-
// sees a `.css` extension it errors out unless something else loads the
|
|
6
|
-
// file first. We hook into `load` (with a path filter) BEFORE Rolldown's
|
|
7
|
-
// CSS classification fires and emit a JS module whose default export is
|
|
8
|
-
// the CSS source as a string. Consumers can then do:
|
|
9
|
-
//
|
|
10
|
-
// import css from './app.css';
|
|
11
|
-
// provider.load_from_string(css);
|
|
12
|
-
//
|
|
13
|
-
// — the canonical pattern for `Gtk.CssProvider` under GJS.
|
|
14
|
-
//
|
|
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
|
-
// Backend selection (Phase D-2 decision matrix in
|
|
23
|
-
// `docs/poc/lightningcss-decision.md`):
|
|
24
|
-
//
|
|
25
|
-
// 1. `@gjsify/lightningcss-native` when its prebuild is loadable on
|
|
26
|
-
// the running architecture (3-5× faster than the WASM track,
|
|
27
|
-
// ~960× faster cold init). Only relevant when `gjsify build`
|
|
28
|
-
// itself runs under GJS (Phase D-3).
|
|
29
|
-
// 2. npm `lightningcss` for everything else (Node, unsupported
|
|
30
|
-
// arches, dev machines without the prebuild). Existing behavior;
|
|
31
|
-
// keeps the regular dependency on this package.
|
|
32
|
-
//
|
|
33
|
-
// Selection is lazy and silent — the first `.css` load probes for
|
|
34
|
-
// the native bridge once, caches the answer, and routes the rest of
|
|
35
|
-
// the build through the chosen backend. Set the env var
|
|
36
|
-
// `GJSIFY_CSS_BACKEND={native|npm}` to force a specific backend
|
|
37
|
-
// (mainly useful for benchmarking + the integration suite).
|
|
38
|
-
|
|
39
|
-
import { readFile } from 'node:fs/promises';
|
|
40
|
-
import type { Plugin } from 'rolldown';
|
|
41
|
-
|
|
42
|
-
export interface CssAsStringOptions {
|
|
43
|
-
/**
|
|
44
|
-
* lightningcss browser targets passed to `bundleAsync`. When set,
|
|
45
|
-
* nesting + modern syntax are lowered for the given engines. The
|
|
46
|
-
* GJS orchestrator defaults this to `{ firefox: 60 << 16 }` to
|
|
47
|
-
* match the GTK4 CSS parser. Omit or leave undefined to skip
|
|
48
|
-
* lowering (output stays as-authored except for `@import` inlining).
|
|
49
|
-
*/
|
|
50
|
-
targets?: import('lightningcss').Targets;
|
|
51
|
-
/**
|
|
52
|
-
* When true (default), `@import` statements are resolved by
|
|
53
|
-
* lightningcss `bundleAsync`. Set false to fall back to a plain
|
|
54
|
-
* `readFile` — useful only when you want to keep `@import` strings
|
|
55
|
-
* verbatim in the bundled JS (rare).
|
|
56
|
-
*/
|
|
57
|
-
bundle?: boolean;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
interface BundleResult {
|
|
61
|
-
code: Uint8Array;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
type Bundler = (filename: string, targets: import('lightningcss').Targets | undefined) => Promise<BundleResult>;
|
|
65
|
-
|
|
66
|
-
let _bundlerPromise: Promise<Bundler> | null = null;
|
|
67
|
-
|
|
68
|
-
async function pickBundler(): Promise<Bundler> {
|
|
69
|
-
const forced = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env?.GJSIFY_CSS_BACKEND;
|
|
70
|
-
|
|
71
|
-
if (forced === 'npm') return loadNpmBundler();
|
|
72
|
-
if (forced === 'native') {
|
|
73
|
-
const native = await tryLoadNativeBundler();
|
|
74
|
-
if (!native) throw new Error('GJSIFY_CSS_BACKEND=native but @gjsify/lightningcss-native is not loadable');
|
|
75
|
-
return native;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const native = await tryLoadNativeBundler();
|
|
79
|
-
return native ?? loadNpmBundler();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Local mirror of the @gjsify/lightningcss-native surface we touch. We
|
|
83
|
-
// can't rely on the published types here because the package is an
|
|
84
|
-
// OPTIONAL peer dep — under Node it's not installed, so `import type`
|
|
85
|
-
// from it would break tsc on every Node consumer. Local interface
|
|
86
|
-
// keeps the type narrow + decouples the plugin's typecheck from
|
|
87
|
-
// whether the prebuild package is installed.
|
|
88
|
-
interface NativeLightningcssSurface {
|
|
89
|
-
hasNativeLightningcss(): boolean;
|
|
90
|
-
bundle(input: {
|
|
91
|
-
filename: string;
|
|
92
|
-
targets?: string;
|
|
93
|
-
minify?: boolean;
|
|
94
|
-
sourceMap?: boolean;
|
|
95
|
-
errorRecovery?: boolean;
|
|
96
|
-
}): { code: Uint8Array; map?: Uint8Array };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async function tryLoadNativeBundler(): Promise<Bundler | null> {
|
|
100
|
-
// The native bridge only exists under GJS — `imports.gi` marker. Skip
|
|
101
|
-
// the dynamic import entirely on Node so it doesn't even register as a
|
|
102
|
-
// resolved dep, which would inflate the CLI's bundled output.
|
|
103
|
-
const isGjs = typeof (globalThis as { imports?: { gi?: unknown } }).imports?.gi !== 'undefined';
|
|
104
|
-
if (!isGjs) return null;
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
// Indirect specifier so tsc + Rolldown don't try to resolve the
|
|
108
|
-
// optional peer dep at build time. Resolution happens only at
|
|
109
|
-
// runtime under GJS (where the prebuild is installed).
|
|
110
|
-
const specifier = '@gjsify/lightningcss-native';
|
|
111
|
-
const mod = (await import(/* @vite-ignore */ specifier)) as NativeLightningcssSurface;
|
|
112
|
-
if (!mod.hasNativeLightningcss()) return null;
|
|
113
|
-
return async (filename, targets) => {
|
|
114
|
-
// The native shim accepts a browserslist string; the npm
|
|
115
|
-
// `lightningcss` Targets struct is bitfield-encoded
|
|
116
|
-
// (`firefox: 60 << 16` etc). Convert by extracting major
|
|
117
|
-
// version per browser key and re-emitting as the equivalent
|
|
118
|
-
// browserslist query.
|
|
119
|
-
const query = targetsToBrowserslist(targets);
|
|
120
|
-
return mod.bundle({
|
|
121
|
-
filename,
|
|
122
|
-
targets: query,
|
|
123
|
-
minify: false,
|
|
124
|
-
sourceMap: false,
|
|
125
|
-
errorRecovery: true,
|
|
126
|
-
});
|
|
127
|
-
};
|
|
128
|
-
} catch {
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
async function loadNpmBundler(): Promise<Bundler> {
|
|
134
|
-
const { bundleAsync } = await import('lightningcss');
|
|
135
|
-
return async (filename, targets) => {
|
|
136
|
-
const result = await bundleAsync({
|
|
137
|
-
filename,
|
|
138
|
-
targets,
|
|
139
|
-
minify: false,
|
|
140
|
-
errorRecovery: true,
|
|
141
|
-
});
|
|
142
|
-
return { code: result.code };
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function targetsToBrowserslist(
|
|
147
|
-
targets: import('lightningcss').Targets | undefined,
|
|
148
|
-
): string | undefined {
|
|
149
|
-
if (!targets) return undefined;
|
|
150
|
-
const parts: string[] = [];
|
|
151
|
-
for (const [browser, encoded] of Object.entries(targets) as [string, number | undefined][]) {
|
|
152
|
-
if (typeof encoded !== 'number') continue;
|
|
153
|
-
// npm lightningcss encodes versions as `(major << 16) | (minor << 8) | patch`.
|
|
154
|
-
const major = (encoded >>> 16) & 0xff;
|
|
155
|
-
if (major === 0) continue;
|
|
156
|
-
const name = browser === 'ios_saf' ? 'ios' : browser;
|
|
157
|
-
parts.push(`${name} >= ${major}`);
|
|
158
|
-
}
|
|
159
|
-
return parts.length ? parts.join(', ') : undefined;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export function cssAsStringPlugin(options: CssAsStringOptions = {}): Plugin {
|
|
163
|
-
const { targets, bundle = true } = options;
|
|
164
|
-
return {
|
|
165
|
-
name: 'gjsify-css-as-string',
|
|
166
|
-
load: {
|
|
167
|
-
filter: { id: /\.css$/ },
|
|
168
|
-
async handler(id: string) {
|
|
169
|
-
const code = bundle
|
|
170
|
-
? new TextDecoder('utf-8').decode(await loadAndBundleCss(id, targets))
|
|
171
|
-
: await readFile(id, 'utf8');
|
|
172
|
-
return {
|
|
173
|
-
code: `export default ${JSON.stringify(code)};`,
|
|
174
|
-
moduleType: 'js' as const,
|
|
175
|
-
};
|
|
176
|
-
},
|
|
177
|
-
},
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
async function loadAndBundleCss(
|
|
182
|
-
filename: string,
|
|
183
|
-
targets: import('lightningcss').Targets | undefined,
|
|
184
|
-
): Promise<Uint8Array> {
|
|
185
|
-
if (!_bundlerPromise) _bundlerPromise = pickBundler();
|
|
186
|
-
const bundler = await _bundlerPromise;
|
|
187
|
-
const { code } = await bundler(filename, targets);
|
|
188
|
-
return code;
|
|
189
|
-
}
|