Skip to content

Commit 0c0d41f

Browse files
feat: add bun target
1 parent c1dec16 commit 0c0d41f

50 files changed

Lines changed: 766 additions & 7 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/bun-target.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"webpack": minor
3+
---
4+
5+
Add a `bun` target that emits ESM and externalizes `bun:*` and node.js built-in modules.

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ webpack is a JavaScript module bundler. Package manager: **yarn**.
1919
- `lib/` — Main source code (CommonJS only; types declared via JSDoc `@typedef`).
2020
- `lib/asset/` — Asset modules (images, fonts, raw files).
2121
- `lib/async-modules/` — Top-level await.
22+
- `lib/bun/` — Bun target externals preset (`bun:*` and node.js built-in modules).
2223
- `lib/cache/` — Filesystem and memory caches.
2324
- `lib/config/` — Config defaults, normalization, target presets.
2425
- `lib/container/` — Module Federation.

declarations/WebpackOptions.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,6 +1488,10 @@ export interface LazyCompilationDefaultBackendOptions {
14881488
* Enable presets of externals for specific targets.
14891489
*/
14901490
export interface ExternalsPresets {
1491+
/**
1492+
* Treat bun built-in modules like 'bun', 'bun:sqlite' or 'bun:ffi' and node.js built-in modules as external and load them via import when used (for the Bun runtime).
1493+
*/
1494+
bun?: boolean;
14911495
/**
14921496
* Treat node.js built-in modules like fs, path or vm as external and load them via the required 'node:' specifier when used (for the Deno runtime).
14931497
*/

lib/Compiler.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ class Compiler {
316316
webworker: null,
317317
node: null,
318318
deno: null,
319+
bun: null,
319320
nwjs: null,
320321
electron: null,
321322
universal: null

lib/WebpackOptionsApply.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,11 @@ class WebpackOptionsApply extends OptionsApply {
154154

155155
new DenoTargetPlugin().apply(compiler);
156156
}
157+
if (options.externalsPresets.bun) {
158+
const BunTargetPlugin = require("./bun/BunTargetPlugin");
159+
160+
new BunTargetPlugin().apply(compiler);
161+
}
157162
if (options.externalsPresets.webAsync || options.externalsPresets.web) {
158163
const type = options.externalsPresets.webAsync ? "import" : "module";
159164

lib/bun/BunTargetPlugin.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author sheo13666q @sheo13666q
4+
*/
5+
6+
"use strict";
7+
8+
const ExternalsPlugin = require("../ExternalsPlugin");
9+
const NodeTargetPlugin = require("../node/NodeTargetPlugin");
10+
11+
/** @typedef {import("../Compiler")} Compiler */
12+
13+
// Bun exposes the node.js core modules; externalize them with the `node:`
14+
// specifier (Bun resolves both forms) like the deno target.
15+
// cspell:word pnpapi
16+
const coreModules = new Set(
17+
NodeTargetPlugin.builtins.filter(
18+
(builtin) => typeof builtin === "string" && builtin !== "pnpapi"
19+
)
20+
);
21+
22+
// Bun's own built-in modules (`bun`, `bun:sqlite`, ...) are provided by the
23+
// runtime, never bundled.
24+
const BUN_BUILTINS = /^bun(?::|$)/;
25+
26+
class BunTargetPlugin {
27+
/**
28+
* Applies the plugin by registering its hooks on the compiler.
29+
* @param {Compiler} compiler the compiler instance
30+
* @returns {void}
31+
*/
32+
apply(compiler) {
33+
new ExternalsPlugin(
34+
(dependency) =>
35+
dependency.category === "commonjs" ? "node-commonjs" : "module-import",
36+
({ request }, callback) => {
37+
if (!request) return callback();
38+
if (request.startsWith("node:") || BUN_BUILTINS.test(request)) {
39+
return callback(null, request);
40+
}
41+
if (coreModules.has(request)) return callback(null, `node:${request}`);
42+
callback();
43+
}
44+
).apply(compiler);
45+
}
46+
}
47+
48+
module.exports = BunTargetPlugin;

lib/config/defaults.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,7 @@ const applyWebpackOptionsDefaults = (options, compilerIndex) => {
553553
webworker: targetProperties.webworker,
554554
node: targetProperties.node,
555555
deno: targetProperties.deno,
556+
bun: targetProperties.bun,
556557
nwjs: targetProperties.nwjs,
557558
electron: targetProperties.electron,
558559
// spans both web and node (target "universal" or ["web", "node"])
@@ -585,11 +586,14 @@ const applyExperimentsDefaults = (
585586
Boolean(targetProperties) &&
586587
/** @type {TargetProperties} */ (targetProperties).node === null &&
587588
/** @type {TargetProperties} */ (targetProperties).web === null;
588-
// the deno target only emits ECMAScript modules
589+
// the deno and bun targets only emit ECMAScript modules
589590
const deno =
590591
Boolean(targetProperties) &&
591592
/** @type {TargetProperties} */ (targetProperties).deno === true;
592-
D(experiments, "outputModule", universal || deno);
593+
const bun =
594+
Boolean(targetProperties) &&
595+
/** @type {TargetProperties} */ (targetProperties).bun === true;
596+
D(experiments, "outputModule", universal || deno || bun);
593597
D(experiments, "lazyCompilation", undefined);
594598
D(experiments, "buildHttp", undefined);
595599
D(experiments, "cacheUnaffected", experiments.futureDefaults);
@@ -1982,7 +1986,9 @@ const applyExternalsPresetsDefaults = (
19821986
/** @type {boolean | undefined} */
19831987
(
19841988
targetProperties &&
1985-
((targetProperties.node && !targetProperties.deno) ||
1989+
((targetProperties.node &&
1990+
!targetProperties.deno &&
1991+
!targetProperties.bun) ||
19861992
isUniversal("node"))
19871993
)
19881994
);
@@ -1992,6 +1998,12 @@ const applyExternalsPresetsDefaults = (
19921998
/** @type {boolean | undefined} */
19931999
(Boolean(targetProperties && targetProperties.deno))
19942000
);
2001+
D(
2002+
externalsPresets,
2003+
"bun",
2004+
/** @type {boolean | undefined} */
2005+
(Boolean(targetProperties && targetProperties.bun))
2006+
);
19952007
D(
19962008
externalsPresets,
19972009
"nwjs",
@@ -2055,6 +2067,7 @@ const applyLoaderDefaults = (loader, { targetProperties, environment }) => {
20552067
}
20562068
if (targetProperties.nwjs) return "nwjs";
20572069
if (targetProperties.deno) return "deno";
2070+
if (targetProperties.bun) return "bun";
20582071
if (targetProperties.node) return "node";
20592072
if (targetProperties.web) return "web";
20602073
}
@@ -2249,6 +2262,7 @@ const getResolveDefaults = ({
22492262

22502263
if (targetProperties) {
22512264
if (targetProperties.deno) conditions.push("deno");
2265+
if (targetProperties.bun) conditions.push("bun");
22522266
if (targetProperties.webworker) conditions.push("worker");
22532267
if (targetProperties.node) conditions.push("node");
22542268
if (targetProperties.web) conditions.push("browser");

lib/config/target.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const getDefaultTarget = (context) => {
2929
* @property {boolean | null=} webworker (Web)Worker platform, running in a web/shared/service worker
3030
* @property {boolean | null=} node node platform, require of node built-in modules is available
3131
* @property {boolean | null=} deno deno platform, node built-in modules (via the `node:` specifier) and web APIs are available
32+
* @property {boolean | null=} bun bun platform, bun and node built-in modules are available
3233
* @property {boolean | null=} nwjs nwjs platform, require of legacy nw.gui is available
3334
* @property {boolean | null=} electron electron platform, require of some electron built-in modules is available
3435
* @property {boolean | null=} universal universal ESM target spanning both web and node (target `"universal"` or `["web", "node"]`)
@@ -142,6 +143,7 @@ You can also more options via the 'target' option: 'browserslist' / 'browserslis
142143
() => ({
143144
node: false,
144145
deno: false,
146+
bun: false,
145147
web: true,
146148
webworker: null,
147149
browser: true,
@@ -164,6 +166,7 @@ You can also more options via the 'target' option: 'browserslist' / 'browserslis
164166
() => ({
165167
node: false,
166168
deno: false,
169+
bun: false,
167170
web: true,
168171
webworker: true,
169172
browser: true,
@@ -188,6 +191,7 @@ You can also more options via the 'target' option: 'browserslist' / 'browserslis
188191
() => ({
189192
node: null,
190193
deno: null,
194+
bun: null,
191195
web: null,
192196
webworker: null,
193197
browser: null,
@@ -221,6 +225,7 @@ You can also more options via the 'target' option: 'browserslist' / 'browserslis
221225
return {
222226
node: true,
223227
deno: false,
228+
bun: false,
224229
web: false,
225230
webworker: false,
226231
browser: false,
@@ -314,6 +319,57 @@ You can also more options via the 'target' option: 'browserslist' / 'browserslis
314319
};
315320
}
316321
],
322+
[
323+
"bun[X[.Y]]",
324+
"Bun in version X.Y. Output is always ECMAScript modules. Examples: bun, bun1.1.",
325+
/^bun((\d+)(?:\.(\d+))?)?$/,
326+
() => ({
327+
// Bun is a modern, Node.js-compatible runtime; every released version
328+
// supports the full modern ECMAScript feature set, so the version is
329+
// accepted but does not gate any feature. Output is always ESM, so sync
330+
// CommonJS `require` is never available.
331+
node: true,
332+
bun: true,
333+
web: false,
334+
webworker: false,
335+
browser: false,
336+
electron: false,
337+
nwjs: false,
338+
339+
require: false,
340+
nodeBuiltins: true,
341+
nodePrefixForCoreModules: true,
342+
nodeBuiltinModuleGetter: true,
343+
importMetaDirnameAndFilename: true,
344+
// Bun implements web-standard globals; use `globalThis` and load wasm
345+
// via `fetch()` (Bun's fetch resolves `file:` URLs) like the deno target,
346+
// instead of proprietary `Bun.*` runtime APIs.
347+
global: false,
348+
document: false,
349+
fetchWasm: true,
350+
importScripts: false,
351+
importScriptsInWorker: false,
352+
353+
globalThis: true,
354+
symbol: true,
355+
hasOwn: true,
356+
const: true,
357+
let: true,
358+
logicalAssignment: true,
359+
templateLiteral: true,
360+
optionalChaining: true,
361+
spread: true,
362+
methodShorthand: true,
363+
arrowFunction: true,
364+
asyncFunction: true,
365+
forOf: true,
366+
destructuring: true,
367+
bigIntLiteral: true,
368+
dynamicImport: true,
369+
dynamicImportInWorker: true,
370+
module: true
371+
})
372+
],
317373
[
318374
"electron[X[.Y]]-main/preload/renderer",
319375
"Electron in version X.Y. Script is running in main, preload resp. renderer context.",
@@ -324,6 +380,7 @@ You can also more options via the 'target' option: 'browserslist' / 'browserslis
324380
return {
325381
node: true,
326382
deno: false,
383+
bun: false,
327384
web: context !== "main",
328385
webworker: false,
329386
browser: false,
@@ -381,6 +438,7 @@ You can also more options via the 'target' option: 'browserslist' / 'browserslis
381438
return {
382439
node: true,
383440
deno: false,
441+
bun: false,
384442
web: true,
385443
webworker: null,
386444
browser: false,

schemas/WebpackOptions.check.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

schemas/WebpackOptions.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,6 +1435,10 @@
14351435
"type": "object",
14361436
"additionalProperties": false,
14371437
"properties": {
1438+
"bun": {
1439+
"description": "Treat bun built-in modules like 'bun', 'bun:sqlite' or 'bun:ffi' and node.js built-in modules as external and load them via import when used (for the Bun runtime).",
1440+
"type": "boolean"
1441+
},
14381442
"deno": {
14391443
"description": "Treat node.js built-in modules like fs, path or vm as external and load them via the required 'node:' specifier when used (for the Deno runtime).",
14401444
"type": "boolean"

0 commit comments

Comments
 (0)