You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: docs/bundler/plugins.md
+345-1
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,43 @@ Bun provides a universal plugin API that can be used to extend both the _runtime
2
2
3
3
Plugins intercept imports and perform custom loading logic: reading files, transpiling code, etc. They can be used to add support for additional file types, like `.scss` or `.yaml`. In the context of Bun's bundler, plugins can be used to implement framework-level features like CSS extraction, macros, and client-server code co-location.
4
4
5
-
For more complete documentation of the Plugin API, see [Runtime > Plugins](https://bun.sh/docs/runtime/plugins).
5
+
## Lifecycle hooks
6
+
7
+
Plugins can register callbacks to be run at various points in the lifecycle of a bundle:
8
+
9
+
-[`onStart()`](#onstart): Run once the bundler has started a bundle
10
+
-[`onResolve()`](#onresolve): Run before a module is resolved
11
+
-[`onLoad()`](#onload): Run before a module is loaded.
12
+
-[`onBeforeParse()`](#onbeforeparse): Run zero-copy native addons in the parser thread before a file is parsed.
13
+
14
+
### Reference
15
+
16
+
A rough overview of the types (please refer to Bun's `bun.d.ts` for the full type definitions):
`onLoad` and `onResolve` accept an optional `namespace` string. What is a namespaace?
73
+
74
+
Every module has a namespace. Namespaces are used to prefix the import in transpiled code; for instance, a loader with a `filter: /\.yaml$/` and `namespace: "yaml:"` will transform an import from `./myfile.yaml` into `yaml:./myfile.yaml`.
75
+
76
+
The default namespace is `"file"` and it is not necessary to specify it, for instance: `import myModule from "./my-module.ts"` is the same as `import myModule from "file:./my-module.ts"`.
77
+
78
+
Other common namespaces are:
79
+
80
+
-`"bun"`: for Bun-specific modules (e.g. `"bun:test"`, `"bun:sqlite"`)
81
+
-`"node"`: for Node.js modules (e.g. `"node:fs"`, `"node:path"`)
82
+
83
+
### `onStart`
84
+
85
+
```ts
86
+
onStart(callback: () =>void): Promise<void>|void;
87
+
```
88
+
89
+
Registers a callback to be run when the bundler starts a new bundle.
90
+
91
+
```ts
92
+
import { plugin } from"bun";
93
+
94
+
plugin({
95
+
name: "onStart example",
96
+
97
+
setup(build) {
98
+
build.onStart(() => {
99
+
console.log("Bundle started!");
100
+
});
101
+
},
102
+
});
103
+
```
104
+
105
+
The callback can return a `Promise`. After the bundle process has initialized, the bundler waits until all `onStart()` callbacks have completed before continuing.
106
+
107
+
For example:
108
+
109
+
```ts
110
+
const result =awaitBun.build({
111
+
entrypoints: ["./app.ts"],
112
+
outdir: "./dist",
113
+
sourcemap: "external",
114
+
plugins: [
115
+
{
116
+
name: "Sleep for 10 seconds",
117
+
setup(build) {
118
+
build.onStart(async () => {
119
+
awaitBunlog.sleep(10_000);
120
+
});
121
+
},
122
+
},
123
+
{
124
+
name: "Log bundle time to a file",
125
+
setup(build) {
126
+
build.onStart(async () => {
127
+
const now =Date.now();
128
+
awaitBun.$`echo ${now} > bundle-time.txt`;
129
+
});
130
+
},
131
+
},
132
+
],
133
+
});
134
+
```
135
+
136
+
In the above example, Bun will wait until the first `onStart()` (sleeping for 10 seconds) has completed, _as well as_ the second `onStart()` (writing the bundle time to a file).
137
+
138
+
Note that `onStart()` callbacks (like every other lifecycle callback) do not have the ability to modify the `build.config` object. If you want to mutate `build.config`, you must do so directly in the `setup()` function.
To bundle your project, Bun walks down the dependency tree of all modules in your project. For each imported module, Bun actually has to find and read that module. The "finding" part is known as "resolving" a module.
153
+
154
+
The `onResolve()` plugin lifecycle callback allows you to configure how a module is resolved.
155
+
156
+
The first argument to `onResolve()` is an object with a `filter` and [`namespace`](#what-is-a-namespace) property. The filter is a regular expression which is run on the import string. Effectively, these allow you to filter which modules your custom resolution logic will apply to.
157
+
158
+
The second argument to `onResolve()` is a callback which is run for each module import Bun finds that matches the `filter` and `namespace` defined in the first argument.
159
+
160
+
The callback receives as input the _path_ to the matching module. The callback can return a _new path_ for the module. Bun will read the contents of the _new path_ and parse it as a module.
161
+
162
+
For example, redirecting all imports to `images/` to `./public/images/`:
After Bun's bundler has resolved a module, it needs to read the contents of the module and parse it.
196
+
197
+
The `onLoad()` plugin lifecycle callback allows you to modify the _contents_ of a module before it is read and parsed by Bun.
198
+
199
+
Like `onResolve()`, the first argument to `onLoad()` allows you to filter which modules this invocation of `onLoad()` will apply to.
200
+
201
+
The second argument to `onLoad()` is a callback which is run for each matching module _before_ Bun loads the contents of the module into memory.
202
+
203
+
This callback receives as input the _path_ to the matching module, the _importer_ of the module (the module that imported the module), the _namespace_ of the module, and the _kind_ of the module.
204
+
205
+
The callback can return a new `contents` string for the module as well as a new `loader`.
This plugin will transform all imports of the form `import env from "env"` into a JavaScript module that exports the current environment variables.
235
+
236
+
#### `.defer()`
237
+
238
+
One of the arguments passed to the `onLoad` callback is a `defer` function. This function returns a `Promise` that is resolved when all _other_ modules have been loaded.
239
+
240
+
This allows you to delay execution of the `onLoad` callback until all other modules have been loaded.
241
+
242
+
This is useful for returning contens of a module that depends on other modules.
243
+
244
+
##### Example: tracking and reporting unused exports
245
+
246
+
```ts
247
+
import { plugin } from"bun";
248
+
249
+
plugin({
250
+
name: "track imports",
251
+
setup(build) {
252
+
const transpiler =newBun.Transpiler();
253
+
254
+
let trackedImports:Record<string, number> = {};
255
+
256
+
// Each module that goes through this onLoad callback
Note that the `.defer()` function currently has the limitation that it can only be called once per `onLoad` callback.
287
+
288
+
## Native plugins
289
+
290
+
One of the reasons why Bun's bundler is so fast is that it is written in native code and leverages multi-threading to load and parse modules in parallel.
291
+
292
+
However, one limitation of plugins written in JavaScript is that JavaScript itself is single-threaded.
293
+
294
+
Native plugins are written as [NAPI](/docs/node-api) modules and can be run on multiple threads. This allows native plugins to run much faster than JavaScript plugins.
295
+
296
+
In addition, native plugins can skip unnecessary work such as the UTF-8 -> UTF-16 conversion needed to pass strings to JavaScript.
297
+
298
+
These are the following lifecycle hooks which are available to native plugins:
299
+
300
+
-[`onBeforeParse()`](#onbeforeparse): Called on any thread before a file is parsed by Bun's bundler.
301
+
302
+
Native plugins are NAPI modules which expose lifecycle hooks as C ABI functions.
303
+
304
+
To create a native plugin, you must export a C ABI function which matches the signature of the native lifecycle hook you want to implement.
305
+
306
+
### Creating a native plugin in Rust
307
+
308
+
Native plugins are NAPI modules which expose lifecycle hooks as C ABI functions.
309
+
310
+
To create a native plugin, you must export a C ABI function which matches the signature of the native lifecycle hook you want to implement.
311
+
312
+
ve bundler plugins are NAPI modules, the easiest way to get started is to create a new [napi-rs](https://github.com/napi-rs/napi-rs) project:
313
+
314
+
```bash
315
+
bun add -g @napi-rs/cli
316
+
napi new
317
+
```
318
+
319
+
Then install this crate:
320
+
321
+
```bash
322
+
cargo add bun-native-plugin
323
+
```
324
+
325
+
Now, inside the `lib.rs` file, we'll use the `bun_native_plugin::bun` proc macro to define a function which
326
+
will implement our native plugin.
327
+
328
+
Here's an example implementing the `onBeforeParse` hook:
0 commit comments