mirror of
https://github.com/oven-sh/bun
synced 2026-02-12 20:09:04 +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:
@@ -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)) {
|
||||
|
||||
Reference in New Issue
Block a user