Skip to content

Commit be6c5c1

Browse files
committed
feat!: use rolldown to bundle jsvascript
1 parent 20c168f commit be6c5c1

8 files changed

Lines changed: 170 additions & 10 deletions

File tree

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@
6868
"directories": {
6969
"lib": "dist"
7070
},
71+
"peerDependencies": {
72+
"esbuild": "^0.27.2"
73+
},
74+
"peerDependenciesMeta": {
75+
"esbuild": {
76+
"optional": true
77+
}
78+
},
7179
"dependencies": {
7280
"@fluent/syntax": "^0.19.0",
7381
"adm-zip": "^0.5.16",
@@ -76,7 +84,6 @@
7684
"chokidar": "^5.0.0",
7785
"commander": "^14.0.3",
7886
"es-toolkit": "^1.45.1",
79-
"esbuild": "^0.27.4",
8087
"esrap": "^2.2.3",
8188
"fs-extra": "^11.3.4",
8289
"hookable": "^6.0.1",

src/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ const defaultConfig = {
110110
dts: "typings/prefs.d.ts",
111111
},
112112
esbuildOptions: [],
113+
bundle: [],
113114
makeManifest: {
114115
enable: true,
115116
template: {

src/core/builder/esbuild.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { BuildOptions, BuildResult } from "esbuild";
22
import type { BuildConfig } from "../../types/config.js";
33
import { resolve } from "node:path";
4-
import { build as buildAsync } from "esbuild";
4+
55
import { logger } from "../../utils/logger.js";
66

77
export function resolveConfig(dist: string, esbuildOptions: BuildConfig["esbuildOptions"]): BuildOptions[] {
@@ -27,6 +27,8 @@ export default async function esbuild(dist: string, esbuildOptions: BuildConfig[
2727

2828
const options = resolveConfig(dist, esbuildOptions);
2929

30+
const { build: buildAsync } = await import("esbuild");
31+
3032
return await Promise.all(
3133
options.map(esbuildOption =>
3234
buildAsync(esbuildOption),

src/core/builder/index.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import buildLocale from "./fluent.js";
1111
import buildManifest from "./manifest.js";
1212
import buildPrefs from "./prefs.js";
1313
import replaceDefine from "./replace.js";
14+
import rolldownBuild from "./rolldown.js";
1415
import buildUpdateJson from "./update-json.js";
1516
import pack from "./zip.js";
1617

@@ -72,8 +73,11 @@ export default class Build extends Base {
7273
}
7374

7475
async bundle(): Promise<void> {
75-
const { dist, build: { esbuildOptions } } = this.ctx;
76-
await esbuild(dist, esbuildOptions);
76+
const { dist, build: { esbuildOptions, bundle } } = this.ctx;
77+
if (esbuildOptions.length !== 0)
78+
await esbuild(dist, esbuildOptions);
79+
80+
await rolldownBuild(dist, bundle);
7781
await this.ctx.hooks.callHook("build:bundle", this.ctx);
7882
}
7983

src/core/builder/rolldown.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import type { InputOptions, OutputOptions } from "rolldown";
2+
import type { BuildConfig, BundleItem, Config } from "../../types/config.js";
3+
import { resolve } from "node:path";
4+
import process from "node:process";
5+
import { rolldown } from "rolldown";
6+
import { toArray } from "../../utils/string.js";
7+
8+
export type RolldownBuildOptions = InputOptions & { output?: OutputOptions | OutputOptions[] };
9+
10+
export function resolveConfig(dist: Config["dist"], configs: BundleItem[]): RolldownBuildOptions[] {
11+
const distAbsolute = resolve(dist);
12+
13+
// ensure output.file and output.dir are in dist folder
14+
return configs.map((config) => {
15+
// Extract config fields from new format with proper typing
16+
const { input, minify, rolldown: rolldownInputConfig } = config;
17+
18+
// Normalize output directory path
19+
const outputDir = `${dist}/addon`;
20+
const resolvedDir = resolve(outputDir).startsWith(distAbsolute)
21+
? outputDir
22+
: `${dist}/${outputDir}`;
23+
24+
return {
25+
...rolldownInputConfig,
26+
input,
27+
transform: {
28+
...rolldownInputConfig?.transform,
29+
target: "firefox140",
30+
define: {
31+
__env__: `"${process.env.NODE_ENV || "production"}"`,
32+
...(rolldownInputConfig?.transform?.define),
33+
},
34+
},
35+
output: {
36+
dir: resolvedDir,
37+
format: "esm",
38+
minify,
39+
// Since the Zotero sandbox environment does not support loading subpackages,
40+
// we hope to output a single file
41+
// Error: export declarations may only appear at top level of a module
42+
codeSplitting: false,
43+
},
44+
};
45+
});
46+
}
47+
48+
export default async function rolldownBuild(dist: string, bundleConfigs: BuildConfig["bundle"]): Promise<void> {
49+
const configs = toArray(bundleConfigs);
50+
51+
if (configs.length === 0)
52+
return;
53+
54+
const options = resolveConfig(dist, configs);
55+
56+
await Promise.all(
57+
options.map(async (rolldownOption) => {
58+
const { output, ...inputOptions } = rolldownOption;
59+
const bundle = await rolldown(inputOptions);
60+
if (output) {
61+
const outputConfig = Array.isArray(output) ? output : [output];
62+
await bundle.write(outputConfig[0]);
63+
}
64+
await bundle.close();
65+
}),
66+
);
67+
}

src/types/config.ts

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import type { GitCommit } from "changelogen";
2-
import type { BuildOptions } from "esbuild";
2+
import type { BuildOptions as esbuildConfig } from "esbuild";
3+
import type { InputOptions, RolldownPluginOption, TransformOptions } from "rolldown";
4+
import type { MinifyOptions } from "rolldown/experimental";
35
import type { LogLevelType } from "../utils/logger.js";
46
import type { Context } from "./index.js";
57
import type { Manifest } from "./manifest.js";
68
import type { UpdateJSON } from "./update-json.js";
9+
import type { Arrayable } from "./utils.js";
710

811
export interface Config {
912
/**
@@ -174,6 +177,58 @@ export interface Config {
174177
logLevel: LogLevelType;
175178
}
176179

180+
export interface BundleItem {
181+
/**
182+
*
183+
* @example
184+
*
185+
* ```ts
186+
* // Single entry
187+
* entries: "src/index.ts" // -> outDir/index.js
188+
*
189+
* // Multiple entries
190+
* entries: [
191+
* "src/index.ts", // -> outDir/index.js
192+
* "src/another-entry.ts" // -> outDir/another-entry.js
193+
* ]
194+
*
195+
* // Named multiple entries
196+
* entries: {
197+
* bootstrap: "src/bootstrap.ts", // -> outDir/bootstrap.js
198+
* content: "src/content.ts", // -> outDir/content.js
199+
* 'components/Foo': 'src/components/Foo.js', // -> outDir/components/Foo.js
200+
* }
201+
* ```
202+
*
203+
* The outDir is based on {@linkcode Config.dist | dist/build/addon }.
204+
*
205+
* @see https://rolldown.rs/reference/InputOptions.input
206+
*/
207+
input: InputOptions["input"];
208+
209+
/**
210+
* Replace global variables or [property accessors](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors) with the provided values.
211+
*
212+
* See Oxc's [`define` option](https://oxc.rs/docs/guide/usage/transformer/global-variable-replacement.html#define) for more details.
213+
*
214+
*/
215+
define?: TransformOptions["define"];
216+
217+
/**
218+
* Minify the output using rolldown.
219+
*
220+
* Defaults to `false` if not provided.
221+
*/
222+
minify?: boolean | "dce-only" | MinifyOptions;
223+
224+
/**
225+
* Options passed to rolldown.
226+
*
227+
* See [rolldown config options](https://rolldown.rs/reference/config-options) for more details.
228+
*/
229+
rolldown?: Omit<InputOptions, "input"> & { plugins?: RolldownPluginOption[] };
230+
}
231+
177232
export interface BuildConfig {
178233
/**
179234
* The static assets.
@@ -294,22 +349,42 @@ export interface BuildConfig {
294349
dts: false | string;
295350
};
296351
/**
297-
* Configurations of esbuild.
352+
* Configurations for code bundler (rolldown).
353+
*
354+
* Paths should be relative to the root directory of the project.
355+
* And if `output.file` and `output.dir` not start with `dist`,
356+
* `dist/` will be automatically added as a prefix.
357+
*
358+
* 代码捆绑器(rolldown)的配置。
359+
*
360+
* 路径应相对于项目根目录。
361+
* 其中"output.file"和"output.dir"若不以 `dist` 开头,
362+
* 则会自动添加 `dist/` 前缀。
363+
*
364+
* @default []
365+
*
366+
* @deprecated Use `build.bundle` instead.
367+
*
368+
*/
369+
esbuildOptions: esbuildConfig[];
370+
/**
371+
* Configurations for code bundler (rolldown).
298372
*
299373
* Paths should be relative to the root directory of the project.
300-
* And if `outfile` and `outdir` not start with `dist`,
374+
* And if `output.file` and `output.dir` not start with `dist`,
301375
* `dist/` will be automatically added as a prefix.
302376
*
303-
* esbuild 配置
377+
* 代码捆绑器(rolldown)的配置
304378
*
305379
* 路径应相对于项目根目录。
306-
* 其中“outfile”和“outdir”若不以 `dist` 开头,
380+
* 其中"output.file"和"output.dir"若不以 `dist` 开头,
307381
* 则会自动添加 `dist/` 前缀。
308382
*
309383
* @default []
310384
*
311385
*/
312-
esbuildOptions: BuildOptions[];
386+
bundle: Arrayable<BundleItem>;
387+
313388
/**
314389
* Make manifest.json.
315390
*

src/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { Config, Hooks } from "./config.js";
44
import type { RecursivePartial } from "./utils.js";
55

66
export { Config, Hooks };
7+
export type { BuildConfig } from "./config.js";
78

89
/**
910
* User config

src/types/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,6 @@ type RecursiveOptionalKeys<T> = {
2323
export type RecursivePickOptional<T> = {
2424
[K in keyof T as K extends RecursiveOptionalKeys<T> ? K : never]?: T[K] extends object ? RecursivePickOptional<T[K]> : T[K];
2525
};
26+
27+
export type Awaitable<T> = T | Promise<T>;
28+
export type Arrayable<T> = T | T[];

0 commit comments

Comments
 (0)