bun-types 1.1.39-canary.20241215T140539 → 1.1.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bun.d.ts +2 -0
- package/docs/api/fetch.md +1 -1
- package/docs/api/utils.md +25 -0
- package/docs/bundler/plugins.md +372 -2
- package/docs/cli/install.md +2 -1
- package/docs/project/bindgen.md +30 -4
- package/docs/runtime/plugins.md +0 -147
- package/jsc.d.ts +14 -0
- package/package.json +1 -1
package/bun.d.ts
CHANGED
package/docs/api/fetch.md
CHANGED
package/docs/api/utils.md
CHANGED
|
@@ -771,3 +771,28 @@ console.log(obj); // => { foo: "bar" }
|
|
|
771
771
|
```
|
|
772
772
|
|
|
773
773
|
Internally, [`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) and [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) serialize and deserialize the same way. This exposes the underlying [HTML Structured Clone Algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) to JavaScript as an ArrayBuffer.
|
|
774
|
+
|
|
775
|
+
## `estimateShallowMemoryUsageOf` in `bun:jsc`
|
|
776
|
+
|
|
777
|
+
The `estimateShallowMemoryUsageOf` function returns a best-effort estimate of the memory usage of an object in bytes, excluding the memory usage of properties or other objects it references. For accurate per-object memory usage, use `Bun.generateHeapSnapshot`.
|
|
778
|
+
|
|
779
|
+
```js
|
|
780
|
+
import { estimateShallowMemoryUsageOf } from "bun:jsc";
|
|
781
|
+
|
|
782
|
+
const obj = { foo: "bar" };
|
|
783
|
+
const usage = estimateShallowMemoryUsageOf(obj);
|
|
784
|
+
console.log(usage); // => 16
|
|
785
|
+
|
|
786
|
+
const buffer = Buffer.alloc(1024 * 1024);
|
|
787
|
+
estimateShallowMemoryUsageOf(buffer);
|
|
788
|
+
// => 1048624
|
|
789
|
+
|
|
790
|
+
const req = new Request("https://bun.sh");
|
|
791
|
+
estimateShallowMemoryUsageOf(req);
|
|
792
|
+
// => 167
|
|
793
|
+
|
|
794
|
+
const array = Array(1024).fill({ a: 1 });
|
|
795
|
+
// Arrays are usually not stored contiguously in memory, so this will not return a useful value (which isn't a bug).
|
|
796
|
+
estimateShallowMemoryUsageOf(array);
|
|
797
|
+
// => 16
|
|
798
|
+
```
|
package/docs/bundler/plugins.md
CHANGED
|
@@ -2,11 +2,47 @@ 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
|
-
|
|
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):
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
type PluginBuilder = {
|
|
20
|
+
onStart(callback: () => void): void;
|
|
21
|
+
onResolve: (
|
|
22
|
+
args: { filter: RegExp; namespace?: string },
|
|
23
|
+
callback: (args: { path: string; importer: string }) => {
|
|
24
|
+
path: string;
|
|
25
|
+
namespace?: string;
|
|
26
|
+
} | void,
|
|
27
|
+
) => void;
|
|
28
|
+
onLoad: (
|
|
29
|
+
args: { filter: RegExp; namespace?: string },
|
|
30
|
+
defer: () => Promise<void>,
|
|
31
|
+
callback: (args: { path: string }) => {
|
|
32
|
+
loader?: Loader;
|
|
33
|
+
contents?: string;
|
|
34
|
+
exports?: Record<string, any>;
|
|
35
|
+
},
|
|
36
|
+
) => void;
|
|
37
|
+
config: BuildConfig;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
type Loader = "js" | "jsx" | "ts" | "tsx" | "css" | "json" | "toml";
|
|
41
|
+
```
|
|
6
42
|
|
|
7
43
|
## Usage
|
|
8
44
|
|
|
9
|
-
A plugin is defined as simple JavaScript object containing a `name` property and a `setup` function.
|
|
45
|
+
A plugin is defined as simple JavaScript object containing a `name` property and a `setup` function.
|
|
10
46
|
|
|
11
47
|
```tsx#myPlugin.ts
|
|
12
48
|
import type { BunPlugin } from "bun";
|
|
@@ -28,3 +64,337 @@ Bun.build({
|
|
|
28
64
|
plugins: [myPlugin],
|
|
29
65
|
});
|
|
30
66
|
```
|
|
67
|
+
|
|
68
|
+
## Plugin lifecycle
|
|
69
|
+
|
|
70
|
+
### Namespaces
|
|
71
|
+
|
|
72
|
+
`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 = await Bun.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
|
+
await Bunlog.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
|
+
await Bun.$`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.
|
|
139
|
+
|
|
140
|
+
### `onResolve`
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
onResolve(
|
|
144
|
+
args: { filter: RegExp; namespace?: string },
|
|
145
|
+
callback: (args: { path: string; importer: string }) => {
|
|
146
|
+
path: string;
|
|
147
|
+
namespace?: string;
|
|
148
|
+
} | void,
|
|
149
|
+
): void;
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
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/`:
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
import { plugin } from "bun";
|
|
166
|
+
|
|
167
|
+
plugin({
|
|
168
|
+
name: "onResolve example",
|
|
169
|
+
setup(build) {
|
|
170
|
+
build.onResolve({ filter: /.*/, namespace: "file" }, args => {
|
|
171
|
+
if (args.path.startsWith("images/")) {
|
|
172
|
+
return {
|
|
173
|
+
path: args.path.replace("images/", "./public/images/"),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### `onLoad`
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
onLoad(
|
|
185
|
+
args: { filter: RegExp; namespace?: string },
|
|
186
|
+
defer: () => Promise<void>,
|
|
187
|
+
callback: (args: { path: string, importer: string, namespace: string, kind: ImportKind }) => {
|
|
188
|
+
loader?: Loader;
|
|
189
|
+
contents?: string;
|
|
190
|
+
exports?: Record<string, any>;
|
|
191
|
+
},
|
|
192
|
+
): void;
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
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`.
|
|
206
|
+
|
|
207
|
+
For example:
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
import { plugin } from "bun";
|
|
211
|
+
|
|
212
|
+
const envPlugin: BunPlugin = {
|
|
213
|
+
name: "env plugin",
|
|
214
|
+
setup(build) {
|
|
215
|
+
build.onLoad({ filter: /env/, namespace: "file" }, args => {
|
|
216
|
+
return {
|
|
217
|
+
contents: `export default ${JSON.stringify(process.env)}`,
|
|
218
|
+
loader: "js",
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
Bun.build({
|
|
225
|
+
entrypoints: ["./app.ts"],
|
|
226
|
+
outdir: "./dist",
|
|
227
|
+
plugins: [envPlugin],
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// import env from "env"
|
|
231
|
+
// env.FOO === "bar"
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
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 = new Bun.Transpiler();
|
|
253
|
+
|
|
254
|
+
let trackedImports: Record<string, number> = {};
|
|
255
|
+
|
|
256
|
+
// Each module that goes through this onLoad callback
|
|
257
|
+
// will record its imports in `trackedImports`
|
|
258
|
+
build.onLoad({ filter: /\.ts/ }, async ({ path }) => {
|
|
259
|
+
const contents = await Bun.file(path).arrayBuffer();
|
|
260
|
+
|
|
261
|
+
const imports = transpiler.scanImports(contents);
|
|
262
|
+
|
|
263
|
+
for (const i of imports) {
|
|
264
|
+
trackedImports[i.path] = (trackedImports[i.path] || 0) + 1;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return undefined;
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
build.onLoad({ filter: /stats\.json/ }, async ({ defer }) => {
|
|
271
|
+
// Wait for all files to be loaded, ensuring
|
|
272
|
+
// that every file goes through the above `onLoad()` function
|
|
273
|
+
// and their imports tracked
|
|
274
|
+
await defer();
|
|
275
|
+
|
|
276
|
+
// Emit JSON containing the stats of each import
|
|
277
|
+
return {
|
|
278
|
+
contents: `export default ${JSON.stringify(trackedImports)}`,
|
|
279
|
+
loader: "json",
|
|
280
|
+
};
|
|
281
|
+
});
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
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
|
+
```bash
|
|
313
|
+
bun add -g @napi-rs/cli
|
|
314
|
+
napi new
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Then install this crate:
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
cargo add bun-native-plugin
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Now, inside the `lib.rs` file, we'll use the `bun_native_plugin::bun` proc macro to define a function which
|
|
324
|
+
will implement our native plugin.
|
|
325
|
+
|
|
326
|
+
Here's an example implementing the `onBeforeParse` hook:
|
|
327
|
+
|
|
328
|
+
```rs
|
|
329
|
+
use bun_native_plugin::{define_bun_plugin, OnBeforeParse, bun, Result, anyhow, BunLoader};
|
|
330
|
+
use napi_derive::napi;
|
|
331
|
+
|
|
332
|
+
/// Define the plugin and its name
|
|
333
|
+
define_bun_plugin!("replace-foo-with-bar");
|
|
334
|
+
|
|
335
|
+
/// Here we'll implement `onBeforeParse` with code that replaces all occurrences of
|
|
336
|
+
/// `foo` with `bar`.
|
|
337
|
+
///
|
|
338
|
+
/// We use the #[bun] macro to generate some of the boilerplate code.
|
|
339
|
+
///
|
|
340
|
+
/// The argument of the function (`handle: &mut OnBeforeParse`) tells
|
|
341
|
+
/// the macro that this function implements the `onBeforeParse` hook.
|
|
342
|
+
#[bun]
|
|
343
|
+
pub fn replace_foo_with_bar(handle: &mut OnBeforeParse) -> Result<()> {
|
|
344
|
+
// Fetch the input source code.
|
|
345
|
+
let input_source_code = handle.input_source_code()?;
|
|
346
|
+
|
|
347
|
+
// Get the Loader for the file
|
|
348
|
+
let loader = handle.output_loader();
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
let output_source_code = input_source_code.replace("foo", "bar");
|
|
352
|
+
|
|
353
|
+
handle.set_output_source_code(output_source_code, BunLoader::BUN_LOADER_JSX);
|
|
354
|
+
|
|
355
|
+
Ok(())
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
And to use it in Bun.build():
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
import myNativeAddon from "./my-native-addon";
|
|
363
|
+
Bun.build({
|
|
364
|
+
entrypoints: ["./app.tsx"],
|
|
365
|
+
plugins: [
|
|
366
|
+
{
|
|
367
|
+
name: "my-plugin",
|
|
368
|
+
|
|
369
|
+
setup(build) {
|
|
370
|
+
build.onBeforeParse(
|
|
371
|
+
{
|
|
372
|
+
namespace: "file",
|
|
373
|
+
filter: "**/*.tsx",
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
napiModule: myNativeAddon,
|
|
377
|
+
symbol: "replace_foo_with_bar",
|
|
378
|
+
// external: myNativeAddon.getSharedState()
|
|
379
|
+
},
|
|
380
|
+
);
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
],
|
|
384
|
+
});
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### `onBeforeParse`
|
|
388
|
+
|
|
389
|
+
```ts
|
|
390
|
+
onBeforeParse(
|
|
391
|
+
args: { filter: RegExp; namespace?: string },
|
|
392
|
+
callback: { napiModule: NapiModule; symbol: string; external?: unknown },
|
|
393
|
+
): void;
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
This lifecycle callback is run immediately before a file is parsed by Bun's bundler.
|
|
397
|
+
|
|
398
|
+
As input, it receives the file's contents and can optionally return new source code.
|
|
399
|
+
|
|
400
|
+
This callback can be called from any thread and so the napi module implementation must be thread-safe.
|
package/docs/cli/install.md
CHANGED
|
@@ -149,7 +149,8 @@ Bun supports installing dependencies from Git, GitHub, and local or remotely-hos
|
|
|
149
149
|
"lodash": "git+ssh://github.com/lodash/lodash.git#4.17.21",
|
|
150
150
|
"moment": "git@github.com:moment/moment.git",
|
|
151
151
|
"zod": "github:colinhacks/zod",
|
|
152
|
-
"react": "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
|
|
152
|
+
"react": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
|
153
|
+
"bun-types": "npm:@types/bun"
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
156
|
```
|
package/docs/project/bindgen.md
CHANGED
|
@@ -61,7 +61,10 @@ This function declaration is equivalent to:
|
|
|
61
61
|
declare function add(a: number, b: number = 1): number;
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
The code generator will provide `bun.gen.math.jsAdd`, which is the native
|
|
64
|
+
The code generator will provide `bun.gen.math.jsAdd`, which is the native
|
|
65
|
+
function implementation. To pass to JavaScript, use
|
|
66
|
+
`bun.gen.math.createAddCallback(global)`. JS files in `src/js/` may use
|
|
67
|
+
`$bindgenFn("math.bind.ts", "add")` to get a handle to the implementation.
|
|
65
68
|
|
|
66
69
|
## Strings
|
|
67
70
|
|
|
@@ -104,7 +107,7 @@ export const action = fn({
|
|
|
104
107
|
|
|
105
108
|
In Zig, each variant gets a number, based on the order the schema defines.
|
|
106
109
|
|
|
107
|
-
```
|
|
110
|
+
```zig
|
|
108
111
|
fn action1(a: i32) i32 {
|
|
109
112
|
return a;
|
|
110
113
|
}
|
|
@@ -180,9 +183,9 @@ export const add = fn({
|
|
|
180
183
|
// enforce in i32 range
|
|
181
184
|
a: t.i32.enforceRange(),
|
|
182
185
|
// clamp to u16 range
|
|
183
|
-
|
|
186
|
+
b: t.u16,
|
|
184
187
|
// enforce in arbitrary range, with a default if not provided
|
|
185
|
-
|
|
188
|
+
c: t.i32.enforceRange(0, 1000).default(5),
|
|
186
189
|
// clamp to arbitrary range, or null
|
|
187
190
|
d: t.u16.clamp(0, 10).optional,
|
|
188
191
|
},
|
|
@@ -190,6 +193,29 @@ export const add = fn({
|
|
|
190
193
|
});
|
|
191
194
|
```
|
|
192
195
|
|
|
196
|
+
Various Node.js validator functions such as `validateInteger`, `validateNumber`, and more are available. Use these when implementing Node.js APIs, so the error messages match 1:1 what Node would do.
|
|
197
|
+
|
|
198
|
+
Unlike `enforceRange`, which is taken from WebIDL, `validate*` functions are much more strict on the input they accept. For example, Node's numerical validator check `typeof value === 'number'`, while WebIDL uses `ToNumber` for lossy conversion.
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
import { t, fn } from "bindgen";
|
|
202
|
+
|
|
203
|
+
export const add = fn({
|
|
204
|
+
args: {
|
|
205
|
+
global: t.globalObject,
|
|
206
|
+
// throw if not given a number
|
|
207
|
+
a: t.f64.validateNumber(),
|
|
208
|
+
// valid in i32 range
|
|
209
|
+
a: t.i32.validateInt32(),
|
|
210
|
+
// f64 within safe integer range
|
|
211
|
+
b: t.f64.validateInteger(),
|
|
212
|
+
// f64 in given range
|
|
213
|
+
c: t.f64.validateNumber(-10000, 10000),
|
|
214
|
+
},
|
|
215
|
+
ret: t.i32,
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
193
219
|
## Callbacks
|
|
194
220
|
|
|
195
221
|
TODO
|
package/docs/runtime/plugins.md
CHANGED
|
@@ -553,150 +553,3 @@ plugin({
|
|
|
553
553
|
```
|
|
554
554
|
|
|
555
555
|
This plugin will transform all imports of the form `import env from "env"` into a JavaScript module that exports the current environment variables.
|
|
556
|
-
|
|
557
|
-
#### `.defer()`
|
|
558
|
-
|
|
559
|
-
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.
|
|
560
|
-
|
|
561
|
-
This allows you to delay execution of the `onLoad` callback until all other modules have been loaded.
|
|
562
|
-
|
|
563
|
-
This is useful for returning contens of a module that depends on other modules.
|
|
564
|
-
|
|
565
|
-
##### Example: tracking and reporting unused exports
|
|
566
|
-
|
|
567
|
-
```ts
|
|
568
|
-
import { plugin } from "bun";
|
|
569
|
-
|
|
570
|
-
plugin({
|
|
571
|
-
name: "track imports",
|
|
572
|
-
setup(build) {
|
|
573
|
-
const transpiler = new Bun.Transpiler();
|
|
574
|
-
|
|
575
|
-
let trackedImports: Record<string, number> = {};
|
|
576
|
-
|
|
577
|
-
// Each module that goes through this onLoad callback
|
|
578
|
-
// will record its imports in `trackedImports`
|
|
579
|
-
build.onLoad({ filter: /\.ts/ }, async ({ path }) => {
|
|
580
|
-
const contents = await Bun.file(path).arrayBuffer();
|
|
581
|
-
|
|
582
|
-
const imports = transpiler.scanImports(contents);
|
|
583
|
-
|
|
584
|
-
for (const i of imports) {
|
|
585
|
-
trackedImports[i.path] = (trackedImports[i.path] || 0) + 1;
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
return undefined;
|
|
589
|
-
});
|
|
590
|
-
|
|
591
|
-
build.onLoad({ filter: /stats\.json/ }, async ({ defer }) => {
|
|
592
|
-
// Wait for all files to be loaded, ensuring
|
|
593
|
-
// that every file goes through the above `onLoad()` function
|
|
594
|
-
// and their imports tracked
|
|
595
|
-
await defer();
|
|
596
|
-
|
|
597
|
-
// Emit JSON containing the stats of each import
|
|
598
|
-
return {
|
|
599
|
-
contents: `export default ${JSON.stringify(trackedImports)}`,
|
|
600
|
-
loader: "json",
|
|
601
|
-
};
|
|
602
|
-
});
|
|
603
|
-
},
|
|
604
|
-
});
|
|
605
|
-
```
|
|
606
|
-
|
|
607
|
-
Note that the `.defer()` function currently has the limitation that it can only be called once per `onLoad` callback.
|
|
608
|
-
|
|
609
|
-
## Native plugins
|
|
610
|
-
|
|
611
|
-
{% callout %}
|
|
612
|
-
**NOTE** — This is an advanced and experiemental API recommended for plugin developers who are familiar with systems programming and the C ABI. Use with caution.
|
|
613
|
-
{% /callout %}
|
|
614
|
-
|
|
615
|
-
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.
|
|
616
|
-
|
|
617
|
-
However, one limitation of plugins written in JavaScript is that JavaScript itself is single-threaded.
|
|
618
|
-
|
|
619
|
-
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.
|
|
620
|
-
|
|
621
|
-
In addition, native plugins can skip unnecessary work such as the UTF-8 -> UTF-16 conversion needed to pass strings to JavaScript.
|
|
622
|
-
|
|
623
|
-
These are the following lifecycle hooks which are available to native plugins:
|
|
624
|
-
|
|
625
|
-
- [`onBeforeParse()`](#onbeforeparse): Called on any thread before a file is parsed by Bun's bundler.
|
|
626
|
-
|
|
627
|
-
### Creating a native plugin
|
|
628
|
-
|
|
629
|
-
Native plugins are NAPI modules which expose lifecycle hooks as C ABI functions.
|
|
630
|
-
|
|
631
|
-
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.
|
|
632
|
-
|
|
633
|
-
#### Example: Rust with napi-rs
|
|
634
|
-
|
|
635
|
-
First initialize a napi project (see [here](https://napi.rs/docs/introduction/getting-started) for a more comprehensive guide).
|
|
636
|
-
|
|
637
|
-
Then install Bun's official safe plugin wrapper crate:
|
|
638
|
-
|
|
639
|
-
```bash
|
|
640
|
-
cargo add bun-native-plugin
|
|
641
|
-
```
|
|
642
|
-
|
|
643
|
-
Now you can export an `extern "C" fn` which is the implementation of your plugin:
|
|
644
|
-
|
|
645
|
-
```rust
|
|
646
|
-
#[no_mangle]
|
|
647
|
-
extern "C" fn on_before_parse_impl(
|
|
648
|
-
args: *const bun_native_plugin::sys::OnBeforeParseArguments,
|
|
649
|
-
result: *mut bun_native_plugin::sys::OnBeforeParseResult,
|
|
650
|
-
) {
|
|
651
|
-
let args = unsafe { &*args };
|
|
652
|
-
let result = unsafe { &mut *result };
|
|
653
|
-
|
|
654
|
-
let mut handle = match bun_native_plugin::OnBeforeParse::from_raw(args, result) {
|
|
655
|
-
Ok(handle) => handle,
|
|
656
|
-
Err(_) => {
|
|
657
|
-
return;
|
|
658
|
-
}
|
|
659
|
-
};
|
|
660
|
-
|
|
661
|
-
let source_code = match handle.input_source_code() {
|
|
662
|
-
Ok(source_code) => source_code,
|
|
663
|
-
Err(_) => {
|
|
664
|
-
handle.log_error("Fetching source code failed!");
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
};
|
|
668
|
-
|
|
669
|
-
let loader = handle.output_loader();
|
|
670
|
-
handle.set_output_source_code(source_code.replace("foo", "bar"), loader);
|
|
671
|
-
```
|
|
672
|
-
|
|
673
|
-
Use napi-rs to compile the plugin to a `.node` file, then you can `require()` it from JS and use it:
|
|
674
|
-
|
|
675
|
-
```js
|
|
676
|
-
await Bun.build({
|
|
677
|
-
entrypoints: ["index.ts"],
|
|
678
|
-
setup(build) {
|
|
679
|
-
const myNativePlugin = require("./path/to/plugin.node");
|
|
680
|
-
|
|
681
|
-
build.onBeforeParse(
|
|
682
|
-
{ filter: /\.ts/ },
|
|
683
|
-
{ napiModule: myNativePlugin, symbol: "on_before_parse_impl" },
|
|
684
|
-
);
|
|
685
|
-
},
|
|
686
|
-
});
|
|
687
|
-
```
|
|
688
|
-
|
|
689
|
-
### `onBeforeParse`
|
|
690
|
-
|
|
691
|
-
```ts
|
|
692
|
-
onBeforeParse(
|
|
693
|
-
args: { filter: RegExp; namespace?: string },
|
|
694
|
-
callback: { napiModule: NapiModule; symbol: string; external?: unknown },
|
|
695
|
-
): void;
|
|
696
|
-
```
|
|
697
|
-
|
|
698
|
-
This lifecycle callback is run immediately before a file is parsed by Bun's bundler.
|
|
699
|
-
|
|
700
|
-
As input, it receives the file's contents and can optionally return new source code.
|
|
701
|
-
|
|
702
|
-
This callback can be called from any thread and so the napi module implementation must be thread-safe.
|
package/jsc.d.ts
CHANGED
|
@@ -226,4 +226,18 @@ declare module "bun:jsc" {
|
|
|
226
226
|
* Run JavaScriptCore's sampling profiler
|
|
227
227
|
*/
|
|
228
228
|
function startSamplingProfiler(optionalDirectory?: string): void;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Non-recursively estimate the memory usage of an object, excluding the memory usage of
|
|
232
|
+
* properties or other objects it references. For more accurate per-object
|
|
233
|
+
* memory usage, use {@link Bun.generateHeapSnapshot}.
|
|
234
|
+
*
|
|
235
|
+
* This is a best-effort estimate. It may not be 100% accurate. When it's
|
|
236
|
+
* wrong, it may mean the memory is non-contiguous (such as a large array).
|
|
237
|
+
*
|
|
238
|
+
* Passing a primitive type that isn't heap allocated returns 0.
|
|
239
|
+
*/
|
|
240
|
+
function estimateShallowMemoryUsageOf(
|
|
241
|
+
value: object | CallableFunction | bigint | symbol | string,
|
|
242
|
+
): number;
|
|
229
243
|
}
|
package/package.json
CHANGED