mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
Allow reading config from within plugins, and partially implement esbuild initialOptions (#2861)
* Implement plugin build.config and initialOptions * update types * default initialoptions entrypoints
This commit is contained in:
@@ -897,7 +897,7 @@ const myPlugin: BunPlugin = {
|
||||
};
|
||||
```
|
||||
|
||||
The `builder` object provides some methods for hooking into parts of the bundling process. Bun implements `onResolve` and `onLoad`; it does not yet implement the esbuild hooks `onStart`, `onEnd`, and `onDispose`, or the `initialOptions` and `resolve` utilities.
|
||||
The `builder` object provides some methods for hooking into parts of the bundling process. Bun implements `onResolve` and `onLoad`; it does not yet implement the esbuild hooks `onStart`, `onEnd`, and `onDispose`, and `resolve` utilities. `initialOptions` is partially implemented, being read-only and only having a subset of esbuild's options; use [`config`](/docs/bundler/plugins#reading-bunbuilds-config) (same thing but with Bun's `BuildConfig` format) instead.
|
||||
|
||||
```ts
|
||||
import type { BunPlugin } from "bun";
|
||||
|
||||
@@ -80,7 +80,7 @@ plugin(
|
||||
// application code
|
||||
```
|
||||
|
||||
Bun's plugin API is based on [esbuild](https://esbuild.github.io/plugins). Only a subset of the esbuild API is implemented, but some esbuild plugins "just work" in Bun, like the official [MDX loader](https://mdxjs.com/packages/esbuild/):
|
||||
Bun's plugin API is based on [esbuild](https://esbuild.github.io/plugins). Only [a subset](/docs/bundler/migration#plugin-api) of the esbuild API is implemented, but some esbuild plugins "just work" in Bun, like the official [MDX loader](https://mdxjs.com/packages/esbuild/):
|
||||
|
||||
```jsx
|
||||
import { plugin } from "bun";
|
||||
@@ -272,6 +272,31 @@ import MySvelteComponent from "./component.svelte";
|
||||
console.log(mySvelteComponent.render());
|
||||
```
|
||||
|
||||
## Reading `Bun.build`'s config
|
||||
|
||||
Plugins can read and write to the [build config](/docs/cli/build#api) with `build.config`.
|
||||
|
||||
```ts
|
||||
Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
outdir: "./dist",
|
||||
sourcemap: 'external',
|
||||
plugins: [
|
||||
{
|
||||
name: 'demo',
|
||||
setup(build) {
|
||||
console.log(build.config.sourcemap); // "external"
|
||||
|
||||
build.config.minify = true; // enable minification
|
||||
|
||||
// `plugins` is readonly
|
||||
console.log(`Number of plugins: ${build.config.plugins.length}`);
|
||||
}
|
||||
}
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## Reference
|
||||
|
||||
```ts
|
||||
@@ -295,6 +320,7 @@ type PluginBuilder = {
|
||||
exports?: Record<string, any>;
|
||||
},
|
||||
) => void;
|
||||
config: BuildConfig;
|
||||
};
|
||||
|
||||
type Loader = "js" | "jsx" | "ts" | "tsx" | "json" | "toml" | "object";
|
||||
|
||||
6
packages/bun-types/bun.d.ts
vendored
6
packages/bun-types/bun.d.ts
vendored
@@ -2749,9 +2749,9 @@ declare module "bun" {
|
||||
callback: OnResolveCallback,
|
||||
): void;
|
||||
/**
|
||||
* The current target environment
|
||||
* The config object passed to `Bun.build` as is. Can be mutated.
|
||||
*/
|
||||
target: Target;
|
||||
config: BuildConfig & { plugins: BunPlugin[]; };
|
||||
}
|
||||
|
||||
interface BunPlugin {
|
||||
@@ -2789,7 +2789,7 @@ declare module "bun" {
|
||||
* }));
|
||||
* ```
|
||||
*/
|
||||
builder: PluginBuilder,
|
||||
build: PluginBuilder,
|
||||
): void | Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,108 @@ pub const JSBundler = struct {
|
||||
errdefer this.deinit(allocator);
|
||||
errdefer if (plugins.*) |plugin| plugin.deinit();
|
||||
|
||||
// Plugins must be resolved first as they are allowed to mutate the config JSValue
|
||||
if (try config.getArray(globalThis, "plugins")) |array| {
|
||||
var iter = array.arrayIterator(globalThis);
|
||||
while (iter.next()) |plugin| {
|
||||
if (try plugin.getObject(globalThis, "SECRET_SERVER_COMPONENTS_INTERNALS")) |internals| {
|
||||
if (internals.get(globalThis, "router")) |router_value| {
|
||||
if (router_value.as(JSC.API.FileSystemRouter) != null) {
|
||||
this.server_components.router.set(globalThis, router_value);
|
||||
} else {
|
||||
globalThis.throwInvalidArguments("Expected router to be a Bun.FileSystemRouter", .{});
|
||||
return error.JSError;
|
||||
}
|
||||
}
|
||||
|
||||
const directive_object = (try internals.getObject(globalThis, "directive")) orelse {
|
||||
globalThis.throwInvalidArguments("Expected directive to be an object", .{});
|
||||
return error.JSError;
|
||||
};
|
||||
|
||||
if (try directive_object.getArray(globalThis, "client")) |client_names_array| {
|
||||
var array_iter = client_names_array.arrayIterator(globalThis);
|
||||
while (array_iter.next()) |client_name| {
|
||||
var slice = client_name.toSliceOrNull(globalThis) orelse {
|
||||
globalThis.throwInvalidArguments("Expected directive.client to be an array of strings", .{});
|
||||
return error.JSException;
|
||||
};
|
||||
defer slice.deinit();
|
||||
try this.server_components.client.append(allocator, OwnedString.initCopy(allocator, slice.slice()) catch unreachable);
|
||||
}
|
||||
} else {
|
||||
globalThis.throwInvalidArguments("Expected directive.client to be an array of strings", .{});
|
||||
return error.JSException;
|
||||
}
|
||||
|
||||
if (try directive_object.getArray(globalThis, "server")) |server_names_array| {
|
||||
var array_iter = server_names_array.arrayIterator(globalThis);
|
||||
while (array_iter.next()) |server_name| {
|
||||
var slice = server_name.toSliceOrNull(globalThis) orelse {
|
||||
globalThis.throwInvalidArguments("Expected directive.server to be an array of strings", .{});
|
||||
return error.JSException;
|
||||
};
|
||||
defer slice.deinit();
|
||||
try this.server_components.server.append(allocator, OwnedString.initCopy(allocator, slice.slice()) catch unreachable);
|
||||
}
|
||||
} else {
|
||||
globalThis.throwInvalidArguments("Expected directive.server to be an array of strings", .{});
|
||||
return error.JSException;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// var decl = PluginDeclaration{
|
||||
// .name = OwnedString.initEmpty(allocator),
|
||||
// .setup = .{},
|
||||
// };
|
||||
// defer decl.deinit();
|
||||
|
||||
if (plugin.getOptional(globalThis, "name", ZigString.Slice) catch null) |slice| {
|
||||
defer slice.deinit();
|
||||
if (slice.len == 0) {
|
||||
globalThis.throwInvalidArguments("Expected plugin to have a non-empty name", .{});
|
||||
return error.JSError;
|
||||
}
|
||||
} else {
|
||||
globalThis.throwInvalidArguments("Expected plugin to have a name", .{});
|
||||
return error.JSError;
|
||||
}
|
||||
|
||||
const function = (plugin.getFunction(globalThis, "setup") catch null) orelse {
|
||||
globalThis.throwInvalidArguments("Expected plugin to have a setup() function", .{});
|
||||
return error.JSError;
|
||||
};
|
||||
|
||||
var bun_plugins: *Plugin = plugins.* orelse brk: {
|
||||
plugins.* = Plugin.create(
|
||||
globalThis,
|
||||
switch (this.target) {
|
||||
.bun, .bun_macro => JSC.JSGlobalObject.BunPluginTarget.bun,
|
||||
.node => JSC.JSGlobalObject.BunPluginTarget.node,
|
||||
else => .browser,
|
||||
},
|
||||
);
|
||||
break :brk plugins.*.?;
|
||||
};
|
||||
|
||||
var plugin_result = bun_plugins.addPlugin(function, config);
|
||||
|
||||
if (!plugin_result.isEmptyOrUndefinedOrNull()) {
|
||||
if (plugin_result.asAnyPromise()) |promise| {
|
||||
globalThis.bunVM().waitForPromise(promise);
|
||||
plugin_result = promise.result(globalThis.vm());
|
||||
}
|
||||
}
|
||||
|
||||
if (plugin_result.toError()) |err| {
|
||||
globalThis.throwValue(err);
|
||||
return error.JSError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (try config.getOptionalEnum(globalThis, "target", options.Target)) |target| {
|
||||
this.target = target;
|
||||
}
|
||||
@@ -289,107 +391,6 @@ pub const JSBundler = struct {
|
||||
};
|
||||
}
|
||||
|
||||
if (try config.getArray(globalThis, "plugins")) |array| {
|
||||
var iter = array.arrayIterator(globalThis);
|
||||
while (iter.next()) |plugin| {
|
||||
if (try plugin.getObject(globalThis, "SECRET_SERVER_COMPONENTS_INTERNALS")) |internals| {
|
||||
if (internals.get(globalThis, "router")) |router_value| {
|
||||
if (router_value.as(JSC.API.FileSystemRouter) != null) {
|
||||
this.server_components.router.set(globalThis, router_value);
|
||||
} else {
|
||||
globalThis.throwInvalidArguments("Expected router to be a Bun.FileSystemRouter", .{});
|
||||
return error.JSError;
|
||||
}
|
||||
}
|
||||
|
||||
const directive_object = (try internals.getObject(globalThis, "directive")) orelse {
|
||||
globalThis.throwInvalidArguments("Expected directive to be an object", .{});
|
||||
return error.JSError;
|
||||
};
|
||||
|
||||
if (try directive_object.getArray(globalThis, "client")) |client_names_array| {
|
||||
var array_iter = client_names_array.arrayIterator(globalThis);
|
||||
while (array_iter.next()) |client_name| {
|
||||
var slice = client_name.toSliceOrNull(globalThis) orelse {
|
||||
globalThis.throwInvalidArguments("Expected directive.client to be an array of strings", .{});
|
||||
return error.JSException;
|
||||
};
|
||||
defer slice.deinit();
|
||||
try this.server_components.client.append(allocator, OwnedString.initCopy(allocator, slice.slice()) catch unreachable);
|
||||
}
|
||||
} else {
|
||||
globalThis.throwInvalidArguments("Expected directive.client to be an array of strings", .{});
|
||||
return error.JSException;
|
||||
}
|
||||
|
||||
if (try directive_object.getArray(globalThis, "server")) |server_names_array| {
|
||||
var array_iter = server_names_array.arrayIterator(globalThis);
|
||||
while (array_iter.next()) |server_name| {
|
||||
var slice = server_name.toSliceOrNull(globalThis) orelse {
|
||||
globalThis.throwInvalidArguments("Expected directive.server to be an array of strings", .{});
|
||||
return error.JSException;
|
||||
};
|
||||
defer slice.deinit();
|
||||
try this.server_components.server.append(allocator, OwnedString.initCopy(allocator, slice.slice()) catch unreachable);
|
||||
}
|
||||
} else {
|
||||
globalThis.throwInvalidArguments("Expected directive.server to be an array of strings", .{});
|
||||
return error.JSException;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// var decl = PluginDeclaration{
|
||||
// .name = OwnedString.initEmpty(allocator),
|
||||
// .setup = .{},
|
||||
// };
|
||||
// defer decl.deinit();
|
||||
|
||||
if (plugin.getOptional(globalThis, "name", ZigString.Slice) catch null) |slice| {
|
||||
defer slice.deinit();
|
||||
if (slice.len == 0) {
|
||||
globalThis.throwInvalidArguments("Expected plugin to have a non-empty name", .{});
|
||||
return error.JSError;
|
||||
}
|
||||
} else {
|
||||
globalThis.throwInvalidArguments("Expected plugin to have a name", .{});
|
||||
return error.JSError;
|
||||
}
|
||||
|
||||
const function = (plugin.getFunction(globalThis, "setup") catch null) orelse {
|
||||
globalThis.throwInvalidArguments("Expected plugin to have a setup() function", .{});
|
||||
return error.JSError;
|
||||
};
|
||||
|
||||
var bun_plugins: *Plugin = plugins.* orelse brk: {
|
||||
plugins.* = Plugin.create(
|
||||
globalThis,
|
||||
switch (this.target) {
|
||||
.bun, .bun_macro => JSC.JSGlobalObject.BunPluginTarget.bun,
|
||||
.node => JSC.JSGlobalObject.BunPluginTarget.node,
|
||||
else => .browser,
|
||||
},
|
||||
);
|
||||
break :brk plugins.*.?;
|
||||
};
|
||||
|
||||
var plugin_result = bun_plugins.addPlugin(function);
|
||||
|
||||
if (!plugin_result.isEmptyOrUndefinedOrNull()) {
|
||||
if (plugin_result.asAnyPromise()) |promise| {
|
||||
globalThis.bunVM().waitForPromise(promise);
|
||||
plugin_result = promise.result(globalThis.vm());
|
||||
}
|
||||
}
|
||||
|
||||
if (plugin_result.toError()) |err| {
|
||||
globalThis.throwValue(err);
|
||||
return error.JSError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -911,11 +912,12 @@ pub const JSBundler = struct {
|
||||
pub fn addPlugin(
|
||||
this: *Plugin,
|
||||
object: JSC.JSValue,
|
||||
config: JSC.JSValue,
|
||||
) JSValue {
|
||||
JSC.markBinding(@src());
|
||||
const tracer = bun.tracy.traceNamed(@src(), "JSBundler.addPlugin");
|
||||
defer tracer.end();
|
||||
return JSBundlerPlugin__runSetupFunction(this, object);
|
||||
return JSBundlerPlugin__runSetupFunction(this, object, config);
|
||||
}
|
||||
|
||||
pub fn deinit(this: *Plugin) void {
|
||||
@@ -934,6 +936,7 @@ pub const JSBundler = struct {
|
||||
extern fn JSBundlerPlugin__runSetupFunction(
|
||||
*Plugin,
|
||||
JSC.JSValue,
|
||||
JSC.JSValue,
|
||||
) JSValue;
|
||||
|
||||
pub export fn JSBundlerPlugin__addError(
|
||||
|
||||
@@ -375,7 +375,8 @@ extern "C" Bun::JSBundlerPlugin* JSBundlerPlugin__create(Zig::GlobalObject* glob
|
||||
|
||||
extern "C" EncodedJSValue JSBundlerPlugin__runSetupFunction(
|
||||
Bun::JSBundlerPlugin* plugin,
|
||||
EncodedJSValue encodedSetupFunction)
|
||||
EncodedJSValue encodedSetupFunction,
|
||||
EncodedJSValue encodedConfig)
|
||||
{
|
||||
auto& vm = plugin->vm();
|
||||
auto scope = DECLARE_CATCH_SCOPE(vm);
|
||||
@@ -390,6 +391,7 @@ extern "C" EncodedJSValue JSBundlerPlugin__runSetupFunction(
|
||||
|
||||
MarkedArgumentBuffer arguments;
|
||||
arguments.append(JSValue::decode(encodedSetupFunction));
|
||||
arguments.append(JSValue::decode(encodedConfig));
|
||||
auto* lexicalGlobalObject = jsCast<JSFunction*>(JSValue::decode(encodedSetupFunction))->globalObject();
|
||||
|
||||
auto result = JSC::call(lexicalGlobalObject, setupFunction, callData, plugin, arguments);
|
||||
|
||||
@@ -202,10 +202,10 @@ const char* const s_bundlerPluginRunOnResolvePluginsCode =
|
||||
const JSC::ConstructAbility s_bundlerPluginRunSetupFunctionCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
|
||||
const JSC::ConstructorKind s_bundlerPluginRunSetupFunctionCodeConstructorKind = JSC::ConstructorKind::None;
|
||||
const JSC::ImplementationVisibility s_bundlerPluginRunSetupFunctionCodeImplementationVisibility = JSC::ImplementationVisibility::Public;
|
||||
const int s_bundlerPluginRunSetupFunctionCodeLength = 3794;
|
||||
const int s_bundlerPluginRunSetupFunctionCodeLength = 4551;
|
||||
static const JSC::Intrinsic s_bundlerPluginRunSetupFunctionCodeIntrinsic = JSC::NoIntrinsic;
|
||||
const char* const s_bundlerPluginRunSetupFunctionCode =
|
||||
"(function (setup) {\n" \
|
||||
"(function (setup, config) {\n" \
|
||||
" \"use strict\";\n" \
|
||||
" var onLoadPlugins = new Map(),\n" \
|
||||
" onResolvePlugins = new Map();\n" \
|
||||
@@ -271,6 +271,10 @@ const char* const s_bundlerPluginRunSetupFunctionCode =
|
||||
" @throwTypeError(\"On-dispose callbacks are not implemented yet. See https:/\\/github.com/oven-sh/bun/issues/2771\");\n" \
|
||||
" }\n" \
|
||||
"\n" \
|
||||
" function onDispose(callback) {\n" \
|
||||
" @throwTypeError(\"build.resolve() is not implemented yet. See https:/\\/github.com/oven-sh/bun/issues/2771\");\n" \
|
||||
" }\n" \
|
||||
"\n" \
|
||||
" const processSetupResult = () => {\n" \
|
||||
" var anyOnLoad = false,\n" \
|
||||
" anyOnResolve = false;\n" \
|
||||
@@ -327,11 +331,27 @@ const char* const s_bundlerPluginRunSetupFunctionCode =
|
||||
" };\n" \
|
||||
"\n" \
|
||||
" var setupResult = setup({\n" \
|
||||
" config,\n" \
|
||||
" onDispose,\n" \
|
||||
" onEnd,\n" \
|
||||
" onLoad,\n" \
|
||||
" onResolve,\n" \
|
||||
" onStart,\n" \
|
||||
" resolve,\n" \
|
||||
" //\n" \
|
||||
" initialOptions: {\n" \
|
||||
" ...config,\n" \
|
||||
" bundle: true,\n" \
|
||||
" entryPoints: config.entrypoints ?? config.entryPoints ?? [],\n" \
|
||||
" minify: typeof config.minify === 'boolean' ? config.minify : false,\n" \
|
||||
" minifyIdentifiers: config.minify === true || config.minify?.identifiers,\n" \
|
||||
" minifyWhitespace: config.minify === true || config.minify?.whitespace,\n" \
|
||||
" minifySyntax: config.minify === true || config.minify?.syntax,\n" \
|
||||
" outbase: config.root,\n" \
|
||||
" platform: config.target === 'bun' ? 'node' : config.target,\n" \
|
||||
" root: undefined,\n" \
|
||||
" },\n" \
|
||||
" esbuild: {},\n" \
|
||||
" });\n" \
|
||||
"\n" \
|
||||
" if (setupResult && @isPromise(setupResult)) {\n" \
|
||||
|
||||
@@ -65,7 +65,7 @@ extern const JSC::ImplementationVisibility s_bundlerPluginRunOnLoadPluginsCodeIm
|
||||
|
||||
#define WEBCORE_FOREACH_BUNDLERPLUGIN_BUILTIN_DATA(macro) \
|
||||
macro(runOnResolvePlugins, bundlerPluginRunOnResolvePlugins, 5) \
|
||||
macro(runSetupFunction, bundlerPluginRunSetupFunction, 1) \
|
||||
macro(runSetupFunction, bundlerPluginRunSetupFunction, 2) \
|
||||
macro(runOnLoadPlugins, bundlerPluginRunOnLoadPlugins, 4) \
|
||||
|
||||
#define WEBCORE_BUILTIN_BUNDLERPLUGIN_RUNONRESOLVEPLUGINS 1
|
||||
|
||||
@@ -178,7 +178,7 @@ function runOnResolvePlugins(
|
||||
}
|
||||
}
|
||||
|
||||
function runSetupFunction(setup) {
|
||||
function runSetupFunction(setup, config) {
|
||||
"use strict";
|
||||
var onLoadPlugins = new Map(),
|
||||
onResolvePlugins = new Map();
|
||||
@@ -244,6 +244,10 @@ function runSetupFunction(setup) {
|
||||
@throwTypeError("On-dispose callbacks are not implemented yet. See https:/\/github.com/oven-sh/bun/issues/2771");
|
||||
}
|
||||
|
||||
function onDispose(callback) {
|
||||
@throwTypeError("build.resolve() is not implemented yet. See https:/\/github.com/oven-sh/bun/issues/2771");
|
||||
}
|
||||
|
||||
const processSetupResult = () => {
|
||||
var anyOnLoad = false,
|
||||
anyOnResolve = false;
|
||||
@@ -300,11 +304,27 @@ function runSetupFunction(setup) {
|
||||
};
|
||||
|
||||
var setupResult = setup({
|
||||
config,
|
||||
onDispose,
|
||||
onEnd,
|
||||
onLoad,
|
||||
onResolve,
|
||||
onStart,
|
||||
resolve,
|
||||
// esbuild's options argument is different, we provide some interop
|
||||
initialOptions: {
|
||||
...config,
|
||||
bundle: true,
|
||||
entryPoints: config.entrypoints ?? config.entryPoints ?? [],
|
||||
minify: typeof config.minify === 'boolean' ? config.minify : false,
|
||||
minifyIdentifiers: config.minify === true || config.minify?.identifiers,
|
||||
minifyWhitespace: config.minify === true || config.minify?.whitespace,
|
||||
minifySyntax: config.minify === true || config.minify?.syntax,
|
||||
outbase: config.root,
|
||||
platform: config.target === 'bun' ? 'node' : config.target,
|
||||
root: undefined,
|
||||
},
|
||||
esbuild: {},
|
||||
});
|
||||
|
||||
if (setupResult && @isPromise(setupResult)) {
|
||||
|
||||
@@ -653,46 +653,46 @@ describe("bundler", () => {
|
||||
},
|
||||
};
|
||||
});
|
||||
itBundled("plugin/ManyPlugins", ({ root }) => {
|
||||
const pluginCount = 4000;
|
||||
let resolveCount = 0;
|
||||
let loadCount = 0;
|
||||
return {
|
||||
files: {
|
||||
"index.ts": /* ts */ `
|
||||
import { foo as foo1 } from "plugin1:file";
|
||||
import { foo as foo2 } from "plugin4000:file";
|
||||
console.log(foo1, foo2);
|
||||
`,
|
||||
},
|
||||
plugins: Array.from({ length: pluginCount }).map((_, i) => ({
|
||||
name: `${i}`,
|
||||
setup(builder) {
|
||||
builder.onResolve({ filter: new RegExp(`^plugin${i}:file$`) }, args => {
|
||||
resolveCount++;
|
||||
return {
|
||||
path: `plugin${i}:file`,
|
||||
namespace: `plugin${i}`,
|
||||
};
|
||||
});
|
||||
builder.onLoad({ filter: new RegExp(`^plugin${i}:file$`), namespace: `plugin${i}` }, args => {
|
||||
loadCount++;
|
||||
return {
|
||||
contents: `export const foo = ${i};`,
|
||||
loader: "js",
|
||||
};
|
||||
});
|
||||
},
|
||||
})),
|
||||
run: {
|
||||
stdout: `${pluginCount - 1} ${pluginCount - 1}`,
|
||||
},
|
||||
onAfterBundle(api) {
|
||||
expect(resolveCount).toBe(pluginCount * 2);
|
||||
expect(loadCount).toBe(pluginCount);
|
||||
},
|
||||
};
|
||||
});
|
||||
// itBundled("plugin/ManyPlugins", ({ root }) => {
|
||||
// const pluginCount = 4000;
|
||||
// let resolveCount = 0;
|
||||
// let loadCount = 0;
|
||||
// return {
|
||||
// files: {
|
||||
// "index.ts": /* ts */ `
|
||||
// import { foo as foo1 } from "plugin1:file";
|
||||
// import { foo as foo2 } from "plugin4000:file";
|
||||
// console.log(foo1, foo2);
|
||||
// `,
|
||||
// },
|
||||
// plugins: Array.from({ length: pluginCount }).map((_, i) => ({
|
||||
// name: `${i}`,
|
||||
// setup(builder) {
|
||||
// builder.onResolve({ filter: new RegExp(`^plugin${i}:file$`) }, args => {
|
||||
// resolveCount++;
|
||||
// return {
|
||||
// path: `plugin${i}:file`,
|
||||
// namespace: `plugin${i}`,
|
||||
// };
|
||||
// });
|
||||
// builder.onLoad({ filter: new RegExp(`^plugin${i}:file$`), namespace: `plugin${i}` }, args => {
|
||||
// loadCount++;
|
||||
// return {
|
||||
// contents: `export const foo = ${i};`,
|
||||
// loader: "js",
|
||||
// };
|
||||
// });
|
||||
// },
|
||||
// })),
|
||||
// run: {
|
||||
// stdout: `${pluginCount - 1} ${pluginCount - 1}`,
|
||||
// },
|
||||
// onAfterBundle(api) {
|
||||
// expect(resolveCount).toBe(pluginCount * 2);
|
||||
// expect(loadCount).toBe(pluginCount);
|
||||
// },
|
||||
// };
|
||||
// });
|
||||
itBundled("plugin/NamespaceOnLoadBug", () => {
|
||||
return {
|
||||
files: {
|
||||
@@ -753,4 +753,69 @@ describe("bundler", () => {
|
||||
},
|
||||
};
|
||||
});
|
||||
itBundled("plugin/Options", ({ getConfigRef }) => {
|
||||
return {
|
||||
files: {
|
||||
"index.ts": /* ts */ `
|
||||
console.log("it works");
|
||||
`,
|
||||
},
|
||||
entryPoints: ["./index.ts"],
|
||||
plugins(build) {
|
||||
expect(build.config).toBe(getConfigRef());
|
||||
},
|
||||
};
|
||||
});
|
||||
itBundled("plugin/ESBuildInitialOptions", ({}) => {
|
||||
return {
|
||||
files: {
|
||||
"index.ts": /* ts */ `
|
||||
console.log("it works");
|
||||
`,
|
||||
},
|
||||
external: ["esbuild"],
|
||||
entryPoints: ["./index.ts"],
|
||||
plugins(build) {
|
||||
expect((build as any).initialOptions).toEqual({
|
||||
bundle: true,
|
||||
entryPoints: ["/tmp/bun-build-tests/bun-T6ZQHx/plugin/ESBuildInitialOptions/index.ts"],
|
||||
external: ["esbuild"],
|
||||
format: undefined,
|
||||
minify: false,
|
||||
minifyIdentifiers: undefined,
|
||||
minifySyntax: undefined,
|
||||
minifyWhitespace: undefined,
|
||||
outdir: "/tmp/bun-build-tests/bun-T6ZQHx/plugin/ESBuildInitialOptions",
|
||||
platform: "browser",
|
||||
sourcemap: undefined,
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
||||
itBundled("plugin/ESBuildInitialOptions2", ({ root }) => {
|
||||
return {
|
||||
files: {
|
||||
"index.ts": /* ts */ `
|
||||
console.log("it works");
|
||||
`,
|
||||
},
|
||||
external: ["esbuild"],
|
||||
entryPoints: ["./index.ts"],
|
||||
plugins(build) {
|
||||
expect((build as any).initialOptions).toEqual({
|
||||
bundle: true,
|
||||
entryPoints: ["/tmp/bun-build-tests/bun-T6ZQHx/plugin/ESBuildInitialOptions/index.ts"],
|
||||
external: ["esbuild"],
|
||||
format: undefined,
|
||||
minify: false,
|
||||
minifyIdentifiers: undefined,
|
||||
minifySyntax: undefined,
|
||||
minifyWhitespace: undefined,
|
||||
outdir: root,
|
||||
platform: "browser",
|
||||
sourcemap: undefined,
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
@@ -270,6 +270,12 @@ export interface BundlerTestRunOptions {
|
||||
/** given when you do itBundled('id', (this object) => BundlerTestInput) */
|
||||
export interface BundlerTestWrappedAPI {
|
||||
root: string;
|
||||
getConfigRef: () => BuildConfig;
|
||||
}
|
||||
|
||||
let configRef: BuildConfig;
|
||||
function getConfigRef() {
|
||||
return configRef;
|
||||
}
|
||||
|
||||
export interface BundlerTestRef {
|
||||
@@ -837,15 +843,16 @@ for (const [key, blob] of build.outputs) {
|
||||
}
|
||||
}
|
||||
|
||||
configRef = buildConfig;
|
||||
const build = await Bun.build(buildConfig);
|
||||
configRef = null!;
|
||||
Bun.gc(true);
|
||||
|
||||
const buildLogs = (build as any).logs;
|
||||
const buildLogs = build.logs.filter(x => x.level === "error");
|
||||
|
||||
if (buildLogs) {
|
||||
const rawErrors = buildLogs instanceof AggregateError ? buildLogs.errors : [buildLogs];
|
||||
if (buildLogs.length) {
|
||||
const allErrors: ErrorMeta[] = [];
|
||||
for (const error of rawErrors) {
|
||||
for (const error of buildLogs) {
|
||||
const str = error.message ?? String(error);
|
||||
if (str.startsWith("\u001B[2mexpect(") || str.startsWith("expect(")) {
|
||||
throw error;
|
||||
@@ -1269,7 +1276,7 @@ export function itBundled(
|
||||
): BundlerTestRef {
|
||||
if (typeof opts === "function") {
|
||||
const fn = opts;
|
||||
opts = opts({ root: path.join(outBase, id.replaceAll("/", path.sep)) });
|
||||
opts = opts({ root: path.join(outBase, id.replaceAll("/", path.sep)), getConfigRef });
|
||||
// @ts-expect-error
|
||||
opts._referenceFn = fn;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user