aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md675
1 files changed, 339 insertions, 336 deletions
diff --git a/README.md b/README.md
index 731d05864..6c7ff98bb 100644
--- a/README.md
+++ b/README.md
@@ -197,7 +197,7 @@ bun.js focuses on performance, developer experience and compatibility with the J
export default {
port: 3000,
fetch(request: Request) {
- return new Response('Hello World');
+ return new Response("Hello World");
},
};
@@ -237,8 +237,8 @@ The runtime uses JavaScriptCore, the JavaScript engine powering WebKit and Safar
```js
// cat.js
-import {resolve} from 'path';
-import {write, stdout, file, argv} from 'bun';
+import { resolve } from "path";
+import { write, stdout, file, argv } from "bun";
const path = resolve(argv.at(-1));
@@ -246,7 +246,7 @@ await write(
// stdout is a Blob
stdout,
// file(path) returns a Blob - https://developer.mozilla.org/en-US/docs/Web/API/Blob
- file(path)
+ file(path),
);
// bun ./cat.js ./path-to-file
@@ -257,7 +257,7 @@ Server-side render React:
```js
// requires Bun v0.1.0 or later
// react-ssr.tsx
-import {renderToReadableStream} from 'react-dom/server';
+import { renderToReadableStream } from "react-dom/server";
const dt = new Intl.DateTimeFormat();
@@ -274,8 +274,8 @@ export default {
<h1>Hello from React!</h1>
<p>The date is {dt.format(new Date())}</p>
</body>
- </html>
- )
+ </html>,
+ ),
);
},
};
@@ -328,8 +328,8 @@ bun.js has fast paths for common use cases that make Web APIs live up to the per
When you pass a file blob to `Bun.write`, Bun automatically uses a faster system call:
```js
-const blob = Bun.file('input.txt');
-await Bun.write('output.txt', blob);
+const blob = Bun.file("input.txt");
+await Bun.write("output.txt", blob);
```
On Linux, this uses the [`copy_file_range`](https://man7.org/linux/man-pages/man2/copy_file_range.2.html) syscall and on macOS, this becomes `clonefile` (or [`fcopyfile`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/copyfile.3.html)).
@@ -338,7 +338,7 @@ On Linux, this uses the [`copy_file_range`](https://man7.org/linux/man-pages/man
```js
// Eventually, this will stream the response to disk but today it buffers
-await Bun.write('index.html', await fetch('https://example.com'));
+await Bun.write("index.html", await fetch("https://example.com"));
```
## Using bun as a package manager
@@ -714,16 +714,16 @@ When importing CSS in JavaScript-like loaders, CSS is treated special.
By default, bun will transform a statement like this:
```js
-import '../styles/global.css';
+import "../styles/global.css";
```
##### When `platform` is `browser`
```js
globalThis.document?.dispatchEvent(
- new CustomEvent('onimportcss', {
- detail: 'http://localhost:3000/styles/globals.css',
- })
+ new CustomEvent("onimportcss", {
+ detail: "http://localhost:3000/styles/globals.css",
+ }),
);
```
@@ -742,9 +742,9 @@ Additionally, bun exposes an API for SSR/SSG that returns a flat list of URLs to
// This API needs to be changed somewhat to work more generally with Bun.js
// Initially, you could only use bun.js through `bun dev`
// and this API was created at that time
-addEventListener('fetch', async (event: FetchEvent) => {
+addEventListener("fetch", async (event: FetchEvent) => {
let route = Bun.match(event);
- const App = await import('pages/_app');
+ const App = await import("pages/_app");
// This returns all .css files that were imported in the line above.
// It’s recursive, so any file that imports a CSS file will be included.
@@ -763,9 +763,9 @@ bun bundles `.css` files imported via `@import` into a single file. It doesn’t
This input:
```css
-@import url('./hi.css');
-@import url('./hello.css');
-@import url('./yo.css');
+@import url("./hi.css");
+@import url("./hello.css");
+@import url("./yo.css");
```
Becomes:
@@ -881,7 +881,7 @@ type Framework = Environment & {
// If the framework does routing, you may want to handle CSS manually
// "facade" removes CSS imports from JavaScript files,
// and replaces an imported object with a proxy that mimics CSS module support without doing any class renaming.
- css?: 'onimportcss' | 'facade';
+ css?: "onimportcss" | "facade";
// bun’s filesystem router
router?: Router;
@@ -893,7 +893,7 @@ type Define = {
// When "*", all environment variables will be automatically injected into the JavaScript loader
// When a string like "NEXT_PUBLIC_", only environment variables starting with that prefix will be injected
- '.env': string | '*';
+ ".env": string | "*";
// These environment variables will be injected into the JavaScript loader
// These are the equivalent of Webpack’s resolve.alias and esbuild’s --define.
@@ -1117,7 +1117,7 @@ export interface Install {
globalBinDir: string;
cache: Cache;
lockfile: Lockfile;
- logLevel: 'debug' | 'error' | 'warn';
+ logLevel: "debug" | "error" | "warn";
}
type Registry =
@@ -1138,7 +1138,7 @@ export interface Cache {
}
export interface Lockfile {
- print?: 'yarn';
+ print?: "yarn";
path: string;
savePath: string;
save: boolean;
@@ -1371,7 +1371,7 @@ globalThis.reloadCount = reloadCount + 1;
export default {
fetch(req: Request) {
return new Response(`Code reloaded ${reloadCount} times`, {
- headers: {'content-type': 'text/plain'},
+ headers: { "content-type": "text/plain" },
});
},
};
@@ -1413,7 +1413,7 @@ const reloadServer = (globalThis.reloadServer ||= (() => {
const handler = {
fetch(req: Request) {
return new Response(`Code reloaded ${reloadCount} times`, {
- headers: {'content-type': 'text/plain'},
+ headers: { "content-type": "text/plain" },
});
},
};
@@ -1825,18 +1825,18 @@ This is an `"object"` loader. `object` loaders let you return a JS object that B
Plugin implementation (`my-yaml-plugin.js`)
```js
-import {plugin} from 'bun';
+import { plugin } from "bun";
plugin({
- name: 'YAML',
+ name: "YAML",
setup(builder) {
- const {load} = require('js-yaml');
- const {readFileSync} = require('fs');
+ const { load } = require("js-yaml");
+ const { readFileSync } = require("fs");
// Run this function on any import that ends with .yaml or .yml
- builder.onLoad({filter: /\.(yaml|yml)$/}, (args) => {
+ builder.onLoad({ filter: /\.(yaml|yml)$/ }, (args) => {
// Read the YAML file from disk
- const text = readFileSync(args.path, 'utf8');
+ const text = readFileSync(args.path, "utf8");
// parse the YAML file with js-yaml
const exports = load(text);
@@ -1846,7 +1846,7 @@ plugin({
exports,
// we're returning an object
- loader: 'object',
+ loader: "object",
};
});
},
@@ -1856,8 +1856,8 @@ plugin({
Plugin usage:
```js
-import './my-yaml-plugin.js';
-import {hello} from './myfile.yaml';
+import "./my-yaml-plugin.js";
+import { hello } from "./myfile.yaml";
console.log(hello); // "world"
```
@@ -1869,25 +1869,25 @@ This is a `"js"` loader, which lets you return a JS string or `ArrayBufferView`
Plugin implementation (`myplugin.js`)
```js
-import {plugin} from 'bun';
+import { plugin } from "bun";
await plugin({
- name: 'svelte loader',
+ name: "svelte loader",
async setup(builder) {
- const {compile} = await import('svelte/compiler');
- const {readFileSync} = await import('fs');
+ const { compile } = await import("svelte/compiler");
+ const { readFileSync } = await import("fs");
// Register a loader for .svelte files
- builder.onLoad({filter: /\.svelte$/}, ({path}) => ({
+ builder.onLoad({ filter: /\.svelte$/ }, ({ path }) => ({
// Run the Svelte compiler on the import path
- contents: compile(readFileSync(path, 'utf8'), {
+ contents: compile(readFileSync(path, "utf8"), {
filename: path,
- generate: 'ssr',
+ generate: "ssr",
}).js.code,
// Set the loader to "js"
// This runs it through Bun's transpiler
- loader: 'js',
+ loader: "js",
}));
},
});
@@ -1898,8 +1898,8 @@ Note: in a production implementation, you'd want to cache the compiled output an
Plugin usage:
```js
-import './myplugin.js';
-import MySvelteComponent from './component.svelte';
+import "./myplugin.js";
+import MySvelteComponent from "./component.svelte";
console.log(mySvelteComponent.render());
```
@@ -1911,15 +1911,15 @@ Bun's loader API interface is loosely based on [esbuild](https://esbuild.github.
MDX:
```jsx
-import {plugin} from 'bun';
-import {renderToStaticMarkup} from 'react-dom/server';
+import { plugin } from "bun";
+import { renderToStaticMarkup } from "react-dom/server";
// it's the esbuild plugin, but it works using Bun's transpiler.
-import mdx from '@mdx-js/esbuild';
+import mdx from "@mdx-js/esbuild";
plugin(mdx());
-import Foo from './bar.mdx';
+import Foo from "./bar.mdx";
console.log(renderToStaticMarkup(<Foo />));
```
@@ -1932,11 +1932,11 @@ At the top-level, a `plugin` function exported from `"bun"` expects a `"name"` s
For plugins to automatically activate, the `plugin` function must be from an import statement like this:
```js
-import {plugin} from 'bun';
+import { plugin } from "bun";
// This automatically activates on import
plugin({
- name: 'my plugin',
+ name: "my plugin",
setup(builder) {},
});
@@ -2007,8 +2007,8 @@ Bun.serve({
Node:
```ts
-require('http')
- .createServer((req, res) => res.end('bun!'))
+require("http")
+ .createServer((req, res) => res.end("bun!"))
.listen(8080);
```
@@ -2028,7 +2028,7 @@ If the file used to start bun has a default export with a `fetch` function, it w
// hi.js
export default {
fetch(req) {
- return new Response('HI!');
+ return new Response("HI!");
},
};
@@ -2042,7 +2042,7 @@ export default {
```ts
Bun.serve({
fetch(req) {
- return new Response('HI!');
+ return new Response("HI!");
},
});
```
@@ -2062,10 +2062,10 @@ It will hopefully make it easier to debug issues with bun until bun gets debugge
```js
Bun.serve({
fetch(req) {
- throw new Error('woops!');
+ throw new Error("woops!");
},
error(error: Error) {
- return new Response('Uh oh!!\n' + error.toString(), {status: 500});
+ return new Response("Uh oh!!\n" + error.toString(), { status: 500 });
},
});
```
@@ -2077,7 +2077,7 @@ To stop the server, call `server.stop()`:
```ts
const server = Bun.serve({
fetch() {
- return new Response('HI!');
+ return new Response("HI!");
},
});
@@ -2093,20 +2093,20 @@ Example:
```ts
Bun.serve({
fetch(req) {
- return new Response('Hello!!!');
+ return new Response("Hello!!!");
},
/**
* File path to a TLS key
*
* To enable TLS, this option is required.
*/
- keyFile: './key.pem',
+ keyFile: "./key.pem",
/**
* File path to a TLS certificate
*
* To enable TLS, this option is required.
*/
- certFile: './cert.pem',
+ certFile: "./cert.pem",
/**
* Optional SSL options
@@ -2153,7 +2153,7 @@ Bun.serve({
// When upgrading, we return undefined since we don't want to send a Response
return;
- return new Response('Regular HTTP response');
+ return new Response("Regular HTTP response");
},
});
```
@@ -2167,45 +2167,45 @@ type User = {
Bun.serve<User>({
fetch(req, server) {
- if (req.url === '/chat') {
+ if (req.url === "/chat") {
if (
server.upgrade(req, {
// This User object becomes ws.data
data: {
- name: new URL(req.url).searchParams.get('name') || 'Friend',
+ name: new URL(req.url).searchParams.get("name") || "Friend",
},
// Pass along some headers to the client
headers: {
- 'Set-Cookie': 'name=' + new URL(req.url).searchParams.get('name'),
+ "Set-Cookie": "name=" + new URL(req.url).searchParams.get("name"),
},
})
)
return;
}
- return new Response('Expected a websocket connection', {status: 400});
+ return new Response("Expected a websocket connection", { status: 400 });
},
websocket: {
open(ws) {
- console.log('WebSocket opened');
+ console.log("WebSocket opened");
// subscribe to "the-group-chat" topic
- ws.subscribe('the-group-chat');
+ ws.subscribe("the-group-chat");
},
message(ws, message) {
// In a group chat, we want to broadcast to everyone
// so we use publish()
- ws.publish('the-group-chat', `${ws.data.name}: ${message}`);
+ ws.publish("the-group-chat", `${ws.data.name}: ${message}`);
},
close(ws, code, reason) {
- ws.publish('the-group-chat', `${ws.data.name} left the chat`);
+ ws.publish("the-group-chat", `${ws.data.name} left the chat`);
},
drain(ws) {
- console.log('Please send me data. I am ready to receive it.');
+ console.log("Please send me data. I am ready to receive it.");
},
// enable compression
@@ -2277,7 +2277,9 @@ For server websocket connections, Bun exposes a `ServerWebSocket` class which is
```ts
Bun.serve({
fetch(req, server) {
- if (server.upgrade(req, {headers: {'Set-Cookie': 'name=HiThereMyNameIs'}}))
+ if (
+ server.upgrade(req, { headers: { "Set-Cookie": "name=HiThereMyNameIs" } })
+ )
return;
},
websocket: {
@@ -2295,7 +2297,7 @@ The web-standard `WebSocket` API does not let you specify headers.
`ServerWebSocket` has `publish()`, `subscribe()`, and `unsubscribe` methods which let you broadcast the same message to all clients connected to a topic in one line of code.
```ts
-ws.publish('stock-prices/GOOG', `${price}`);
+ws.publish("stock-prices/GOOG", `${price}`);
```
##### Backpressure
@@ -2312,7 +2314,7 @@ You can also enable/disable compression per message with the `compress` option:
```ts
// this will compress
-ws.send('Hello'.repeat(1000), true);
+ws.send("Hello".repeat(1000), true);
```
`WebSocket.send` returns `undefined` and does not indicate backpressure, which can cause issues if you are sending a lot of data.
@@ -2346,11 +2348,11 @@ The HTTP server and server-side websockets are based on [uWebSockets](https://gi
`Bun.spawn` lets you quickly spawn a process. Available as of Bun v0.2.0.
```ts
-import {spawn} from 'bun';
+import { spawn } from "bun";
-const {stdout} = spawn(['esbuild'], {
+const { stdout } = spawn(["esbuild"], {
stdin: await fetch(
- 'https://raw.githubusercontent.com/oven-sh/bun/main/examples/hashing.js'
+ "https://raw.githubusercontent.com/oven-sh/bun/main/examples/hashing.js",
),
});
@@ -2381,9 +2383,9 @@ spawnSync echo hi 1.47 ms/iter (1.14 ms … 2.64 ms) 1.57 ms 2.37 ms
Synchronous example:
```ts
-import {spawnSync} from 'bun';
+import { spawnSync } from "bun";
-const {stdout} = spawnSync(['echo', 'hi']);
+const { stdout } = spawnSync(["echo", "hi"]);
// When using spawnSync, stdout is a Buffer
// this lets you read from it synchronously
@@ -2395,24 +2397,24 @@ console.log(text); // "hi\n"
You can pass an object as the second argument to customize the process:
```ts
-import {spawn} from 'bun';
+import { spawn } from "bun";
-const {stdout} = spawn(['printenv', 'FOO'], {
- cwd: '/tmp',
+const { stdout } = spawn(["printenv", "FOO"], {
+ cwd: "/tmp",
env: {
...process.env,
- FOO: 'bar',
+ FOO: "bar",
},
// Disable stdin
stdin: null,
// Allow us to read from stdout
- stdout: 'pipe',
+ stdout: "pipe",
// Point stderr to write to "/tmp/stderr.log"
- stderr: Bun.file('/tmp/stderr.log'),
+ stderr: Bun.file("/tmp/stderr.log"),
});
const text = await new Response(stdout).text();
@@ -2422,12 +2424,12 @@ console.log(text); // "bar\n"
You can also pass a `Bun.file` for `stdin`:
```ts
-import {spawn, file, write} from 'bun';
+import { spawn, file, write } from "bun";
-await write('/tmp/foo.txt', 'hi');
-const {stdout} = spawn(['cat'], {
+await write("/tmp/foo.txt", "hi");
+const { stdout } = spawn(["cat"], {
// Set /tmp/foo.txt as stdin
- stdin: file('/tmp/foo.txt'),
+ stdin: file("/tmp/foo.txt"),
});
const text = await new Response(stdout).text();
@@ -2437,11 +2439,11 @@ console.log(text); // "hi\n"
`stdin` also accepts a TypedArray:
```ts
-import {spawn} from 'bun';
+import { spawn } from "bun";
-const {stdout} = spawn(['cat'], {
- stdin: new TextEncoder().encode('hi'),
- stdout: 'pipe',
+const { stdout } = spawn(["cat"], {
+ stdin: new TextEncoder().encode("hi"),
+ stdout: "pipe",
});
const text = await new Response(stdout).text();
@@ -2453,16 +2455,16 @@ console.log(text); // "hi\n"
> :warning: **This API is a little buggy right now**
```ts
-import {spawn} from 'bun';
+import { spawn } from "bun";
-const {stdin, stdout} = spawn(['cat'], {
- stdin: 'pipe',
- stdout: 'pipe',
+const { stdin, stdout } = spawn(["cat"], {
+ stdin: "pipe",
+ stdout: "pipe",
});
// You can pass it strings or TypedArrays
// Write "hi" to stdin
-stdin.write('hi');
+stdin.write("hi");
// By default, stdin is buffered so you need to call flush() to send it
stdin.flush(true);
@@ -2549,15 +2551,15 @@ interface Subprocess {
Find the path to an executable, similar to typing `which` in your terminal.
```ts
-const ls = Bun.which('ls');
+const ls = Bun.which("ls");
console.log(ls); // "/usr/bin/ls"
```
`Bun.which` defaults the `PATH` to the current `PATH` environment variable, but you can customize it
```ts
-const ls = Bun.which('ls', {
- PATH: '/usr/local/bin:/usr/bin:/bin',
+const ls = Bun.which("ls", {
+ PATH: "/usr/local/bin:/usr/bin:/bin",
});
console.log(ls); // "/usr/bin/ls"
```
@@ -2565,9 +2567,9 @@ console.log(ls); // "/usr/bin/ls"
`Bun.which` also accepts a `cwd` option to search for the binary in a specific directory.
```ts
-const ls = Bun.which('ls', {
- cwd: '/tmp',
- PATH: '',
+const ls = Bun.which("ls", {
+ cwd: "/tmp",
+ PATH: "",
});
console.log(ls); // null
@@ -2582,20 +2584,20 @@ Start a TCP server with `Bun.listen`:
```ts
// The server
Bun.listen({
- hostname: 'localhost',
+ hostname: "localhost",
port: 8080,
socket: {
open(socket) {
- socket.write('hello world');
+ socket.write("hello world");
},
data(socket, data) {
console.log(data instanceof Uint8Array); // true
},
drain(socket) {
- console.log('gimme more data');
+ console.log("gimme more data");
},
close(socket) {
- console.log('goodbye!');
+ console.log("goodbye!");
},
},
// This is a TLS socket
@@ -2609,7 +2611,7 @@ Bun.listen({
```ts
// The client
Bun.connect({
- hostname: 'localhost',
+ hostname: "localhost",
port: 8080,
socket: {
@@ -2617,21 +2619,21 @@ Bun.connect({
socket.write("hello server, i'm the client!");
},
data(socket, message) {
- socket.write('thanks for the message! Sincerely, ' + socket.data.name);
+ socket.write("thanks for the message! Sincerely, " + socket.data.name);
},
drain(socket) {
- console.log('my socket is ready for more data');
+ console.log("my socket is ready for more data");
},
close(socket) {
- console.log('');
+ console.log("");
},
timeout(socket) {
- console.log('socket timed out');
+ console.log("socket timed out");
},
},
data: {
- name: 'Clienty McClientface',
+ name: "Clienty McClientface",
},
});
```
@@ -2653,7 +2655,7 @@ Bun.listen({
close(socket) {},
error(socket, error) {},
},
- hostname: 'localhost',
+ hostname: "localhost",
port: 8080,
});
```
@@ -2665,7 +2667,7 @@ How do you pass per-socket data to each socket object?
`**data**` is a property on the `TCPSocket` & `TLSSocket` object that you can use to store per-socket data.
```ts
-socket.data = {name: 'Clienty McClientface'};
+socket.data = { name: "Clienty McClientface" };
```
You can assign a default value to `data` in the `connect` or `listen` options.
@@ -2678,7 +2680,7 @@ Bun.listen({
},
},
data: {
- name: 'Servery McServerface',
+ name: "Servery McServerface",
},
});
```
@@ -2689,16 +2691,16 @@ Bun.listen({
```ts
const socket = Bun.connect({
- hostname: 'localhost',
+ hostname: "localhost",
port: 8080,
socket: {
data(socket, msg) {
- console.log('wow i got a message!');
+ console.log("wow i got a message!");
// this will be called the next time the server sends a message
socket.reload({
data(socket) {
- console.log('okay, not so surprising this time');
+ console.log("okay, not so surprising this time");
},
});
},
@@ -2715,29 +2717,29 @@ Your TCP client/server will have abysmal performance if you don't consider buffe
For example, this:
```ts
-socket.write('h');
-socket.write('e');
-socket.write('l');
-socket.write('l');
-socket.write('o');
+socket.write("h");
+socket.write("e");
+socket.write("l");
+socket.write("l");
+socket.write("o");
```
Performs significantly worse than:
```ts
-socket.write('hello');
+socket.write("hello");
```
To simplify this for now, consider using `ArrayBufferSink` with the `{stream: true}` option:
```ts
-const sink = new ArrayBufferSink({stream: true, highWaterMark: 1024});
+const sink = new ArrayBufferSink({ stream: true, highWaterMark: 1024 });
-sink.write('h');
-sink.write('e');
-sink.write('l');
-sink.write('l');
-sink.write('o');
+sink.write("h");
+sink.write("e");
+sink.write("l");
+sink.write("l");
+sink.write("o");
queueMicrotask(() => {
var data = sink.flush();
@@ -2757,9 +2759,9 @@ Builtin buffering is planned in a future version of Bun.
This function was added in Bun v0.2.2.
```ts
-import {peek} from 'bun';
+import { peek } from "bun";
-const promise = Promise.resolve('hi');
+const promise = Promise.resolve("hi");
// no await!
const result = peek(promise);
@@ -2770,10 +2772,10 @@ console.log(result); // "hi"
`Bun.peek` is useful for performance-sensitive code that wants to reduce the number of extra microticks. It's an advanced API and you probably shouldn't use it unless you know what you're doing.
```ts
-import {peek} from 'bun';
-import {expect, test} from 'bun:test';
+import { peek } from "bun";
+import { expect, test } from "bun:test";
-test('peek', () => {
+test("peek", () => {
const promise = Promise.resolve(true);
// no await necessary!
@@ -2795,27 +2797,27 @@ test('peek', () => {
// - returns the error
// - does not mark the promise as handled
const rejected = Promise.reject(
- new Error('Succesfully tested promise rejection')
+ new Error("Succesfully tested promise rejection"),
);
- expect(peek(rejected).message).toBe('Succesfully tested promise rejection');
+ expect(peek(rejected).message).toBe("Succesfully tested promise rejection");
});
```
`peek.status` lets you read the status of a promise without resolving it.
```ts
-import {peek} from 'bun';
-import {expect, test} from 'bun:test';
+import { peek } from "bun";
+import { expect, test } from "bun:test";
-test('peek.status', () => {
+test("peek.status", () => {
const promise = Promise.resolve(true);
- expect(peek.status(promise)).toBe('fulfilled');
+ expect(peek.status(promise)).toBe("fulfilled");
const pending = new Promise(() => {});
- expect(peek.status(pending)).toBe('pending');
+ expect(peek.status(pending)).toBe("pending");
- const rejected = Promise.reject(new Error('oh nooo'));
- expect(peek.status(rejected)).toBe('rejected');
+ const rejected = Promise.reject(new Error("oh nooo"));
+ expect(peek.status(rejected)).toBe("rejected");
});
```
@@ -2827,7 +2829,7 @@ test('peek.status', () => {
interface Bun {
write(
destination: string | number | FileBlob,
- input: string | FileBlob | Blob | ArrayBufferView
+ input: string | FileBlob | Blob | ArrayBufferView,
): Promise<number>;
}
```
@@ -2849,24 +2851,24 @@ All this complexity is handled by a single function.
```ts
// Write "Hello World" to output.txt
-await Bun.write('output.txt', 'Hello World');
+await Bun.write("output.txt", "Hello World");
```
```ts
// log a file to stdout
-await Bun.write(Bun.stdout, Bun.file('input.txt'));
+await Bun.write(Bun.stdout, Bun.file("input.txt"));
```
```ts
// write the HTTP response body to disk
-await Bun.write('index.html', await fetch('http://example.com'));
+await Bun.write("index.html", await fetch("http://example.com"));
// this does the same thing
-await Bun.write(Bun.file('index.html'), await fetch('http://example.com'));
+await Bun.write(Bun.file("index.html"), await fetch("http://example.com"));
```
```ts
// copy input.txt to output.txt
-await Bun.write('output.txt', Bun.file('input.txt'));
+await Bun.write("output.txt", Bun.file("input.txt"));
```
## bun:sqlite (SQLite3 module)
@@ -2890,35 +2892,35 @@ Installation:
Example:
```ts
-import {Database} from 'bun:sqlite';
+import { Database } from "bun:sqlite";
-const db = new Database('mydb.sqlite');
+const db = new Database("mydb.sqlite");
db.run(
- 'CREATE TABLE IF NOT EXISTS foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)'
+ "CREATE TABLE IF NOT EXISTS foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)",
);
-db.run('INSERT INTO foo (greeting) VALUES (?)', 'Welcome to bun!');
-db.run('INSERT INTO foo (greeting) VALUES (?)', 'Hello World!');
+db.run("INSERT INTO foo (greeting) VALUES (?)", "Welcome to bun!");
+db.run("INSERT INTO foo (greeting) VALUES (?)", "Hello World!");
// get the first row
-db.query('SELECT * FROM foo').get();
+db.query("SELECT * FROM foo").get();
// { id: 1, greeting: "Welcome to bun!" }
// get all rows
-db.query('SELECT * FROM foo').all();
+db.query("SELECT * FROM foo").all();
// [
// { id: 1, greeting: "Welcome to bun!" },
// { id: 2, greeting: "Hello World!" },
// ]
// get all rows matching a condition
-db.query('SELECT * FROM foo WHERE greeting = ?').all('Welcome to bun!');
+db.query("SELECT * FROM foo WHERE greeting = ?").all("Welcome to bun!");
// [
// { id: 1, greeting: "Welcome to bun!" },
// ]
// get first row matching a named condition
-db.query('SELECT * FROM foo WHERE greeting = $greeting').get({
- $greeting: 'Welcome to bun!',
+db.query("SELECT * FROM foo WHERE greeting = $greeting").get({
+ $greeting: "Welcome to bun!",
});
// [
// { id: 1, greeting: "Welcome to bun!" },
@@ -3005,52 +3007,52 @@ constructor(
To open or create a SQLite3 database:
```ts
-import {Database} from 'bun:sqlite';
+import { Database } from "bun:sqlite";
-const db = new Database('mydb.sqlite');
+const db = new Database("mydb.sqlite");
```
Open an in-memory database:
```ts
-import {Database} from 'bun:sqlite';
+import { Database } from "bun:sqlite";
// all of these do the same thing
-let db = new Database(':memory:');
+let db = new Database(":memory:");
let db = new Database();
-let db = new Database('');
+let db = new Database("");
```
Open read-write and throw if the database doesn't exist:
```ts
-import {Database} from 'bun:sqlite';
-const db = new Database('mydb.sqlite', {readwrite: true});
+import { Database } from "bun:sqlite";
+const db = new Database("mydb.sqlite", { readwrite: true });
```
Open read-only and throw if the database doesn't exist:
```ts
-import {Database} from 'bun:sqlite';
-const db = new Database('mydb.sqlite', {readonly: true});
+import { Database } from "bun:sqlite";
+const db = new Database("mydb.sqlite", { readonly: true });
```
Open read-write, don't throw if new file:
```ts
-import {Database} from 'bun:sqlite';
-const db = new Database('mydb.sqlite', {readonly: true, create: true});
+import { Database } from "bun:sqlite";
+const db = new Database("mydb.sqlite", { readonly: true, create: true });
```
Open a database from a `Uint8Array`:
```ts
-import {Database} from 'bun:sqlite';
-import {readFileSync} from 'fs';
+import { Database } from "bun:sqlite";
+import { readFileSync } from "fs";
// unlike passing a filepath, this will not persist any changes to disk
// it will be read-write but not persistent
-const db = new Database(readFileSync('mydb.sqlite'));
+const db = new Database(readFileSync("mydb.sqlite"));
```
Close a database:
@@ -3084,24 +3086,24 @@ This intended to make it easier for `bun:sqlite` to be fast by default. Calling
You can bind parameters on any call to a statement.
```js
-import {Database} from 'bun:sqlite';
+import { Database } from "bun:sqlite";
// generate some data
let db = new Database();
db.run(
- 'CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)'
+ "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)",
);
-db.run('INSERT INTO foo (greeting) VALUES ($greeting)', {
- $greeting: 'Welcome to bun',
+db.run("INSERT INTO foo (greeting) VALUES ($greeting)", {
+ $greeting: "Welcome to bun",
});
// get the query
-const stmt = db.query('SELECT * FROM foo WHERE greeting = ?');
+const stmt = db.query("SELECT * FROM foo WHERE greeting = ?");
// run the query
-stmt.all('Welcome to bun!');
-stmt.get('Welcome to bun!');
-stmt.run('Welcome to bun!');
+stmt.all("Welcome to bun!");
+stmt.get("Welcome to bun!");
+stmt.run("Welcome to bun!");
```
#### Database.prototype.prepare
@@ -3111,19 +3113,19 @@ stmt.run('Welcome to bun!');
Unlike `query()`, this does not cache the compiled query.
```ts
-import {Database} from 'bun:sqlite';
+import { Database } from "bun:sqlite";
// generate some data
let db = new Database();
db.run(
- 'CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)'
+ "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)",
);
// compile the prepared statement
-const stmt = db.prepare('SELECT * FROM foo WHERE bar = ?');
+const stmt = db.prepare("SELECT * FROM foo WHERE bar = ?");
// run the prepared statement
-stmt.all('baz');
+stmt.all("baz");
```
Internally, this calls [`sqlite3_prepare_v3`](https://www.sqlite.org/c3ref/prepare.html).
@@ -3146,27 +3148,27 @@ This is useful for things like
Creating a table:
```ts
-import {Database} from 'bun:sqlite';
+import { Database } from "bun:sqlite";
let db = new Database();
db.exec(
- 'CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)'
+ "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)",
);
```
Inserting one row:
```ts
-import {Database} from 'bun:sqlite';
+import { Database } from "bun:sqlite";
let db = new Database();
db.exec(
- 'CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)'
+ "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)",
);
// insert one row
-db.exec('INSERT INTO foo (greeting) VALUES ($greeting)', {
- $greeting: 'Welcome to bun',
+db.exec("INSERT INTO foo (greeting) VALUES ($greeting)", {
+ $greeting: "Welcome to bun",
});
```
@@ -3180,21 +3182,21 @@ Creates a function that always runs inside a transaction. When the function is i
```ts
// setup
-import {Database} from 'bun:sqlite';
-const db = Database.open(':memory:');
+import { Database } from "bun:sqlite";
+const db = Database.open(":memory:");
db.exec(
- 'CREATE TABLE cats (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, age INTEGER)'
+ "CREATE TABLE cats (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, age INTEGER)",
);
-const insert = db.prepare('INSERT INTO cats (name, age) VALUES ($name, $age)');
+const insert = db.prepare("INSERT INTO cats (name, age) VALUES ($name, $age)");
const insertMany = db.transaction((cats) => {
for (const cat of cats) insert.run(cat);
});
insertMany([
- {$name: 'Joey', $age: 2},
- {$name: 'Sally', $age: 4},
- {$name: 'Junior', $age: 1},
+ { $name: "Joey", $age: 2 },
+ { $name: "Sally", $age: 4 },
+ { $name: "Junior", $age: 1 },
]);
```
@@ -3202,31 +3204,31 @@ Transaction functions can be called from inside other transaction functions. Whe
```ts
// setup
-import {Database} from 'bun:sqlite';
-const db = Database.open(':memory:');
+import { Database } from "bun:sqlite";
+const db = Database.open(":memory:");
db.exec(
- 'CREATE TABLE expenses (id INTEGER PRIMARY KEY AUTOINCREMENT, note TEXT, dollars INTEGER);'
+ "CREATE TABLE expenses (id INTEGER PRIMARY KEY AUTOINCREMENT, note TEXT, dollars INTEGER);",
);
db.exec(
- 'CREATE TABLE cats (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, age INTEGER)'
+ "CREATE TABLE cats (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, age INTEGER)",
);
const newExpense = db.prepare(
- 'INSERT INTO expenses (note, dollars) VALUES (?, ?)'
+ "INSERT INTO expenses (note, dollars) VALUES (?, ?)",
);
-const insert = db.prepare('INSERT INTO cats (name, age) VALUES ($name, $age)');
+const insert = db.prepare("INSERT INTO cats (name, age) VALUES ($name, $age)");
const insertMany = db.transaction((cats) => {
for (const cat of cats) insert.run(cat);
});
const adopt = db.transaction((cats) => {
- newExpense.run('adoption fees', 20);
+ newExpense.run("adoption fees", 20);
insertMany(cats); // nested transaction
});
adopt([
- {$name: 'Joey', $age: 2},
- {$name: 'Sally', $age: 4},
- {$name: 'Junior', $age: 1},
+ { $name: "Joey", $age: 2 },
+ { $name: "Sally", $age: 4 },
+ { $name: "Junior", $age: 1 },
]);
```
@@ -3254,16 +3256,16 @@ let db = new Database();
// write some data
db.run(
- 'CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)'
+ "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)",
);
-db.run('INSERT INTO foo VALUES (?)', 'Welcome to bun!');
-db.run('INSERT INTO foo VALUES (?)', 'Hello World!');
+db.run("INSERT INTO foo VALUES (?)", "Welcome to bun!");
+db.run("INSERT INTO foo VALUES (?)", "Hello World!");
const copy = db.serialize();
// => Uint8Array
const db2 = new Database(copy);
-db2.query('SELECT * FROM foo').all();
+db2.query("SELECT * FROM foo").all();
// => [
// { id: 1, greeting: "Welcome to bun!" },
// { id: 2, greeting: "Hello World!" },
@@ -3281,26 +3283,26 @@ Internally, it calls [`sqlite3_serialize`](https://www.sqlite.org/c3ref/serializ
To load a SQLite extension, call `Database.prototype.loadExtension(name)`:
```ts
-import {Database} from 'bun:sqlite';
+import { Database } from "bun:sqlite";
let db = new Database();
-db.loadExtension('myext');
+db.loadExtension("myext");
```
If you're on macOS, you will need to first use a custom SQLite install (you can install with homebrew). By default, bun uses Apple's proprietary build of SQLite because it benchmarks about 50% faster. However, they disabled extension support, so you will need to have a custom build of SQLite to use extensions on macOS.
```ts
-import {Database} from 'bun:sqlite';
+import { Database } from "bun:sqlite";
// on macOS, this must be run before any other calls to `Database`
// if called on linux, it will return true and do nothing
// on linux it will still check that a string was passed
-Database.setCustomSQLite('/path/to/sqlite.dylib');
+Database.setCustomSQLite("/path/to/sqlite.dylib");
let db = new Database();
-db.loadExtension('myext');
+db.loadExtension("myext");
```
To install sqlite with homebrew:
@@ -3327,18 +3329,18 @@ TLDR:
You can bind parameters on any call to a statement. Named parameters and positional parameters are supported. Bound parameters are remembered between calls and reset the next time you pass parameters to bind.
```ts
-import {Database} from 'bun:sqlite';
+import { Database } from "bun:sqlite";
// setup
let db = new Database();
db.run(
- 'CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)'
+ "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)",
);
-db.run('INSERT INTO foo VALUES (?)', 'Welcome to bun!');
-db.run('INSERT INTO foo VALUES (?)', 'Hello World!');
+db.run("INSERT INTO foo VALUES (?)", "Welcome to bun!");
+db.run("INSERT INTO foo VALUES (?)", "Hello World!");
// Statement object
-let statement = db.query('SELECT * FROM foo');
+let statement = db.query("SELECT * FROM foo");
// returns all the rows
statement.all();
@@ -3355,23 +3357,23 @@ statement.run();
Calling `all()` on a `Statement` instance runs the query and returns the rows as an array of objects.
```ts
-import {Database} from 'bun:sqlite';
+import { Database } from "bun:sqlite";
// setup
let db = new Database();
db.run(
- 'CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)'
+ "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)",
);
-db.run('INSERT INTO foo (greeting, count) VALUES (?, ?)', 'Welcome to bun!', 2);
-db.run('INSERT INTO foo (greeting, count) VALUES (?, ?)', 'Hello World!', 0);
+db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Welcome to bun!", 2);
+db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Hello World!", 0);
db.run(
- 'INSERT INTO foo (greeting, count) VALUES (?, ?)',
- 'Welcome to bun!!!!',
- 2
+ "INSERT INTO foo (greeting, count) VALUES (?, ?)",
+ "Welcome to bun!!!!",
+ 2,
);
// Statement object
-let statement = db.query('SELECT * FROM foo WHERE count = ?');
+let statement = db.query("SELECT * FROM foo WHERE count = ?");
// return all the query results, binding 2 to the count parameter
statement.all(2);
@@ -3388,23 +3390,23 @@ Internally, this calls [`sqlite3_reset`](https://www.sqlite.org/capi3ref.html#sq
Calling `values()` on a `Statement` instance runs the query and returns the rows as an array of arrays.
```ts
-import {Database} from 'bun:sqlite';
+import { Database } from "bun:sqlite";
// setup
let db = new Database();
db.run(
- 'CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)'
+ "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)",
);
-db.run('INSERT INTO foo (greeting, count) VALUES (?, ?)', 'Welcome to bun!', 2);
-db.run('INSERT INTO foo (greeting, count) VALUES (?, ?)', 'Hello World!', 0);
+db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Welcome to bun!", 2);
+db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Hello World!", 0);
db.run(
- 'INSERT INTO foo (greeting, count) VALUES (?, ?)',
- 'Welcome to bun!!!!',
- 2
+ "INSERT INTO foo (greeting, count) VALUES (?, ?)",
+ "Welcome to bun!!!!",
+ 2,
);
// Statement object
-let statement = db.query('SELECT * FROM foo WHERE count = ?');
+let statement = db.query("SELECT * FROM foo WHERE count = ?");
// return all the query results as an array of arrays, binding 2 to "count"
statement.values(2);
@@ -3414,10 +3416,10 @@ statement.values(2);
// ]
// Statement object, but with named parameters
-let statement = db.query('SELECT * FROM foo WHERE count = $count');
+let statement = db.query("SELECT * FROM foo WHERE count = $count");
// return all the query results as an array of arrays, binding 2 to "count"
-statement.values({$count: 2});
+statement.values({ $count: 2 });
// => [
// [ 1, "Welcome to bun!", 2 ],
// [ 3, "Welcome to bun!!!!", 2 ],
@@ -3431,33 +3433,33 @@ Internally, this calls [`sqlite3_reset`](https://www.sqlite.org/capi3ref.html#sq
Calling `get()` on a `Statement` instance runs the query and returns the first result as an object.
```ts
-import {Database} from 'bun:sqlite';
+import { Database } from "bun:sqlite";
// setup
let db = new Database();
db.run(
- 'CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)'
+ "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)",
);
-db.run('INSERT INTO foo (greeting, count) VALUES (?, ?)', 'Welcome to bun!', 2);
-db.run('INSERT INTO foo (greeting, count) VALUES (?, ?)', 'Hello World!', 0);
+db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Welcome to bun!", 2);
+db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Hello World!", 0);
db.run(
- 'INSERT INTO foo (greeting, count) VALUES (?, ?)',
- 'Welcome to bun!!!!',
- 2
+ "INSERT INTO foo (greeting, count) VALUES (?, ?)",
+ "Welcome to bun!!!!",
+ 2,
);
// Statement object
-let statement = db.query('SELECT * FROM foo WHERE count = ?');
+let statement = db.query("SELECT * FROM foo WHERE count = ?");
// return the first row as an object, binding 2 to the count parameter
statement.get(2);
// => { id: 1, greeting: "Welcome to bun!", count: 2 }
// Statement object, but with named parameters
-let statement = db.query('SELECT * FROM foo WHERE count = $count');
+let statement = db.query("SELECT * FROM foo WHERE count = $count");
// return the first row as an object, binding 2 to the count parameter
-statement.get({$count: 2});
+statement.get({ $count: 2 });
// => { id: 1, greeting: "Welcome to bun!", count: 2 }
```
@@ -3470,23 +3472,23 @@ Calling `run()` on a `Statement` instance runs the query and returns nothing.
This is useful if you want to repeatedly run a query, but don't care about the results.
```ts
-import {Database} from 'bun:sqlite';
+import { Database } from "bun:sqlite";
// setup
let db = new Database();
db.run(
- 'CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)'
+ "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)",
);
-db.run('INSERT INTO foo (greeting, count) VALUES (?, ?)', 'Welcome to bun!', 2);
-db.run('INSERT INTO foo (greeting, count) VALUES (?, ?)', 'Hello World!', 0);
+db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Welcome to bun!", 2);
+db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Hello World!", 0);
db.run(
- 'INSERT INTO foo (greeting, count) VALUES (?, ?)',
- 'Welcome to bun!!!!',
- 2
+ "INSERT INTO foo (greeting, count) VALUES (?, ?)",
+ "Welcome to bun!!!!",
+ 2,
);
// Statement object (TODO: use a better example query)
-let statement = db.query('SELECT * FROM foo');
+let statement = db.query("SELECT * FROM foo");
// run the query, returning nothing
statement.run();
@@ -3503,23 +3505,23 @@ After a statement has been finalized, it cannot be used for any further queries.
It is a good idea to finalize a statement when you are done with it, but the garbage collector will do it for you if you don't.
```ts
-import {Database} from 'bun:sqlite';
+import { Database } from "bun:sqlite";
// setup
let db = new Database();
db.run(
- 'CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)'
+ "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)",
);
-db.run('INSERT INTO foo (greeting, count) VALUES (?, ?)', 'Welcome to bun!', 2);
-db.run('INSERT INTO foo (greeting, count) VALUES (?, ?)', 'Hello World!', 0);
+db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Welcome to bun!", 2);
+db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Hello World!", 0);
db.run(
- 'INSERT INTO foo (greeting, count) VALUES (?, ?)',
- 'Welcome to bun!!!!',
- 2
+ "INSERT INTO foo (greeting, count) VALUES (?, ?)",
+ "Welcome to bun!!!!",
+ 2,
);
// Statement object
-let statement = db.query('SELECT * FROM foo WHERE count = ?');
+let statement = db.query("SELECT * FROM foo WHERE count = ?");
statement.finalize();
@@ -3532,23 +3534,23 @@ statement.run();
Calling `toString()` on a `Statement` instance prints the expanded SQL query. This is useful for debugging.
```ts
-import {Database} from 'bun:sqlite';
+import { Database } from "bun:sqlite";
// setup
let db = new Database();
db.run(
- 'CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)'
+ "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)",
);
-db.run('INSERT INTO foo (greeting, count) VALUES (?, ?)', 'Welcome to bun!', 2);
-db.run('INSERT INTO foo (greeting, count) VALUES (?, ?)', 'Hello World!', 0);
+db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Welcome to bun!", 2);
+db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Hello World!", 0);
db.run(
- 'INSERT INTO foo (greeting, count) VALUES (?, ?)',
- 'Welcome to bun!!!!',
- 2
+ "INSERT INTO foo (greeting, count) VALUES (?, ?)",
+ "Welcome to bun!!!!",
+ 2,
);
// Statement object
-const statement = db.query('SELECT * FROM foo WHERE count = ?');
+const statement = db.query("SELECT * FROM foo WHERE count = ?");
console.log(statement.toString());
// => "SELECT * FROM foo WHERE count = NULL"
@@ -3580,7 +3582,7 @@ Internally, this calls [`sqlite3_expanded_sql`](https://www.sqlite.org/capi3ref.
This snippet prints sqlite3's version number:
```ts
-import {dlopen, FFIType, suffix} from 'bun:ffi';
+import { dlopen, FFIType, suffix } from "bun:ffi";
// `suffix` is either "dylib", "so", or "dll" depending on the platform
// you don't have to use "suffix", it's just there for convenience
@@ -3649,7 +3651,7 @@ zig build-lib add.zig -dynamic -OReleaseFast
Pass `dlopen` the path to the shared library and the list of symbols you want to import.
```ts
-import {dlopen, FFIType, suffix} from 'bun:ffi';
+import { dlopen, FFIType, suffix } from "bun:ffi";
const path = `libadd.${suffix}`;
@@ -3791,7 +3793,7 @@ getVersion();
If you have multiple function pointers, you can define them all at once with `linkSymbols`:
```ts
-import {linkSymbols} from 'bun:ffi';
+import { linkSymbols } from "bun:ffi";
// getVersionPtrs defined elsewhere
const [majorPtr, minorPtr, patchPtr] = getVersionPtrs();
@@ -3799,7 +3801,7 @@ const [majorPtr, minorPtr, patchPtr] = getVersionPtrs();
const lib = linkSymbols({
// Unlike with dlopen(), the names here can be whatever you want
getMajor: {
- returns: 'cstring',
+ returns: "cstring",
args: [],
// Since this doesn't use dlsym(), you have to provide a valid ptr
@@ -3808,12 +3810,12 @@ const lib = linkSymbols({
ptr: majorPtr,
},
getMinor: {
- returns: 'cstring',
+ returns: "cstring",
args: [],
ptr: minorPtr,
},
getPatch: {
- returns: 'cstring',
+ returns: "cstring",
args: [],
ptr: patchPtr,
},
@@ -3831,27 +3833,27 @@ const [major, minor, patch] = [
Bun v0.2.3 added `JSCallback` which lets you create JavaScript callback functions that you can pass to C/FFI functions. The C/FFI function can call into the JavaScript/TypeScript code. This is useful for asynchronous code or otherwise when you want to call into JavaScript code from C.
```ts
-import {dlopen, JSCallback, ptr, CString} from 'bun:ffi';
+import { dlopen, JSCallback, ptr, CString } from "bun:ffi";
const {
- symbols: {search},
+ symbols: { search },
close,
-} = dlopen('libmylib', {
+} = dlopen("libmylib", {
search: {
- returns: 'usize',
- args: ['cstring', 'callback'],
+ returns: "usize",
+ args: ["cstring", "callback"],
},
});
const searchIterator = new JSCallback(
(ptr, length) => /hello/.test(new CString(ptr, length)),
{
- returns: 'bool',
- args: ['ptr', 'usize'],
- }
+ returns: "bool",
+ args: ["ptr", "usize"],
+ },
);
-const str = Buffer.from('wwutwutwutwutwutwutwutwutwutwutut\0', 'utf8');
+const str = Buffer.from("wwutwutwutwutwutwutwutwutwutwutut\0", "utf8");
if (search(ptr(str), searchIterator)) {
// found a match!
}
@@ -3869,12 +3871,12 @@ For a slight performance boost, directly pass `JSCallback.prototype.ptr` instead
```ts
const onResolve = new JSCallback((arg) => arg === 42, {
- returns: 'bool',
- args: ['i32'],
+ returns: "bool",
+ args: ["i32"],
});
const setOnResolve = new CFunction({
- returns: 'bool',
- args: ['function'],
+ returns: "bool",
+ args: ["function"],
ptr: myNativeLibrarySetOnResolve,
});
@@ -3908,7 +3910,7 @@ If you pass a `BigInt` to a function, it will be converted to a `number`
**To convert from a TypedArray to a pointer**:
```ts
-import {ptr} from 'bun:ffi';
+import { ptr } from "bun:ffi";
let myTypedArray = new Uint8Array(32);
const myPtr = ptr(myTypedArray);
```
@@ -3916,7 +3918,7 @@ const myPtr = ptr(myTypedArray);
**To convert from a pointer to an ArrayBuffer**:
```ts
-import {ptr, toArrayBuffer} from 'bun:ffi';
+import { ptr, toArrayBuffer } from "bun:ffi";
let myTypedArray = new Uint8Array(32);
const myPtr = ptr(myTypedArray);
@@ -3932,14 +3934,14 @@ You have two options.
For long-lived pointers, a `DataView` is the fastest option:
```ts
-import {toArrayBuffer} from 'bun:ffi';
+import { toArrayBuffer } from "bun:ffi";
let myDataView = new DataView(toArrayBuffer(myPtr, 0, 32));
console.log(
myDataView.getUint8(0, true),
myDataView.getUint8(1, true),
myDataView.getUint8(2, true),
- myDataView.getUint8(3, true)
+ myDataView.getUint8(3, true),
);
```
@@ -3948,14 +3950,14 @@ For short-lived pointers, `read` is the fastest option:
_Available in Bun v0.1.12+_
```ts
-import {read} from 'bun:ffi';
+import { read } from "bun:ffi";
console.log(
// ptr, byteOffset
read.u8(myPtr, 0),
read.u8(myPtr, 1),
read.u8(myPtr, 2),
- read.u8(myPtr, 3)
+ read.u8(myPtr, 3),
);
```
@@ -3996,7 +3998,7 @@ typedef void (*JSTypedArrayBytesDeallocator)(void *bytes, void *deallocatorConte
```
```ts
-import {toArrayBuffer} from 'bun:ffi';
+import { toArrayBuffer } from "bun:ffi";
// with a deallocatorContext:
toArrayBuffer(
@@ -4009,7 +4011,7 @@ toArrayBuffer(
deallocatorContext,
// this is a pointer to a function
- jsTypedArrayBytesDeallocator
+ jsTypedArrayBytesDeallocator,
);
// without a deallocatorContext:
@@ -4020,7 +4022,7 @@ toArrayBuffer(
byteLength,
// this is a pointer to a function
- jsTypedArrayBytesDeallocator
+ jsTypedArrayBytesDeallocator,
);
```
@@ -4043,14 +4045,14 @@ Where FFI functions expect a pointer, pass a TypedArray of equivalent size
Easymode:
```ts
-import {dlopen, FFIType} from 'bun:ffi';
+import { dlopen, FFIType } from "bun:ffi";
const {
- symbols: {encode_png},
+ symbols: { encode_png },
} = dlopen(myLibraryPath, {
encode_png: {
// FFIType's can be specified as strings too
- args: ['ptr', 'u32', 'u32'],
+ args: ["ptr", "u32", "u32"],
returns: FFIType.ptr,
},
});
@@ -4064,7 +4066,7 @@ const out = encode_png(
pixels,
128,
- 128
+ 128,
);
```
@@ -4077,14 +4079,14 @@ The [auto-generated wrapper](https://github.com/oven-sh/bun/blob/6a65631cbdcae75
If you don't want the automatic conversion or you want a pointer to a specific byte offset within the TypedArray, you can also directly get the pointer to the TypedArray:
```ts
-import {dlopen, FFIType, ptr} from 'bun:ffi';
+import { dlopen, FFIType, ptr } from "bun:ffi";
const {
- symbols: {encode_png},
+ symbols: { encode_png },
} = dlopen(myLibraryPath, {
encode_png: {
// FFIType's can be specified as strings too
- args: ['ptr', 'u32', 'u32'],
+ args: ["ptr", "u32", "u32"],
returns: FFIType.ptr,
},
});
@@ -4100,7 +4102,7 @@ const out = encode_png(
// dimensions:
128,
- 128
+ 128,
);
```
@@ -4115,14 +4117,14 @@ const out = encode_png(
// dimensions:
128,
- 128
+ 128,
);
// assuming it is 0-terminated, it can be read like this:
let png = new Uint8Array(toArrayBuffer(out));
// save it to disk:
-await Bun.write('out.png', png);
+await Bun.write("out.png", png);
```
##### Not implemented yet
@@ -4141,14 +4143,14 @@ You can see the status of [this here](https://github.com/oven-sh/bun/issues/158)
Loading Node-API modules in Bun.js works the same as in Node.js:
```js
-const napi = require('./my-node-module.node');
+const napi = require("./my-node-module.node");
```
You can also use `process.dlopen`:
```js
-let mod = {exports: {}};
-process.dlopen(mod, './my-node-module.node');
+let mod = { exports: {} };
+process.dlopen(mod, "./my-node-module.node");
```
As part of that work, Bun.js also polyfills the [`detect-libc`](https://npmjs.com/package/detect-libc) package, which is used by many Node-API modules to detect which `.node` binding to `require`.
@@ -4161,10 +4163,10 @@ When requiring a `*.node` module, Bun's JavaScript transpiler transforms the `re
```js
// this is the input
-require('./my-node-module.node');
+require("./my-node-module.node");
// this is the output
-import.meta.require('./my-node-module.node');
+import.meta.require("./my-node-module.node");
```
Bun doesn't currently support dynamic requires, but `import.meta.require` is an escape hatch for that. It uses a [JavaScriptCore built-in function](https://github.com/oven-sh/bun/blob/aa87d40f4b7fdfb52575f44d151906ddba6a82d0/src/javascript/jsc/bindings/builtins/js/JSZigGlobalObject.js#L26).
@@ -4256,24 +4258,24 @@ This lets you transpile JavaScript, TypeScript, TSX, and JSX using Bun's transpi
It is synchronous and runs in the same thread as other JavaScript code.
```js
-const transpiler = new Bun.Transpiler({loader: 'jsx'});
-transpiler.transformSync('<div>hi!</div>');
+const transpiler = new Bun.Transpiler({ loader: "jsx" });
+transpiler.transformSync("<div>hi!</div>");
```
```js
-import {__require as require} from 'bun:wrap';
-import * as JSX from 'react/jsx-dev-runtime';
+import { __require as require } from "bun:wrap";
+import * as JSX from "react/jsx-dev-runtime";
var jsx = require(JSX).jsxDEV;
export default jsx(
- 'div',
+ "div",
{
- children: 'hi!',
+ children: "hi!",
},
undefined,
false,
undefined,
- this
+ this,
);
```
@@ -4290,31 +4292,31 @@ If code uses a macro, it will potentially spawn a new copy of Bun.js' JavaScript
Unless you're transpiling _many_ large files, you should probably use `Bun.Transpiler.transformSync`. The cost of the threadpool will often take longer than actually transpiling code.
```js
-const transpiler = new Bun.Transpiler({loader: 'jsx'});
-await transpiler.transform('<div>hi!</div>');
+const transpiler = new Bun.Transpiler({ loader: "jsx" });
+await transpiler.transform("<div>hi!</div>");
```
```js
-import {__require as require} from 'bun:wrap';
-import * as JSX from 'react/jsx-dev-runtime';
+import { __require as require } from "bun:wrap";
+import * as JSX from "react/jsx-dev-runtime";
var jsx = require(JSX).jsxDEV;
export default jsx(
- 'div',
+ "div",
{
- children: 'hi!',
+ children: "hi!",
},
undefined,
false,
undefined,
- this
+ this,
);
```
You can also pass a `Loader` as a string
```js
-await transpiler.transform('<div>hi!</div>', 'tsx');
+await transpiler.transform("<div>hi!</div>", "tsx");
```
#### `Bun.Transpiler.scan`
@@ -4324,7 +4326,7 @@ This is a fast way to get a list of imports & exports used in a JavaScript/jsx o
This function is synchronous.
```ts
-const transpiler = new Bun.Transpiler({loader: 'ts'});
+const transpiler = new Bun.Transpiler({ loader: "ts" });
transpiler.scan(`
import React from 'react';
@@ -4365,7 +4367,7 @@ This is a fast path for getting a list of imports used in a JavaScript/jsx or Ty
This function is synchronous.
```ts
-const transpiler = new Bun.Transpiler({loader: 'ts'});
+const transpiler = new Bun.Transpiler({ loader: "ts" });
transpiler.scanImports(`
import React from 'react';
@@ -4672,6 +4674,7 @@ bun also statically links these libraries:
- [`lol-html`](https://github.com/cloudflare/lol-html/tree/master/c-api), which is BSD 3-Clause licensed
- [`mimalloc`](https://github.com/microsoft/mimalloc), which is MIT licensed
- [`picohttp`](https://github.com/h2o/picohttpparser), which is dual-licensed under the Perl License or the MIT License
+- [`simdutf`](https://github.com/simdutf/simdutf), which is Apache 2.0 licensed
- [`tinycc`](https://github.com/tinycc/tinycc), which is LGPL v2.1 licensed
- [`uSockets`](https://github.com/uNetworking/uSockets), which is Apache 2.0 licensed
- [`zlib-cloudflare`](https://github.com/cloudflare/zlib), which is zlib licensed