diff options
author | 2023-05-11 22:58:41 -0400 | |
---|---|---|
committer | 2023-05-11 22:58:41 -0400 | |
commit | dfd0f3e2527daffa06da791ccaed7dfe3240963e (patch) | |
tree | 535d9c676190d50e04194d78d2efba98ee9317cb /src | |
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
Diffstat (limited to 'src')
-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 |
5 files changed, 152 insertions, 107 deletions
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)) { |