diff options
author | 2023-05-11 22:58:41 -0400 | |
---|---|---|
committer | 2023-05-11 22:58:41 -0400 | |
commit | dfd0f3e2527daffa06da791ccaed7dfe3240963e (patch) | |
tree | 535d9c676190d50e04194d78d2efba98ee9317cb | |
parent | 136b50c74639cb1f583435a318d283028ee57dc5 (diff) | |
download | bun-dfd0f3e2527daffa06da791ccaed7dfe3240963e.tar.gz bun-dfd0f3e2527daffa06da791ccaed7dfe3240963e.tar.zst bun-dfd0f3e2527daffa06da791ccaed7dfe3240963e.zip |
Allow reading config from within plugins, and partially implement esbuild `initialOptions` (#2861)
* Implement plugin build.config and initialOptions
* update types
* default initialoptions entrypoints
-rw-r--r-- | docs/bundler/migration.md | 2 | ||||
-rw-r--r-- | docs/bundler/plugins.md | 28 | ||||
-rw-r--r-- | packages/bun-types/bun.d.ts | 6 | ||||
-rw-r--r-- | src/bun.js/api/JSBundler.zig | 207 | ||||
-rw-r--r-- | src/bun.js/bindings/JSBundlerPlugin.cpp | 4 | ||||
-rw-r--r-- | src/bun.js/builtins/cpp/BundlerPluginBuiltins.cpp | 24 | ||||
-rw-r--r-- | src/bun.js/builtins/cpp/BundlerPluginBuiltins.h | 2 | ||||
-rw-r--r-- | src/bun.js/builtins/js/BundlerPlugin.js | 22 | ||||
-rw-r--r-- | test/bundler/bundler_plugin.test.ts | 145 | ||||
-rw-r--r-- | test/bundler/expectBundled.ts | 17 |
10 files changed, 300 insertions, 157 deletions
diff --git a/docs/bundler/migration.md b/docs/bundler/migration.md index bfe5a63bc..7b375fda5 100644 --- a/docs/bundler/migration.md +++ b/docs/bundler/migration.md @@ -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"; diff --git a/docs/bundler/plugins.md b/docs/bundler/plugins.md index c652e0583..2062135ec 100644 --- a/docs/bundler/plugins.md +++ b/docs/bundler/plugins.md @@ -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"; diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts index 0fc63f911..3dea90969 100644 --- a/packages/bun-types/bun.d.ts +++ b/packages/bun-types/bun.d.ts @@ -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>; } diff --git a/src/bun.js/api/JSBundler.zig b/src/bun.js/api/JSBundler.zig index 04ca81a8c..f17fe99d1 100644 --- a/src/bun.js/api/JSBundler.zig +++ b/src/bun.js/api/JSBundler.zig @@ -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( diff --git a/src/bun.js/bindings/JSBundlerPlugin.cpp b/src/bun.js/bindings/JSBundlerPlugin.cpp index 5f1d5c96b..279673afb 100644 --- a/src/bun.js/bindings/JSBundlerPlugin.cpp +++ b/src/bun.js/bindings/JSBundlerPlugin.cpp @@ -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); diff --git a/src/bun.js/builtins/cpp/BundlerPluginBuiltins.cpp b/src/bun.js/builtins/cpp/BundlerPluginBuiltins.cpp index be049ae13..f66e568a3 100644 --- a/src/bun.js/builtins/cpp/BundlerPluginBuiltins.cpp +++ b/src/bun.js/builtins/cpp/BundlerPluginBuiltins.cpp @@ -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" \ diff --git a/src/bun.js/builtins/cpp/BundlerPluginBuiltins.h b/src/bun.js/builtins/cpp/BundlerPluginBuiltins.h index 91b3233f2..d1fdaf4ec 100644 --- a/src/bun.js/builtins/cpp/BundlerPluginBuiltins.h +++ b/src/bun.js/builtins/cpp/BundlerPluginBuiltins.h @@ -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 diff --git a/src/bun.js/builtins/js/BundlerPlugin.js b/src/bun.js/builtins/js/BundlerPlugin.js index ec8fee397..43f6e889a 100644 --- a/src/bun.js/builtins/js/BundlerPlugin.js +++ b/src/bun.js/builtins/js/BundlerPlugin.js @@ -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)) { diff --git a/test/bundler/bundler_plugin.test.ts b/test/bundler/bundler_plugin.test.ts index a22ec49e3..4d31c9330 100644 --- a/test/bundler/bundler_plugin.test.ts +++ b/test/bundler/bundler_plugin.test.ts @@ -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, + }); + }, + }; + }); }); diff --git a/test/bundler/expectBundled.ts b/test/bundler/expectBundled.ts index d17c9de3e..6b682ebfd 100644 --- a/test/bundler/expectBundled.ts +++ b/test/bundler/expectBundled.ts @@ -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; } |