mirror of
https://github.com/oven-sh/bun
synced 2026-02-16 05:42:43 +00:00
feat(build): PluginBuilder supports method chaining (#17683)
This commit is contained in:
18
packages/bun-types/bun.d.ts
vendored
18
packages/bun-types/bun.d.ts
vendored
@@ -5536,12 +5536,14 @@ declare module "bun" {
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @returns `this` for method chaining
|
||||
*/
|
||||
onStart(callback: OnStartCallback): void;
|
||||
onStart(callback: OnStartCallback): this;
|
||||
onBeforeParse(
|
||||
constraints: PluginConstraints,
|
||||
callback: { napiModule: unknown; symbol: string; external?: unknown | undefined },
|
||||
): void;
|
||||
): this;
|
||||
/**
|
||||
* Register a callback to load imports with a specific import specifier
|
||||
* @param constraints The constraints to apply the plugin to
|
||||
@@ -5556,8 +5558,10 @@ declare module "bun" {
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @returns `this` for method chaining
|
||||
*/
|
||||
onLoad(constraints: PluginConstraints, callback: OnLoadCallback): void;
|
||||
onLoad(constraints: PluginConstraints, callback: OnLoadCallback): this;
|
||||
/**
|
||||
* Register a callback to resolve imports matching a filter and/or namespace
|
||||
* @param constraints The constraints to apply the plugin to
|
||||
@@ -5572,8 +5576,10 @@ declare module "bun" {
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @returns `this` for method chaining
|
||||
*/
|
||||
onResolve(constraints: PluginConstraints, callback: OnResolveCallback): void;
|
||||
onResolve(constraints: PluginConstraints, callback: OnResolveCallback): this;
|
||||
/**
|
||||
* The config object passed to `Bun.build` as is. Can be mutated.
|
||||
*/
|
||||
@@ -5604,8 +5610,10 @@ declare module "bun" {
|
||||
* const { foo } = require("hello:world");
|
||||
* console.log(foo); // "bar"
|
||||
* ```
|
||||
*
|
||||
* @returns `this` for method chaining
|
||||
*/
|
||||
module(specifier: string, callback: () => OnLoadResult | Promise<OnLoadResult>): void;
|
||||
module(specifier: string, callback: () => OnLoadResult | Promise<OnLoadResult>): this;
|
||||
}
|
||||
|
||||
interface BunPlugin {
|
||||
|
||||
@@ -94,7 +94,7 @@ static JSC::EncodedJSValue jsFunctionAppendOnLoadPluginBody(JSC::JSGlobalObject*
|
||||
plugin.append(vm, filter->regExp(), func.getObject(), namespaceString);
|
||||
callback(ctx, globalObject);
|
||||
|
||||
return JSValue::encode(jsUndefined());
|
||||
return JSValue::encode(callframe->thisValue());
|
||||
}
|
||||
|
||||
static EncodedJSValue jsFunctionAppendVirtualModulePluginBody(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callframe)
|
||||
@@ -150,7 +150,7 @@ static EncodedJSValue jsFunctionAppendVirtualModulePluginBody(JSC::JSGlobalObjec
|
||||
global->requireMap()->remove(globalObject, moduleIdValue);
|
||||
global->esmRegistryMap()->remove(globalObject, moduleIdValue);
|
||||
|
||||
return JSValue::encode(jsUndefined());
|
||||
return JSValue::encode(callframe->thisValue());
|
||||
}
|
||||
|
||||
static JSC::EncodedJSValue jsFunctionAppendOnResolvePluginBody(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callframe, BunPluginTarget target, BunPlugin::Base& plugin, void* ctx, OnAppendPluginCallback callback)
|
||||
@@ -204,7 +204,7 @@ static JSC::EncodedJSValue jsFunctionAppendOnResolvePluginBody(JSC::JSGlobalObje
|
||||
plugin.append(vm, filter->regExp(), jsCast<JSObject*>(func), namespaceString);
|
||||
callback(ctx, globalObject);
|
||||
|
||||
return JSValue::encode(jsUndefined());
|
||||
return JSValue::encode(callframe->thisValue());
|
||||
}
|
||||
|
||||
static JSC::EncodedJSValue jsFunctionAppendOnResolvePluginGlobal(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callframe, BunPluginTarget target)
|
||||
@@ -260,6 +260,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionAppendOnResolvePluginBrowser, (JSC::JSGlobalO
|
||||
return jsFunctionAppendOnResolvePluginGlobal(globalObject, callframe, BunPluginTargetBrowser);
|
||||
}
|
||||
|
||||
/// `Bun.plugin()`
|
||||
static inline JSC::EncodedJSValue setupBunPlugin(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callframe, BunPluginTarget target)
|
||||
{
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
|
||||
namespace Bun {
|
||||
|
||||
extern "C" void CrashHandler__setInsideNativePlugin(const char* plugin_name);
|
||||
extern "C" int OnBeforeParsePlugin__isDone(void* context);
|
||||
extern "C" void OnBeforeParseResult__reset(OnBeforeParseResult* result);
|
||||
#define WRAP_BUNDLER_PLUGIN(argName) jsDoubleNumber(std::bit_cast<double>(reinterpret_cast<uintptr_t>(argName)))
|
||||
@@ -189,6 +190,7 @@ DEFINE_VISIT_CHILDREN(JSBundlerPlugin);
|
||||
|
||||
const JSC::ClassInfo JSBundlerPlugin::s_info = { "BundlerPlugin"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSBundlerPlugin) };
|
||||
|
||||
/// `BundlerPlugin.prototype.addFilter(filter: RegExp, namespace: string, isOnLoad: 0 | 1): void`
|
||||
JSC_DEFINE_HOST_FUNCTION(jsBundlerPluginFunction_addFilter, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSBundlerPlugin* thisObject = jsCast<JSBundlerPlugin*>(callFrame->thisValue());
|
||||
@@ -273,8 +275,6 @@ bool BundlerPlugin::FilterRegExp::match(JSC::VM& vm, const String& path)
|
||||
return regex.match(path) != -1;
|
||||
}
|
||||
|
||||
extern "C" void CrashHandler__setInsideNativePlugin(const char* plugin_name);
|
||||
|
||||
int BundlerPlugin::NativePluginList::call(JSC::VM& vm, BundlerPlugin* plugin, int* shouldContinue, void* bunContextPtr, const BunString* namespaceStr, const BunString* pathString, OnBeforeParseArguments* onBeforeParseArgs, OnBeforeParseResult* onBeforeParseResult)
|
||||
{
|
||||
unsigned index = 0;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import type { BuildConfig, BunPlugin, OnLoadCallback, OnResolveCallback, PluginBuilder, PluginConstraints } from "bun";
|
||||
type AnyFunction = (...args: any[]) => any;
|
||||
|
||||
/**
|
||||
* @see `JSBundlerPlugin.h`
|
||||
*/
|
||||
interface BundlerPlugin {
|
||||
onLoad: Map<string, [RegExp, OnLoadCallback][]>;
|
||||
onResolve: Map<string, [RegExp, OnResolveCallback][]>;
|
||||
@@ -108,10 +111,10 @@ export function runSetupFunction(
|
||||
promises: Array<Promise<any>> | undefined,
|
||||
is_last: boolean,
|
||||
isBake: boolean,
|
||||
): Promise<Array<Promise<any>>> | undefined {
|
||||
): Promise<Promise<any>[]> | Promise<any>[] | undefined {
|
||||
this.promises = promises;
|
||||
var onLoadPlugins = new Map<string, [RegExp, AnyFunction][]>();
|
||||
var onResolvePlugins = new Map<string, [RegExp, AnyFunction][]>();
|
||||
var onLoadPlugins = new Map<string, [filter: RegExp, callback: OnLoadCallback][]>();
|
||||
var onResolvePlugins = new Map<string, [filter: RegExp, OnResolveCallback][]>();
|
||||
var onBeforeParsePlugins = new Map<
|
||||
string,
|
||||
[RegExp, napiModule: unknown, symbol: string, external?: undefined | unknown][]
|
||||
@@ -172,23 +175,27 @@ export function runSetupFunction(
|
||||
}
|
||||
}
|
||||
|
||||
function onLoad(filterObject, callback) {
|
||||
function onLoad(this: PluginBuilder, filterObject: PluginConstraints, callback: OnLoadCallback): PluginBuilder {
|
||||
validate(filterObject, callback, onLoadPlugins, undefined, undefined);
|
||||
return this;
|
||||
}
|
||||
|
||||
function onResolve(filterObject, callback) {
|
||||
function onResolve(this: PluginBuilder, filterObject: PluginConstraints, callback): PluginBuilder {
|
||||
validate(filterObject, callback, onResolvePlugins, undefined, undefined);
|
||||
return this;
|
||||
}
|
||||
|
||||
function onBeforeParse(
|
||||
filterObject,
|
||||
this: PluginBuilder,
|
||||
filterObject: PluginConstraints,
|
||||
{ napiModule, external, symbol }: { napiModule: unknown; symbol: string; external?: undefined | unknown },
|
||||
) {
|
||||
): PluginBuilder {
|
||||
validate(filterObject, napiModule, onBeforeParsePlugins, symbol, external);
|
||||
return this;
|
||||
}
|
||||
|
||||
const self = this;
|
||||
function onStart(callback) {
|
||||
function onStart(this: PluginBuilder, callback): PluginBuilder {
|
||||
if (isBake) {
|
||||
throw new TypeError("onStart() is not supported in Bake yet");
|
||||
}
|
||||
@@ -203,6 +210,7 @@ export function runSetupFunction(
|
||||
self.promises.push(ret);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
const processSetupResult = () => {
|
||||
@@ -210,14 +218,14 @@ export function runSetupFunction(
|
||||
anyOnResolve = false,
|
||||
anyOnBeforeParse = false;
|
||||
|
||||
for (var [namespace, callbacks] of onLoadPlugins.entries()) {
|
||||
for (let [namespace, callbacks] of onLoadPlugins.entries()) {
|
||||
for (var [filter] of callbacks) {
|
||||
this.addFilter(filter, namespace, 1);
|
||||
anyOnLoad = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (var [namespace, callbacks] of onResolvePlugins.entries()) {
|
||||
for (let [namespace, callbacks] of onResolvePlugins.entries()) {
|
||||
for (var [filter] of callbacks) {
|
||||
this.addFilter(filter, namespace, 0);
|
||||
anyOnResolve = true;
|
||||
@@ -236,7 +244,7 @@ export function runSetupFunction(
|
||||
if (!onResolveObject) {
|
||||
this.onResolve = onResolvePlugins;
|
||||
} else {
|
||||
for (var [namespace, callbacks] of onResolvePlugins.entries()) {
|
||||
for (let [namespace, callbacks] of onResolvePlugins.entries()) {
|
||||
var existing = onResolveObject.$get(namespace) as [RegExp, AnyFunction][];
|
||||
|
||||
if (!existing) {
|
||||
@@ -253,7 +261,7 @@ export function runSetupFunction(
|
||||
if (!onLoadObject) {
|
||||
this.onLoad = onLoadPlugins;
|
||||
} else {
|
||||
for (var [namespace, callbacks] of onLoadPlugins.entries()) {
|
||||
for (let [namespace, callbacks] of onLoadPlugins.entries()) {
|
||||
var existing = onLoadObject.$get(namespace) as [RegExp, AnyFunction][];
|
||||
|
||||
if (!existing) {
|
||||
@@ -284,6 +292,9 @@ export function runSetupFunction(
|
||||
module: () => {
|
||||
throw new TypeError("module() is not supported in Bun.build() yet. Only via Bun.plugin() at runtime");
|
||||
},
|
||||
addPreload: () => {
|
||||
throw new TypeError("addPreload() is not supported in Bun.build() yet.");
|
||||
},
|
||||
// esbuild's options argument is different, we provide some interop
|
||||
initialOptions: {
|
||||
...config,
|
||||
@@ -297,7 +308,7 @@ export function runSetupFunction(
|
||||
platform: config.target === "bun" ? "node" : config.target,
|
||||
},
|
||||
esbuild: {},
|
||||
} satisfies PluginBuilderExt as PluginBuilder);
|
||||
} as PluginBuilderExt);
|
||||
|
||||
if (setupResult && $isPromise(setupResult)) {
|
||||
if ($getPromiseInternalField(setupResult, $promiseFieldFlags) & $promiseStateFulfilled) {
|
||||
|
||||
@@ -97,7 +97,11 @@ values;`,
|
||||
{
|
||||
name: "xXx123_foo_counter_321xXx",
|
||||
setup(build) {
|
||||
build.onBeforeParse({ filter: /\.ts/ }, { napiModule, symbol: "plugin_impl", external });
|
||||
const chainedThis = build.onBeforeParse(
|
||||
{ filter: /\.ts/ },
|
||||
{ napiModule, symbol: "plugin_impl", external },
|
||||
);
|
||||
expect(chainedThis).toBe(build);
|
||||
|
||||
build.onLoad({ filter: /lmao\.json/ }, async ({ defer }) => {
|
||||
await defer();
|
||||
|
||||
@@ -16,20 +16,22 @@ declare global {
|
||||
plugin({
|
||||
name: "url text file loader",
|
||||
setup(builder) {
|
||||
builder.onResolve({ namespace: "http", filter: /.*/ }, ({ path }) => {
|
||||
var chainedThis = builder.onResolve({ namespace: "http", filter: /.*/ }, ({ path }) => {
|
||||
return {
|
||||
path,
|
||||
namespace: "url",
|
||||
};
|
||||
});
|
||||
expect(chainedThis).toBe(builder);
|
||||
|
||||
builder.onLoad({ filter: /.*/, namespace: "url" }, async ({ path, namespace }) => {
|
||||
chainedThis = builder.onLoad({ filter: /.*/, namespace: "url" }, async ({ path, namespace }) => {
|
||||
const res = await fetch("http://" + path);
|
||||
return {
|
||||
exports: { default: await res.text() },
|
||||
loader: "object",
|
||||
};
|
||||
});
|
||||
expect(chainedThis).toBe(builder);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user