diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..484dea173f --- /dev/null +++ b/docs/README.md @@ -0,0 +1,28 @@ +

+ + Logo + +

+

Bun Documentation

+ +Official documentation for Bun: the fast, all-in-one JavaScript runtime. + +## Development + +Install the [Mintlify CLI](https://www.npmjs.com/package/mint) to preview the documentation locally: + +```bash +bun install -g mint +``` + +Run the development server: + +```bash +mint dev +``` + +The site will be available at `http://localhost:3000`. + +## Contributing + +Contributions are welcome! Please open an issue or submit a pull request. diff --git a/docs/api/file.md b/docs/api/file.md deleted file mode 100644 index 16e5359017..0000000000 --- a/docs/api/file.md +++ /dev/null @@ -1,19 +0,0 @@ -Bun.js has fast paths for common use cases that make Web APIs live up to the performance demands of servers and CLIs. - -`Bun.file(path)` returns a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) that represents a lazily-loaded file. - -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); -``` - -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)). - -`Bun.write` also supports [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects. It automatically converts to a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob). - -```js -// Eventually, this will stream the response to disk but today it buffers -await Bun.write("index.html", await fetch("https://example.com")); -``` diff --git a/docs/api/globals.md b/docs/api/globals.md deleted file mode 100644 index 1a98bb0899..0000000000 --- a/docs/api/globals.md +++ /dev/null @@ -1,387 +0,0 @@ -Bun implements the following globals. - -{% table %} - -- Global -- Source -- Notes - ---- - -- [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) -- Web --   - ---- - -- [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) -- Web --   - ---- - -- [`alert`](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert) -- Web -- Intended for command-line tools - ---- - -- [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) -- Web --   - ---- - -- [`Buffer`](https://nodejs.org/api/buffer.html#class-buffer) -- Node.js -- See [Node.js > `Buffer`](https://bun.com/docs/runtime/nodejs-apis#node-buffer) - ---- - -- `Bun` -- Bun -- Subject to change as additional APIs are added - ---- - -- [`ByteLengthQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/ByteLengthQueuingStrategy) -- Web --   - ---- - -- [`confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm) -- Web -- Intended for command-line tools - ---- - -- [`__dirname`](https://nodejs.org/api/globals.html#__dirname) -- Node.js --   - ---- - -- [`__filename`](https://nodejs.org/api/globals.html#__filename) -- Node.js --   - ---- - -- [`atob()`](https://developer.mozilla.org/en-US/docs/Web/API/atob) -- Web --   - ---- - -- [`btoa()`](https://developer.mozilla.org/en-US/docs/Web/API/btoa) -- Web --   - ---- - -- `BuildMessage` -- Bun --   - ---- - -- [`clearImmediate()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearImmediate) -- Web --   - ---- - -- [`clearInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearInterval) -- Web --   - ---- - -- [`clearTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearTimeout) -- Web --   - ---- - -- [`console`](https://developer.mozilla.org/en-US/docs/Web/API/console) -- Web --   - ---- - -- [`CountQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/CountQueuingStrategy) -- Web --   - ---- - -- [`Crypto`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto) -- Web --   - ---- - -- [`crypto`](https://developer.mozilla.org/en-US/docs/Web/API/crypto) -- Web --   - ---- - -- [`CryptoKey`](https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey) -- Web --   - ---- - -- [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) -- Web --   - ---- - -- [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) -- Web -- Also [`ErrorEvent`](https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent) [`CloseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) [`MessageEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent). - ---- - -- [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) -- Web --   - ---- - -- [`exports`](https://nodejs.org/api/globals.html#exports) -- Node.js --   - ---- - -- [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch) -- Web --   - ---- - -- [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) -- Web --   - ---- - -- [`global`](https://nodejs.org/api/globals.html#global) -- Node.js -- See [Node.js > `global`](https://bun.com/docs/runtime/nodejs-apis#global). - ---- - -- [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis) -- Cross-platform -- Aliases to `global` - ---- - -- [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) -- Web --   - ---- - -- [`HTMLRewriter`](https://bun.com/docs/api/html-rewriter) -- Cloudflare --   - ---- - -- [`JSON`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON) -- Web --   - ---- - -- [`MessageEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent) -- Web --   - ---- - -- [`module`](https://nodejs.org/api/globals.html#module) -- Node.js --   - ---- - -- [`performance`](https://developer.mozilla.org/en-US/docs/Web/API/performance) -- Web --   - ---- - -- [`process`](https://nodejs.org/api/process.html) -- Node.js -- See [Node.js > `process`](https://bun.com/docs/runtime/nodejs-apis#node-process) - ---- - -- [`prompt`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt) -- Web -- Intended for command-line tools - ---- - -- [`queueMicrotask()`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask) -- Web --   - ---- - -- [`ReadableByteStreamController`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableByteStreamController) -- Web --   - ---- - -- [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) -- Web --   - ---- - -- [`ReadableStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultController) -- Web --   - ---- - -- [`ReadableStreamDefaultReader`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader) -- Web --   - ---- - -- [`reportError`](https://developer.mozilla.org/en-US/docs/Web/API/reportError) -- Web --   - ---- - -- [`require()`](https://nodejs.org/api/globals.html#require) -- Node.js --   - ---- - -- `ResolveMessage` -- Bun --   - ---- - -- [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) -- Web --   - ---- - -- [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) -- Web --   - ---- - -- [`setImmediate()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate) -- Web --   - ---- - -- [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval) -- Web --   - ---- - -- [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout) -- Web --   - ---- - -- [`ShadowRealm`](https://github.com/tc39/proposal-shadowrealm) -- Web -- Stage 3 proposal - ---- - -- [`SubtleCrypto`](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto) -- Web --   - ---- - -- [`DOMException`](https://developer.mozilla.org/en-US/docs/Web/API/DOMException) -- Web --   - ---- - -- [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder) -- Web --   - ---- - -- [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder) -- Web --   - ---- - -- [`TransformStream`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream) -- Web --   - ---- - -- [`TransformStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStreamDefaultController) -- Web --   - ---- - -- [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) -- Web --   - ---- - -- [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) -- Web --   - ---- - -- [`WebAssembly`](https://nodejs.org/api/globals.html#webassembly) -- Web --   - ---- - -- [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream) -- Web --   - ---- - -- [`WritableStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultController) -- Web --   - ---- - -- [`WritableStreamDefaultWriter`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter) -- Web --   - -{% /table %} diff --git a/docs/api/http.md b/docs/api/http.md deleted file mode 100644 index 201e911cfe..0000000000 --- a/docs/api/http.md +++ /dev/null @@ -1,1408 +0,0 @@ -The page primarily documents the Bun-native `Bun.serve` API. Bun also implements [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and the Node.js [`http`](https://nodejs.org/api/http.html) and [`https`](https://nodejs.org/api/https.html) modules. - -{% callout %} -These modules have been re-implemented to use Bun's fast internal HTTP infrastructure. Feel free to use these modules directly; frameworks like [Express](https://expressjs.com/) that depend on these modules should work out of the box. For granular compatibility information, see [Runtime > Node.js APIs](https://bun.com/docs/runtime/nodejs-apis). -{% /callout %} - -To start a high-performance HTTP server with a clean API, the recommended approach is [`Bun.serve`](#start-a-server-bun-serve). - -## `Bun.serve()` - -Use `Bun.serve` to start an HTTP server in Bun. - -```ts -Bun.serve({ - // `routes` requires Bun v1.2.3+ - routes: { - // Static routes - "/api/status": new Response("OK"), - - // Dynamic routes - "/users/:id": req => { - return new Response(`Hello User ${req.params.id}!`); - }, - - // Per-HTTP method handlers - "/api/posts": { - GET: () => new Response("List posts"), - POST: async req => { - const body = await req.json(); - return Response.json({ created: true, ...body }); - }, - }, - - // Wildcard route for all routes that start with "/api/" and aren't otherwise matched - "/api/*": Response.json({ message: "Not found" }, { status: 404 }), - - // Redirect from /blog/hello to /blog/hello/world - "/blog/hello": Response.redirect("/blog/hello/world"), - - // Serve a file by buffering it in memory - "/favicon.ico": new Response(await Bun.file("./favicon.ico").bytes(), { - headers: { - "Content-Type": "image/x-icon", - }, - }), - }, - - // (optional) fallback for unmatched routes: - // Required if Bun's version < 1.2.3 - fetch(req) { - return new Response("Not Found", { status: 404 }); - }, -}); -``` - -### Routing - -Routes in `Bun.serve()` receive a `BunRequest` (which extends [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)) and return a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) or `Promise`. This makes it easier to use the same code for both sending & receiving HTTP requests. - -```ts -// Simplified for brevity -interface BunRequest extends Request { - params: Record; - readonly cookies: CookieMap; -} -``` - -#### Async/await in routes - -You can use async/await in route handlers to return a `Promise`. - -```ts -import { sql, serve } from "bun"; - -serve({ - port: 3001, - routes: { - "/api/version": async () => { - const [version] = await sql`SELECT version()`; - return Response.json(version); - }, - }, -}); -``` - -#### Promise in routes - -You can also return a `Promise` from a route handler. - -```ts -import { sql, serve } from "bun"; - -serve({ - routes: { - "/api/version": () => { - return new Promise(resolve => { - setTimeout(async () => { - const [version] = await sql`SELECT version()`; - resolve(Response.json(version)); - }, 100); - }); - }, - }, -}); -``` - -#### Type-safe route parameters - -TypeScript parses route parameters when passed as a string literal, so that your editor will show autocomplete when accessing `request.params`. - -```ts -import type { BunRequest } from "bun"; - -Bun.serve({ - routes: { - // TypeScript knows the shape of params when passed as a string literal - "/orgs/:orgId/repos/:repoId": req => { - const { orgId, repoId } = req.params; - return Response.json({ orgId, repoId }); - }, - - "/orgs/:orgId/repos/:repoId/settings": ( - // optional: you can explicitly pass a type to BunRequest: - req: BunRequest<"/orgs/:orgId/repos/:repoId/settings">, - ) => { - const { orgId, repoId } = req.params; - return Response.json({ orgId, repoId }); - }, - }, -}); -``` - -Percent-encoded route parameter values are automatically decoded. Unicode characters are supported. Invalid unicode is replaced with the unicode replacement character `&0xFFFD;`. - -### Static responses - -Routes can also be `Response` objects (without the handler function). Bun.serve() optimizes it for zero-allocation dispatch - perfect for health checks, redirects, and fixed content: - -```ts -Bun.serve({ - routes: { - // Health checks - "/health": new Response("OK"), - "/ready": new Response("Ready", { - headers: { - // Pass custom headers - "X-Ready": "1", - }, - }), - - // Redirects - "/blog": Response.redirect("https://bun.com/blog"), - - // API responses - "/api/config": Response.json({ - version: "1.0.0", - env: "production", - }), - }, -}); -``` - -Static responses do not allocate additional memory after initialization. You can generally expect at least a 15% performance improvement over manually returning a `Response` object. - -Static route responses are cached for the lifetime of the server object. To reload static routes, call `server.reload(options)`. - -### File Responses vs Static Responses - -When serving files in routes, there are two distinct behaviors depending on whether you buffer the file content or serve it directly: - -```ts -Bun.serve({ - routes: { - // Static route - content is buffered in memory at startup - "/logo.png": new Response(await Bun.file("./logo.png").bytes()), - - // File route - content is read from filesystem on each request - "/download.zip": new Response(Bun.file("./download.zip")), - }, -}); -``` - -**Static routes** (`new Response(await file.bytes())`) buffer content in memory at startup: - -- **Zero filesystem I/O** during requests - content served entirely from memory -- **ETag support** - Automatically generates and validates ETags for caching -- **If-None-Match** - Returns `304 Not Modified` when client ETag matches -- **No 404 handling** - Missing files cause startup errors, not runtime 404s -- **Memory usage** - Full file content stored in RAM -- **Best for**: Small static assets, API responses, frequently accessed files - -**File routes** (`new Response(Bun.file(path))`) read from filesystem per request: - -- **Filesystem reads** on each request - checks file existence and reads content -- **Built-in 404 handling** - Returns `404 Not Found` if file doesn't exist or becomes inaccessible -- **Last-Modified support** - Uses file modification time for `If-Modified-Since` headers -- **If-Modified-Since** - Returns `304 Not Modified` when file hasn't changed since client's cached version -- **Range request support** - Automatically handles partial content requests with `Content-Range` headers -- **Streaming transfers** - Uses buffered reader with backpressure handling for efficient memory usage -- **Memory efficient** - Only buffers small chunks during transfer, not entire file -- **Best for**: Large files, dynamic content, user uploads, files that change frequently - -### HTTP Caching Behavior - -Both route types implement HTTP caching standards but with different strategies: - -#### Static Routes Caching - -- **ETag generation**: Automatically computes ETag hash from content at startup -- **If-None-Match**: Validates client ETag against server ETag -- **304 responses**: Returns `304 Not Modified` with empty body when ETags match -- **Cache headers**: Inherits any `Cache-Control` headers you provide in the Response -- **Consistency**: ETag remains constant until server restart or route reload - -#### File Routes Caching - -- **Last-Modified**: Uses file's `mtime` for `Last-Modified` header -- **If-Modified-Since**: Compares client date with file modification time -- **304 responses**: Returns `304 Not Modified` when file unchanged since client's cached version -- **Content-Length**: Automatically set based on current file size -- **Dynamic validation**: Checks file modification time on each request - -#### Status Code Handling - -Both route types automatically adjust status codes: - -- **200 → 204**: Empty files (0 bytes) return `204 No Content` instead of `200 OK` -- **200 → 304**: Successful cache validation returns `304 Not Modified` -- **File routes only**: Missing or inaccessible files return `404 Not Found` - -```ts -const server = Bun.serve({ - static: { - "/api/time": new Response(new Date().toISOString()), - }, - - fetch(req) { - return new Response("404!"); - }, -}); - -// Update the time every second. -setInterval(() => { - server.reload({ - static: { - "/api/time": new Response(new Date().toISOString()), - }, - - fetch(req) { - return new Response("404!"); - }, - }); -}, 1000); -``` - -Reloading routes only impact the next request. In-flight requests continue to use the old routes. After in-flight requests to old routes are finished, the old routes are freed from memory. - -To simplify error handling, static routes do not support streaming response bodies from `ReadableStream` or an `AsyncIterator`. Fortunately, you can still buffer the response in memory first: - -```ts -const time = await fetch("https://api.example.com/v1/data"); -// Buffer the response in memory first. -const blob = await time.blob(); - -const server = Bun.serve({ - static: { - "/api/data": new Response(blob), - }, - - fetch(req) { - return new Response("404!"); - }, -}); -``` - -### Route precedence - -Routes are matched in order of specificity: - -1. Exact routes (`/users/all`) -2. Parameter routes (`/users/:id`) -3. Wildcard routes (`/users/*`) -4. Global catch-all (`/*`) - -```ts -Bun.serve({ - routes: { - // Most specific first - "/api/users/me": () => new Response("Current user"), - "/api/users/:id": req => new Response(`User ${req.params.id}`), - "/api/*": () => new Response("API catch-all"), - "/*": () => new Response("Global catch-all"), - }, -}); -``` - -### Per-HTTP Method Routes - -Route handlers can be specialized by HTTP method: - -```ts -Bun.serve({ - routes: { - "/api/posts": { - // Different handlers per method - GET: () => new Response("List posts"), - POST: async req => { - const post = await req.json(); - return Response.json({ id: crypto.randomUUID(), ...post }); - }, - PUT: async req => { - const updates = await req.json(); - return Response.json({ updated: true, ...updates }); - }, - DELETE: () => new Response(null, { status: 204 }), - }, - }, -}); -``` - -You can pass any of the following methods: - -| Method | Usecase example | -| --------- | ------------------------------- | -| `GET` | Fetch a resource | -| `HEAD` | Check if a resource exists | -| `OPTIONS` | Get allowed HTTP methods (CORS) | -| `DELETE` | Delete a resource | -| `PATCH` | Update a resource | -| `POST` | Create a resource | -| `PUT` | Update a resource | - -When passing a function instead of an object, all methods will be handled by that function: - -```ts -const server = Bun.serve({ - routes: { - "/api/version": () => Response.json({ version: "1.0.0" }), - }, -}); - -await fetch(new URL("/api/version", server.url)); -await fetch(new URL("/api/version", server.url), { method: "PUT" }); -// ... etc -``` - -### Hot Route Reloading - -Update routes without server restarts using `server.reload()`: - -```ts -const server = Bun.serve({ - routes: { - "/api/version": () => Response.json({ version: "1.0.0" }), - }, -}); - -// Deploy new routes without downtime -server.reload({ - routes: { - "/api/version": () => Response.json({ version: "2.0.0" }), - }, -}); -``` - -### Error Handling - -Bun provides structured error handling for routes: - -```ts -Bun.serve({ - routes: { - // Errors are caught automatically - "/api/risky": () => { - throw new Error("Something went wrong"); - }, - }, - // Global error handler - error(error) { - console.error(error); - return new Response(`Internal Error: ${error.message}`, { - status: 500, - headers: { - "Content-Type": "text/plain", - }, - }); - }, -}); -``` - -### HTML imports - -Bun supports importing HTML files directly into your server code, enabling full-stack applications with both server-side and client-side code. HTML imports work in two modes: - -**Development (`bun --hot`):** Assets are bundled on-demand at runtime, enabling hot module replacement (HMR) for a fast, iterative development experience. When you change your frontend code, the browser automatically updates without a full page reload. - -**Production (`bun build`):** When building with `bun build --target=bun`, the `import index from "./index.html"` statement resolves to a pre-built manifest object containing all bundled client assets. `Bun.serve` consumes this manifest to serve optimized assets with zero runtime bundling overhead. This is ideal for deploying to production. - -```ts -import myReactSinglePageApp from "./index.html"; - -Bun.serve({ - routes: { - "/": myReactSinglePageApp, - }, -}); -``` - -HTML imports don't just serve HTML — it's a full-featured frontend bundler, transpiler, and toolkit built using Bun's [bundler](https://bun.com/docs/bundler), JavaScript transpiler and CSS parser. You can use this to build full-featured frontends with React, TypeScript, Tailwind CSS, and more. - -For a complete guide on building full-stack applications with HTML imports, including detailed examples and best practices, see [/docs/bundler/fullstack](https://bun.com/docs/bundler/fullstack). - -### Practical example: REST API - -Here's a basic database-backed REST API using Bun's router with zero dependencies: - -{% codetabs %} - -```ts#server.ts -import type { Post } from "./types.ts"; -import { Database } from "bun:sqlite"; - -const db = new Database("posts.db"); -db.exec(` - CREATE TABLE IF NOT EXISTS posts ( - id TEXT PRIMARY KEY, - title TEXT NOT NULL, - content TEXT NOT NULL, - created_at TEXT NOT NULL - ) -`); - -Bun.serve({ - routes: { - // List posts - "/api/posts": { - GET: () => { - const posts = db.query("SELECT * FROM posts").all(); - return Response.json(posts); - }, - - // Create post - POST: async req => { - const post: Omit = await req.json(); - const id = crypto.randomUUID(); - - db.query( - `INSERT INTO posts (id, title, content, created_at) - VALUES (?, ?, ?, ?)`, - ).run(id, post.title, post.content, new Date().toISOString()); - - return Response.json({ id, ...post }, { status: 201 }); - }, - }, - - // Get post by ID - "/api/posts/:id": req => { - const post = db - .query("SELECT * FROM posts WHERE id = ?") - .get(req.params.id); - - if (!post) { - return new Response("Not Found", { status: 404 }); - } - - return Response.json(post); - }, - }, - - error(error) { - console.error(error); - return new Response("Internal Server Error", { status: 500 }); - }, -}); -``` - -```ts#types.ts -export interface Post { - id: string; - title: string; - content: string; - created_at: string; -} -``` - -{% /codetabs %} - -### Routing performance - -`Bun.serve()`'s router builds on top uWebSocket's [tree-based approach](https://github.com/oven-sh/bun/blob/0d1a00fa0f7830f8ecd99c027fce8096c9d459b6/packages/bun-uws/src/HttpRouter.h#L57-L64) to add [SIMD-accelerated route parameter decoding](https://github.com/oven-sh/bun/blob/main/src/bun.js/bindings/decodeURIComponentSIMD.cpp#L21-L271) and [JavaScriptCore structure caching](https://github.com/oven-sh/bun/blob/main/src/bun.js/bindings/ServerRouteList.cpp#L100-L101) to push the performance limits of what modern hardware allows. - -### `fetch` request handler - -The `fetch` handler handles incoming requests that weren't matched by any route. It receives a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object and returns a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) or [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). - -```ts -Bun.serve({ - fetch(req) { - const url = new URL(req.url); - if (url.pathname === "/") return new Response("Home page!"); - if (url.pathname === "/blog") return new Response("Blog!"); - return new Response("404!"); - }, -}); -``` - -The `fetch` handler supports async/await: - -```ts -import { sleep, serve } from "bun"; -serve({ - async fetch(req) { - const start = performance.now(); - await sleep(10); - const end = performance.now(); - return new Response(`Slept for ${end - start}ms`); - }, -}); -``` - -Promise-based responses are also supported: - -```ts -Bun.serve({ - fetch(req) { - // Forward the request to another server. - return fetch("https://example.com"); - }, -}); -``` - -You can also access the `Server` object from the `fetch` handler. It's the second argument passed to the `fetch` function. - -```ts -// `server` is passed in as the second argument to `fetch`. -const server = Bun.serve({ - fetch(req, server) { - const ip = server.requestIP(req); - return new Response(`Your IP is ${ip.address}`); - }, -}); -``` - -### Changing the `port` and `hostname` - -To configure which port and hostname the server will listen on, set `port` and `hostname` in the options object. - -```ts -Bun.serve({ - port: 8080, // defaults to $BUN_PORT, $PORT, $NODE_PORT otherwise 3000 - hostname: "mydomain.com", // defaults to "0.0.0.0" - fetch(req) { - return new Response("404!"); - }, -}); -``` - -To randomly select an available port, set `port` to `0`. - -```ts -const server = Bun.serve({ - port: 0, // random port - fetch(req) { - return new Response("404!"); - }, -}); - -// server.port is the randomly selected port -console.log(server.port); -``` - -You can view the chosen port by accessing the `port` property on the server object, or by accessing the `url` property. - -```ts -console.log(server.port); // 3000 -console.log(server.url); // http://localhost:3000 -``` - -#### Configuring a default port - -Bun supports several options and environment variables to configure the default port. The default port is used when the `port` option is not set. - -- `--port` CLI flag - -```sh -$ bun --port=4002 server.ts -``` - -- `BUN_PORT` environment variable - -```sh -$ BUN_PORT=4002 bun server.ts -``` - -- `PORT` environment variable - -```sh -$ PORT=4002 bun server.ts -``` - -- `NODE_PORT` environment variable - -```sh -$ NODE_PORT=4002 bun server.ts -``` - -### Unix domain sockets - -To listen on a [unix domain socket](https://en.wikipedia.org/wiki/Unix_domain_socket), pass the `unix` option with the path to the socket. - -```ts -Bun.serve({ - unix: "/tmp/my-socket.sock", // path to socket - fetch(req) { - return new Response(`404!`); - }, -}); -``` - -### Abstract namespace sockets - -Bun supports Linux abstract namespace sockets. To use an abstract namespace socket, prefix the `unix` path with a null byte. - -```ts -Bun.serve({ - unix: "\0my-abstract-socket", // abstract namespace socket - fetch(req) { - return new Response(`404!`); - }, -}); -``` - -Unlike unix domain sockets, abstract namespace sockets are not bound to the filesystem and are automatically removed when the last reference to the socket is closed. - -## Error handling - -To activate development mode, set `development: true`. - -```ts -Bun.serve({ - development: true, - fetch(req) { - throw new Error("woops!"); - }, -}); -``` - -In development mode, Bun will surface errors in-browser with a built-in error page. - -{% image src="/images/exception_page.png" caption="Bun's built-in 500 page" /%} - -### `error` callback - -To handle server-side errors, implement an `error` handler. This function should return a `Response` to serve to the client when an error occurs. This response will supersede Bun's default error page in `development` mode. - -```ts -Bun.serve({ - fetch(req) { - throw new Error("woops!"); - }, - error(error) { - return new Response(`
${error}\n${error.stack}
`, { - headers: { - "Content-Type": "text/html", - }, - }); - }, -}); -``` - -{% callout %} -[Learn more about debugging in Bun](https://bun.com/docs/runtime/debugger) -{% /callout %} - -The call to `Bun.serve` returns a `Server` object. To stop the server, call the `.stop()` method. - -```ts -const server = Bun.serve({ - fetch() { - return new Response("Bun!"); - }, -}); - -server.stop(); -``` - -## TLS - -Bun supports TLS out of the box, powered by [BoringSSL](https://boringssl.googlesource.com/boringssl). Enable TLS by passing in a value for `key` and `cert`; both are required to enable TLS. - -```ts-diff - Bun.serve({ - fetch(req) { - return new Response("Hello!!!"); - }, - -+ tls: { -+ key: Bun.file("./key.pem"), -+ cert: Bun.file("./cert.pem"), -+ } - }); -``` - -The `key` and `cert` fields expect the _contents_ of your TLS key and certificate, _not a path to it_. This can be a string, `BunFile`, `TypedArray`, or `Buffer`. - -```ts -Bun.serve({ - fetch() {}, - - tls: { - // BunFile - key: Bun.file("./key.pem"), - // Buffer - key: fs.readFileSync("./key.pem"), - // string - key: fs.readFileSync("./key.pem", "utf8"), - // array of above - key: [Bun.file("./key1.pem"), Bun.file("./key2.pem")], - }, -}); -``` - -If your private key is encrypted with a passphrase, provide a value for `passphrase` to decrypt it. - -```ts-diff - Bun.serve({ - fetch(req) { - return new Response("Hello!!!"); - }, - - tls: { - key: Bun.file("./key.pem"), - cert: Bun.file("./cert.pem"), -+ passphrase: "my-secret-passphrase", - } - }); -``` - -Optionally, you can override the trusted CA certificates by passing a value for `ca`. By default, the server will trust the list of well-known CAs curated by Mozilla. When `ca` is specified, the Mozilla list is overwritten. - -```ts-diff - Bun.serve({ - fetch(req) { - return new Response("Hello!!!"); - }, - tls: { - key: Bun.file("./key.pem"), // path to TLS key - cert: Bun.file("./cert.pem"), // path to TLS cert -+ ca: Bun.file("./ca.pem"), // path to root CA certificate - } - }); -``` - -To override Diffie-Hellman parameters: - -```ts -Bun.serve({ - // ... - tls: { - // other config - dhParamsFile: "/path/to/dhparams.pem", // path to Diffie Hellman parameters - }, -}); -``` - -### Server name indication (SNI) - -To configure the server name indication (SNI) for the server, set the `serverName` field in the `tls` object. - -```ts -Bun.serve({ - // ... - tls: { - // ... other config - serverName: "my-server.com", // SNI - }, -}); -``` - -To allow multiple server names, pass an array of objects to `tls`, each with a `serverName` field. - -```ts -Bun.serve({ - // ... - tls: [ - { - key: Bun.file("./key1.pem"), - cert: Bun.file("./cert1.pem"), - serverName: "my-server1.com", - }, - { - key: Bun.file("./key2.pem"), - cert: Bun.file("./cert2.pem"), - serverName: "my-server2.com", - }, - ], -}); -``` - -## idleTimeout - -To configure the idle timeout, set the `idleTimeout` field in Bun.serve. - -```ts -Bun.serve({ - // 10 seconds: - idleTimeout: 10, - - fetch(req) { - return new Response("Bun!"); - }, -}); -``` - -This is the maximum amount of time a connection is allowed to be idle before the server closes it. A connection is idling if there is no data sent or received. - -## export default syntax - -Thus far, the examples on this page have used the explicit `Bun.serve` API. Bun also supports an alternate syntax. - -```ts#server.ts -import {type Serve} from "bun"; - -export default { - fetch(req) { - return new Response("Bun!"); - }, -} satisfies Serve; -``` - -Instead of passing the server options into `Bun.serve`, `export default` it. This file can be executed as-is; when Bun sees a file with a `default` export containing a `fetch` handler, it passes it into `Bun.serve` under the hood. - - - - - - - -## Streaming files - -To stream a file, return a `Response` object with a `BunFile` object as the body. - -```ts -Bun.serve({ - fetch(req) { - return new Response(Bun.file("./hello.txt")); - }, -}); -``` - -{% callout %} -⚡️ **Speed** — Bun automatically uses the [`sendfile(2)`](https://man7.org/linux/man-pages/man2/sendfile.2.html) system call when possible, enabling zero-copy file transfers in the kernel—the fastest way to send files. -{% /callout %} - -You can send part of a file using the [`slice(start, end)`](https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice) method on the `Bun.file` object. This automatically sets the `Content-Range` and `Content-Length` headers on the `Response` object. - -```ts -Bun.serve({ - fetch(req) { - // parse `Range` header - const [start = 0, end = Infinity] = req.headers - .get("Range") // Range: bytes=0-100 - .split("=") // ["Range: bytes", "0-100"] - .at(-1) // "0-100" - .split("-") // ["0", "100"] - .map(Number); // [0, 100] - - // return a slice of the file - const bigFile = Bun.file("./big-video.mp4"); - return new Response(bigFile.slice(start, end)); - }, -}); -``` - -## Server Lifecycle Methods - -### server.stop() - Stop the server - -To stop the server from accepting new connections: - -```ts -const server = Bun.serve({ - fetch(req) { - return new Response("Hello!"); - }, -}); - -// Gracefully stop the server (waits for in-flight requests) -await server.stop(); - -// Force stop and close all active connections -await server.stop(true); -``` - -By default, `stop()` allows in-flight requests and WebSocket connections to complete. Pass `true` to immediately terminate all connections. - -### server.ref() and server.unref() - Process lifecycle control - -Control whether the server keeps the Bun process alive: - -```ts -// Don't keep process alive if server is the only thing running -server.unref(); - -// Restore default behavior - keep process alive -server.ref(); -``` - -### server.reload() - Hot reload handlers - -Update the server's handlers without restarting: - -```ts -const server = Bun.serve({ - routes: { - "/api/version": Response.json({ version: "v1" }), - }, - fetch(req) { - return new Response("v1"); - }, -}); - -// Update to new handler -server.reload({ - routes: { - "/api/version": Response.json({ version: "v2" }), - }, - fetch(req) { - return new Response("v2"); - }, -}); -``` - -This is useful for development and hot reloading. Only `fetch`, `error`, and `routes` can be updated. - -## Per-Request Controls - - - -### server.timeout(Request, seconds) - Custom request timeouts - -Set a custom idle timeout for individual requests: - -```ts -const server = Bun.serve({ - fetch(req, server) { - // Set 60 second timeout for this request - server.timeout(req, 60); - - // If they take longer than 60 seconds to send the body, the request will be aborted - await req.text(); - - return new Response("Done!"); - }, -}); -``` - -Pass `0` to disable the timeout for a request. - -### server.requestIP(Request) - Get client information - -Get client IP and port information: - -```ts -const server = Bun.serve({ - fetch(req, server) { - const address = server.requestIP(req); - if (address) { - return new Response( - `Client IP: ${address.address}, Port: ${address.port}`, - ); - } - return new Response("Unknown client"); - }, -}); -``` - -Returns `null` for closed requests or Unix domain sockets. - -## Working with Cookies - -Bun provides a built-in API for working with cookies in HTTP requests and responses. The `BunRequest` object includes a `cookies` property that provides a `CookieMap` for easily accessing and manipulating cookies. When using `routes`, `Bun.serve()` automatically tracks `request.cookies.set` and applies them to the response. - -### Reading cookies - -Read cookies from incoming requests using the `cookies` property on the `BunRequest` object: - -```ts -Bun.serve({ - routes: { - "/profile": req => { - // Access cookies from the request - const userId = req.cookies.get("user_id"); - const theme = req.cookies.get("theme") || "light"; - - return Response.json({ - userId, - theme, - message: "Profile page", - }); - }, - }, -}); -``` - -### Setting cookies - -To set cookies, use the `set` method on the `CookieMap` from the `BunRequest` object. - -```ts -Bun.serve({ - routes: { - "/login": req => { - const cookies = req.cookies; - - // Set a cookie with various options - cookies.set("user_id", "12345", { - maxAge: 60 * 60 * 24 * 7, // 1 week - httpOnly: true, - secure: true, - path: "/", - }); - - // Add a theme preference cookie - cookies.set("theme", "dark"); - - // Modified cookies from the request are automatically applied to the response - return new Response("Login successful"); - }, - }, -}); -``` - -`Bun.serve()` automatically tracks modified cookies from the request and applies them to the response. - -### Deleting cookies - -To delete a cookie, use the `delete` method on the `request.cookies` (`CookieMap`) object: - -```ts -Bun.serve({ - routes: { - "/logout": req => { - // Delete the user_id cookie - req.cookies.delete("user_id", { - path: "/", - }); - - return new Response("Logged out successfully"); - }, - }, -}); -``` - -Deleted cookies become a `Set-Cookie` header on the response with the `maxAge` set to `0` and an empty `value`. - -## Server Metrics - -### server.pendingRequests and server.pendingWebSockets - -Monitor server activity with built-in counters: - -```ts -const server = Bun.serve({ - fetch(req, server) { - return new Response( - `Active requests: ${server.pendingRequests}\n` + - `Active WebSockets: ${server.pendingWebSockets}`, - ); - }, -}); -``` - -### server.subscriberCount(topic) - WebSocket subscribers - -Get count of subscribers for a WebSocket topic: - -```ts -const server = Bun.serve({ - fetch(req, server) { - const chatUsers = server.subscriberCount("chat"); - return new Response(`${chatUsers} users in chat`); - }, - websocket: { - message(ws) { - ws.subscribe("chat"); - }, - }, -}); -``` - -## WebSocket Configuration - -### server.publish(topic, data, compress) - WebSocket Message Publishing - -The server can publish messages to all WebSocket clients subscribed to a topic: - -```ts -const server = Bun.serve({ - websocket: { - message(ws) { - // Publish to all "chat" subscribers - server.publish("chat", "Hello everyone!"); - }, - }, - - fetch(req) { - // ... - }, -}); -``` - -The `publish()` method returns: - -- Number of bytes sent if successful -- `0` if the message was dropped -- `-1` if backpressure was applied - -### WebSocket Handler Options - -When configuring WebSockets, several advanced options are available through the `websocket` handler: - -```ts -Bun.serve({ - websocket: { - // Maximum message size (in bytes) - maxPayloadLength: 64 * 1024, - - // Backpressure limit before messages are dropped - backpressureLimit: 1024 * 1024, - - // Close connection if backpressure limit is hit - closeOnBackpressureLimit: true, - - // Handler called when backpressure is relieved - drain(ws) { - console.log("Backpressure relieved"); - }, - - // Enable per-message deflate compression - perMessageDeflate: { - compress: true, - decompress: true, - }, - - // Send ping frames to keep connection alive - sendPings: true, - - // Handlers for ping/pong frames - ping(ws, data) { - console.log("Received ping"); - }, - pong(ws, data) { - console.log("Received pong"); - }, - - // Whether server receives its own published messages - publishToSelf: false, - }, -}); -``` - -## Benchmarks - -Below are Bun and Node.js implementations of a simple HTTP server that responds `Bun!` to each incoming `Request`. - -{% codetabs %} - -```ts#Bun -Bun.serve({ - fetch(req: Request) { - return new Response("Bun!"); - }, - port: 3000, -}); -``` - -```ts#Node -require("http") - .createServer((req, res) => res.end("Bun!")) - .listen(8080); -``` - -{% /codetabs %} -The `Bun.serve` server can handle roughly 2.5x more requests per second than Node.js on Linux. - -{% table %} - -- Runtime -- Requests per second - ---- - -- Node 16 -- ~64,000 - ---- - -- Bun -- ~160,000 - -{% /table %} - -{% image width="499" alt="image" src="https://user-images.githubusercontent.com/709451/162389032-fc302444-9d03-46be-ba87-c12bd8ce89a0.png" /%} - -## Reference - -{% details summary="See TypeScript definitions" %} - -```ts -interface Server extends Disposable { - /** - * Stop the server from accepting new connections. - * @param closeActiveConnections If true, immediately terminates all connections - * @returns Promise that resolves when the server has stopped - */ - stop(closeActiveConnections?: boolean): Promise; - - /** - * Update handlers without restarting the server. - * Only fetch and error handlers can be updated. - */ - reload(options: Serve): void; - - /** - * Make a request to the running server. - * Useful for testing or internal routing. - */ - fetch(request: Request | string): Response | Promise; - - /** - * Upgrade an HTTP request to a WebSocket connection. - * @returns true if upgrade successful, false if failed - */ - upgrade( - request: Request, - options?: { - headers?: Bun.HeadersInit; - data?: T; - }, - ): boolean; - - /** - * Publish a message to all WebSocket clients subscribed to a topic. - * @returns Bytes sent, 0 if dropped, -1 if backpressure applied - */ - publish( - topic: string, - data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, - compress?: boolean, - ): ServerWebSocketSendStatus; - - /** - * Get count of WebSocket clients subscribed to a topic. - */ - subscriberCount(topic: string): number; - - /** - * Get client IP address and port. - * @returns null for closed requests or Unix sockets - */ - requestIP(request: Request): SocketAddress | null; - - /** - * Set custom idle timeout for a request. - * @param seconds Timeout in seconds, 0 to disable - */ - timeout(request: Request, seconds: number): void; - - /** - * Keep process alive while server is running. - */ - ref(): void; - - /** - * Allow process to exit if server is only thing running. - */ - unref(): void; - - /** Number of in-flight HTTP requests */ - readonly pendingRequests: number; - - /** Number of active WebSocket connections */ - readonly pendingWebSockets: number; - - /** Server URL including protocol, hostname and port */ - readonly url: URL; - - /** Port server is listening on */ - readonly port: number; - - /** Hostname server is bound to */ - readonly hostname: string; - - /** Whether server is in development mode */ - readonly development: boolean; - - /** Server instance identifier */ - readonly id: string; -} - -interface WebSocketHandler { - /** Maximum WebSocket message size in bytes */ - maxPayloadLength?: number; - - /** Bytes of queued messages before applying backpressure */ - backpressureLimit?: number; - - /** Whether to close connection when backpressure limit hit */ - closeOnBackpressureLimit?: boolean; - - /** Called when backpressure is relieved */ - drain?(ws: ServerWebSocket): void | Promise; - - /** Seconds before idle timeout */ - idleTimeout?: number; - - /** Enable per-message deflate compression */ - perMessageDeflate?: - | boolean - | { - compress?: WebSocketCompressor | boolean; - decompress?: WebSocketCompressor | boolean; - }; - - /** Send ping frames to keep connection alive */ - sendPings?: boolean; - - /** Whether server receives its own published messages */ - publishToSelf?: boolean; - - /** Called when connection opened */ - open?(ws: ServerWebSocket): void | Promise; - - /** Called when message received */ - message( - ws: ServerWebSocket, - message: string | Buffer, - ): void | Promise; - - /** Called when connection closed */ - close?( - ws: ServerWebSocket, - code: number, - reason: string, - ): void | Promise; - - /** Called when ping frame received */ - ping?(ws: ServerWebSocket, data: Buffer): void | Promise; - - /** Called when pong frame received */ - pong?(ws: ServerWebSocket, data: Buffer): void | Promise; -} - -interface TLSOptions { - /** Certificate authority chain */ - ca?: string | Buffer | BunFile | Array; - - /** Server certificate */ - cert?: string | Buffer | BunFile | Array; - - /** Path to DH parameters file */ - dhParamsFile?: string; - - /** Private key */ - key?: string | Buffer | BunFile | Array; - - /** Reduce TLS memory usage */ - lowMemoryMode?: boolean; - - /** Private key passphrase */ - passphrase?: string; - - /** OpenSSL options flags */ - secureOptions?: number; - - /** Server name for SNI */ - serverName?: string; -} -``` - -{% /details %} diff --git a/docs/api/import-meta.md b/docs/api/import-meta.md deleted file mode 100644 index 7be078a2d1..0000000000 --- a/docs/api/import-meta.md +++ /dev/null @@ -1,69 +0,0 @@ -The `import.meta` object is a way for a module to access information about itself. It's part of the JavaScript language, but its contents are not standardized. Each "host" (browser, runtime, etc) is free to implement any properties it wishes on the `import.meta` object. - -Bun implements the following properties. - -```ts#/path/to/project/file.ts -import.meta.dir; // => "/path/to/project" -import.meta.file; // => "file.ts" -import.meta.path; // => "/path/to/project/file.ts" -import.meta.url; // => "file:///path/to/project/file.ts" - -import.meta.main; // `true` if this file is directly executed by `bun run` - // `false` otherwise - -import.meta.resolve("zod"); // => "file:///path/to/project/node_modules/zod/index.js" -``` - -{% table %} - ---- - -- `import.meta.dir` -- Absolute path to the directory containing the current file, e.g. `/path/to/project`. Equivalent to `__dirname` in CommonJS modules (and Node.js) - ---- - -- `import.meta.dirname` -- An alias to `import.meta.dir`, for Node.js compatibility - ---- - -- `import.meta.env` -- An alias to `process.env`. - ---- - -- `import.meta.file` -- The name of the current file, e.g. `index.tsx` - ---- - -- `import.meta.path` -- Absolute path to the current file, e.g. `/path/to/project/index.ts`. Equivalent to `__filename` in CommonJS modules (and Node.js) - ---- - -- `import.meta.filename` -- An alias to `import.meta.path`, for Node.js compatibility - ---- - -- `import.meta.main` -- Indicates whether the current file is the entrypoint to the current `bun` process. Is the file being directly executed by `bun run` or is it being imported? - ---- - -- `import.meta.resolve` -- Resolve a module specifier (e.g. `"zod"` or `"./file.tsx"`) to a url. Equivalent to [`import.meta.resolve` in browsers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta#resolve) - - ```ts - import.meta.resolve("zod"); - // => "file:///path/to/project/node_modules/zod/index.ts" - ``` - ---- - -- `import.meta.url` -- A `string` url to the current file, e.g. `file:///path/to/project/index.ts`. Equivalent to [`import.meta.url` in browsers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta#url) - -{% /table %} diff --git a/docs/benchmarks.md b/docs/benchmarks.md deleted file mode 100644 index fd80513aef..0000000000 --- a/docs/benchmarks.md +++ /dev/null @@ -1,120 +0,0 @@ -Bun.js focuses on performance, developer experience, and compatibility with the JavaScript ecosystem. - -## HTTP Requests - -```ts -// http.ts -export default { - port: 3000, - fetch(request: Request) { - return new Response("Hello World"); - }, -}; - -// bun ./http.ts -``` - -| Requests per second | OS | CPU | Bun version | -| ---------------------------------------------------------------------- | ----- | ------------------------------ | ----------- | -| [260,000](https://twitter.com/jarredsumner/status/1512040623200616449) | macOS | Apple Silicon M1 Max | 0.0.76 | -| [160,000](https://twitter.com/jarredsumner/status/1511988933587976192) | Linux | AMD Ryzen 5 3600 6-Core 2.2ghz | 0.0.76 | - -{% details summary="See benchmark details" %} -Measured with [`http_load_test`](https://github.com/uNetworking/uSockets/blob/master/examples/http_load_test.c) by running: - -```bash -$ ./http_load_test 20 127.0.0.1 3000 -``` - -{% /details %} - -## File System - -`cat` clone that runs [2x faster than GNU cat](https://twitter.com/jarredsumner/status/1511707890708586496) for large files on Linux - -```js -// cat.js -import { resolve } from "path"; -import { write, stdout, file, argv } from "bun"; - -const path = resolve(argv.at(-1)); - -await write( - // stdout is a Blob - stdout, - // file(path) returns a Blob - https://developer.mozilla.org/en-US/docs/Web/API/Blob - file(path), -); -``` - -Run this with `bun cat.js /path/to/big/file`. - -## Reading from standard input - -```ts -for await (const line of console) { - // line of text from stdin - console.log(line); -} -``` - -## React SSR - -```js -import { renderToReadableStream } from "react-dom/server"; - -const dt = new Intl.DateTimeFormat(); - -export default { - port: 3000, - async fetch(request: Request) { - return new Response( - await renderToReadableStream( - - - Hello World - - -

Hello from React!

-

The date is {dt.format(new Date())}

- - , - ), - ); - }, -}; -``` - -Write to stdout with `console.write`: - -```js -// no trailing newline -// works with strings and typed arrays -console.write("Hello World!"); -``` - -There are some more examples in the [examples](./examples) folder. - -PRs adding more examples are very welcome! - -## Fast paths for Web APIs - -Bun.js has fast paths for common use cases that make Web APIs live up to the performance demands of servers and CLIs. - -`Bun.file(path)` returns a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) that represents a lazily-loaded file. - -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); -``` - -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)). - -`Bun.write` also supports [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects. It automatically converts to a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob). - -```js -// Eventually, this will stream the response to disk but today it buffers -await Bun.write("index.html", await fetch("https://example.com")); -``` diff --git a/docs/bun-flavored-toml.md b/docs/bun-flavored-toml.md deleted file mode 100644 index ab7beb5920..0000000000 --- a/docs/bun-flavored-toml.md +++ /dev/null @@ -1,42 +0,0 @@ -[TOML](https://toml.io/) is a minimal configuration file format designed to be easy for humans to read. - -Bun implements a TOML parser with a few tweaks designed for better interoperability with INI files and with JavaScript. - -### ; and # are comments - -In Bun-flavored TOML, comments start with `#` or `;` - -```ini -# This is a comment -; This is also a comment -``` - -This matches the behavior of INI files. - -In TOML, comments start with `#` - -```toml -# This is a comment -``` - -### String escape characters - -Bun-flavored adds a few more escape sequences to TOML to work better with JavaScript strings. - -``` -# Bun-flavored TOML extras -\x{XX} - ASCII (U+00XX) -\u{x+} - unicode (U+0000000X) - (U+XXXXXXXX) -\v - vertical tab - -# Regular TOML -\b - backspace (U+0008) -\t - tab (U+0009) -\n - linefeed (U+000A) -\f - form feed (U+000C) -\r - carriage return (U+000D) -\" - quote (U+0022) -\\ - backslash (U+005C) -\uXXXX - unicode (U+XXXX) -\UXXXXXXXX - unicode (U+XXXXXXXX) -``` diff --git a/docs/bundler/bytecode.mdx b/docs/bundler/bytecode.mdx new file mode 100644 index 0000000000..748d81eebd --- /dev/null +++ b/docs/bundler/bytecode.mdx @@ -0,0 +1,465 @@ +--- +title: Bytecode Caching +description: Speed up JavaScript execution with bytecode caching in Bun's bundler +--- + +Bytecode caching is a build-time optimization that dramatically improves application startup time by pre-compiling your JavaScript to bytecode. For example, when compiling TypeScript's `tsc` with bytecode enabled, startup time improves by **2x**. + +## Usage + +### Basic usage + +Enable bytecode caching with the `--bytecode` flag: + +```bash terminal icon="terminal" +bun build ./index.ts --target=bun --bytecode --outdir=./dist +``` + +This generates two files: + +- `dist/index.js` - Your bundled JavaScript +- `dist/index.jsc` - The bytecode cache file + +At runtime, Bun automatically detects and uses the `.jsc` file: + +```bash terminal icon="terminal" +bun ./dist/index.js # Automatically uses index.jsc +``` + +### With standalone executables + +When creating executables with `--compile`, bytecode is embedded into the binary: + +```bash terminal icon="terminal" +bun build ./cli.ts --compile --bytecode --outfile=mycli +``` + +The resulting executable contains both the code and bytecode, giving you maximum performance in a single file. + +### Combining with other optimizations + +Bytecode works great with minification and source maps: + +```bash terminal icon="terminal" +bun build --compile --bytecode --minify --sourcemap ./cli.ts --outfile=mycli +``` + +- `--minify` reduces code size before generating bytecode (less code -> less bytecode) +- `--sourcemap` preserves error reporting (errors still point to original source) +- `--bytecode` eliminates parsing overhead + +## Performance impact + +The performance improvement scales with your codebase size: + +| Application size | Typical startup improvement | +| ------------------------- | --------------------------- | +| Small CLI (< 100 KB) | 1.5-2x faster | +| Medium-large app (> 5 MB) | 2.5x-4x faster | + +Larger applications benefit more because they have more code to parse. + +## When to use bytecode + +### Great for: + +#### CLI tools + +- Invoked frequently (linters, formatters, git hooks) +- Startup time is the entire user experience +- Users notice the difference between 90ms and 45ms startup +- Example: TypeScript compiler, Prettier, ESLint + +#### Build tools and task runners + +- Run hundreds or thousands of times during development +- Milliseconds saved per run compound quickly +- Developer experience improvement +- Example: Build scripts, test runners, code generators + +#### Standalone executables + +- Distributed to users who care about snappy performance +- Single-file distribution is convenient +- File size less important than startup time +- Example: CLIs distributed via npm or as binaries + +### Skip it for: + +- ❌ **Small scripts** +- ❌ **Code that runs once** +- ❌ **Development builds** +- ❌ **Size-constrained environments** +- ❌ **Code with top-level await** (not supported) + +## Limitations + +### CommonJS only + +Bytecode caching currently works with CommonJS output format. Bun's bundler automatically converts most ESM code to CommonJS, but **top-level await** is the exception: + +```js +// This prevents bytecode caching +const data = await fetch("https://api.example.com"); +export default data; +``` + +**Why**: Top-level await requires async module evaluation, which can't be represented in CommonJS. The module graph becomes asynchronous, and the CommonJS wrapper function model breaks down. + +**Workaround**: Move async initialization into a function: + +```js +async function init() { + const data = await fetch("https://api.example.com"); + return data; +} + +export default init; +``` + +Now the module exports a function that the consumer can await when needed. + +### Version compatibility + +Bytecode is **not portable across Bun versions**. The bytecode format is tied to JavaScriptCore's internal representation, which changes between versions. + +When you update Bun, you must regenerate bytecode: + +```bash terminal icon="terminal" +# After updating Bun +bun build --bytecode ./index.ts --outdir=./dist +``` + +If bytecode doesn't match the current Bun version, it's automatically ignored and your code falls back to parsing the JavaScript source. Your app still runs - you just lose the performance optimization. + +**Best practice**: Generate bytecode as part of your CI/CD build process. Don't commit `.jsc` files to git. Regenerate them whenever you update Bun. + +### Source code still required + +- The `.js` file (your bundled source code) +- The `.jsc` file (the bytecode cache) + +At runtime: + +1. Bun loads the `.js` file, sees a `@bytecode` pragma, and checks the `.jsc` file +2. Bun loads the `.jsc` file +3. Bun validates the bytecode hash matches the source +4. If valid, Bun uses the bytecode +5. If invalid, Bun falls back to parsing the source + +### Bytecode is not obfuscation + +Bytecode **does not obscure your source code**. It's an optimization, not a security measure. + +## Production deployment + +### Docker + +Include bytecode generation in your Dockerfile: + +```dockerfile Dockerfile icon="docker" +FROM oven/bun:1 AS builder +WORKDIR /app +COPY package.json bun.lock ./ +RUN bun install --frozen-lockfile + +COPY . . +RUN bun build --bytecode --minify --sourcemap \ + --target=bun \ + --outdir=./dist \ + --compile \ + ./src/server.ts --outfile=./dist/server + +FROM oven/bun:1 AS runner +WORKDIR /app +COPY --from=builder /dist/server /app/server +CMD ["./server"] +``` + +The bytecode is architecture-independent. + +### CI/CD + +Generate bytecode during your build pipeline: + +```yaml workflow.yml icon="file-code" +# GitHub Actions +- name: Build with bytecode + run: | + bun install + bun build --bytecode --minify \ + --outdir=./dist \ + --target=bun \ + ./src/index.ts +``` + +## Debugging + +### Verify bytecode is being used + +Check that the `.jsc` file exists: + +```bash terminal icon="terminal" +ls -lh dist/ +``` + +```txt +-rw-r--r-- 1 user staff 245K index.js +-rw-r--r-- 1 user staff 1.1M index.jsc +``` + +The `.jsc` file should be 2-8x larger than the `.js` file. + +To log if bytecode is being used, set `BUN_JSC_verboseDiskCache=1` in your environment. + +On success, it will log something like: + +```txt +[Disk cache] cache hit for sourceCode +``` + +If you see a cache miss, it will log something like: + +```txt +[Disk cache] cache miss for sourceCode +``` + +It's normal for it it to log a cache miss multiple times since Bun doesn't currently bytecode cache JavaScript code used in builtin modules. + +### Common issues + +**Bytecode silently ignored**: Usually caused by a Bun version update. The cache version doesn't match, so bytecode is rejected. Regenerate to fix. + +**File size too large**: This is expected. Consider: + +- Using `--minify` to reduce code size before bytecode generation +- Compressing `.jsc` files for network transfer (gzip/brotli) +- Evaluating if the startup performance gain is worth the size increase + +**Top-level await**: Not supported. Refactor to use async initialization functions. + +## What is bytecode? + +When you run JavaScript, the JavaScript engine doesn't execute your source code directly. Instead, it goes through several steps: + +1. **Parsing**: The engine reads your JavaScript source code and converts it into an Abstract Syntax Tree (AST) +2. **Bytecode compilation**: The AST is compiled into bytecode - a lower-level representation that's faster to execute +3. **Execution**: The bytecode is executed by the engine's interpreter or JIT compiler + +Bytecode is an intermediate representation - it's lower-level than JavaScript source code, but higher-level than machine code. Think of it as assembly language for a virtual machine. Each bytecode instruction represents a single operation like "load this variable," "add two numbers," or "call this function." + +This happens **every single time** you run your code. If you have a CLI tool that runs 100 times a day, your code gets parsed 100 times. If you have a serverless function with frequent cold starts, parsing happens on every cold start. + +With bytecode caching, Bun moves steps 1 and 2 to the build step. At runtime, the engine loads the pre-compiled bytecode and jumps straight to execution. + +### Why lazy parsing makes this even better + +Modern JavaScript engines use a clever optimization called **lazy parsing**. They don't parse all your code upfront - instead, functions are only parsed when they're first called: + +```js +// Without bytecode caching: +function rarely_used() { + // This 500-line function is only parsed + // when it's actually called +} + +function main() { + console.log("Starting app"); + // rarely_used() is never called, so it's never parsed +} +``` + +This means parsing overhead isn't just a startup cost - it happens throughout your application's lifetime as different code paths execute. With bytecode caching, **all functions are pre-compiled**, even the ones that are lazily parsed. The parsing work happens once at build time instead of being distributed throughout your application's execution. + +## The bytecode format + +### Inside a .jsc file + +A `.jsc` file contains a serialized bytecode structure. Understanding what's inside helps explain both the performance benefits and the file size tradeoff. + +**Header section** (validated on every load): + +- **Cache version**: A hash tied to the JavaScriptCore framework version. This ensures bytecode generated with one version of Bun only runs with that exact version. +- **Code block type tag**: Identifies whether this is a Program, Module, Eval, or Function code block. + +**SourceCodeKey** (validates bytecode matches source): + +- **Source code hash**: A hash of the original JavaScript source code. Bun verifies this matches before using the bytecode. +- **Source code length**: The exact length of the source, for additional validation. +- **Compilation flags**: Critical compilation context like strict mode, whether it's a script vs module, eval context type, etc. The same source code compiled with different flags produces different bytecode. + +**Bytecode instructions**: + +- **Instruction stream**: The actual bytecode opcodes - the compiled representation of your JavaScript. This is a variable-length sequence of bytecode instructions. +- **Metadata table**: Each opcode has associated metadata - things like profiling counters, type hints, and execution counts (even if not yet populated). +- **Jump targets**: Pre-computed addresses for control flow (if/else, loops, switch statements). +- **Switch tables**: Optimized lookup tables for switch statements. + +**Constants and identifiers**: + +- **Constant pool**: All literal values in your code - numbers, strings, booleans, null, undefined. These are stored as actual JavaScript values (JSValues) so they don't need to be parsed from source at runtime. +- **Identifier table**: All variable and function names used in the code. Stored as deduplicated strings. +- **Source code representation markers**: Flags indicating how constants should be represented (as integers, doubles, big ints, etc.). + +**Function metadata** (for each function in your code): + +- **Register allocation**: How many registers (local variables) the function needs - `thisRegister`, `scopeRegister`, `numVars`, `numCalleeLocals`, `numParameters`. +- **Code features**: A bitmask of function characteristics: is it a constructor? an arrow function? does it use `super`? does it have tail calls? These affect how the function is executed. +- **Lexically scoped features**: Strict mode and other lexical context. +- **Parse mode**: The mode in which the function was parsed (normal, async, generator, async generator). + +**Nested structures**: + +- **Function declarations and expressions**: Each nested function gets its own bytecode block, recursively. A file with 100 functions has 100 separate bytecode blocks, all nested in the structure. +- **Exception handlers**: Try/catch/finally blocks with their boundaries and handler addresses pre-computed. +- **Expression info**: Maps bytecode positions back to source code locations for error reporting and debugging. + +### What bytecode does NOT contain + +Importantly, **bytecode does not embed your source code**. Instead: + +- The JavaScript source is stored separately (in the `.js` file) +- The bytecode only stores a hash and length of the source +- At load time, Bun validates the bytecode matches the current source code + +This is why you need to deploy both the `.js` and `.jsc` files. The `.jsc` file is useless without its corresponding `.js` file. + +## The tradeoff: file size + +Bytecode files are significantly larger than source code - typically 2-8x larger. + +### Why is bytecode so much larger? + +**Bytecode instructions are verbose**: +A single line of minified JavaScript might compile to dozens of bytecode instructions. For example: + +```js +const sum = arr.reduce((a, b) => a + b, 0); +``` + +Compiles to bytecode that: + +- Loads the `arr` variable +- Gets the `reduce` property +- Creates the arrow function (which itself has bytecode) +- Loads the initial value `0` +- Sets up the call with the right number of arguments +- Actually performs the call +- Stores the result in `sum` + +Each of these steps is a separate bytecode instruction with its own metadata. + +**Constant pools store everything**: +Every string literal, number, property name - everything gets stored in the constant pool. Even if your source code has `"hello"` a hundred times, the constant pool stores it once, but the identifier table and constant references add overhead. + +**Per-function metadata**: +Each function - even small one-line functions - gets its own complete metadata: + +- Register allocation info +- Code features bitmask +- Parse mode +- Exception handlers +- Expression info for debugging + +A file with 1,000 small functions has 1,000 sets of metadata. + +**Profiling data structures**: +Even though profiling data isn't populated yet, the _structures_ to hold profiling data are allocated. This includes: + +- Value profile slots (tracking what types flow through each operation) +- Array profile slots (tracking array access patterns) +- Binary arithmetic profile slots (tracking number types in math operations) +- Unary arithmetic profile slots + +These take up space even when empty. + +**Pre-computed control flow**: +Jump targets, switch tables, and exception handler boundaries are all pre-computed and stored. This makes execution faster but increases file size. + +### Mitigation strategies + +**Compression**: +Bytecode compresses extremely well with gzip/brotli (60-70% compression). The repetitive structure and metadata compress efficiently. + +**Minification first**: +Using `--minify` before bytecode generation helps: + +- Shorter identifiers → smaller identifier table +- Dead code elimination → less bytecode generated +- Constant folding → fewer constants in the pool + +**The tradeoff**: +You're trading 2-4x larger files for 2-4x faster startup. For CLIs, this is usually worth it. For long-running servers where a few megabytes of disk space don't matter, it's even less of an issue. + +## Versioning and portability + +### Cross-architecture portability: ✅ + +Bytecode is **architecture-independent**. You can: + +- Build on macOS ARM64, deploy to Linux x64 +- Build on Linux x64, deploy to AWS Lambda ARM64 +- Build on Windows x64, deploy to macOS ARM64 + +The bytecode contains abstract instructions that work on any architecture. Architecture-specific optimizations happen during JIT compilation at runtime, not in the cached bytecode. + +### Cross-version portability: ❌ + +Bytecode is **not stable across Bun versions**. Here's why: + +**Bytecode format changes**: +JavaScriptCore's bytecode format evolves. New opcodes get added, old ones get removed or changed, metadata structures change. Each version of JavaScriptCore has a different bytecode format. + +**Version validation**: +The cache version in the `.jsc` file header is a hash of the JavaScriptCore framework. When Bun loads bytecode: + +1. It extracts the cache version from the `.jsc` file +2. It computes the current JavaScriptCore version +3. If they don't match, the bytecode is **silently rejected** +4. Bun falls back to parsing the `.js` source code + +Your application still runs - you just lose the performance optimization. + +**Graceful degradation**: +This design means bytecode caching "fails open" - if anything goes wrong (version mismatch, corrupted file, missing file), your code still runs normally. You might see slower startup, but you won't see errors. + +## Unlinked vs. linked bytecode + +JavaScriptCore makes a crucial distinction between "unlinked" and "linked" bytecode. This separation is what makes bytecode caching possible: + +### Unlinked bytecode (what's cached) + +The bytecode saved in `.jsc` files is **unlinked bytecode**. It contains: + +- The compiled bytecode instructions +- Structural information about the code +- Constants and identifiers +- Control flow information + +But it **doesn't** contain: + +- Pointers to actual runtime objects +- JIT-compiled machine code +- Profiling data from previous runs +- Call link information (which functions call which) + +Unlinked bytecode is **immutable and shareable**. Multiple executions of the same code can all reference the same unlinked bytecode. + +### Linked bytecode (runtime execution) + +When Bun runs bytecode, it "links" it - creating a runtime wrapper that adds: + +- **Call link information**: As your code runs, the engine learns which functions call which and optimizes those call sites. +- **Profiling data**: The engine tracks how many times each instruction executes, what types of values flow through the code, array access patterns, etc. +- **JIT compilation state**: References to baseline JIT or optimizing JIT (DFG/FTL) compiled versions of hot code. +- **Runtime objects**: Pointers to actual JavaScript objects, prototypes, scopes, etc. + +This linked representation is created fresh every time you run your code. This allows: + +1. **Caching the expensive work** (parsing and compilation to unlinked bytecode) +2. **Still collecting runtime profiling data** to guide optimizations +3. **Still applying JIT optimizations** based on actual execution patterns + +Bytecode caching moves expensive work (parsing and compiling to bytecode) from runtime to build time. For applications that start frequently, this can halve your startup time at the cost of larger files on disk. + +For production CLIs and serverless deployments, the combination of `--bytecode --minify --sourcemap` gives you the best performance while maintaining debuggability. diff --git a/docs/bundler/css.md b/docs/bundler/css.mdx similarity index 86% rename from docs/bundler/css.md rename to docs/bundler/css.mdx index 6bb0fa5b6d..628a9fa4b8 100644 --- a/docs/bundler/css.md +++ b/docs/bundler/css.mdx @@ -1,3 +1,8 @@ +--- +title: CSS +description: Bun's bundler has built-in support for CSS with modern features +--- + Bun's bundler has built-in support for CSS with the following features: - Transpiling modern/future features to work on all browsers (including vendor prefixing) @@ -9,11 +14,11 @@ Bun's bundler has built-in support for CSS with the following features: Bun's CSS bundler lets you use modern/future CSS features without having to worry about browser compatibility — all thanks to its transpiling and vendor prefixing features which are enabled by default. -Bun's CSS parser and bundler is a direct Rust → Zig port of [LightningCSS](https://lightningcss.dev/), with a bundling approach inspired by esbuild. The transpiler converts modern CSS syntax into backwards-compatible equivalents that work across browsers. +Bun's CSS parser and bundler is a direct Rust → Zig port of LightningCSS, with a bundling approach inspired by esbuild. The transpiler converts modern CSS syntax into backwards-compatible equivalents that work across browsers. -A huge thanks goes to the amazing work from the authors of [LightningCSS](https://lightningcss.dev/) and [esbuild](https://esbuild.github.io/). +A huge thanks goes to the amazing work from the authors of LightningCSS and esbuild. -### Browser Compatibility +## Browser Compatibility By default, Bun's CSS bundler targets the following browsers: @@ -23,13 +28,13 @@ By default, Bun's CSS bundler targets the following browsers: - Chrome 87+ - Safari 14+ -### Syntax Lowering +## Syntax Lowering -#### Nesting +### Nesting The CSS Nesting specification allows you to write more concise and intuitive stylesheets by nesting selectors inside one another. Instead of repeating parent selectors across your CSS file, you can write child styles directly within their parent blocks. -```css +```css title="styles.css" icon="file-code" /* With nesting */ .card { background: white; @@ -48,7 +53,7 @@ The CSS Nesting specification allows you to write more concise and intuitive sty Bun's CSS bundler automatically converts this nested syntax into traditional flat CSS that works in all browsers: -```css +```css title="styles.css" icon="file-code" /* Compiled output */ .card { background: white; @@ -67,7 +72,7 @@ Bun's CSS bundler automatically converts this nested syntax into traditional fla You can also nest media queries and other at-rules inside selectors, eliminating the need to repeat selector patterns: -```css +```css title="styles.css" icon="file-code" .responsive-element { display: block; @@ -79,7 +84,7 @@ You can also nest media queries and other at-rules inside selectors, eliminating This compiles to: -```css +```css title="styles.css" icon="file-code" .responsive-element { display: block; } @@ -91,11 +96,11 @@ This compiles to: } ``` -#### Color mix +### Color mix The `color-mix()` function gives you an easy way to blend two colors together according to a specified ratio in a chosen color space. This powerful feature lets you create color variations without manually calculating the resulting values. -```css +```css title="styles.css" icon="file-code" .button { /* Mix blue and red in the RGB color space with a 30/70 proportion */ background-color: color-mix(in srgb, blue 30%, red); @@ -109,7 +114,7 @@ The `color-mix()` function gives you an easy way to blend two colors together ac Bun's CSS bundler evaluates these color mixes at build time when all color values are known (not CSS variables), generating static color values that work in all browsers: -```css +```css title="styles.css" icon="file-code" .button { /* Computed to the exact resulting color */ background-color: #b31a1a; @@ -122,11 +127,11 @@ Bun's CSS bundler evaluates these color mixes at build time when all color value This feature is particularly useful for creating color systems with programmatically derived shades, tints, and accents without needing preprocessors or custom tooling. -#### Relative colors +### Relative colors CSS now allows you to modify individual components of a color using relative color syntax. This powerful feature lets you create color variations by adjusting specific attributes like lightness, saturation, or individual channels without having to recalculate the entire color. -```css +```css title="styles.css" icon="file-code" .theme-color { /* Start with a base color and increase lightness by 15% */ --accent: lch(from purple calc(l + 15%) c h); @@ -147,27 +152,23 @@ Bun's CSS bundler computes these relative color modifications at build time (whe This approach is extremely useful for theme generation, creating accessible color variants, or building color scales based on mathematical relationships instead of hard-coding each value. -#### LAB colors +### LAB colors Modern CSS supports perceptually uniform color spaces like LAB, LCH, OKLAB, and OKLCH that offer significant advantages over traditional RGB. These color spaces can represent colors outside the standard RGB gamut, resulting in more vibrant and visually consistent designs. -```css +```css title="styles.css" icon="file-code" .vibrant-element { /* A vibrant red that exceeds sRGB gamut boundaries */ color: lab(55% 78 35); /* A smooth gradient using perceptual color space */ - background: linear-gradient( - to right, - oklch(65% 0.25 10deg), - oklch(65% 0.25 250deg) - ); + background: linear-gradient(to right, oklch(65% 0.25 10deg), oklch(65% 0.25 250deg)); } ``` Bun's CSS bundler automatically converts these advanced color formats to backwards-compatible alternatives for browsers that don't yet support them: -```css +```css title="styles.css" icon="file-code" .vibrant-element { /* Fallback to closest RGB approximation */ color: #ff0f52; @@ -177,21 +178,17 @@ Bun's CSS bundler automatically converts these advanced color formats to backwar color: lab(55% 78 35); background: linear-gradient(to right, #cd4e15, #3887ab); - background: linear-gradient( - to right, - oklch(65% 0.25 10deg), - oklch(65% 0.25 250deg) - ); + background: linear-gradient(to right, oklch(65% 0.25 10deg), oklch(65% 0.25 250deg)); } ``` This layered approach ensures optimal color rendering across all browsers while allowing you to use the latest color technologies in your designs. -#### Color function +### Color function The `color()` function provides a standardized way to specify colors in various predefined color spaces, expanding your design options beyond the traditional RGB space. This allows you to access wider color gamuts and create more vibrant designs. -```css +```css title="styles.css" icon="file-code" .vivid-element { /* Using the Display P3 color space for wider gamut colors */ color: color(display-p3 1 0.1 0.3); @@ -203,7 +200,7 @@ The `color()` function provides a standardized way to specify colors in various For browsers that don't support these advanced color functions yet, Bun's CSS bundler provides appropriate RGB fallbacks: -```css +```css title="styles.css" icon="file-code" .vivid-element { /* RGB fallback first for maximum compatibility */ color: #fa1a4c; @@ -217,11 +214,11 @@ For browsers that don't support these advanced color functions yet, Bun's CSS bu This functionality lets you use modern color spaces immediately while ensuring your designs remain functional across all browsers, with optimal colors displayed in supporting browsers and reasonable approximations elsewhere. -#### HWB colors +### HWB colors The HWB (Hue, Whiteness, Blackness) color model provides an intuitive way to express colors based on how much white or black is mixed with a pure hue. Many designers find this approach more natural for creating color variations compared to manipulating RGB or HSL values. -```css +```css title="styles.css" icon="file-code" .easy-theming { /* Pure cyan with no white or black added */ --primary: hwb(180 0% 0%); @@ -239,7 +236,7 @@ The HWB (Hue, Whiteness, Blackness) color model provides an intuitive way to exp Bun's CSS bundler automatically converts HWB colors to RGB for compatibility with all browsers: -```css +```css title="styles.css" icon="file-code" .easy-theming { --primary: #00ffff; --primary-light: #33ffff; @@ -250,11 +247,11 @@ Bun's CSS bundler automatically converts HWB colors to RGB for compatibility wit The HWB model makes it particularly easy to create systematic color variations for design systems, providing a more intuitive approach to creating consistent tints and shades than working directly with RGB or HSL values. -#### Color notation +### Color notation Modern CSS has introduced more intuitive and concise ways to express colors. Space-separated color syntax eliminates the need for commas in RGB and HSL values, while hex colors with alpha channels provide a compact way to specify transparency. -```css +```css title="styles.css" icon="file-code" .modern-styling { /* Space-separated RGB notation (no commas) */ color: rgb(50 100 200); @@ -272,7 +269,7 @@ Modern CSS has introduced more intuitive and concise ways to express colors. Spa Bun's CSS bundler automatically converts these modern color formats to ensure compatibility with older browsers: -```css +```css title="styles.css" icon="file-code" .modern-styling { /* Converted to comma format for older browsers */ color: rgb(50, 100, 200); @@ -289,11 +286,11 @@ Bun's CSS bundler automatically converts these modern color formats to ensure co This conversion process lets you write cleaner, more modern CSS while ensuring your styles work correctly across all browsers. -#### light-dark() color function +### light-dark() color function The `light-dark()` function provides an elegant solution for implementing color schemes that respect the user's system preference without requiring complex media queries. This function accepts two color values and automatically selects the appropriate one based on the current color scheme context. -```css +```css title="styles.css" icon="file-code" :root { /* Define color scheme support */ color-scheme: light dark; @@ -318,7 +315,7 @@ The `light-dark()` function provides an elegant solution for implementing color For browsers that don't support this feature yet, Bun's CSS bundler converts it to use CSS variables with proper fallbacks: -```css +```css title="styles.css" icon="file-code" :root { --lightningcss-light: initial; --lightningcss-dark: ; @@ -345,21 +342,19 @@ For browsers that don't support this feature yet, Bun's CSS bundler converts it } .themed-component { - background-color: var(--lightningcss-light, #ffffff) - var(--lightningcss-dark, #121212); + background-color: var(--lightningcss-light, #ffffff) var(--lightningcss-dark, #121212); color: var(--lightningcss-light, #333333) var(--lightningcss-dark, #eeeeee); - border-color: var(--lightningcss-light, #dddddd) - var(--lightningcss-dark, #555555); + border-color: var(--lightningcss-light, #dddddd) var(--lightningcss-dark, #555555); } ``` This approach gives you a clean way to handle light and dark themes without duplicating styles or writing complex media queries, while maintaining compatibility with browsers that don't yet support the feature natively. -#### Logical properties +### Logical properties CSS logical properties let you define layout, spacing, and sizing relative to the document's writing mode and text direction rather than physical screen directions. This is crucial for creating truly international layouts that automatically adapt to different writing systems. -```css +```css title="styles.css" icon="file-code" .multilingual-component { /* Margin that adapts to writing direction */ margin-inline-start: 1rem; @@ -378,7 +373,7 @@ CSS logical properties let you define layout, spacing, and sizing relative to th For browsers that don't fully support logical properties, Bun's CSS bundler compiles them to physical properties with appropriate directional adjustments: -```css +```css title="styles.css" icon="file-code" /* For left-to-right languages */ .multilingual-component:dir(ltr) { margin-left: 1rem; @@ -402,11 +397,11 @@ For browsers that don't fully support logical properties, Bun's CSS bundler comp If the `:dir()` selector isn't supported, additional fallbacks are automatically generated to ensure your layouts work properly across all browsers and writing systems. This makes creating internationalized designs much simpler while maintaining compatibility with older browsers. -#### :dir() selector +### :dir() selector The `:dir()` pseudo-class selector allows you to style elements based on their text direction (RTL or LTR), providing a powerful way to create direction-aware designs without JavaScript. This selector matches elements based on their directionality as determined by the document or explicit direction attributes. -```css +```css title="styles.css" icon="file-code" /* Apply different styles based on text direction */ .nav-arrow:dir(ltr) { transform: rotate(0deg); @@ -428,7 +423,7 @@ The `:dir()` pseudo-class selector allows you to style elements based on their t For browsers that don't support the `:dir()` selector yet, Bun's CSS bundler converts it to the more widely supported `:lang()` selector with appropriate language mappings: -```css +```css title="styles.css" icon="file-code" /* Converted to use language-based selectors as fallback */ .nav-arrow:lang(en, fr, de, es, it, pt, nl) { transform: rotate(0deg); @@ -449,11 +444,11 @@ For browsers that don't support the `:dir()` selector yet, Bun's CSS bundler con This conversion lets you write direction-aware CSS that works reliably across browsers, even those that don't yet support the `:dir()` selector natively. If multiple arguments to `:lang()` aren't supported, further fallbacks are automatically provided. -#### :lang() selector +### :lang() selector The `:lang()` pseudo-class selector allows you to target elements based on the language they're in, making it easy to apply language-specific styling. Modern CSS allows the `:lang()` selector to accept multiple language codes, letting you group language-specific rules more efficiently. -```css +```css title="styles.css" icon="file-code" /* Typography adjustments for CJK languages */ :lang(zh, ja, ko) { line-height: 1.8; @@ -472,7 +467,7 @@ blockquote:lang(de, nl, da, sv) { For browsers that don't support multiple arguments in the `:lang()` selector, Bun's CSS bundler converts this syntax to use the `:is()` selector to maintain the same behavior: -```css +```css title="styles.css" icon="file-code" /* Multiple languages grouped with :is() for better browser support */ :is(:lang(zh), :lang(ja), :lang(ko)) { line-height: 1.8; @@ -490,11 +485,11 @@ blockquote:is(:lang(de), :lang(nl), :lang(da), :lang(sv)) { If needed, Bun can provide additional fallbacks for `:is()` as well, ensuring your language-specific styles work across all browsers. This approach simplifies creating internationalized designs with distinct typographic and styling rules for different language groups. -#### :is() selector +### :is() selector The `:is()` pseudo-class function (formerly `:matches()`) allows you to create more concise and readable selectors by grouping multiple selectors together. It accepts a selector list as its argument and matches if any of the selectors in that list match, significantly reducing repetition in your CSS. -```css +```css title="styles.css" icon="file-code" /* Instead of writing these separately */ /* .article h1, @@ -547,13 +542,17 @@ For browsers that don't support `:is()`, Bun's CSS bundler provides fallbacks us } ``` -It's worth noting that the vendor-prefixed versions have some limitations compared to the standardized `:is()` selector, particularly with complex selectors. Bun handles these limitations intelligently, only using prefixed versions when they'll work correctly. + + The vendor-prefixed versions have some limitations compared to the standardized `:is()` selector, particularly with + complex selectors. Bun handles these limitations intelligently, only using prefixed versions when they'll work + correctly. + -#### :not() selector +### :not() selector The `:not()` pseudo-class allows you to exclude elements that match a specific selector. The modern version of this selector accepts multiple arguments, letting you exclude multiple patterns with a single, concise selector. -```css +```css title="styles.css" icon="file-code" /* Select all buttons except primary and secondary variants */ button:not(.primary, .secondary) { background-color: #f5f5f5; @@ -568,7 +567,7 @@ h2:not(.sidebar *, footer *) { For browsers that don't support multiple arguments in `:not()`, Bun's CSS bundler converts this syntax to a more compatible form while preserving the same behavior: -```css +```css title="styles.css" icon="file-code" /* Converted to use :not with :is() for compatibility */ button:not(:is(.primary, .secondary)) { background-color: #f5f5f5; @@ -582,7 +581,7 @@ h2:not(:is(.sidebar *, footer *)) { And if `:is()` isn't supported, Bun can generate further fallbacks: -```css +```css title="styles.css" icon="file-code" /* Even more fallbacks for maximum compatibility */ button:not(:-webkit-any(.primary, .secondary)) { background-color: #f5f5f5; @@ -602,11 +601,11 @@ button:not(:is(.primary, .secondary)) { This conversion ensures your negative selectors work correctly across all browsers while maintaining the correct specificity and behavior of the original selector. -#### Math functions +### Math functions CSS now includes a rich set of mathematical functions that let you perform complex calculations directly in your stylesheets. These include standard math functions (`round()`, `mod()`, `rem()`, `abs()`, `sign()`), trigonometric functions (`sin()`, `cos()`, `tan()`, `asin()`, `acos()`, `atan()`, `atan2()`), and exponential functions (`pow()`, `sqrt()`, `exp()`, `log()`, `hypot()`). -```css +```css title="styles.css" icon="file-code" .dynamic-sizing { /* Clamp a value between minimum and maximum */ width: clamp(200px, 50%, 800px); @@ -625,7 +624,7 @@ CSS now includes a rich set of mathematical functions that let you perform compl Bun's CSS bundler evaluates these mathematical expressions at build time when all values are known constants (not variables), resulting in optimized output: -```css +```css title="styles.css" icon="file-code" .dynamic-sizing { width: clamp(200px, 50%, 800px); padding: 15px; @@ -637,11 +636,11 @@ Bun's CSS bundler evaluates these mathematical expressions at build time when al This approach lets you write more expressive and maintainable CSS with meaningful mathematical relationships, which then gets compiled to optimized values for maximum browser compatibility and performance. -#### Media query ranges +### Media query ranges Modern CSS supports intuitive range syntax for media queries, allowing you to specify breakpoints using comparison operators like `<`, `>`, `<=`, and `>=` instead of the more verbose `min-` and `max-` prefixes. This syntax is more readable and matches how we normally think about values and ranges. -```css +```css title="styles.css" icon="file-code" /* Modern syntax with comparison operators */ @media (width >= 768px) { .container { @@ -666,7 +665,7 @@ Modern CSS supports intuitive range syntax for media queries, allowing you to sp Bun's CSS bundler converts these modern range queries to traditional media query syntax for compatibility with all browsers: -```css +```css title="styles.css" icon="file-code" /* Converted to traditional min/max syntax */ @media (min-width: 768px) { .container { @@ -689,11 +688,11 @@ Bun's CSS bundler converts these modern range queries to traditional media query This lets you write more intuitive and mathematical media queries while ensuring your stylesheets work correctly across all browsers, including those that don't support the modern range syntax. -#### Shorthands +### Shorthands CSS has introduced several modern shorthand properties that improve code readability and maintainability. Bun's CSS bundler ensures these convenient shorthands work on all browsers by converting them to their longhand equivalents when needed. -```css +```css title="styles.css" icon="file-code" /* Alignment shorthands */ .flex-container { /* Shorthand for align-items and justify-items */ @@ -729,7 +728,7 @@ CSS has introduced several modern shorthand properties that improve code readabi For browsers that don't support these modern shorthands, Bun converts them to their component longhand properties: -```css +```css title="styles.css" icon="file-code" .flex-container { /* Expanded alignment properties */ align-items: center; @@ -766,11 +765,11 @@ For browsers that don't support these modern shorthands, Bun converts them to th This conversion ensures your stylesheets remain clean and maintainable while providing the broadest possible browser compatibility. -#### Double position gradients +### Double position gradients The double position gradient syntax is a modern CSS feature that allows you to create hard color stops in gradients by specifying the same color at two adjacent positions. This creates a sharp transition rather than a smooth fade, which is useful for creating stripes, color bands, and other multi-color designs. -```css +```css title="styles.css" icon="file-code" .striped-background { /* Creates a sharp transition from green to red at 30%-40% */ background: linear-gradient( @@ -799,7 +798,7 @@ The double position gradient syntax is a modern CSS feature that allows you to c For browsers that don't support this syntax, Bun's CSS bundler automatically converts it to the traditional format by duplicating color stops: -```css +```css title="styles.css" icon="file-code" .striped-background { background: linear-gradient( to right, @@ -830,11 +829,11 @@ For browsers that don't support this syntax, Bun's CSS bundler automatically con This conversion lets you use the cleaner double position syntax in your source code while ensuring gradients display correctly in all browsers. -#### system-ui font +### system-ui font The `system-ui` generic font family lets you use the device's native UI font, creating interfaces that feel more integrated with the operating system. This provides a more native look and feel without having to specify different font stacks for each platform. -```css +```css title="styles.css" icon="file-code" .native-interface { /* Use the system's default UI font */ font-family: system-ui; @@ -848,7 +847,7 @@ The `system-ui` generic font family lets you use the device's native UI font, cr For browsers that don't support `system-ui`, Bun's CSS bundler automatically expands it to a comprehensive cross-platform font stack: -```css +```css title="styles.css" icon="file-code" .native-interface { /* Expanded to support all major platforms */ font-family: @@ -883,7 +882,7 @@ This approach gives you the simplicity of writing just `system-ui` in your sourc ## CSS Modules -Bun's bundler also supports bundling [CSS modules](https://css-tricks.com/css-modules-part-1-need/) in addition to [regular CSS](/docs/bundler/css) with support for the following features: +Bun's bundler also supports bundling CSS modules in addition to regular CSS with support for the following features: - Automatically detecting CSS module files (`.module.css`) with zero configuration - Composition (`composes` property) @@ -894,17 +893,17 @@ A CSS module is a CSS file (with the `.module.css` extension) where are all clas Under the hood, Bun's bundler transforms locally scoped class names into unique identifiers. -## Getting started +### Getting started Create a CSS file with the `.module.css` extension: -```css -/* styles.module.css */ +```css title="styles.module.css" icon="file-code" .button { color: red; } +``` -/* other-styles.module.css */ +```css title="other-styles.module.css" icon="file-code" .button { color: blue; } @@ -912,7 +911,7 @@ Create a CSS file with the `.module.css` extension: You can then import this file, for example into a TSX file: -```tsx +```tsx title="app.tsx" icon="/icons/typescript.svg" import styles from "./styles.module.css"; import otherStyles from "./other-styles.module.css"; @@ -926,10 +925,9 @@ export default function App() { } ``` -The `styles` object from importing the CSS module file will be an object with all class names as keys and -their unique identifiers as values: +The styles object from importing the CSS module file will be an object with all class names as keys and their unique identifiers as values: -```tsx +```ts title="app.tsx" icon="/icons/typescript.svg" import styles from "./styles.module.css"; import otherStyles from "./other-styles.module.css"; @@ -939,7 +937,7 @@ console.log(otherStyles); This will output: -```ts +```ts title="app.tsx" icon="/icons/typescript.svg" { button: "button_123"; } @@ -953,12 +951,11 @@ As you can see, the class names are unique to each file, avoiding any collisions ### Composition -CSS modules allow you to _compose_ class selectors together. This lets you reuse style rules across multiple classes. +CSS modules allow you to compose class selectors together. This lets you reuse style rules across multiple classes. For example: -```css -/* styles.module.css */ +```css title="styles.module.css" icon="file-code" .button { composes: background; color: red; @@ -971,7 +968,7 @@ For example: Would be the same as writing: -```css +```css title="styles.module.css" icon="file-code" .button { background-color: blue; color: red; @@ -982,13 +979,14 @@ Would be the same as writing: } ``` -{% callout %} There are a couple rules to keep in mind when using `composes`: -- A `composes` property must come before any regular CSS properties or declarations -- You can only use `composes` on a **simple selector with a single class name**: + + **Composition Rules:** - A `composes` property must come before any regular CSS properties or declarations - You can + only use `composes` on a simple selector with a single class name + -```css +```css title="styles.module.css" icon="file-code" #button { /* Invalid! `#button` is not a class selector */ composes: background; @@ -1001,28 +999,26 @@ There are a couple rules to keep in mind when using `composes`: } ``` -{% /callout %} - ### Composing from a separate CSS module file You can also compose from a separate CSS module file: -```css -/* background.module.css */ +```css title="background.module.css" icon="file-code" .background { background-color: blue; } +``` -/* styles.module.css */ +```css title="styles.module.css" icon="file-code" .button { composes: background from "./background.module.css"; color: red; } ``` -{% callout %} + When composing classes from separate files, be sure that they do not contain the same properties. -The CSS module spec says that composing classes from separate files with conflicting properties is -undefined behavior, meaning that the output may differ and be unreliable. -{% /callout %} +The CSS module spec says that composing classes from separate files with conflicting properties is undefined behavior, meaning that the output may differ and be unreliable. + + diff --git a/docs/bundler/css_modules.md b/docs/bundler/css_modules.md deleted file mode 100644 index 271ab4621d..0000000000 --- a/docs/bundler/css_modules.md +++ /dev/null @@ -1,145 +0,0 @@ -# CSS Modules - -Bun's bundler also supports bundling [CSS modules](https://css-tricks.com/css-modules-part-1-need/) in addition to [regular CSS](/docs/bundler/css) with support for the following features: - -- Automatically detecting CSS module files (`.module.css`) with zero configuration -- Composition (`composes` property) -- Importing CSS modules into JSX/TSX -- Warnings/errors for invalid usages of CSS modules - -A CSS module is a CSS file (with the `.module.css` extension) where are all class names and animations are scoped to the file. This helps you avoid class name collisions as CSS declarations are globally scoped by default. - -Under the hood, Bun's bundler transforms locally scoped class names into unique identifiers. - -## Getting started - -Create a CSS file with the `.module.css` extension: - -```css -/* styles.module.css */ -.button { - color: red; -} - -/* other-styles.module.css */ -.button { - color: blue; -} -``` - -You can then import this file, for example into a TSX file: - -```tsx -import styles from "./styles.module.css"; -import otherStyles from "./other-styles.module.css"; - -export default function App() { - return ( - <> - - - - ); -} -``` - -The `styles` object from importing the CSS module file will be an object with all class names as keys and -their unique identifiers as values: - -```tsx -import styles from "./styles.module.css"; -import otherStyles from "./other-styles.module.css"; - -console.log(styles); -console.log(otherStyles); -``` - -This will output: - -```ts -{ - button: "button_123"; -} - -{ - button: "button_456"; -} -``` - -As you can see, the class names are unique to each file, avoiding any collisions! - -### Composition - -CSS modules allow you to _compose_ class selectors together. This lets you reuse style rules across multiple classes. - -For example: - -```css -/* styles.module.css */ -.button { - composes: background; - color: red; -} - -.background { - background-color: blue; -} -``` - -Would be the same as writing: - -```css -.button { - background-color: blue; - color: red; -} - -.background { - background-color: blue; -} -``` - -{% callout %} -There are a couple rules to keep in mind when using `composes`: - -- A `composes` property must come before any regular CSS properties or declarations -- You can only use `composes` on a **simple selector with a single class name**: - -```css -#button { - /* Invalid! `#button` is not a class selector */ - composes: background; -} - -.button, -.button-secondary { - /* Invalid! `.button, .button-secondary` is not a simple selector */ - composes: background; -} -``` - -{% /callout %} - -### Composing from a separate CSS module file - -You can also compose from a separate CSS module file: - -```css -/* background.module.css */ -.background { - background-color: blue; -} - -/* styles.module.css */ -.button { - composes: background from "./background.module.css"; - color: red; -} -``` - -{% callout %} -When composing classes from separate files, be sure that they do not contain the same properties. - -The CSS module spec says that composing classes from separate files with conflicting properties is -undefined behavior, meaning that the output may differ and be unreliable. -{% /callout %} diff --git a/docs/bundler/esbuild.mdx b/docs/bundler/esbuild.mdx new file mode 100644 index 0000000000..d9cd38efd7 --- /dev/null +++ b/docs/bundler/esbuild.mdx @@ -0,0 +1,253 @@ +--- +title: esbuild +description: Migration guide from esbuild to Bun's bundler +--- + +Bun's bundler API is inspired heavily by esbuild. Migrating to Bun's bundler from esbuild should be relatively painless. This guide will briefly explain why you might consider migrating to Bun's bundler and provide a side-by-side API comparison reference for those who are already familiar with esbuild's API. + +There are a few behavioral differences to note. + + + **Bundling by default.** Unlike esbuild, Bun always bundles by default. This is why the `--bundle` flag isn't + necessary in the Bun example. To transpile each file individually, use `Bun.Transpiler`. + + + + **It's just a bundler.** Unlike esbuild, Bun's bundler does not include a built-in development server or file watcher. + It's just a bundler. The bundler is intended for use in conjunction with `Bun.serve` and other runtime APIs to achieve + the same effect. As such, all options relating to HTTP/file watching are not applicable. + + +## Performance + +With a performance-minded API coupled with the extensively optimized Zig-based JS/TS parser, Bun's bundler is 1.75x faster than esbuild on esbuild's three.js benchmark. + +Bundling 10 copies of three.js from scratch, with sourcemaps and minification + +## CLI API + +Bun and esbuild both provide a command-line interface. + +```bash terminal icon="terminal" +# esbuild +esbuild --outdir=out --bundle + +# bun +bun build --outdir=out +``` + +In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Other flags like `--outdir ` do accept an argument; these flags can be written as `--outdir out` or `--outdir=out`. Some flags like `--define` can be specified several times: `--define foo=bar --define bar=baz`. + +| esbuild | bun build | Notes | +| ---------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `--bundle` | n/a | Bun always bundles, use `--no-bundle` to disable this behavior. | +| `--define:K=V` | `--define K=V` | Small syntax difference; no colon.
`esbuild --define:foo=bar`
`bun build --define foo=bar` | +| `--external:` | `--external ` | Small syntax difference; no colon.
`esbuild --external:react`
`bun build --external react` | +| `--format` | `--format` | Bun supports `"esm"` and `"cjs"` currently, but more module formats are planned. esbuild defaults to `"iife"`. | +| `--loader:.ext=loader` | `--loader .ext:loader` | Bun supports a different set of built-in loaders than esbuild; see Bundler > Loaders for a complete reference. The esbuild loaders `dataurl`, `binary`, `base64`, `copy`, and `empty` are not yet implemented.

The syntax for `--loader` is slightly different.
`esbuild app.ts --bundle --loader:.svg=text`
`bun build app.ts --loader .svg:text` | +| `--minify` | `--minify` | No differences | +| `--outdir` | `--outdir` | No differences | +| `--outfile` | `--outfile` | No differences | +| `--packages` | `--packages` | No differences | +| `--platform` | `--target` | Renamed to `--target` for consistency with tsconfig. Does not support `neutral`. | +| `--serve` | n/a | Not applicable | +| `--sourcemap` | `--sourcemap` | No differences | +| `--splitting` | `--splitting` | No differences | +| `--target` | n/a | Not supported. Bun's bundler performs no syntactic down-leveling at this time. | +| `--watch` | `--watch` | No differences | +| `--allow-overwrite` | n/a | Overwriting is never allowed | +| `--analyze` | n/a | Not supported | +| `--asset-names` | `--asset-naming` | Renamed for consistency with naming in JS API | +| `--banner` | `--banner` | Only applies to js bundles | +| `--footer` | `--footer` | Only applies to js bundles | +| `--certfile` | n/a | Not applicable | +| `--charset=utf8` | n/a | Not supported | +| `--chunk-names` | `--chunk-naming` | Renamed for consistency with naming in JS API | +| `--color` | n/a | Always enabled | +| `--drop` | `--drop` | | +| `--entry-names` | `--entry-naming` | Renamed for consistency with naming in JS API | +| `--global-name` | n/a | Not applicable, Bun does not support `iife` output at this time | +| `--ignore-annotations` | `--ignore-dce-annotations` | | +| `--inject` | n/a | Not supported | +| `--jsx` | `--jsx-runtime ` | Supports `"automatic"` (uses jsx transform) and `"classic"` (uses `React.createElement`) | +| `--jsx-dev` | n/a | Bun reads `compilerOptions.jsx` from `tsconfig.json` to determine a default. If `compilerOptions.jsx` is `"react-jsx"`, or if `NODE_ENV=production`, Bun will use the jsx transform. Otherwise, it uses `jsxDEV`. The bundler does not support `preserve`. | +| `--jsx-factory` | `--jsx-factory` | | +| `--jsx-fragment` | `--jsx-fragment` | | +| `--jsx-import-source` | `--jsx-import-source` | | +| `--jsx-side-effects` | n/a | JSX is always assumed to be side-effect-free | +| `--keep-names` | n/a | Not supported | +| `--keyfile` | n/a | Not applicable | +| `--legal-comments` | n/a | Not supported | +| `--log-level` | n/a | Not supported. This can be set in `bunfig.toml` as `logLevel`. | +| `--log-limit` | n/a | Not supported | +| `--log-override:X=Y` | n/a | Not supported | +| `--main-fields` | n/a | Not supported | +| `--mangle-cache` | n/a | Not supported | +| `--mangle-props` | n/a | Not supported | +| `--mangle-quoted` | n/a | Not supported | +| `--metafile` | n/a | Not supported | +| `--minify-whitespace` | `--minify-whitespace` | | +| `--minify-identifiers` | `--minify-identifiers` | | +| `--minify-syntax` | `--minify-syntax` | | +| `--out-extension` | n/a | Not supported | +| `--outbase` | `--root` | | +| `--preserve-symlinks` | n/a | Not supported | +| `--public-path` | `--public-path` | | +| `--pure` | n/a | Not supported | +| `--reserve-props` | n/a | Not supported | +| `--resolve-extensions` | n/a | Not supported | +| `--servedir` | n/a | Not applicable | +| `--source-root` | n/a | Not supported | +| `--sourcefile` | n/a | Not supported. Bun does not support stdin input yet. | +| `--sourcemap` | `--sourcemap` | No differences | +| `--sources-content` | n/a | Not supported | +| `--supported` | n/a | Not supported | +| `--tree-shaking` | n/a | Always true | +| `--tsconfig` | `--tsconfig-override` | | +| `--version` | n/a | Run `bun --version` to see the version of Bun. | + +## JavaScript API + +| esbuild.build() | Bun.build() | Notes | +| ------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `absWorkingDir` | n/a | Always set to `process.cwd()` | +| `alias` | n/a | Not supported | +| `allowOverwrite` | n/a | Always false | +| `assetNames` | `naming.asset` | Uses same templating syntax as esbuild, but `[ext]` must be included explicitly.

`ts
Bun.build({
entrypoints: ["./index.tsx"],
naming: {
asset: "[name].[ext]",
},
});
` | +| `banner` | n/a | Not supported | +| `bundle` | n/a | Always true. Use `Bun.Transpiler` to transpile without bundling. | +| `charset` | n/a | Not supported | +| `chunkNames` | `naming.chunk` | Uses same templating syntax as esbuild, but `[ext]` must be included explicitly.

`ts
Bun.build({
entrypoints: ["./index.tsx"],
naming: {
chunk: "[name].[ext]",
},
});
` | +| `color` | n/a | Bun returns logs in the `logs` property of the build result. | +| `conditions` | n/a | Not supported. Export conditions priority is determined by `target`. | +| `define` | `define` | | +| `drop` | n/a | Not supported | +| `entryNames` | `naming` or `naming.entry` | Bun supports a `naming` key that can either be a string or an object. Uses same templating syntax as esbuild, but `[ext]` must be included explicitly.

`ts
Bun.build({
entrypoints: ["./index.tsx"],
// when string, this is equivalent to entryNames
naming: "[name].[ext]",

// granular naming options
naming: {
entry: "[name].[ext]",
asset: "[name].[ext]",
chunk: "[name].[ext]",
},
});
` | +| `entryPoints` | `entrypoints` | Capitalization difference | +| `external` | `external` | No differences | +| `footer` | n/a | Not supported | +| `format` | `format` | Only supports `"esm"` currently. Support for `"cjs"` and `"iife"` is planned. | +| `globalName` | n/a | Not supported | +| `ignoreAnnotations` | n/a | Not supported | +| `inject` | n/a | Not supported | +| `jsx` | `jsx` | Not supported in JS API, configure in `tsconfig.json` | +| `jsxDev` | `jsxDev` | Not supported in JS API, configure in `tsconfig.json` | +| `jsxFactory` | `jsxFactory` | Not supported in JS API, configure in `tsconfig.json` | +| `jsxFragment` | `jsxFragment` | Not supported in JS API, configure in `tsconfig.json` | +| `jsxImportSource` | `jsxImportSource` | Not supported in JS API, configure in `tsconfig.json` | +| `jsxSideEffects` | `jsxSideEffects` | Not supported in JS API, configure in `tsconfig.json` | +| `keepNames` | n/a | Not supported | +| `legalComments` | n/a | Not supported | +| `loader` | `loader` | Bun supports a different set of built-in loaders than esbuild; see Bundler > Loaders for a complete reference. The esbuild loaders `dataurl`, `binary`, `base64`, `copy`, and `empty` are not yet implemented. | +| `logLevel` | n/a | Not supported | +| `logLimit` | n/a | Not supported | +| `logOverride` | n/a | Not supported | +| `mainFields` | n/a | Not supported | +| `mangleCache` | n/a | Not supported | +| `mangleProps` | n/a | Not supported | +| `mangleQuoted` | n/a | Not supported | +| `metafile` | n/a | Not supported | +| `minify` | `minify` | In Bun, `minify` can be a boolean or an object.

`ts
await Bun.build({
entrypoints: ['./index.tsx'],
// enable all minification
minify: true

// granular options
minify: {
identifiers: true,
syntax: true,
whitespace: true
}
})
` | +| `minifyIdentifiers` | `minify.identifiers` | See `minify` | +| `minifySyntax` | `minify.syntax` | See `minify` | +| `minifyWhitespace` | `minify.whitespace` | See `minify` | +| `nodePaths` | n/a | Not supported | +| `outExtension` | n/a | Not supported | +| `outbase` | `root` | Different name | +| `outdir` | `outdir` | No differences | +| `outfile` | `outfile` | No differences | +| `packages` | n/a | Not supported, use `external` | +| `platform` | `target` | Supports `"bun"`, `"node"` and `"browser"` (the default). Does not support `"neutral"`. | +| `plugins` | `plugins` | Bun's plugin API is a subset of esbuild's. Some esbuild plugins will work out of the box with Bun. | +| `preserveSymlinks` | n/a | Not supported | +| `publicPath` | `publicPath` | No differences | +| `pure` | n/a | Not supported | +| `reserveProps` | n/a | Not supported | +| `resolveExtensions` | n/a | Not supported | +| `sourceRoot` | n/a | Not supported | +| `sourcemap` | `sourcemap` | Supports `"inline"`, `"external"`, and `"none"` | +| `sourcesContent` | n/a | Not supported | +| `splitting` | `splitting` | No differences | +| `stdin` | n/a | Not supported | +| `supported` | n/a | Not supported | +| `target` | n/a | No support for syntax downleveling | +| `treeShaking` | n/a | Always true | +| `tsconfig` | n/a | Not supported | +| `write` | n/a | Set to true if `outdir`/`outfile` is set, otherwise false | + +## Plugin API + +Bun's plugin API is designed to be esbuild compatible. Bun doesn't support esbuild's entire plugin API surface, but the core functionality is implemented. Many third-party esbuild plugins will work out of the box with Bun. + + + Long term, we aim for feature parity with esbuild's API, so if something doesn't work please file an issue to help us + prioritize. + + +Plugins in Bun and esbuild are defined with a builder object. + +```ts title="myPlugin.ts" icon="/icons/typescript.svg" +import type { BunPlugin } from "bun"; + +const myPlugin: BunPlugin = { + name: "my-plugin", + setup(builder) { + // define plugin + }, +}; +``` + +The builder object provides some methods for hooking into parts of the bundling process. Bun implements `onResolve` and `onLoad`; it does not yet implement the esbuild hooks `onStart`, `onEnd`, and `onDispose`, and `resolve` utilities. `initialOptions` is partially implemented, being read-only and only having a subset of esbuild's options; use `config` (same thing but with Bun's `BuildConfig` format) instead. + +```ts title="myPlugin.ts" icon="/icons/typescript.svg" +import type { BunPlugin } from "bun"; +const myPlugin: BunPlugin = { + name: "my-plugin", + setup(builder) { + builder.onResolve( + { + /* onResolve.options */ + }, + args => { + return { + /* onResolve.results */ + }; + }, + ); + builder.onLoad( + { + /* onLoad.options */ + }, + args => { + return { + /* onLoad.results */ + }; + }, + ); + }, +}; +``` + +### onResolve + + + - 🟢 `filter` - 🟢 `namespace` + + - 🟢 `path` - 🟢 `importer` - 🔴 `namespace` - 🔴 `resolveDir` - 🔴 `kind` - 🔴 `pluginData` + + + - 🟢 `namespace` - 🟢 `path` - 🔴 `errors` - 🔴 `external` - 🔴 `pluginData` - 🔴 `pluginName` - 🔴 `sideEffects` - + 🔴 `suffix` - 🔴 `warnings` - 🔴 `watchDirs` - 🔴 `watchFiles` + + + +### onLoad + + + - 🟢 `filter` - 🟢 `namespace` + - 🟢 `path` - 🔴 `namespace` - 🔴 `suffix` - 🔴 `pluginData` + + - 🟢 `contents` - 🟢 `loader` - 🔴 `errors` - 🔴 `pluginData` - 🔴 `pluginName` - 🔴 `resolveDir` - 🔴 `warnings` - + 🔴 `watchDirs` - 🔴 `watchFiles` + + diff --git a/docs/bundler/executables.md b/docs/bundler/executables.mdx similarity index 68% rename from docs/bundler/executables.md rename to docs/bundler/executables.mdx index 097c64f978..fb7059435f 100644 --- a/docs/bundler/executables.md +++ b/docs/bundler/executables.mdx @@ -1,33 +1,43 @@ +--- +title: "Single-file executable" +description: "Generate standalone executables from TypeScript or JavaScript files with Bun" +--- + Bun's bundler implements a `--compile` flag for generating a standalone binary from a TypeScript or JavaScript file. -{% codetabs %} + -```bash -$ bun build ./cli.ts --compile --outfile mycli +```bash terminal icon="terminal" +bun build ./cli.ts --compile --outfile mycli ``` -```ts#cli.ts +```ts cli.ts icon="/icons/typescript.svg" console.log("Hello world!"); ``` -{% /codetabs %} + This bundles `cli.ts` into an executable that can be executed directly: +```bash terminal icon="terminal" +./mycli ``` -$ ./mycli + +```txt Hello world! ``` All imported files and packages are bundled into the executable, along with a copy of the Bun runtime. All built-in Bun and Node.js APIs are supported. +--- + ## Cross-compile to other platforms The `--target` flag lets you compile your standalone executable for a different operating system, architecture, or version of Bun than the machine you're running `bun build` on. To build for Linux x64 (most servers): -```sh +```bash icon="terminal" terminal bun build --compile --target=bun-linux-x64 ./index.ts --outfile myapp # To support CPUs from before 2013, use the baseline version (nehalem) @@ -40,14 +50,14 @@ bun build --compile --target=bun-linux-x64-modern ./index.ts --outfile myapp To build for Linux ARM64 (e.g. Graviton or Raspberry Pi): -```sh +```bash icon="terminal" terminal # Note: the default architecture is x64 if no architecture is specified. bun build --compile --target=bun-linux-arm64 ./index.ts --outfile myapp ``` To build for Windows x64: -```sh +```bash icon="terminal" terminal bun build --compile --target=bun-windows-x64 ./path/to/my/app.ts --outfile myapp # To support CPUs from before 2013, use the baseline version (nehalem) @@ -61,17 +71,17 @@ bun build --compile --target=bun-windows-x64-modern ./path/to/my/app.ts --outfil To build for macOS arm64: -```sh +```bash icon="terminal" terminal bun build --compile --target=bun-darwin-arm64 ./path/to/my/app.ts --outfile myapp ``` To build for macOS x64: -```sh +```bash icon="terminal" terminal bun build --compile --target=bun-darwin-x64 ./path/to/my/app.ts --outfile myapp ``` -#### Supported targets +### Supported targets The order of the `--target` flag does not matter, as long as they're delimited by a `-`. @@ -86,21 +96,32 @@ The order of the `--target` flag does not matter, as long as they're delimited b | bun-linux-x64-musl | Linux | x64 | ✅ | ✅ | musl | | bun-linux-arm64-musl | Linux | arm64 | ✅ | N/A | musl | -On x64 platforms, Bun uses SIMD optimizations which require a modern CPU supporting AVX2 instructions. The `-baseline` build of Bun is for older CPUs that don't support these optimizations. Normally, when you install Bun we automatically detect which version to use but this can be harder to do when cross-compiling since you might not know the target CPU. You usually don't need to worry about it on Darwin x64, but it is relevant for Windows x64 and Linux x64. If you or your users see `"Illegal instruction"` errors, you might need to use the baseline version. + + On x64 platforms, Bun uses SIMD optimizations which require a modern CPU supporting AVX2 instructions. The `-baseline` + build of Bun is for older CPUs that don't support these optimizations. Normally, when you install Bun we automatically + detect which version to use but this can be harder to do when cross-compiling since you might not know the target CPU. + You usually don't need to worry about it on Darwin x64, but it is relevant for Windows x64 and Linux x64. If you or + your users see `"Illegal instruction"` errors, you might need to use the baseline version. + + +--- ## Build-time constants Use the `--define` flag to inject build-time constants into your executable, such as version numbers, build timestamps, or configuration values: -```bash -$ bun build --compile --define BUILD_VERSION='"1.2.3"' --define BUILD_TIME='"2024-01-15T10:30:00Z"' src/cli.ts --outfile mycli +```bash icon="terminal" terminal +bun build --compile --define BUILD_VERSION='"1.2.3"' --define BUILD_TIME='"2024-01-15T10:30:00Z"' src/cli.ts --outfile mycli ``` These constants are embedded directly into your compiled binary at build time, providing zero runtime overhead and enabling dead code elimination optimizations. -{% callout type="info" %} -For comprehensive examples and advanced patterns, see the [Build-time constants guide](/guides/runtime/build-time-constants). -{% /callout %} + + For comprehensive examples and advanced patterns, see the [Build-time constants + guide](https://bun.com/guides/runtime/build-time-constants). + + +--- ## Deploying to production @@ -112,7 +133,7 @@ With compiled executables, you can move that cost from runtime to build-time. When deploying to production, we recommend the following: -```sh +```bash icon="terminal" terminal bun build --compile --minify --sourcemap ./path/to/my/app.ts --outfile myapp ``` @@ -120,17 +141,22 @@ bun build --compile --minify --sourcemap ./path/to/my/app.ts --outfile myapp To improve startup time, enable bytecode compilation: -```sh +```bash icon="terminal" terminal bun build --compile --minify --sourcemap --bytecode ./path/to/my/app.ts --outfile myapp ``` Using bytecode compilation, `tsc` starts 2x faster: -{% image src="https://github.com/user-attachments/assets/dc8913db-01d2-48f8-a8ef-ac4e984f9763" width="689" /%} + + ![Bytecode performance comparison](https://github.com/user-attachments/assets/dc8913db-01d2-48f8-a8ef-ac4e984f9763) + Bytecode compilation moves parsing overhead for large input files from runtime to bundle time. Your app starts faster, in exchange for making the `bun build` command a little slower. It doesn't obscure source code. -**Experimental:** Bytecode compilation is an experimental feature introduced in Bun v1.1.30. Only `cjs` format is supported (which means no top-level-await). Let us know if you run into any issues! + + **Experimental:** Bytecode compilation is an experimental feature introduced in Bun v1.1.30. Only `cjs` format is + supported (which means no top-level-await). Let us know if you run into any issues! + ### What do these flags do? @@ -140,68 +166,77 @@ The `--sourcemap` argument embeds a sourcemap compressed with zstd, so that erro The `--bytecode` argument enables bytecode compilation. Every time you run JavaScript code in Bun, JavaScriptCore (the engine) will compile your source code into bytecode. We can move this parsing work from runtime to bundle time, saving you startup time. +--- + ## Embedding runtime arguments **`--compile-exec-argv="args"`** - Embed runtime arguments that are available via `process.execArgv`: -```bash +```bash icon="terminal" terminal bun build --compile --compile-exec-argv="--smol --user-agent=MyBot" ./app.ts --outfile myapp ``` -```js +```ts app.ts icon="/icons/typescript.svg" // In the compiled app console.log(process.execArgv); // ["--smol", "--user-agent=MyBot"] ``` +--- + ## Act as the Bun CLI -{% note %} - -New in Bun v1.2.16 - -{% /note %} +New in Bun v1.2.16 You can run a standalone executable as if it were the `bun` CLI itself by setting the `BUN_BE_BUN=1` environment variable. When this variable is set, the executable will ignore its bundled entrypoint and instead expose all the features of Bun's CLI. For example, consider an executable compiled from a simple script: -```sh -$ cat such-bun.js -console.log("you shouldn't see this"); +```bash icon="terminal" terminal +echo "console.log(\"you shouldn't see this\");" > such-bun.js +bun build --compile ./such-bun.js +``` -$ bun build --compile ./such-bun.js - [3ms] bundle 1 modules +```txt +[3ms] bundle 1 modules [89ms] compile such-bun ``` -Normally, running `./such-bun` with arguments would execute the script. However, with the `BUN_BE_BUN=1` environment variable, it acts just like the `bun` binary: +Normally, running `./such-bun` with arguments would execute the script. -```sh +```bash icon="terminal" terminal # Executable runs its own entrypoint by default -$ ./such-bun install -you shouldn't see this +./such-bun install +``` +```txt +you shouldn't see this +``` + +However, with the `BUN_BE_BUN=1` environment variable, it acts just like the `bun` binary: + +```bash icon="terminal" terminal # With the env var, the executable acts like the `bun` CLI -$ BUN_BE_BUN=1 ./such-bun install +bun_BE_BUN=1 ./such-bun install +``` + +```txt bun install v1.2.16-canary.1 (1d1db811) Checked 63 installs across 64 packages (no changes) [5.00ms] ``` This is useful for building CLI tools on top of Bun that may need to install packages, bundle dependencies, run different or local files and more without needing to download a separate binary or install bun. +--- + ## Full-stack executables -{% note %} - -New in Bun v1.2.17 - -{% /note %} +New in Bun v1.2.17 Bun's `--compile` flag can create standalone executables that contain both server and client code, making it ideal for full-stack applications. When you import an HTML file in your server code, Bun automatically bundles all frontend assets (JavaScript, CSS, etc.) and embeds them into the executable. When Bun sees the HTML import on the server, it kicks off a frontend build process to bundle JavaScript, CSS, and other assets. -{% codetabs %} + -```ts#server.ts +```ts server.ts icon="/icons/typescript.svg" import { serve } from "bun"; import index from "./index.html"; @@ -215,12 +250,12 @@ const server = serve({ console.log(`Server running at http://localhost:${server.port}`); ``` -```html#index.html +```html index.html icon="file-code" My App - +

Hello World

@@ -229,21 +264,21 @@ console.log(`Server running at http://localhost:${server.port}`); ``` -```js#app.js +```ts app.js icon="file-code" console.log("Hello from the client!"); ``` -```css#styles.css +```css styles.css icon="file-code" body { background-color: #f0f0f0; } ``` -{% /codetabs %} +
To build this into a single executable: -```sh +```bash terminal icon="terminal" bun build --compile ./server.ts --outfile myapp ``` @@ -256,25 +291,27 @@ This creates a self-contained binary that includes: The result is a single file that can be deployed anywhere without needing Node.js, Bun, or any dependencies installed. Just run: -```sh +```bash terminal icon="terminal" ./myapp ``` Bun automatically handles serving the frontend assets with proper MIME types and cache headers. The HTML import is replaced with a manifest object that `Bun.serve` uses to efficiently serve pre-bundled assets. -For more details on building full-stack applications with Bun, see the [full-stack guide](/docs/bundler/fullstack). +For more details on building full-stack applications with Bun, see the [full-stack guide](/bundler/fullstack). + +--- ## Worker To use workers in a standalone executable, add the worker's entrypoint to the CLI arguments: -```sh -$ bun build --compile ./index.ts ./my-worker.ts --outfile myapp +```bash terminal icon="terminal" +bun build --compile ./index.ts ./my-worker.ts --outfile myapp ``` Then, reference the worker in your code: -```ts +```ts index.ts icon="/icons/typescript.svg" console.log("Hello from Bun!"); // Any of these will work: @@ -289,13 +326,15 @@ In the future, we may automatically detect usages of statically-known paths in ` If you use a relative path to a file not included in the standalone executable, it will attempt to load that path from disk relative to the current working directory of the process (and then error if it doesn't exist). +--- + ## SQLite You can use `bun:sqlite` imports with `bun build --compile`. By default, the database is resolved relative to the current working directory of the process. -```js +```ts index.ts icon="/icons/typescript.svg" import db from "./my.db" with { type: "sqlite" }; console.log(db.query("select * from users LIMIT 1").get()); @@ -303,10 +342,12 @@ console.log(db.query("select * from users LIMIT 1").get()); That means if the executable is located at `/usr/bin/hello`, the user's terminal is located at `/home/me/Desktop`, it will look for `/home/me/Desktop/my.db`. +```bash terminal icon="terminal" +cd /home/me/Desktop +./hello ``` -$ cd /home/me/Desktop -$ ./hello -``` + +--- ## Embed assets & files @@ -314,7 +355,7 @@ Standalone executables support embedding files. To embed files into an executable with `bun build --compile`, import the file in your code. -```ts +```ts index.ts icon="/icons/typescript.svg" // this becomes an internal file path import icon from "./icon.png" with { type: "file" }; import { file } from "bun"; @@ -331,7 +372,7 @@ Embedded files can be read using `Bun.file`'s functions or the Node.js `fs.readF For example, to read the contents of the embedded file: -```js +```ts index.ts icon="/icons/typescript.svg" import icon from "./icon.png" with { type: "file" }; import { file } from "bun"; @@ -344,7 +385,7 @@ const bytes = await file(icon).arrayBuffer(); If your application wants to embed a SQLite database, set `type: "sqlite"` in the import attribute and the `embed` attribute to `"true"`. -```js +```ts index.ts icon="/icons/typescript.svg" import myEmbeddedDb from "./my.db" with { type: "sqlite", embed: "true" }; console.log(myEmbeddedDb.query("select * from users LIMIT 1").get()); @@ -356,7 +397,7 @@ This database is read-write, but all changes are lost when the executable exits As of Bun v1.0.23, you can embed `.node` files into executables. -```js +```ts index.ts icon="/icons/typescript.svg" const addon = require("./addon.node"); console.log(addon.hello()); @@ -368,13 +409,13 @@ Unfortunately, if you're using `@mapbox/node-pre-gyp` or other similar tools, yo To embed a directory with `bun build --compile`, use a shell glob in your `bun build` command: -```sh -$ bun build --compile ./index.ts ./public/**/*.png +```bash terminal icon="terminal" +bun build --compile ./index.ts ./public/**/*.png ``` Then, you can reference the files in your code: -```ts +```ts index.ts icon="/icons/typescript.svg" import icon from "./public/assets/icon.png" with { type: "file" }; import { file } from "bun"; @@ -392,7 +433,7 @@ This is honestly a workaround, and we expect to improve this in the future with To get a list of all embedded files, use `Bun.embeddedFiles`: -```js +```ts index.ts icon="/icons/typescript.svg" import "./icon.png" with { type: "file" }; import { embeddedFiles } from "bun"; @@ -413,141 +454,40 @@ By default, embedded files have a content hash appended to their name. This is u To disable the content hash, pass `--asset-naming` to `bun build --compile` like this: -```sh -$ bun build --compile --asset-naming="[name].[ext]" ./index.ts +```bash terminal icon="terminal" +bun build --compile --asset-naming="[name].[ext]" ./index.ts ``` +--- + ## Minification To trim down the size of the executable a little, pass `--minify` to `bun build --compile`. This uses Bun's minifier to reduce the code size. Overall though, Bun's binary is still way too big and we need to make it smaller. -## Using Bun.build() API - -You can also generate standalone executables using the `Bun.build()` JavaScript API. This is useful when you need programmatic control over the build process. - -### Basic usage - -```js -await Bun.build({ - entrypoints: ["./app.ts"], - outdir: "./dist", - compile: { - target: "bun-windows-x64", - outfile: "myapp.exe", - }, -}); -``` - -### Windows metadata with Bun.build() - -When targeting Windows, you can specify metadata through the `windows` object: - -```js -await Bun.build({ - entrypoints: ["./app.ts"], - outdir: "./dist", - compile: { - target: "bun-windows-x64", - outfile: "myapp.exe", - windows: { - title: "My Application", - publisher: "My Company Inc", - version: "1.2.3.4", - description: "A powerful application built with Bun", - copyright: "© 2024 My Company Inc", - hideConsole: false, // Set to true for GUI applications - icon: "./icon.ico", // Path to icon file - }, - }, -}); -``` - -### Cross-compilation with Bun.build() - -You can cross-compile for different platforms: - -```js -// Build for multiple platforms -const platforms = [ - { target: "bun-windows-x64", outfile: "app-windows.exe" }, - { target: "bun-linux-x64", outfile: "app-linux" }, - { target: "bun-darwin-arm64", outfile: "app-macos" }, -]; - -for (const platform of platforms) { - await Bun.build({ - entrypoints: ["./app.ts"], - outdir: "./dist", - compile: platform, - }); -} -``` +--- ## Windows-specific flags -When compiling a standalone executable for Windows, there are several platform-specific options that can be used to customize the generated `.exe` file: +When compiling a standalone executable on Windows, there are two platform-specific options that can be used to customize metadata on the generated `.exe` file: -### Visual customization +- `--windows-icon=path/to/icon.ico` to customize the executable file icon. +- `--windows-hide-console` to disable the background terminal, which can be used for applications that do not need a TTY. -- `--windows-icon=path/to/icon.ico` - Set the executable file icon -- `--windows-hide-console` - Disable the background terminal window (useful for GUI applications) +These flags currently cannot be used when cross-compiling because they depend on Windows APIs. -### Metadata customization - -You can embed version information and other metadata into your Windows executable: - -- `--windows-title ` - Set the product name (appears in file properties) -- `--windows-publisher ` - Set the company name -- `--windows-version ` - Set the version number (e.g. "1.2.3.4") -- `--windows-description ` - Set the file description -- `--windows-copyright ` - Set the copyright information - -#### Example with all metadata flags: - -```sh -bun build --compile ./app.ts \ - --outfile myapp.exe \ - --windows-title "My Application" \ - --windows-publisher "My Company Inc" \ - --windows-version "1.2.3.4" \ - --windows-description "A powerful application built with Bun" \ - --windows-copyright "© 2024 My Company Inc" -``` - -This metadata will be visible in Windows Explorer when viewing the file properties: - -1. Right-click the executable in Windows Explorer -2. Select "Properties" -3. Go to the "Details" tab - -#### Version string format - -The `--windows-version` flag accepts version strings in the following formats: - -- `"1"` - Will be normalized to "1.0.0.0" -- `"1.2"` - Will be normalized to "1.2.0.0" -- `"1.2.3"` - Will be normalized to "1.2.3.0" -- `"1.2.3.4"` - Full version format - -Each version component must be a number between 0 and 65535. - -{% callout %} - -These flags currently cannot be used when cross-compiling because they depend on Windows APIs. They are only available when building on Windows itself. - -{% /callout %} +--- ## Code signing on macOS To codesign a standalone executable on macOS (which fixes Gatekeeper warnings), use the `codesign` command. -```sh -$ codesign --deep --force -vvvv --sign "XXXXXXXXXX" ./myapp +```bash terminal icon="terminal" +codesign --deep --force -vvvv --sign "XXXXXXXXXX" ./myapp ``` We recommend including an `entitlements.plist` file with JIT permissions. -```xml#entitlements.plist +```xml icon="xml" title="info.plist" @@ -568,59 +508,28 @@ We recommend including an `entitlements.plist` file with JIT permissions. To codesign with JIT support, pass the `--entitlements` flag to `codesign`. -```sh -$ codesign --deep --force -vvvv --sign "XXXXXXXXXX" --entitlements entitlements.plist ./myapp +```bash terminal icon="terminal" +codesign --deep --force -vvvv --sign "XXXXXXXXXX" --entitlements entitlements.plist ./myapp ``` After codesigning, verify the executable: -```sh -$ codesign -vvv --verify ./myapp +```bash terminal icon="terminal" +codesign -vvv --verify ./myapp ./myapp: valid on disk ./myapp: satisfies its Designated Requirement ``` -{% callout %} +Codesign support requires Bun v1.2.4 or newer. -Codesign support requires Bun v1.2.4 or newer. - -{% /callout %} - -## Code splitting - -Standalone executables support code splitting. Use `--compile` with `--splitting` to create an executable that loads code-split chunks at runtime. - -```bash -$ bun build --compile --splitting ./src/entry.ts --outdir ./build -``` - -{% codetabs %} - -```ts#src/entry.ts -console.log("Entrypoint loaded"); -const lazy = await import("./lazy.ts"); -lazy.hello(); -``` - -```ts#src/lazy.ts -export function hello() { - console.log("Lazy module loaded"); -} -``` - -{% /codetabs %} - -```bash -$ ./build/entry -Entrypoint loaded -Lazy module loaded -``` +--- ## Unsupported CLI arguments Currently, the `--compile` flag can only accept a single entrypoint at a time and does not support the following flags: -- `--outdir` — use `outfile` instead (except when using with `--splitting`). +- `--outdir` — use `outfile` instead. +- `--splitting` - `--public-path` - `--target=node` or `--target=browser` - `--no-bundle` - we always bundle everything into the executable. diff --git a/docs/bundler/fullstack.md b/docs/bundler/fullstack.md deleted file mode 100644 index 16ed1d8402..0000000000 --- a/docs/bundler/fullstack.md +++ /dev/null @@ -1,418 +0,0 @@ -To get started, import HTML files and pass them to the `routes` option in `Bun.serve()`. - -```ts -import { sql, serve } from "bun"; -import dashboard from "./dashboard.html"; -import homepage from "./index.html"; - -const server = serve({ - routes: { - // ** HTML imports ** - // Bundle & route index.html to "/". This uses HTMLRewriter to scan the HTML for ` - - - -``` - -Becomes something like this: - -```html#index.html - - - - Home - - - -
- - - -``` - -### How to use with React - -To use React in your client-side code, import `react-dom/client` and render your app. - -{% codetabs %} - -```ts#src/backend.ts -import dashboard from "../public/dashboard.html"; -import { serve } from "bun"; - -serve({ - routes: { - "/": dashboard, - }, - - async fetch(req) { - // ...api requests - return new Response("hello world"); - }, -}); -``` - -```ts#src/frontend.tsx -import "./styles.css"; -import { createRoot } from "react-dom/client"; -import { App } from "./app.tsx"; - -document.addEventListener("DOMContentLoaded", () => { - const root = createRoot(document.getElementById("root")); - root.render(); -}); -``` - -```html#public/dashboard.html - - - - Dashboard - - -
- - - -``` - -```css#src/styles.css -body { - background-color: red; -} -``` - -```tsx#src/app.tsx -export function App() { - return
Hello World
; -} -``` - -{% /codetabs %} - -### Development mode - -When building locally, enable development mode by setting `development: true` in `Bun.serve()`. - -```js-diff -import homepage from "./index.html"; -import dashboard from "./dashboard.html"; - -Bun.serve({ - routes: { - "/": homepage, - "/dashboard": dashboard, - } - -+ development: true, - - fetch(req) { - // ... api requests - }, -}); -``` - -When `development` is `true`, Bun will: - -- Include the `SourceMap` header in the response so that devtools can show the original source code -- Disable minification -- Re-bundle assets on each request to a .html file -- Enable hot module reloading (unless `hmr: false` is set) - -#### Echo console logs from browser to terminal - -Bun.serve() supports echoing console logs from the browser to the terminal. - -To enable this, pass `console: true` in the `development` object in `Bun.serve()`. - -```ts -import homepage from "./index.html"; - -Bun.serve({ - // development can also be an object. - development: { - // Enable Hot Module Reloading - hmr: true, - - // Echo console logs from the browser to the terminal - console: true, - }, - - routes: { - "/": homepage, - }, -}); -``` - -When `console: true` is set, Bun will stream console logs from the browser to the terminal. This reuses the existing WebSocket connection from HMR to send the logs. - -#### Production mode - -Hot reloading and `development: true` helps you iterate quickly, but in production, your server should be as fast as possible and have as few external dependencies as possible. - -##### Ahead of time bundling (recommended) - -As of Bun v1.2.17, you can use `Bun.build` or `bun build` to bundle your full-stack application ahead of time. - -```sh -$ bun build --target=bun --production --outdir=dist ./src/index.ts -``` - -When Bun's bundler sees an HTML import from server-side code, it will bundle the referenced JavaScript/TypeScript/TSX/JSX and CSS files into a manifest object that Bun.serve() can use to serve the assets. - -```ts -import { serve } from "bun"; -import index from "./index.html"; - -serve({ - routes: { "/": index }, -}); -``` - -{% details summary="Internally, the `index` variable is a manifest object that looks something like this" %} - -```json -{ - "index": "./index.html", - "files": [ - { - "input": "index.html", - "path": "./index-f2me3qnf.js", - "loader": "js", - "isEntry": true, - "headers": { - "etag": "eet6gn75", - "content-type": "text/javascript;charset=utf-8" - } - }, - { - "input": "index.html", - "path": "./index.html", - "loader": "html", - "isEntry": true, - "headers": { - "etag": "r9njjakd", - "content-type": "text/html;charset=utf-8" - } - }, - { - "input": "index.html", - "path": "./index-gysa5fmk.css", - "loader": "css", - "isEntry": true, - "headers": { - "etag": "50zb7x61", - "content-type": "text/css;charset=utf-8" - } - }, - { - "input": "logo.svg", - "path": "./logo-kygw735p.svg", - "loader": "file", - "isEntry": false, - "headers": { - "etag": "kygw735p", - "content-type": "application/octet-stream" - } - }, - { - "input": "react.svg", - "path": "./react-ck11dneg.svg", - "loader": "file", - "isEntry": false, - "headers": { - "etag": "ck11dneg", - "content-type": "application/octet-stream" - } - } - ] -} -``` - -{% /details %} - -##### Runtime bundling - -When adding a build step is too complicated, you can set `development: false` in `Bun.serve()`. - -- Enable in-memory caching of bundled assets. Bun will bundle assets lazily on the first request to an `.html` file, and cache the result in memory until the server restarts. -- Enables `Cache-Control` headers and `ETag` headers -- Minifies JavaScript/TypeScript/TSX/JSX files - -## Plugins - -Bun's [bundler plugins](https://bun.com/docs/bundler/plugins) are also supported when bundling static routes. - -To configure plugins for `Bun.serve`, add a `plugins` array in the `[serve.static]` section of your `bunfig.toml`. - -### Using TailwindCSS in HTML routes - -For example, enable TailwindCSS on your routes by installing and adding the `bun-plugin-tailwind` plugin: - -```sh -$ bun add bun-plugin-tailwind -``` - -```toml#bunfig.toml -[serve.static] -plugins = ["bun-plugin-tailwind"] -``` - -This will allow you to use TailwindCSS utility classes in your HTML and CSS files. All you need to do is import `tailwindcss` somewhere: - -```html#index.html - - - - Home - - - - - - -``` - -Or in your CSS: - -```css#style.css -@import "tailwindcss"; -``` - -### Custom plugins - -Any JS file or module which exports a [valid bundler plugin object](https://bun.com/docs/bundler/plugins#usage) (essentially an object with a `name` and `setup` field) can be placed inside the `plugins` array: - -```toml#bunfig.toml -[serve.static] -plugins = ["./my-plugin-implementation.ts"] -``` - -Bun will lazily resolve and load each plugin and use them to bundle your routes. - -Note: this is currently in `bunfig.toml` to make it possible to know statically which plugins are in use when we eventually integrate this with the `bun build` CLI. These plugins work in `Bun.build()`'s JS API, but are not yet supported in the CLI. - -## How this works - -Bun uses [`HTMLRewriter`](/docs/api/html-rewriter) to scan for ` - ``` - -2. **`` processing** - - Processes CSS imports and `` tags - - Concatenates CSS files - - Rewrites `url` and asset paths to include content-addressable hashes in URLs - - ```html - - ``` - -3. **`` & asset processing** - - Links to assets are rewritten to include content-addressable hashes in URLs - - Small assets in CSS files are inlined into `data:` URLs, reducing the total number of HTTP requests sent over the wire - -4. **Rewrite HTML** - - Combines all ` + + + +``` + +Becomes something like this: + +```html title="index.html" icon="file-code" + + + + Home + + + +
+ + + +``` + +## React Integration + +To use React in your client-side code, import `react-dom/client` and render your app. + + +```ts title="src/backend.ts" icon="/icons/typescript.svg" +import dashboard from "../public/dashboard.html"; +import { serve } from "bun"; + +serve({ +routes: { +"/": dashboard, +}, +async fetch(req) { +// ...api requests +return new Response("hello world"); +}, +}); + +```` + +```tsx title="src/frontend.tsx" icon="/icons/typescript.svg" +import { createRoot } from 'react-dom/client'; +import App from './app'; + +const container = document.getElementById('root'); +const root = createRoot(container!); +root.render(); +```` + +```html title="public/dashboard.html" icon="file-code" + + + + Dashboard + + + +
+ + + +``` + +```tsx title="src/app.tsx" icon="/icons/typescript.svg" +import { useState } from "react"; + +export default function App() { + const [count, setCount] = useState(0); + + return ( +
+

Dashboard

+ +
+ ); +} +``` + +
+ +## Development Mode + +When building locally, enable development mode by setting `development: true` in `Bun.serve()`. + +```ts title="src/backend.ts" icon="/icons/typescript.svg" +import homepage from "./index.html"; +import dashboard from "./dashboard.html"; + +Bun.serve({ + routes: { + "/": homepage, + "/dashboard": dashboard, + }, + + development: true, + + fetch(req) { + // ... api requests + }, +}); +``` + +### Development Mode Features + +When `development` is `true`, Bun will: + +- Include the SourceMap header in the response so that devtools can show the original source code +- Disable minification +- Re-bundle assets on each request to a `.html` file +- Enable hot module reloading (unless `hmr: false` is set) +- Echo console logs from browser to terminal + +### Advanced Development Configuration + +`Bun.serve()` supports echoing console logs from the browser to the terminal. + +To enable this, pass `console: true` in the development object in `Bun.serve()`. + +```ts title="src/backend.ts" icon="/icons/typescript.svg" +import homepage from "./index.html"; + +Bun.serve({ + // development can also be an object. + development: { + // Enable Hot Module Reloading + hmr: true, + + // Echo console logs from the browser to the terminal + console: true, + }, + + routes: { + "/": homepage, + }, +}); +``` + +When `console: true` is set, Bun will stream console logs from the browser to the terminal. This reuses the existing WebSocket connection from HMR to send the logs. + +### Development vs Production + +| Feature | Development | Production | +| ------------------- | --------------------- | ----------- | +| **Source maps** | ✅ Enabled | ❌ Disabled | +| **Minification** | ❌ Disabled | ✅ Enabled | +| **Hot reloading** | ✅ Enabled | ❌ Disabled | +| **Asset bundling** | 🔄 On each request | 💾 Cached | +| **Console logging** | 🖥️ Browser → Terminal | ❌ Disabled | +| **Error details** | 📝 Detailed | 🔒 Minimal | + +## Production Mode + +Hot reloading and `development: true` helps you iterate quickly, but in production, your server should be as fast as possible and have as few external dependencies as possible. + +### Ahead of Time Bundling (Recommended) + +As of Bun v1.2.17, you can use `Bun.build` or `bun build` to bundle your full-stack application ahead of time. + +```bash terminal icon="terminal" +bun build --target=bun --production --outdir=dist ./src/index.ts +``` + +When Bun's bundler sees an HTML import from server-side code, it will bundle the referenced JavaScript/TypeScript/TSX/JSX and CSS files into a manifest object that `Bun.serve()` can use to serve the assets. + +```ts title="src/backend.ts" icon="/icons/typescript.svg" +import { serve } from "bun"; +import index from "./index.html"; + +serve({ + routes: { "/": index }, +}); +``` + +### Runtime Bundling + +When adding a build step is too complicated, you can set `development: false` in `Bun.serve()`. + +This will: + +- Enable in-memory caching of bundled assets. Bun will bundle assets lazily on the first request to an `.html` file, and cache the result in memory until the server restarts. +- Enable `Cache-Control` headers and `ETag` headers +- Minify JavaScript/TypeScript/TSX/JSX files + +```ts title="src/backend.ts" icon="/icons/typescript.svg" +import { serve } from "bun"; +import homepage from "./index.html"; + +serve({ + routes: { + "/": homepage, + }, + + // Production mode + development: false, +}); +``` + +## API Routes + +### HTTP Method Handlers + +Define API endpoints with HTTP method handlers: + +```ts title="src/backend.ts" icon="/icons/typescript.svg" +import { serve } from "bun"; + +serve({ + routes: { + "/api/users": { + async GET(req) { + // Handle GET requests + const users = await getUsers(); + return Response.json(users); + }, + + async POST(req) { + // Handle POST requests + const userData = await req.json(); + const user = await createUser(userData); + return Response.json(user, { status: 201 }); + }, + + async PUT(req) { + // Handle PUT requests + const userData = await req.json(); + const user = await updateUser(userData); + return Response.json(user); + }, + + async DELETE(req) { + // Handle DELETE requests + await deleteUser(req.params.id); + return new Response(null, { status: 204 }); + }, + }, + }, +}); +``` + +### Dynamic Routes + +Use URL parameters in your routes: + +```ts title="src/backend.ts" icon="/icons/typescript.svg" +serve({ + routes: { + // Single parameter + "/api/users/:id": async req => { + const { id } = req.params; + const user = await getUserById(id); + return Response.json(user); + }, + + // Multiple parameters + "/api/users/:userId/posts/:postId": async req => { + const { userId, postId } = req.params; + const post = await getPostByUser(userId, postId); + return Response.json(post); + }, + + // Wildcard routes + "/api/files/*": async req => { + const filePath = req.params["*"]; + const file = await getFile(filePath); + return new Response(file); + }, + }, +}); +``` + +### Request Handling + +```ts title="src/backend.ts" icon="/icons/typescript.svg" +serve({ + routes: { + "/api/data": { + async POST(req) { + // Parse JSON body + const body = await req.json(); + + // Access headers + const auth = req.headers.get("Authorization"); + + // Access URL parameters + const { id } = req.params; + + // Access query parameters + const url = new URL(req.url); + const page = url.searchParams.get("page") || "1"; + + // Return response + return Response.json({ + message: "Data processed", + page: parseInt(page), + authenticated: !!auth, + }); + }, + }, + }, +}); +``` + +## Plugins + +Bun's bundler plugins are also supported when bundling static routes. + +To configure plugins for `Bun.serve`, add a `plugins` array in the `[serve.static]` section of your `bunfig.toml`. + +### TailwindCSS Plugin + +You can use TailwindCSS by installing and adding the `tailwindcss` package and `bun-plugin-tailwind` plugin. + +```bash terminal icon="terminal" +bun add tailwindcss bun-plugin-tailwind +``` + +```toml title="bunfig.toml" icon="settings" +[serve.static] +plugins = ["bun-plugin-tailwind"] +``` + +This will allow you to use TailwindCSS utility classes in your HTML and CSS files. All you need to do is import `tailwindcss` somewhere: + +```html title="index.html" icon="file-code" + + + + + + + + +``` + +Alternatively, you can import TailwindCSS in your CSS file: + +```css title="style.css" icon="file-code" +@import "tailwindcss"; + +.custom-class { + @apply bg-red-500 text-white; +} +``` + +```html index.html icon="file-code" + + + + + + + + +``` + +### Custom Plugins + +Any JS file or module which exports a valid bundler plugin object (essentially an object with a `name` and `setup` field) can be placed inside the plugins array: + +```toml title="bunfig.toml" icon="settings" +[serve.static] +plugins = ["./my-plugin-implementation.ts"] +``` + +```ts title="my-plugin-implementation.ts" icon="/icons/typescript.svg" +import type { BunPlugin } from "bun"; + +const myPlugin: BunPlugin = { + name: "my-custom-plugin", + setup(build) { + // Plugin implementation + build.onLoad({ filter: /\.custom$/ }, async args => { + const text = await Bun.file(args.path).text(); + return { + contents: `export default ${JSON.stringify(text)};`, + loader: "js", + }; + }); + }, +}; + +export default myPlugin; +``` + +Bun will lazily resolve and load each plugin and use them to bundle your routes. + + + This is currently in `bunfig.toml` to make it possible to know statically which plugins are in use when we eventually + integrate this with the `bun build` CLI. These plugins work in `Bun.build()`'s JS API, but are not yet supported in + the CLI. + + +## How It Works + +Bun uses `HTMLRewriter` to scan for ` +``` + + + +- Processes CSS imports and `` tags +- Concatenates CSS files +- Rewrites url and asset paths to include content-addressable hashes in URLs + +```html title="index.html" icon="file-code" + +``` + + + +- Links to assets are rewritten to include content-addressable hashes in URLs +- Small assets in CSS files are inlined into `data:` URLs, reducing the total number of HTTP requests sent over the wire + + +- Combines all ` + + +``` + +```tsx title="src/main.tsx" +import { createRoot } from "react-dom/client"; +import { App } from "./App"; + +const container = document.getElementById("root")!; +const root = createRoot(container); +root.render(); +``` + +```tsx title="src/App.tsx" +import { useState, useEffect } from "react"; + +interface User { + id: number; + name: string; + email: string; + created_at: string; +} + +export function App() { + const [users, setUsers] = useState([]); + const [name, setName] = useState(""); + const [email, setEmail] = useState(""); + const [loading, setLoading] = useState(false); + + const fetchUsers = async () => { + const response = await fetch("/api/users"); + const data = await response.json(); + setUsers(data); + }; + + const createUser = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + + try { + const response = await fetch("/api/users", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ name, email }), + }); + + if (response.ok) { + setName(""); + setEmail(""); + await fetchUsers(); + } else { + const error = await response.json(); + alert(error.error); + } + } catch (error) { + alert("Failed to create user"); + } finally { + setLoading(false); + } + }; + + const deleteUser = async (id: number) => { + if (!confirm("Are you sure?")) return; + + try { + const response = await fetch(`/api/users/${id}`, { + method: "DELETE", + }); + + if (response.ok) { + await fetchUsers(); + } + } catch (error) { + alert("Failed to delete user"); + } + }; + + useEffect(() => { + fetchUsers(); + }, []); + + return ( +
+

User Management

+ +
+ setName(e.target.value)} required /> + setEmail(e.target.value)} required /> + +
+ +
+

Users ({users.length})

+ {users.map(user => ( +
+
+ {user.name} +
+ {user.email} +
+ +
+ ))} +
+
+ ); +} +``` + +```css title="src/styles.css" +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, sans-serif; + background: #f5f5f5; + color: #333; +} + +.container { + max-width: 800px; + margin: 0 auto; + padding: 2rem; +} + +h1 { + color: #2563eb; + margin-bottom: 2rem; +} + +.form { + background: white; + padding: 1.5rem; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + margin-bottom: 2rem; + display: flex; + gap: 1rem; + flex-wrap: wrap; +} + +.form input { + flex: 1; + min-width: 200px; + padding: 0.75rem; + border: 1px solid #ddd; + border-radius: 4px; +} + +.form button { + padding: 0.75rem 1.5rem; + background: #2563eb; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; +} + +.form button:hover { + background: #1d4ed8; +} + +.form button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.users { + background: white; + padding: 1.5rem; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.user-card { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem; + border-bottom: 1px solid #eee; +} + +.user-card:last-child { + border-bottom: none; +} + +.delete-btn { + padding: 0.5rem 1rem; + background: #dc2626; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; +} + +.delete-btn:hover { + background: #b91c1c; +} +``` + +## Best Practices + +### Project Structure + +``` +my-app/ +├── src/ +│ ├── components/ +│ │ ├── Header.tsx +│ │ └── UserList.tsx +│ ├── styles/ +│ │ ├── globals.css +│ │ └── components.css +│ ├── utils/ +│ │ └── api.ts +│ ├── App.tsx +│ └── main.tsx +├── public/ +│ ├── index.html +│ ├── dashboard.html +│ └── favicon.ico +├── server/ +│ ├── routes/ +│ │ ├── users.ts +│ │ └── auth.ts +│ ├── db/ +│ │ └── schema.sql +│ └── index.ts +├── bunfig.toml +└── package.json +``` + +### Environment-Based Configuration + +```ts title="server/config.ts" icon="/icons/typescript.svg" +export const config = { + development: process.env.NODE_ENV !== "production", + port: process.env.PORT || 3000, + database: { + url: process.env.DATABASE_URL || "./dev.db", + }, + cors: { + origin: process.env.CORS_ORIGIN || "*", + }, +}; +``` + +### Error Handling + +```ts title="server/middleware.ts" icon="/icons/typescript.svg" +export function errorHandler(error: Error, req: Request) { + console.error("Server error:", error); + + if (process.env.NODE_ENV === "production") { + return Response.json({ error: "Internal server error" }, { status: 500 }); + } + + return Response.json( + { + error: error.message, + stack: error.stack, + }, + { status: 500 }, + ); +} +``` + +### API Response Helpers + +```ts title="server/utils.ts" icon="/icons/typescript.svg" +export function json(data: any, status = 200) { + return Response.json(data, { status }); +} + +export function error(message: string, status = 400) { + return Response.json({ error: message }, { status }); +} + +export function notFound(message = "Not found") { + return error(message, 404); +} + +export function unauthorized(message = "Unauthorized") { + return error(message, 401); +} +``` + +### Type Safety + +```ts title="types/api.ts" icon="/icons/typescript.svg" +export interface User { + id: number; + name: string; + email: string; + created_at: string; +} + +export interface CreateUserRequest { + name: string; + email: string; +} + +export interface ApiResponse { + data?: T; + error?: string; +} +``` + +## Deployment + +### Production Build + +```bash terminal icon="terminal" +# Build for production +bun build --target=bun --production --outdir=dist ./server/index.ts + +# Run production server +NODE_ENV=production bun dist/index.js +``` + +### Docker Deployment + +```dockerfile title="Dockerfile" icon="docker" +FROM oven/bun:1 as base +WORKDIR /usr/src/app + +# Install dependencies +COPY package.json bun.lockb ./ +RUN bun install --frozen-lockfile + +# Copy source code +COPY . . + +# Build application +RUN bun build --target=bun --production --outdir=dist ./server/index.ts + +# Production stage +FROM oven/bun:1-slim +WORKDIR /usr/src/app +COPY --from=base /usr/src/app/dist ./ +COPY --from=base /usr/src/app/public ./public + +EXPOSE 3000 +CMD ["bun", "index.js"] +``` + +### Environment Variables + +```bash title=".env.production" icon="file-code" +NODE_ENV=production +PORT=3000 +DATABASE_URL=postgresql://user:pass@localhost:5432/myapp +CORS_ORIGIN=https://myapp.com +``` + +## Migration from Other Frameworks + +### From Express + Webpack + +```ts title="server.ts" icon="/icons/typescript.svg" +// Before (Express + Webpack) +app.use(express.static("dist")); +app.get("/api/users", (req, res) => { + res.json(users); +}); + +// After (Bun fullstack) +serve({ + routes: { + "/": homepage, // Replaces express.static + "/api/users": { + GET() { + return Response.json(users); + }, + }, + }, +}); +``` + +### From Next.js API Routes + +```ts title="server.ts" icon="/icons/typescript.svg" +// Before (Next.js) +export default function handler(req, res) { + if (req.method === 'GET') { + res.json(users); + } +} + +// After (Bun) +"/api/users": { + GET() { return Response.json(users); } +} +``` + +## Limitations and Future Plans + +### Current Limitations + +- `bun build` CLI integration is not yet available for fullstack apps +- Auto-discovery of API routes is not implemented +- Server-side rendering (SSR) is not built-in + +### Planned Features + +- Integration with `bun build` CLI +- File-based routing for API endpoints +- Built-in SSR support +- Enhanced plugin ecosystem + +This is a work in progress. Features and APIs may change as Bun continues to evolve. diff --git a/docs/bundler/hmr.md b/docs/bundler/hot-reloading.mdx similarity index 50% rename from docs/bundler/hmr.md rename to docs/bundler/hot-reloading.mdx index 6213561ea4..3e82484220 100644 --- a/docs/bundler/hmr.md +++ b/docs/bundler/hot-reloading.mdx @@ -1,32 +1,35 @@ -Hot Module Replacement (HMR) allows you to update modules in a running -application without needing a full page reload. This preserves the application -state and improves the development experience. +--- +title: Hot reloading +description: Hot Module Replacement (HMR) for Bun's development server +--- -HMR is enabled by default when using Bun's full-stack development server. +Hot Module Replacement (HMR) allows you to update modules in a running application without needing a full page reload. This preserves the application state and improves the development experience. + +HMR is enabled by default when using Bun's full-stack development server. ## `import.meta.hot` API Reference -Bun implements a client-side HMR API modeled after [Vite's `import.meta.hot` API](https://vitejs.dev/guide/api-hmr.html). It can be checked for with `if (import.meta.hot)`, tree-shaking it in production +Bun implements a client-side HMR API modeled after [Vite's `import.meta.hot` API](https://vitejs.dev/guide/api-hmr.html). It can be checked for with `if (import.meta.hot)`, tree-shaking it in production. -```ts +```ts title="index.ts" icon="/icons/typescript.svg" if (import.meta.hot) { // HMR APIs are available. } ``` -However, **this check is often not needed** as Bun will dead-code-eliminate -calls to all of the HMR APIs in production builds. +However, this check is often not needed as Bun will dead-code-eliminate calls to all of the HMR APIs in production builds. -```ts +```ts title="index.ts" icon="/icons/typescript.svg" // This entire function call will be removed in production! import.meta.hot.dispose(() => { console.log("dispose"); }); ``` + For this to work, Bun forces these APIs to be called without indirection. That means the following do not work: -```ts#invalid-hmr-usage.ts +```ts title="index.ts" icon="/icons/typescript.svg" // INVALID: Assigning `hot` to a variable const hot = import.meta.hot; hot.accept(); @@ -46,32 +49,32 @@ import.meta.hot.accept(); doSomething(import.meta.hot.data); ``` -{% callout %} + -**Note** — The HMR API is still a work in progress. Some features are missing. HMR can be disabled in `Bun.serve` by setting the `development` option to `{ hmr: false }`. + +The HMR API is still a work in progress. Some features are missing. HMR can be disabled in `Bun.serve` by setting the development option to `{ hmr: false }`. + -{% endcallout %} +## API Methods -| | Method | Notes | -| --- | ------------------ | --------------------------------------------------------------------- | -| ✅ | `hot.accept()` | Indicate that a hot update can be replaced gracefully. | -| ✅ | `hot.data` | Persist data between module evaluations. | -| ✅ | `hot.dispose()` | Add a callback function to run when a module is about to be replaced. | -| ❌ | `hot.invalidate()` | | -| ✅ | `hot.on()` | Attach an event listener | -| ✅ | `hot.off()` | Remove an event listener from `on`. | -| ❌ | `hot.send()` | | -| 🚧 | `hot.prune()` | **NOTE**: Callback is currently never called. | -| ✅ | `hot.decline()` | No-op to match Vite's `import.meta.hot` | +| Method | Status | Notes | +| ------------------ | ------ | --------------------------------------------------------------------- | +| `hot.accept()` | ✅ | Indicate that a hot update can be replaced gracefully. | +| `hot.data` | ✅ | Persist data between module evaluations. | +| `hot.dispose()` | ✅ | Add a callback function to run when a module is about to be replaced. | +| `hot.invalidate()` | ❌ | | +| `hot.on()` | ✅ | Attach an event listener | +| `hot.off()` | ✅ | Remove an event listener from `on`. | +| `hot.send()` | ❌ | | +| `hot.prune()` | 🚧 | NOTE: Callback is currently never called. | +| `hot.decline()` | ✅ | No-op to match Vite's `import.meta.hot` | -### `import.meta.hot.accept()` +## import.meta.hot.accept() -The `accept()` method indicates that a module can be hot-replaced. When called -without arguments, it indicates that this module can be replaced simply by -re-evaluating the file. After a hot update, importers of this module will be -automatically patched. +The `accept()` method indicates that a module can be hot-replaced. When called without arguments, it indicates that this module can be replaced simply by re-evaluating the file. After a hot update, importers of this module will be automatically patched. -```ts#index.ts +```ts title="index.ts" icon="/icons/typescript.svg" +// index.ts import { getCount } from "./foo.ts"; console.log("count is ", getCount()); @@ -83,28 +86,21 @@ export function getNegativeCount() { } ``` -This creates a hot-reloading boundary for all of the files that `index.ts` -imports. That means whenever `foo.ts` or any of its dependencies are saved, the -update will bubble up to `index.ts` will re-evaluate. Files that import -`index.ts` will then be patched to import the new version of -`getNegativeCount()`. If only `index.ts` is updated, only the one file will be -re-evaluated, and the counter in `foo.ts` is reused. +This creates a hot-reloading boundary for all of the files that `index.ts` imports. That means whenever `foo.ts` or any of its dependencies are saved, the update will bubble up to `index.ts` will re-evaluate. Files that import `index.ts` will then be patched to import the new version of `getNegativeCount()`. If only `index.ts` is updated, only the one file will be re-evaluated, and the counter in `foo.ts` is reused. -This may be used in combination with `import.meta.hot.data` to transfer state -from the previous module to the new one. +This may be used in combination with `import.meta.hot.data` to transfer state from the previous module to the new one. -When no modules call `import.meta.hot.accept()` (and there isn't React Fast -Refresh or a plugin calling it for you), the page will reload when the file -updates, and a console warning shows which files were invalidated. This warning -is safe to ignore if it makes more sense to rely on full page reloads. + + When no modules call `import.meta.hot.accept()` (and there isn't React Fast Refresh or a plugin calling it for you), + the page will reload when the file updates, and a console warning shows which files were invalidated. This warning is + safe to ignore if it makes more sense to rely on full page reloads. + -#### With callback +### With callback -When provided one callback, `import.meta.hot.accept` will function how it does -in Vite. Instead of patching the importers of this module, it will call the -callback with the new module. +When provided one callback, `import.meta.hot.accept` will function how it does in Vite. Instead of patching the importers of this module, it will call the callback with the new module. -```ts +```ts title="index.ts" icon="/icons/typescript.svg" export const count = 0; import.meta.hot.accept(newModule => { @@ -115,11 +111,13 @@ import.meta.hot.accept(newModule => { }); ``` -Prefer using `import.meta.hot.accept()` without an argument as it usually makes your code easier to understand. + + Prefer using `import.meta.hot.accept()` without an argument as it usually makes your code easier to understand. + -#### Accepting other modules +### Accepting other modules -```ts +```ts title="index.ts" icon="/icons/typescript.svg" import { count } from "./foo"; import.meta.hot.accept("./foo", () => { @@ -131,9 +129,9 @@ import.meta.hot.accept("./foo", () => { Indicates that a dependency's module can be accepted. When the dependency is updated, the callback will be called with the new module. -#### With multiple dependencies +### With multiple dependencies -```ts +```ts title="index.ts" icon="/icons/typescript.svg" import.meta.hot.accept(["./foo", "./bar"], newModules => { // newModules is an array where each item corresponds to the updated module // or undefined if that module had a syntax error @@ -142,33 +140,33 @@ import.meta.hot.accept(["./foo", "./bar"], newModules => { Indicates that multiple dependencies' modules can be accepted. This variant accepts an array of dependencies, where the callback will receive the updated modules, and `undefined` for any that had errors. -### `import.meta.hot.data` +## import.meta.hot.data -`import.meta.hot.data` maintains state between module instances during hot -replacement, enabling data transfer from previous to new versions. When -`import.meta.hot.data` is written into, Bun will also mark this module as -capable of self-accepting (equivalent of calling `import.meta.hot.accept()`). +`import.meta.hot.data` maintains state between module instances during hot replacement, enabling data transfer from previous to new versions. When `import.meta.hot.data` is written into, Bun will also mark this module as capable of self-accepting (equivalent of calling `import.meta.hot.accept()`). -```ts +```jsx title="index.ts" icon="/icons/typescript.svg" import { createRoot } from "react-dom/client"; import { App } from "./app"; -const root = import.meta.hot.data.root ??= createRoot(elem); +const root = (import.meta.hot.data.root ??= createRoot(elem)); root.render(); // re-use an existing root ``` In production, `data` is inlined to be `{}`, meaning it cannot be used as a state holder. -The above pattern is recommended for stateful modules because Bun knows it can minify `{}.prop ??= value` into `value` in production. + + The above pattern is recommended for stateful modules because Bun knows it can minify `{}.prop ??= value` into `value` + in production. + -### `import.meta.hot.dispose()` +## import.meta.hot.dispose() Attaches an on-dispose callback. This is called: - Just before the module is replaced with another copy (before the next is loaded) - After the module is detached (removing all imports to this module, see `import.meta.hot.prune()`) -```ts +```ts title="index.ts" icon="/icons/typescript.svg" const sideEffect = setupSideEffect(); import.meta.hot.dispose(() => { @@ -176,21 +174,17 @@ import.meta.hot.dispose(() => { }); ``` -This callback is not called on route navigation or when the browser tab closes. +This callback is not called on route navigation or when the browser tab closes. -Returning a promise will delay module replacement until the module is disposed. -All dispose callbacks are called in parallel. +Returning a promise will delay module replacement until the module is disposed. All dispose callbacks are called in parallel. -### `import.meta.hot.prune()` +## import.meta.hot.prune() -Attaches an on-prune callback. This is called when all imports to this module -are removed, but the module was previously loaded. +Attaches an on-prune callback. This is called when all imports to this module are removed, but the module was previously loaded. -This can be used to clean up resources that were created when the module was -loaded. Unlike `import.meta.hot.dispose()`, this pairs much better with `accept` -and `data` to manage stateful resources. A full example managing a `WebSocket`: +This can be used to clean up resources that were created when the module was loaded. Unlike `import.meta.hot.dispose()`, this pairs much better with `accept` and `data` to manage stateful resources. A full example managing a WebSocket: -```ts +```ts title="index.ts" icon="/icons/typescript.svg" import { something } from "./something"; // Initialize or re-use a WebSocket connection @@ -202,15 +196,16 @@ import.meta.hot.prune(() => { }); ``` -If `dispose` was used instead, the WebSocket would close and re-open on every -hot update. Both versions of the code will prevent page reloads when imported -files are updated. + + If `dispose` was used instead, the WebSocket would close and re-open on every hot update. Both versions of the code + will prevent page reloads when imported files are updated. + -### `import.meta.hot.on()` and `off()` +## import.meta.hot.on() and off() `on()` and `off()` are used to listen for events from the HMR runtime. Event names are prefixed with a prefix so that plugins do not conflict with each other. -```ts +```ts title="index.ts" icon="/icons/typescript.svg" import.meta.hot.on("bun:beforeUpdate", () => { console.log("before a hot update"); }); @@ -218,7 +213,7 @@ import.meta.hot.on("bun:beforeUpdate", () => { When a file is replaced, all of its event listeners are automatically removed. -A list of all built-in events: +### Built-in events | Event | Emitted when | | ---------------------- | ----------------------------------------------------------------------------------------------- | @@ -231,4 +226,4 @@ A list of all built-in events: | `bun:ws:disconnect` | when the HMR WebSocket connection is lost. This can indicate the development server is offline. | | `bun:ws:connect` | when the HMR WebSocket connects or re-connects. | -For compatibility with Vite, the above events are also available via `vite:*` prefix instead of `bun:*`. +For compatibility with Vite, the above events are also available via `vite:*` prefix instead of `bun:*`. diff --git a/docs/bundler/html.md b/docs/bundler/html-static.mdx similarity index 57% rename from docs/bundler/html.md rename to docs/bundler/html-static.mdx index b4de1bdf83..e1a2a3c9b9 100644 --- a/docs/bundler/html.md +++ b/docs/bundler/html-static.mdx @@ -1,6 +1,11 @@ +--- +title: HTML & static sites +description: Build static sites, landing pages, and web applications with Bun's bundler +--- + Bun's bundler has first-class support for HTML. Build static sites, landing pages, and web applications with zero configuration. Just point Bun at your HTML file and it handles everything else. -```html#index.html +```html title="index.html" icon="file-code" @@ -15,7 +20,16 @@ Bun's bundler has first-class support for HTML. Build static sites, landing page To get started, pass HTML files to `bun`. -{% bunDevServerTerminal alt="bun ./index.html" path="./index.html" routes="" /%} +```bash terminal icon="terminal" +bun ./index.html +``` + +``` +Bun v1.2.20 +ready in 6.62ms +→ http://localhost:3000/ +Press h + Enter to show shortcuts +``` Bun's development server provides powerful features with zero configuration: @@ -26,19 +40,26 @@ Bun's development server provides powerful features with zero configuration: - **Plugins** - Plugins for TailwindCSS and more - **ESM & CommonJS** - Use ESM and CommonJS in your JavaScript, TypeScript, and JSX files - **CSS Bundling & Minification** - Bundles CSS from `` tags and `@import` statements -- **Asset Management** - - Automatic copying & hashing of images and assets - - Rewrites asset paths in JavaScript, CSS, and HTML +- **Asset Management** - Automatic copying & hashing of images and assets; Rewrites asset paths in JavaScript, CSS, and HTML ## Single Page Apps (SPA) -When you pass a single .html file to Bun, Bun will use it as a fallback route for all paths. This makes it perfect for single page apps that use client-side routing: +When you pass a single `.html` file to Bun, Bun will use it as a fallback route for all paths. This makes it perfect for single page apps that use client-side routing: -{% bunDevServerTerminal alt="bun index.html" path="index.html" routes="" /%} +```bash terminal icon="terminal" +bun index.html +``` + +``` +Bun v1.2.20 +ready in 6.62ms +→ http://localhost:3000/ +Press h + Enter to show shortcuts +``` Your React or other SPA will work out of the box — no configuration needed. All routes like `/about`, `/users/123`, etc. will serve the same HTML file, letting your client-side router handle the navigation. -```html#index.html +```html title="index.html" icon="file-code" @@ -53,9 +74,21 @@ Your React or other SPA will work out of the box — no configuration needed. Al ## Multi-page apps (MPA) -Some projects have several separate routes or HTML files as entry points. To support multiple entry points, pass them all to `bun` +Some projects have several separate routes or HTML files as entry points. To support multiple entry points, pass them all to `bun`: -{% bunDevServerTerminal alt="bun ./index.html ./about.html" path="./index.html ./about.html" routes="[{\"path\": \"/\", \"file\": \"./index.html\"}, {\"path\": \"/about\", \"file\": \"./about.html\"}]" /%} +```bash terminal icon="terminal" +bun ./index.html ./about.html +``` + +```txt +Bun v1.2.20 +ready in 6.62ms +→ http://localhost:3000/ +Routes: + / ./index.html + /about ./about.html +Press h + Enter to show shortcuts +``` This will serve: @@ -66,19 +99,44 @@ This will serve: To specify multiple files, you can use glob patterns that end in `.html`: -{% bunDevServerTerminal alt="bun ./**/*.html" path="./**/*.html" routes="[{\"path\": \"/\", \"file\": \"./index.html\"}, {\"path\": \"/about\", \"file\": \"./about.html\"}]" /%} +```bash terminal icon="terminal" +bun ./**/*.html +``` + +``` +Bun v1.2.20 +ready in 6.62ms +→ http://localhost:3000/ +Routes: + / ./index.html + /about ./about.html +Press h + Enter to show shortcuts +``` ### Path normalization The base path is chosen from the longest common prefix among all the files. -{% bunDevServerTerminal alt="bun ./index.html ./about/index.html ./about/foo/index.html" path="./index.html ./about/index.html ./about/foo/index.html" routes="[{\"path\": \"/\", \"file\": \"./index.html\"}, {\"path\": \"/about\", \"file\": \"./about/index.html\"}, {\"path\": \"/about/foo\", \"file\": \"./about/foo/index.html\"}]" /%} +```bash terminal icon="terminal" +bun ./index.html ./about/index.html ./about/foo/index.html +``` + +``` +Bun v1.2.20 +ready in 6.62ms +→ http://localhost:3000/ +Routes: + / ./index.html + /about ./about/index.html + /about/foo ./about/foo/index.html +Press h + Enter to show shortcuts +``` ## JavaScript, TypeScript, and JSX -Bun's transpiler natively implements JavaScript, TypeScript, and JSX support. [Learn more about loaders in Bun](/docs/bundler/loaders). +Bun's transpiler natively implements JavaScript, TypeScript, and JSX support. Learn more about loaders in Bun. -Bun's transpiler is also used at runtime. +Bun's transpiler is also used at runtime. ### ES Modules & CommonJS @@ -86,7 +144,7 @@ You can use ESM and CJS in your JavaScript, TypeScript, and JSX files. Bun will There is no pre-build or separate optimization step. It's all done at the same time. -Learn more about [module resolution in Bun](/docs/runtime/modules). +Learn more about module resolution in Bun. ## CSS @@ -96,7 +154,9 @@ It's also a CSS bundler. You can use `@import` in your CSS files to import other For example: -```css#styles.css + + +```css styles.css icon="file-code" @import "./abc.css"; .container { @@ -104,15 +164,17 @@ For example: } ``` -```css#abc.css +```css abc.css body { background-color: red; } ``` + + This outputs: -```css#styles.css +```css body { background-color: red; } @@ -126,7 +188,7 @@ body { You can reference local assets in your CSS files. -```css#styles.css +```css styles.css icon="file-code" body { background-image: url("./logo.png"); } @@ -134,7 +196,7 @@ body { This will copy `./logo.png` to the output directory and rewrite the path in the CSS file to include a content hash. -```css#styles.css +```css styles.css icon="file-code" body { background-image: url("./logo-[ABC123].png"); } @@ -144,7 +206,7 @@ body { To associate a CSS file with a JavaScript file, you can import it in your JavaScript file. -```ts#app.ts +```ts app.ts icon="/icons/typescript.svg" import "./styles.css"; import "./more-styles.css"; ``` @@ -159,84 +221,57 @@ The dev server supports plugins. To use TailwindCSS, install the `bun-plugin-tailwind` plugin: -```bash +```bash terminal icon="terminal" # Or any npm client -$ bun install --dev bun-plugin-tailwind +bun install --dev bun-plugin-tailwind ``` Then, add the plugin to your `bunfig.toml`: -```toml +```toml title="bunfig.toml" icon="settings" [serve.static] plugins = ["bun-plugin-tailwind"] ``` -Then, reference TailwindCSS in your HTML via `` tag, `@import` in CSS, or `import` in JavaScript. +Then, reference TailwindCSS in your HTML via `` tag, `@import` in CSS, or import in JavaScript. -{% codetabs %} + + + ```html title="index.html" icon="file-code" + {/* Reference TailwindCSS in your HTML */} + + ``` + + ```css title="styles.css" icon="file-code" @import "tailwindcss"; ``` + ```ts title="app.ts" icon="/icons/typescript.svg" import "tailwindcss"; ``` + -```html#index.html - - -``` +Only one of those are necessary, not all three. -```css#styles.css -/* Import TailwindCSS in your CSS */ -@import "tailwindcss"; -``` - -```ts#app.ts -/* Import TailwindCSS in your JavaScript */ -import "tailwindcss"; -``` - -{% /codetabs %} - -Only one of those are necessary, not all three. - -### Echo console logs from browser to terminal +## Echo console logs from browser to terminal Bun's dev server supports streaming console logs from the browser to the terminal. To enable, pass the `--console` CLI flag. -{% bunDevServerTerminal alt="bun ./index.html --console" path="./index.html --console" routes="" /%} +```bash terminal icon="terminal" +bun ./index.html --console +``` + +``` +Bun v1.2.20 +ready in 6.62ms +→ http://localhost:3000/ +Press h + Enter to show shortcuts +``` Each call to `console.log` or `console.error` will be broadcast to the terminal that started the server. This is useful to see errors from the browser in the same place you run your server. This is also useful for AI agents that watch terminal output. Internally, this reuses the existing WebSocket connection from hot module reloading to send the logs. -### Edit files in the browser +## Edit files in the browser -Bun's frontend dev server has support for [Automatic Workspace Folders](https://chromium.googlesource.com/devtools/devtools-frontend/+/main/docs/ecosystem/automatic_workspace_folders.md) in Chrome DevTools, which lets you save edits to files in the browser. - -{% image src="/images/bun-chromedevtools.gif" alt="Bun's frontend dev server has support for Automatic Workspace Folders in Chrome DevTools, which lets you save edits to files in the browser." /%} - -{% details summary="How it works" %} - -Bun's dev server automatically adds a `/.well-known/appspecific/com.chrome.devtools.json` route to the server. - -This route returns a JSON object with the following shape: - -```json -{ - "workspace": { - "root": "/path/to/your/project", - "uuid": "a-unique-identifier-for-this-workspace" - } -} -``` - -For security reasons, this is only enabled when: - -1. The request is coming from localhost, 127.0.0.1, or ::1. -2. Hot Module Reloading is enabled. -3. The `chromeDevToolsAutomaticWorkspaceFolders` flag is set to `true` or `undefined`. -4. There are no other routes that match the request. - -You can disable this by passing `development: { chromeDevToolsAutomaticWorkspaceFolders: false }` in `Bun.serve`'s options. - -{% /details %} +Bun's frontend dev server has support for Automatic Workspace Folders in Chrome DevTools, which lets you save edits to files in the browser. ## Keyboard Shortcuts @@ -244,45 +279,45 @@ While the server is running: - `o + Enter` - Open in browser - `c + Enter` - Clear console -- `q + Enter` (or Ctrl+C) - Quit server +- `q + Enter` (or `Ctrl+C`) - Quit server ## Build for Production When you're ready to deploy, use `bun build` to create optimized production bundles: -{% codetabs %} + + + ```bash terminal icon="terminal" + bun build ./index.html --minify --outdir=dist + ``` + + + ```ts title="build.ts" icon="/icons/typescript.svg" + await Bun.build({ + entrypoints: ["./index.html"], + outdir: "./dist", + minify: true, + }); + ``` + + -```bash#CLI -$ bun build ./index.html --minify --outdir=dist -``` - -```ts#API -Bun.build({ - entrypoints: ["./index.html"], - outdir: "./dist", - minify: { - whitespace: true, - identifiers: true, - syntax: true, - } -}); -``` - -{% /codetabs %} - -Currently, plugins are only supported through `Bun.build`'s API or through `bunfig.toml` with the frontend dev server - not yet supported in `bun build`'s CLI. + + Currently, plugins are only supported through `Bun.build`'s API or through `bunfig.toml` with the frontend dev server + - not yet supported in `bun build`'s CLI. + ### Watch Mode You can run `bun build --watch` to watch for changes and rebuild automatically. This works nicely for library development. -You've never seen a watch mode this fast. +You've never seen a watch mode this fast. -### Plugin API +## Plugin API Need more control? Configure the bundler through the JavaScript API and use Bun's builtin `HTMLRewriter` to preprocess HTML. -```ts +```ts title="build.ts" icon="/icons/typescript.svg" await Bun.build({ entrypoints: ["./index.html"], outdir: "./dist", @@ -322,28 +357,30 @@ await Bun.build({ Bun automatically handles all common web assets: -- Scripts (` - - -``` - -Then spin up a static file server serving the `out` directory: - -```bash -$ bunx serve out -``` - -Visit `http://localhost:5000` to see your bundled app in action. - -{% /details %} - -## Watch mode - -Like the runtime and test runner, the bundler supports watch mode natively. - -```sh -$ bun build ./index.tsx --outdir ./out --watch -``` - -## Content types - -Like the Bun runtime, the bundler supports an array of file types out of the box. The following table breaks down the bundler's set of standard "loaders". Refer to [Bundler > File types](https://bun.com/docs/runtime/loaders) for full documentation. - -{% table %} - -- Extensions -- Details - ---- - -- `.js` `.jsx`, `.cjs` `.mjs` `.mts` `.cts` `.ts` `.tsx` -- Uses Bun's built-in transpiler to parse the file and transpile TypeScript/JSX syntax to vanilla JavaScript. The bundler executes a set of default transforms including dead code elimination and tree shaking. At the moment Bun does not attempt to down-convert syntax; if you use recently ECMAScript syntax, that will be reflected in the bundled code. - ---- - -- `.json` -- JSON files are parsed and inlined into the bundle as a JavaScript object. - - ```ts - import pkg from "./package.json"; - pkg.name; // => "my-package" - ``` - ---- - -- `.toml` -- TOML files are parsed and inlined into the bundle as a JavaScript object. - - ```ts - import config from "./bunfig.toml"; - config.logLevel; // => "debug" - ``` - ---- - -- `.txt` -- The contents of the text file are read and inlined into the bundle as a string. - - ```ts - import contents from "./file.txt"; - console.log(contents); // => "Hello, world!" - ``` - ---- - -- `.node` `.wasm` -- These files are supported by the Bun runtime, but during bundling they are treated as [assets](#assets). - -{% /table %} - -### Assets - -If the bundler encounters an import with an unrecognized extension, it treats the imported file as an _external file_. The referenced file is copied as-is into `outdir`, and the import is resolved as a _path_ to the file. - -{% codetabs %} - -```ts#Input -// bundle entrypoint -import logo from "./logo.svg"; -console.log(logo); -``` - -```ts#Output -// bundled output -var logo = "./logo-ab237dfe.svg"; -console.log(logo); -``` - -{% /codetabs %} - -{% callout %} -The exact behavior of the file loader is also impacted by [`naming`](#naming) and [`publicPath`](#publicpath). -{% /callout %} - -Refer to the [Bundler > Loaders](https://bun.com/docs/bundler/loaders#file) page for more complete documentation on the file loader. - -### Plugins - -The behavior described in this table can be overridden or extended with [plugins](https://bun.com/docs/bundler/plugins). Refer to the [Bundler > Loaders](https://bun.com/docs/bundler/plugins) page for complete documentation. - -## API - -### `entrypoints` - -**Required.** An array of paths corresponding to the entrypoints of our application. One bundle will be generated for each entrypoint. - -{% codetabs group="a" %} - -```ts#JavaScript -const result = await Bun.build({ - entrypoints: ["./index.ts"], -}); -// => { success: boolean, outputs: BuildArtifact[], logs: BuildMessage[] } -``` - -```bash#CLI -$ bun build --entrypoints ./index.ts -# the bundle will be printed to stdout -# -``` - -{% /codetabs %} - -### `outdir` - -The directory where output files will be written. - -{% codetabs group="a" %} - -```ts#JavaScript -const result = await Bun.build({ - entrypoints: ['./index.ts'], - outdir: './out' -}); -// => { success: boolean, outputs: BuildArtifact[], logs: BuildMessage[] } -``` - -```bash#CLI -$ bun build --entrypoints ./index.ts --outdir ./out -# a summary of bundled files will be printed to stdout -``` - -{% /codetabs %} - -If `outdir` is not passed to the JavaScript API, bundled code will not be written to disk. Bundled files are returned in an array of `BuildArtifact` objects. These objects are Blobs with extra properties; see [Outputs](#outputs) for complete documentation. - -```ts -const result = await Bun.build({ - entrypoints: ["./index.ts"], -}); - -for (const res of result.outputs) { - // Can be consumed as blobs - await res.text(); - - // Bun will set Content-Type and Etag headers - new Response(res); - - // Can be written manually, but you should use `outdir` in this case. - Bun.write(path.join("out", res.path), res); -} -``` - -When `outdir` is set, the `path` property on a `BuildArtifact` will be the absolute path to where it was written to. - -### `target` - -The intended execution environment for the bundle. - -{% codetabs group="a" %} - -```ts#JavaScript -await Bun.build({ - entrypoints: ['./index.ts'], - outdir: './out', - target: 'browser', // default -}) -``` - -```bash#CLI -$ bun build --entrypoints ./index.ts --outdir ./out --target browser -``` - -{% /codetabs %} - -Depending on the target, Bun will apply different module resolution rules and optimizations. - -### Module resolution - -Bun supports the `NODE_PATH` environment variable for additional module resolution paths: - -```bash -NODE_PATH=./src bun build ./entry.js --outdir ./dist -``` - - - -{% table %} - ---- - -- `browser` -- _Default._ For generating bundles that are intended for execution by a browser. Prioritizes the `"browser"` export condition when resolving imports. Importing any built-in modules, like `node:events` or `node:path` will work, but calling some functions, like `fs.readFile` will not work. - ---- - -- `bun` -- For generating bundles that are intended to be run by the Bun runtime. In many cases, it isn't necessary to bundle server-side code; you can directly execute the source code without modification. However, bundling your server code can reduce startup times and improve running performance. This is the target to use for building full-stack applications with build-time HTML imports, where both server and client code are bundled together. - - All bundles generated with `target: "bun"` are marked with a special `// @bun` pragma, which indicates to the Bun runtime that there's no need to re-transpile the file before execution. - - If any entrypoints contains a Bun shebang (`#!/usr/bin/env bun`) the bundler will default to `target: "bun"` instead of `"browser"`. - - When using `target: "bun"` and `format: "cjs"` together, the `// @bun @bun-cjs` pragma is added and the CommonJS wrapper function is not compatible with Node.js. - ---- - -- `node` -- For generating bundles that are intended to be run by Node.js. Prioritizes the `"node"` export condition when resolving imports, and outputs `.mjs`. In the future, this will automatically polyfill the `Bun` global and other built-in `bun:*` modules, though this is not yet implemented. - -{% /table %} - -### `format` - -Specifies the module format to be used in the generated bundles. - -Bun defaults to `"esm"`, and provides experimental support for `"cjs"` and `"iife"`. - -#### `format: "esm"` - ES Module - -This is the default format, which supports ES Module syntax including top-level `await`, import.meta, and more. - -{% codetabs %} - -```ts#JavaScript -await Bun.build({ - entrypoints: ['./index.tsx'], - outdir: './out', - format: "esm", -}) -``` - -```bash#CLI -$ bun build ./index.tsx --outdir ./out --format esm -``` - -{% /codetabs %} - -To use ES Module syntax in browsers, set `format` to `"esm"` and make sure your ` - - -``` - -```js#index.js -import {sayHello} from "./hello.js"; - -sayHello(); -``` - -```js#hello.js -export function sayHello() { - console.log("Hello, world!"); -} -``` - -{% /codetabs %} - -When a user visits this website, the files are loaded in the following order: - -{% image src="/images/module_loading_unbundled.png" /%} - -{% callout %} -**Relative imports** — Relative imports are resolved relative to the URL of the importing file. Because we're importing `./hello.js` from `/index.js`, the browser resolves it to `/hello.js`. If instead we'd imported `./hello.js` from `/src/index.js`, the browser would have resolved it to `/src/hello.js`. -{% /callout %} - -This approach works, it requires three round-trip HTTP requests before the browser is ready to render the page. On slow internet connections, this may add up to a non-trivial delay. - -This example is extremely simplistic. A modern app may be loading dozens of modules from `node_modules`, each consisting of hundred of files. Loading each of these files with a separate HTTP request becomes untenable very quickly. While most of these requests will be running in parallel, the number of round-trip requests can still be very high; plus, there are limits on how many simultaneous requests a browser can make. - -{% callout %} -Some recent advances like modulepreload and HTTP/3 are intended to solve some of these problems, but at the moment bundling is still the most performant approach. -{% /callout %} - -The answer: bundling. - -## Entrypoints - -A bundler accepts an "entrypoint" to your source code (in this case, `/index.js`) and outputs a single file containing all of the code needed to run your app. If does so by parsing your source code, reading the `import`/`export` statements, and building a "module graph" of your app's dependencies. - -{% image src="/images/bundling.png" /%} - -We can now load `/bundle.js` from our `index.html` file and eliminate a round trip request, decreasing load times for our app. - -{% image src="/images/module_loading_bundled.png" /%} - -## Loaders - -Bundlers typically have some set of built-in "loaders". - -## Transpilation - -The JavaScript files above are just that: plain JavaScript. They can be directly executed by any modern browser. - -But modern tooling goes far beyond HTML, JavaScript, and CSS. JSX, TypeScript, and PostCSS/CSS-in-JS are all popular technologies that involve non-standard syntax that must be converted into vanilla JavaScript and CSS before if can be consumed by a browser. - -## Chunking - -## Module resolution - -## Plugins diff --git a/docs/bundler/loaders.mdx b/docs/bundler/loaders.mdx new file mode 100644 index 0000000000..a610186f58 --- /dev/null +++ b/docs/bundler/loaders.mdx @@ -0,0 +1,356 @@ +--- +title: Loaders +description: Built-in loaders for the Bun bundler and runtime +--- + +The Bun bundler implements a set of default loaders out of the box. + +> As a rule of thumb: **the bundler and the runtime both support the same set of file types out of the box.** + +`.js` `.cjs` `.mjs` `.mts` `.cts` `.ts` `.tsx` `.jsx` `.toml` `.json` `.txt` `.wasm` `.node` `.html` + +Bun uses the file extension to determine which built-in loader should be used to parse the file. Every loader has a name, such as `js`, `tsx`, or `json`. These names are used when building plugins that extend Bun with custom loaders. + +You can explicitly specify which loader to use using the `'loader'` import attribute. + +```ts title="index.ts" icon="/icons/typescript.svg" +import my_toml from "./my_file" with { loader: "toml" }; +``` + +## Built-in loaders + +### `js` + +**JavaScript loader.** Default for `.cjs` and `.mjs`. + +Parses the code and applies a set of default transforms like dead-code elimination and tree shaking. Note that Bun does not attempt to down-convert syntax at the moment. + +--- + +### `jsx` + +**JavaScript + JSX loader.** Default for `.js` and `.jsx`. + +Same as the `js` loader, but JSX syntax is supported. By default, JSX is down-converted to plain JavaScript; the details of how this is done depends on the `jsx*` compiler options in your `tsconfig.json`. Refer to the [TypeScript documentation on JSX](https://www.typescriptlang.org/tsconfig#jsx) for more information. + +--- + +### `ts` + +**TypeScript loader.** Default for `.ts`, `.mts`, and `.cts`. + +Strips out all TypeScript syntax, then behaves identically to the `js` loader. Bun does not perform typechecking. + +--- + +### `tsx` + +**TypeScript + JSX loader.** Default for `.tsx`. + +Transpiles both TypeScript and JSX to vanilla JavaScript. + +--- + +### `json` + +**JSON loader.** Default for `.json`. + +JSON files can be directly imported. + +```js +import pkg from "./package.json"; +pkg.name; // => "my-package" +``` + +During bundling, the parsed JSON is inlined into the bundle as a JavaScript object. + +```js +const pkg = { + name: "my-package", + // ... other fields +}; + +pkg.name; +``` + +If a `.json` file is passed as an entrypoint to the bundler, it will be converted to a `.js` module that `export default`s the parsed object. + + + +```json Input +{ + "name": "John Doe", + "age": 35, + "email": "johndoe@example.com" +} +``` + +```ts Output +export default { + name: "John Doe", + age: 35, + email: "johndoe@example.com", +}; +``` + + + +--- + +### toml + +**TOML loader.** Default for `.toml`. + +TOML files can be directly imported. Bun will parse them with its fast native TOML parser. + +```js +import config from "./bunfig.toml"; +config.logLevel; // => "debug" + +// via import attribute: +// import myCustomTOML from './my.config' with {type: "toml"}; +``` + +During bundling, the parsed TOML is inlined into the bundle as a JavaScript object. + +```js +var config = { + logLevel: "debug", + // ...other fields +}; +config.logLevel; +``` + +If a `.toml` file is passed as an entrypoint, it will be converted to a `.js` module that `export default`s the parsed object. + + + +```toml Input +name = "John Doe" +age = 35 +email = "johndoe@example.com" +``` + +```ts Output +export default { + name: "John Doe", + age: 35, + email: "johndoe@example.com", +}; +``` + + + +--- + +### text + +**Text loader.** Default for `.txt`. + +The contents of the text file are read and inlined into the bundle as a string. Text files can be directly imported. The file is read and returned as a string. + +```js +import contents from "./file.txt"; +console.log(contents); // => "Hello, world!" + +// To import an html file as text +// The "type" attribute can be used to override the default loader. +import html from "./index.html" with { type: "text" }; +``` + +When referenced during a build, the contents are inlined into the bundle as a string. + +```js +var contents = `Hello, world!`; +console.log(contents); +``` + +If a `.txt` file is passed as an entrypoint, it will be converted to a `.js` module that `export default`s the file contents. + + + +```txt Input +Hello, world! +``` + +```ts Output +export default "Hello, world!"; +``` + + + +--- + +### napi + +**Native addon loader.** Default for `.node`. + +In the runtime, native addons can be directly imported. + +```js +import addon from "./addon.node"; +console.log(addon); +``` + +In the bundler, `.node` files are handled using the file loader. + +--- + +### sqlite + +**SQLite loader.** Requires `with { "type": "sqlite" }` import attribute. + +In the runtime and bundler, SQLite databases can be directly imported. This will load the database using `bun:sqlite`. + +```js +import db from "./my.db" with { type: "sqlite" }; +``` + +This is only supported when the target is `bun`. + +By default, the database is external to the bundle (so that you can potentially use a database loaded elsewhere), so the database file on-disk won't be bundled into the final output. + +You can change this behavior with the `"embed"` attribute: + +```js +// embed the database into the bundle +import db from "./my.db" with { type: "sqlite", embed: "true" }; +``` + + +When using a standalone executable, the database is embedded into the single-file executable. + +Otherwise, the database to embed is copied into the `outdir` with a hashed filename. + + + +--- + +### html + +The `html` loader processes HTML files and bundles any referenced assets. It will: + +- Bundle and hash referenced JavaScript files (` + + +``` + +It will output a new HTML file with the bundled assets: + +```html title="dist/index.html" + + + + Local image + External image + + + +``` + +Under the hood, it uses [`lol-html`](https://github.com/cloudflare/lol-html) to extract script and link tags as entrypoints, and other assets as external. + + +Currently, the list of selectors is: + +- `audio[src]` +- `iframe[src]` +- `img[src]` +- `img[srcset]` +- `link:not([rel~='stylesheet']):not([rel~='modulepreload']):not([rel~='manifest']):not([rel~='icon']):not([rel~='apple-touch-icon'])[href]` +- `link[as='font'][href], link[type^='font/'][href]` +- `link[as='image'][href]` +- `link[as='style'][href]` +- `link[as='video'][href], link[as='audio'][href]` +- `link[as='worker'][href]` +- `link[rel='icon'][href], link[rel='apple-touch-icon'][href]` +- `link[rel='manifest'][href]` +- `link[rel='stylesheet'][href]` +- `script[src]` +- `source[src]` +- `source[srcset]` +- `video[poster]` +- `video[src]` + + + + + +**HTML Loader Behavior in Different Contexts** + +The `html` loader behaves differently depending on how it's used: + +- Static Build: When you run `bun build ./index.html`, Bun produces a static site with all assets bundled and hashed. +- Runtime: When you run `bun run server.ts` (where `server.ts` imports an HTML file), Bun bundles assets on-the-fly during development, enabling features like hot module replacement. +- Full-stack Build: When you run `bun build --target=bun server.ts` (where `server.ts` imports an HTML file), the import resolves to a manifest object that `Bun.serve` uses to efficiently serve pre-bundled assets in production. + + + +--- + +### sh + +**Bun Shell loader.** Default for `.sh` files. + +This loader is used to parse Bun Shell scripts. It's only supported when starting Bun itself, so it's not available in the bundler or in the runtime. + +```bash +bun run ./script.sh +``` + +--- + +### file + +**File loader.** Default for all unrecognized file types. + +The file loader resolves the import as a path/URL to the imported file. It's commonly used for referencing media or font assets. + +```js +// logo.ts +import logo from "./logo.svg"; +console.log(logo); +``` + +In the runtime, Bun checks that the `logo.svg` file exists and converts it to an absolute path to the location of `logo.svg` on disk. + +```bash +bun run logo.ts +# Output: /path/to/project/logo.svg +``` + +In the bundler, things are slightly different. The file is copied into `outdir` as-is, and the import is resolved as a relative path pointing to the copied file. + +```js +// Output +var logo = "./logo.svg"; +console.log(logo); +``` + +If a value is specified for `publicPath`, the import will use value as a prefix to construct an absolute path/URL. + +| Public path | Resolved import | +| ---------------------------- | ---------------------------------- | +| `""` (default) | `/logo.svg` | +| `"/assets"` | `/assets/logo.svg` | +| `"https://cdn.example.com/"` | `https://cdn.example.com/logo.svg` | + + +The location and file name of the copied file is determined by the value of `naming.asset`. + +This loader is copied into the `outdir` as-is. The name of the copied file is determined using the value of `naming.asset`. + + diff --git a/docs/bundler/macros.md b/docs/bundler/macros.mdx similarity index 61% rename from docs/bundler/macros.md rename to docs/bundler/macros.mdx index eb4ee464cf..e4cfffeb37 100644 --- a/docs/bundler/macros.md +++ b/docs/bundler/macros.mdx @@ -1,10 +1,13 @@ -Macros are a mechanism for running JavaScript functions _at bundle-time_. The value returned from these functions are directly inlined into your bundle. +--- +title: Macros +description: Run JavaScript functions at bundle-time with Bun macros +--- - +Macros are a mechanism for running JavaScript functions at bundle-time. The value returned from these functions are directly inlined into your bundle. As a toy example, consider this simple function that returns a random number. -```ts +```ts title="random.ts" icon="/icons/typescript.svg" export function random() { return Math.random(); } @@ -12,24 +15,28 @@ export function random() { This is just a regular function in a regular file, but we can use it as a macro like so: -```ts#cli.tsx -import { random } from './random.ts' with { type: 'macro' }; +```tsx title="cli.tsx" icon="/icons/typescript.svg" +import { random } from "./random.ts" with { type: "macro" }; console.log(`Your random number is ${random()}`); ``` -{% callout %} -**Note** — Macros are indicated using [_import attribute_](https://github.com/tc39/proposal-import-attributes) syntax. If you haven't seen this syntax before, it's a Stage 3 TC39 proposal that lets you attach additional metadata to `import` statements. -{% /callout %} + + Macros are indicated using import attribute syntax. If you haven't seen this syntax before, it's a Stage 3 TC39 + proposal that lets you attach additional metadata to import statements. + Now we'll bundle this file with `bun build`. The bundled file will be printed to stdout. -```bash -$ bun build ./cli.tsx +```bash terminal icon="terminal" +bun build ./cli.tsx +``` + +```js console.log(`Your random number is ${0.6805550949689833}`); ``` -As you can see, the source code of the `random` function occurs nowhere in the bundle. Instead, it is executed _during bundling_ and function call (`random()`) is replaced with the result of the function. Since the source code will never be included in the bundle, macros can safely perform privileged operations like reading from a database. +As you can see, the source code of the `random` function occurs nowhere in the bundle. Instead, it is executed during bundling and function call (`random()`) is replaced with the result of the function. Since the source code will never be included in the bundle, macros can safely perform privileged operations like reading from a database. ## When to use macros @@ -41,8 +48,8 @@ If you find yourself running a lot of code at bundle-time though, consider runni Bun Macros are import statements annotated using either: -- `with { type: 'macro' }` — an [import attribute](https://github.com/tc39/proposal-import-attributes), a Stage 3 ECMA Scrd -- `assert { type: 'macro' }` — an import assertion, an earlier incarnation of import attributes that has now been abandoned (but is [already supported](https://caniuse.com/mdn-javascript_statements_import_import_assertions) by a number of browsers and runtimes) +- `with { type: 'macro' }` — an import attribute, a Stage 3 ECMA Script proposal +- `assert { type: 'macro' }` — an import assertion, an earlier incarnation of import attributes that has now been abandoned (but is already supported by a number of browsers and runtimes) ## Security considerations @@ -50,7 +57,7 @@ Macros must explicitly be imported with `{ type: "macro" }` in order to be execu You can disable macros entirely by passing the `--no-macros` flag to Bun. It produces a build error like this: -```js +``` error: Macros are disabled foo(); @@ -58,9 +65,9 @@ foo(); ./hello.js:3:1 53 ``` -To reduce the potential attack surface for malicious packages, macros cannot be _invoked_ from inside `node_modules/**/*`. If a package attempts to invoke a macro, you'll see an error like this: +To reduce the potential attack surface for malicious packages, macros cannot be invoked from inside `node_modules/**/*`. If a package attempts to invoke a macro, you'll see an error like this: -```js +``` error: For security reasons, macros cannot be run from node_modules. beEvil(); @@ -70,17 +77,17 @@ node_modules/evil/index.js:3:1 50 Your application code can still import macros from `node_modules` and invoke them. -```ts +```ts title="cli.tsx" icon="/icons/typescript.svg" import { macro } from "some-package" with { type: "macro" }; macro(); ``` -## Export condition `"macro"` +## Export condition "macro" -When shipping a library containing a macro to `npm` or another package registry, use the `"macro"` [export condition](https://nodejs.org/api/packages.html#conditional-exports) to provide a special version of your package exclusively for the macro environment. +When shipping a library containing a macro to npm or another package registry, use the `"macro"` export condition to provide a special version of your package exclusively for the macro environment. -```jsonc#package.json +```json title="package.json" icon="file-code" { "name": "my-package", "exports": { @@ -94,7 +101,7 @@ When shipping a library containing a macro to `npm` or another package registry, With this configuration, users can consume your package at runtime or at bundle-time using the same import specifier: -```ts +```ts title="index.ts" icon="/icons/typescript.svg" import pkg from "my-package"; // runtime import import { macro } from "my-package" with { type: "macro" }; // macro import ``` @@ -105,15 +112,15 @@ The first import will resolve to `./node_modules/my-package/index.js`, while the When Bun's transpiler sees a macro import, it calls the function inside the transpiler using Bun's JavaScript runtime and converts the return value from JavaScript into an AST node. These JavaScript functions are called at bundle-time, not runtime. -Macros are executed synchronously in the transpiler during the visiting phase—before plugins and before the transpiler generates the AST. They are executed in the order they are imported. The transpiler will wait for the macro to finish executing before continuing. The transpiler will also `await` any `Promise` returned by a macro. +Macros are executed synchronously in the transpiler during the visiting phase—before plugins and before the transpiler generates the AST. They are executed in the order they are imported. The transpiler will wait for the macro to finish executing before continuing. The transpiler will also await any Promise returned by a macro. Bun's bundler is multi-threaded. As such, macros execute in parallel inside of multiple spawned JavaScript "workers". ## Dead code elimination -The bundler performs dead code elimination _after_ running and inlining macros. So given the following macro: +The bundler performs dead code elimination after running and inlining macros. So given the following macro: -```ts#returnFalse.ts +```ts title="returnFalse.ts" icon="/icons/typescript.svg" export function returnFalse() { return false; } @@ -121,7 +128,7 @@ export function returnFalse() { ...then bundling the following file will produce an empty bundle, provided that the minify syntax option is enabled. -```ts +```ts title="index.ts" icon="/icons/typescript.svg" import { returnFalse } from "./returnFalse.ts" with { type: "macro" }; if (returnFalse()) { @@ -133,19 +140,19 @@ if (returnFalse()) { Bun's transpiler needs to be able to serialize the result of the macro so it can be inlined into the AST. All JSON-compatible data structures are supported: -```ts#macro.ts +```ts title="macro.ts" icon="/icons/typescript.svg" export function getObject() { return { foo: "bar", baz: 123, - array: [ 1, 2, { nested: "value" }], + array: [1, 2, { nested: "value" }], }; } ``` -Macros can be async, or return `Promise` instances. Bun's transpiler will automatically `await` the `Promise` and inline the result. +Macros can be async, or return Promise instances. Bun's transpiler will automatically await the Promise and inline the result. -```ts#macro.ts +```ts title="macro.ts" icon="/icons/typescript.svg" export async function getText() { return "async value"; } @@ -153,21 +160,21 @@ export async function getText() { The transpiler implements special logic for serializing common data formats like `Response`, `Blob`, `TypedArray`. -- `TypedArray`: Resolves to a base64-encoded string. -- `Response`: Bun will read the `Content-Type` and serialize accordingly; for instance, a `Response` with type `application/json` will be automatically parsed into an object and `text/plain` will be inlined as a string. Responses with an unrecognized or `undefined` `type` will be base-64 encoded. -- `Blob`: As with `Response`, the serialization depends on the `type` property. +- **TypedArray**: Resolves to a base64-encoded string. +- **Response**: Bun will read the `Content-Type` and serialize accordingly; for instance, a Response with type `application/json` will be automatically parsed into an object and `text/plain` will be inlined as a string. Responses with an unrecognized or undefined type will be base-64 encoded. +- **Blob**: As with Response, the serialization depends on the `type` property. The result of `fetch` is `Promise`, so it can be directly returned. -```ts#macro.ts +```ts title="macro.ts" icon="/icons/typescript.svg" export function getObject() { - return fetch("https://bun.com") + return fetch("https://bun.com"); } ``` Functions and instances of most classes (except those mentioned above) are not serializable. -```ts +```ts title="macro.ts" icon="/icons/typescript.svg" export function getText(url: string) { // this doesn't work! return () => {}; @@ -178,7 +185,7 @@ export function getText(url: string) { Macros can accept inputs, but only in limited cases. The value must be statically known. For example, the following is not allowed: -```ts +```ts title="index.ts" icon="/icons/typescript.svg" import { getText } from "./getText.ts" with { type: "macro" }; export function howLong() { @@ -192,7 +199,7 @@ export function howLong() { However, if the value of `foo` is known at bundle-time (say, if it's a constant or the result of another macro) then it's allowed: -```ts +```ts title="index.ts" icon="/icons/typescript.svg" import { getText } from "./getText.ts" with { type: "macro" }; import { getFoo } from "./getFoo.ts" with { type: "macro" }; @@ -206,7 +213,7 @@ export function howLong() { This outputs: -```ts +```js function howLong() { console.log("The page is", 1322, "characters long"); } @@ -217,11 +224,9 @@ export { howLong }; ### Embed latest git commit hash -{% codetabs %} - -```ts#getGitCommitHash.ts +```ts title="getGitCommitHash.ts" icon="/icons/typescript.svg" export function getGitCommitHash() { - const {stdout} = Bun.spawnSync({ + const { stdout } = Bun.spawnSync({ cmd: ["git", "rev-parse", "HEAD"], stdout: "pipe", }); @@ -230,33 +235,32 @@ export function getGitCommitHash() { } ``` -{% /codetabs %} - - - When we build it, the `getGitCommitHash` is replaced with the result of calling the function: -{% codetabs %} + -```ts#input -import { getGitCommitHash } from './getGitCommitHash.ts' with { type: 'macro' }; +```ts input +import { getGitCommitHash } from "./getGitCommitHash.ts" with { type: "macro" }; console.log(`The current Git commit hash is ${getGitCommitHash()}`); ``` -```bash#output -console.log(`The current Git commit hash is 3ee3259104f`); +```ts output +console.log(`The current Git commit hash is 3ee3259104e4507cf62c160f0ff5357ec4c7a7f8`); ``` -{% /codetabs %} + -You're probably thinking "Why not just use `process.env.GIT_COMMIT_HASH`?" Well, you can do that too. But can you do this with an environment variable? + + You're probably thinking "Why not just use `process.env.GIT_COMMIT_HASH`?" Well, you can do that too. But can you do + this with an environment variable? + -### Make `fetch()` requests at bundle-time +### Make fetch() requests at bundle-time In this example, we make an outgoing HTTP request using `fetch()`, parse the HTML response using `HTMLRewriter`, and return an object containing the title and meta tags–all at bundle-time. -```ts +```ts title="meta.ts" icon="/icons/typescript.svg" export async function extractMetaTags(url: string) { const response = await fetch(url); const meta = { @@ -271,9 +275,7 @@ export async function extractMetaTags(url: string) { .on("meta", { element(element) { const name = - element.getAttribute("name") || - element.getAttribute("property") || - element.getAttribute("itemprop"); + element.getAttribute("name") || element.getAttribute("property") || element.getAttribute("itemprop"); if (name) meta[name] = element.getAttribute("content"); }, @@ -284,14 +286,12 @@ export async function extractMetaTags(url: string) { } ``` - +The `extractMetaTags` function is erased at bundle-time and replaced with the result of the function call. This means that the fetch request happens at bundle-time, and the result is embedded in the bundle. Also, the branch throwing the error is eliminated since it's unreachable. -The `extractMetaTags` function is erased at bundle-time and replaced with the result of the function call. This means that the `fetch` request happens at bundle-time, and the result is embedded in the bundle. Also, the branch throwing the error is eliminated since it's unreachable. + -{% codetabs %} - -```ts#input -import { extractMetaTags } from './meta.ts' with { type: 'macro' }; +```jsx input +import { extractMetaTags } from "./meta.ts" with { type: "macro" }; export const Head = () => { const headTags = extractMetaTags("https://example.com"); @@ -300,30 +300,29 @@ export const Head = () => { throw new Error("Expected title to be 'Example Domain'"); } - return - {headTags.title} - - ; + return ( + + {headTags.title} + + + ); }; ``` -```ts#output -import { jsx, jsxs } from "react/jsx-runtime"; +```jsx output export const Head = () => { - jsxs("head", { - children: [ - jsx("title", { - children: "Example Domain", - }), - jsx("meta", { - name: "viewport", - content: "width=device-width, initial-scale=1", - }), - ], - }); -}; + const headTags = { + title: "Example Domain", + viewport: "width=device-width, initial-scale=1", + }; -export { Head }; + return ( + + {headTags.title} + + + ); +}; ``` -{% /codetabs %} + diff --git a/docs/bundler/minifier.mdx b/docs/bundler/minifier.mdx new file mode 100644 index 0000000000..56a004e295 --- /dev/null +++ b/docs/bundler/minifier.mdx @@ -0,0 +1,1306 @@ +--- +name: Minifier +description: Reduce bundle sizes with Bun's JavaScript and TypeScript minifier +--- + +Bun includes a fast JavaScript and TypeScript minifier that can reduce bundle sizes by 80% or more (depending on the codebase) and make output code run faster. The minifier performs dozens of optimizations including constant folding, dead code elimination, and syntax transformations. Unlike other minifiers, Bun's minifier makes `bun build` run faster since there's less code to print. + +## CLI Usage + +### Enable all minification + +Use the `--minify` flag to enable all minification modes: + +```bash +bun build ./index.ts --minify --outfile=out.js +``` + +The `--minify` flag automatically enables: + +- Whitespace minification +- Syntax minification +- Identifier minification + +### Production mode + +The `--production` flag automatically enables minification: + +```bash +bun build ./index.ts --production --outfile=out.js +``` + +The `--production` flag also: + +- Sets `process.env.NODE_ENV` to `production` +- Enables the production-mode JSX import & transform + +### Granular control + +You can enable specific minification modes individually: + +```bash +# Only remove whitespace +bun build ./index.ts --minify-whitespace --outfile=out.js + +# Only minify syntax +bun build ./index.ts --minify-syntax --outfile=out.js + +# Only minify identifiers +bun build ./index.ts --minify-identifiers --outfile=out.js + +# Combine specific modes +bun build ./index.ts --minify-whitespace --minify-syntax --outfile=out.js +``` + +## JavaScript API + +When using Bun's bundler programmatically, configure minification through the `minify` option: + +```ts +await Bun.build({ + entrypoints: ["./index.ts"], + outdir: "./out", + minify: true, // Enable all minification modes +}); +``` + +For granular control, pass an object: + +```ts +await Bun.build({ + entrypoints: ["./index.ts"], + outdir: "./out", + minify: { + whitespace: true, + syntax: true, + identifiers: true, + }, +}); +``` + +## Minification Modes + +Bun's minifier has three independent modes that can be enabled separately or combined. + +### Whitespace minification (`--minify-whitespace`) + +Removes all unnecessary whitespace, newlines, and formatting from the output. + +### Syntax minification (`--minify-syntax`) + +Rewrites JavaScript syntax to shorter equivalent forms and performs constant folding, dead code elimination, and other optimizations. + +### Identifier minification (`--minify-identifiers`) + +Renames local variables and function names to shorter identifiers using frequency-based optimization. + +## All Transformations + +### Boolean literal shortening + +**Mode:** `--minify-syntax` + +Converts boolean literals to shorter expressions. + +```ts Input +true; +false; +``` + +```ts Output +!0; +!1; +``` + +### Boolean algebra optimizations + +**Mode:** `--minify-syntax` + +Simplifies boolean expressions using logical rules. + +```ts Input +!!x; +x === true; +x && true; +x || false; +!true; +!false; +``` + +```ts Output +x; +x; +x; +x; +!1; +!0; +``` + +### Undefined shortening + +**Mode:** `--minify-syntax` + +Replaces `undefined` with shorter equivalent. + +```ts Input +undefined; +let x = undefined; +``` + +```ts Output +void 0; +let x = void 0; +``` + +### Undefined equality optimization + +**Mode:** `--minify-syntax` + +Optimizes loose equality checks with undefined. + +```ts Input +x == undefined; +x != undefined; +``` + +```ts Output +x == null; +x != null; +``` + +### Infinity shortening + +**Mode:** `--minify-syntax` + +Converts Infinity to mathematical expressions. + +```ts Input +Infinity - Infinity; +``` + +```ts Output +1 / 0 - 1 / 0; +``` + +### Typeof optimizations + +**Mode:** `--minify-syntax` + +Optimizes typeof comparisons and evaluates constant typeof expressions. + +```ts Input +typeof x === "undefined"; +typeof x !== "undefined"; +typeof require; +typeof null; +typeof true; +typeof 123; +typeof "str"; +typeof 123n; +``` + +```ts Output +typeof x > "u"; +typeof x < "u"; +("function"); +("object"); +("boolean"); +("number"); +("string"); +("bigint"); +``` + +### Number formatting + +**Mode:** `--minify-syntax` + +Formats numbers in the most compact representation. + +```ts Input +10000; +100000; +1000000; +1.0 - 42.0; +``` + +```ts Output +1e4; +1e5; +1e6; +1 - 42; +``` + +### Arithmetic constant folding + +**Mode:** `--minify-syntax` + +Evaluates arithmetic operations at compile time. + +```ts Input +1 + 2; +10 - 5; +3 * 4; +10 / 2; +10 % 3; +2 ** 3; +``` + +```ts Output +3; +5; +12; +5; +1; +8; +``` + +### Bitwise constant folding + +**Mode:** `--minify-syntax` + +Evaluates bitwise operations at compile time. + +```ts Input +5 & 3; +5 | 3; +5 ^ 3; +8 << 2; +32 >> 2; +~5; +``` + +```ts Output +1; +7; +6; +32; +8 - 6; +``` + +### String concatenation + +**Mode:** `--minify-syntax` + +Combines string literals at compile time. + +```ts Input +"a" + "b"; +"x" + 123; +"foo" + "bar" + "baz"; +``` + +```ts Output +"ab"; +"x123"; +"foobarbaz"; +``` + +### String indexing + +**Mode:** `--minify-syntax` + +Evaluates string character access at compile time. + +```ts Input +"foo"[2]; +"hello"[0]; +``` + +```ts Output +"o"; +"h"; +``` + +### Template literal folding + +**Mode:** `--minify-syntax` + +Evaluates template literals with constant expressions. + +```ts Input +`a${123}b``result: ${5 + 10}`; +``` + +```ts Output +"a123b"; +"result: 15"; +``` + +### Template literal to string conversion + +**Mode:** `--minify-syntax` + +Converts simple template literals to regular strings. + +```ts Input +`Hello World``Line 1 +Line 2`; +``` + +```ts Output +"Hello World"; +"Line 1\nLine 2"; +``` + +### String quote optimization + +**Mode:** `--minify-syntax` + +Chooses the optimal quote character to minimize escapes. + +```ts Input +"It's a string"; +'He said "hello"'`Simple string`; +``` + +```ts Output +"It's a string"; +'He said "hello"'; +"Simple string"; +``` + +### Array spread inlining + +**Mode:** `--minify-syntax` + +Inlines array spread operations with constant arrays. + +```ts Input +[1, ...[2, 3], 4] +[...[a, b]] +``` + +```ts Output +[1, 2, 3, 4][(a, b)]; +``` + +### Array indexing + +**Mode:** `--minify-syntax` + +Evaluates constant array access at compile time. + +```ts Input +[x][0] +['a', 'b', 'c'][1] +['a', , 'c'][1] +``` + +```ts Output +x; +("b"); +void 0; +``` + +### Property access optimization + +**Mode:** `--minify-syntax` + +Converts bracket notation to dot notation when possible. + +```ts Input +obj["property"]; +obj["validName"]; +obj["123"]; +obj["invalid-name"]; +``` + +```ts Output +obj.property; +obj.validName; +obj["123"]; +obj["invalid-name"]; +``` + +### Comparison folding + +**Mode:** `--minify-syntax` + +Evaluates constant comparisons at compile time. + +```ts Input +3 < 5; +5 > 3; +3 <= 3; +5 >= 6; +"a" < "b"; +``` + +```ts Output +!0; +!0; +!0; +!1; +!0; +``` + +### Logical operation folding + +**Mode:** `--minify-syntax` + +Simplifies logical operations with constant values. + +```ts Input +true && x; +false && x; +true || x; +false || x; +``` + +```ts Output +x; +!1; +!0; +x; +``` + +### Nullish coalescing folding + +**Mode:** `--minify-syntax` + +Evaluates nullish coalescing with known values. + +```ts Input +null ?? x; +undefined ?? x; +42 ?? x; +``` + +```ts Output +x; +x; +42; +``` + +### Comma expression simplification + +**Mode:** `--minify-syntax` + +Removes side-effect-free expressions from comma sequences. + +```ts Input +(0, x)(123, "str", x); +``` + +```ts Output +x; +x; +``` + +### Ternary conditional folding + +**Mode:** `--minify-syntax` + +Evaluates conditional expressions with constant conditions. + +```ts Input +true ? a : b; +false ? a : b; +x ? true : false; +x ? false : true; +``` + +```ts Output +a; +b; +x; +!x; +``` + +### Unary expression folding + +**Mode:** `--minify-syntax` + +Simplifies unary operations. + +```ts Input ++123 + "123" - -x; +~~x; +!!x; +``` + +```ts Output +123; +123; +x; +x; +x; +``` + +### Double negation removal + +**Mode:** `--minify-syntax` + +Removes unnecessary double negations. + +```ts Input +!!x; +!!!x; +``` + +```ts Output +x; +!x; +``` + +### If statement optimization + +**Mode:** `--minify-syntax` + +Optimizes if statements with constant conditions. + +```ts Input +if (true) x; +if (false) x; +if (x) { + a; +} +if (x) { +} else y; +``` + +```ts Output +x; +// removed +if (x) a; +if (!x) y; +``` + +### Dead code elimination + +**Mode:** `--minify-syntax` + +Removes unreachable code and code without side effects. + +```ts Input +if (false) { + unreachable(); +} +function foo() { + return x; + deadCode(); +} +``` + +```ts Output +function foo() { + return x; +} +``` + +### Unreachable branch removal + +**Mode:** `--minify-syntax` + +Removes branches that can never execute. + +```ts Input +while (false) { + neverRuns(); +} +``` + +```ts Output +// removed entirely +``` + +### Empty block removal + +**Mode:** `--minify-syntax` + +Removes empty blocks and unnecessary braces. + +```ts Input +{ +} +if (x) { +} +``` + +```ts Output +// removed +``` + +### Single statement block unwrapping + +**Mode:** `--minify-syntax` + +Removes unnecessary braces around single statements. + +```ts Input +if (condition) { + doSomething(); +} +``` + +```ts Output +if (condition) doSomething(); +``` + +### TypeScript enum inlining + +**Mode:** `--minify-syntax` + +Inlines TypeScript enum values at compile time. + +```ts Input +enum Color { + Red, + Green, + Blue, +} +const x = Color.Red; +``` + +```ts Output +const x = 0; +``` + +### Pure annotation support + +**Mode:** Always active + +Respects `/*@__PURE__*/` annotations for tree shaking. + +```ts Input +const x = /*@__PURE__*/ expensive(); +// If x is unused... +``` + +```ts Output +// removed entirely +``` + +### Identifier renaming + +**Mode:** `--minify-identifiers` + +Renames local variables to shorter names based on usage frequency. + +```ts Input +function calculateSum(firstNumber, secondNumber) { + const result = firstNumber + secondNumber; + return result; +} +``` + +```ts Output +function a(b, c) { + const d = b + c; + return d; +} +``` + +**Naming strategy:** + +- Most frequently used identifiers get the shortest names (a, b, c...) +- Single letters: a-z (26 names) +- Double letters: aa-zz (676 names) +- Triple letters and beyond as needed + +**Preserved identifiers:** + +- JavaScript keywords and reserved words +- Global identifiers +- Named exports (to maintain API) +- CommonJS names: `exports`, `module` + +### Whitespace removal + +**Mode:** `--minify-whitespace` + +Removes all unnecessary whitespace. + +```ts Input +function add(a, b) { + return a + b; +} +let x = 10; +``` + +```ts Output +function add(a, b) { + return a + b; +} +let x = 10; +``` + +### Semicolon optimization + +**Mode:** `--minify-whitespace` + +Inserts semicolons only when necessary. + +```ts Input +let a = 1; +let b = 2; +return a + b; +``` + +```ts Output +let a = 1; +let b = 2; +return a + b; +``` + +### Operator spacing removal + +**Mode:** `--minify-whitespace` + +Removes spaces around operators. + +```ts Input +a + b; +x = y * z; +(foo && bar) || baz; +``` + +```ts Output +a + b; +x = y * z; +(foo && bar) || baz; +``` + +### Comment removal + +**Mode:** `--minify-whitespace` + +Removes comments except important license comments. + +```ts Input +// This comment is removed +/* So is this */ +/*! But this license comment is kept */ +function test() { + /* inline comment */ +} +``` + +```ts Output +/*! But this license comment is kept */ +function test() {} +``` + +### Object and array formatting + +**Mode:** `--minify-whitespace` + +Removes whitespace in object and array literals. + +```ts Input +const obj = { + name: "John", + age: 30, +}; +const arr = [1, 2, 3]; +``` + +```ts Output +const obj = { name: "John", age: 30 }; +const arr = [1, 2, 3]; +``` + +### Control flow formatting + +**Mode:** `--minify-whitespace` + +Removes whitespace in control structures. + +```ts Input +if (condition) { + doSomething(); +} +for (let i = 0; i < 10; i++) { + console.log(i); +} +``` + +```ts Output +if (condition) doSomething(); +for (let i = 0; i < 10; i++) console.log(i); +``` + +### Function formatting + +**Mode:** `--minify-whitespace` + +Removes whitespace in function declarations. + +```ts Input +function myFunction(param1, param2) { + return param1 + param2; +} +const arrow = (a, b) => a + b; +``` + +```ts Output +function myFunction(a, b) { + return a + b; +} +const arrow = (a, b) => a + b; +``` + +### Parentheses minimization + +**Mode:** Always active + +Only adds parentheses when necessary for operator precedence. + +```ts Input +(a + b) * c; +a + (b * c)(x); +``` + +```ts Output +(a + b) * c; +a + b * c; +x; +``` + +### Property mangling + +**Mode:** `--minify-identifiers` (with configuration) + +Renames object properties to shorter names when configured. + +```ts#input.ts (with property mangling enabled) +obj.longPropertyName +``` + +```ts Output +obj.a; +``` + +### Template literal value folding + +**Mode:** `--minify-syntax` + +Converts non-string interpolated values to strings and folds them into the template. + +```ts Input +`hello ${123}``value: ${true}``result: ${null}``status: ${undefined}``big: ${10n}`; +``` + +```ts Output +"hello 123"; +"value: true"; +"result: null"; +"status: undefined"; +"big: 10"; +``` + +### String length constant folding + +**Mode:** `--minify-syntax` + +Evaluates `.length` property on string literals at compile time. + +```ts Input +"hello world".length; +"test".length; +``` + +```ts Output +11; +4; +``` + +### Constructor call simplification + +**Mode:** `--minify-syntax` + +Simplifies constructor calls for built-in types. + +```ts Input +new Object(); +new Object(null); +new Object({ a: 1 }); +new Array(); +new Array(x, y); +``` + +```ts Output +{ +} +{ +} +{ + a: 1; +} +[][(x, y)]; +``` + +### Single property object inlining + +**Mode:** `--minify-syntax` + +Inlines property access for objects with a single property. + +```ts Input +({ fn: () => console.log("hi") }).fn(); +``` + +```ts Output +(() => console.log("hi"))(); +``` + +### String charCodeAt constant folding + +**Mode:** Always active + +Evaluates `charCodeAt()` on string literals for ASCII characters. + +```ts Input +"hello".charCodeAt(1); +"A".charCodeAt(0); +``` + +```ts Output +101; +65; +``` + +### Void 0 equality to null equality + +**Mode:** `--minify-syntax` + +Converts loose equality checks with `void 0` to `null` since they're equivalent. + +```ts Input +x == void 0; +x != void 0; +``` + +```ts Output +x == null; +x != null; +``` + +### Negation operator optimization + +**Mode:** `--minify-syntax` + +Moves negation operator through comma expressions. + +```ts Input +-(a, b) - (x, y, z); +``` + +```ts Output +(a, -b); +(x, y, -z); +``` + +### Import.meta property inlining + +**Mode:** Bundle mode + +Inlines `import.meta` properties at build time when values are known. + +```ts Input +import.meta.dir; +import.meta.file; +import.meta.path; +import.meta.url; +``` + +```ts Output +"/path/to/directory"; +"filename.js"; +"/full/path/to/file.js"; +"file:///full/path/to/file.js"; +``` + +### Variable declaration merging + +**Mode:** `--minify-syntax` + +Merges adjacent variable declarations of the same type. + +```ts Input +let a = 1; +let b = 2; +const c = 3; +const d = 4; +``` + +```ts Output +let a = 1, + b = 2; +const c = 3, + d = 4; +``` + +### Expression statement merging + +**Mode:** `--minify-syntax` + +Merges adjacent expression statements using comma operator. + +```ts Input +console.log(1); +console.log(2); +console.log(3); +``` + +```ts Output +(console.log(1), console.log(2), console.log(3)); +``` + +### Return statement merging + +**Mode:** `--minify-syntax` + +Merges expressions before return with comma operator. + +```ts Input +console.log(x); +return y; +``` + +```ts Output +return (console.log(x), y); +``` + +### Throw statement merging + +**Mode:** `--minify-syntax` + +Merges expressions before throw with comma operator. + +```ts Input +console.log(x); +throw new Error(); +``` + +```ts Output +throw (console.log(x), new Error()); +``` + +### TypeScript enum cross-module inlining + +**Mode:** `--minify-syntax` (bundle mode) + +Inlines enum values across module boundaries. + +```ts#input.ts (lib.ts) +export enum Color { Red, Green, Blue } + +// Input (main.ts) +import { Color } from './lib'; +const x = Color.Red; +``` + +```ts Output +const x = 0; +``` + +### Computed property enum inlining + +**Mode:** `--minify-syntax` + +Inlines enum values used as computed object properties. + +```ts Input +enum Keys { + FOO = "foo", +} +const obj = { [Keys.FOO]: value }; +``` + +```ts Output +const obj = { foo: value }; +``` + +### String number to numeric index + +**Mode:** `--minify-syntax` + +Converts string numeric property access to numeric index. + +```ts Input +obj["0"]; +arr["5"]; +``` + +```ts Output +obj[0]; +arr[5]; +``` + +### Arrow function body shortening + +**Mode:** Always active + +Uses expression body syntax when an arrow function only returns a value. + +```ts Input +() => { + return x; +}; +a => { + return a + 1; +}; +``` + +```ts Output +() => x; +a => a + 1; +``` + +### Object property shorthand + +**Mode:** Always active + +Uses shorthand syntax when property name and value identifier match. + +```ts Input +{ x: x, y: y } +{ name: name, age: age } +``` + +```ts Output +{ + (x, y); +} +{ + (name, age); +} +``` + +### Method shorthand + +**Mode:** Always active + +Uses method shorthand syntax in object literals. + +```ts Input +{ + foo: function() {}, + bar: async function() {} +} +``` + +```ts Output +{ + foo() {}, + async bar() {} +} +``` + +### Drop debugger statements + +**Mode:** `--drop=debugger` + +Removes `debugger` statements from code. + +```ts Input +function test() { + debugger; + return x; +} +``` + +```ts Output +function test() { + return x; +} +``` + +### Drop console calls + +**Mode:** `--drop=console` + +Removes all `console.*` method calls from code. + +```ts Input +console.log("debug"); +console.warn("warning"); +x = console.error("error"); +``` + +```ts Output +void 0; +void 0; +x = void 0; +``` + +### Drop custom function calls + +**Mode:** `--drop=` + +Removes calls to specified global functions or methods. + +```ts#input.ts with --drop=assert +assert(condition); +obj.assert(test); +``` + +```ts Output +void 0; +void 0; +``` + +## Keep Names + +When minifying identifiers, you may want to preserve original function and class names for debugging purposes. Use the `--keep-names` flag: + +```bash +bun build ./index.ts --minify --keep-names --outfile=out.js +``` + +Or in the JavaScript API: + +```ts +await Bun.build({ + entrypoints: ["./index.ts"], + outdir: "./out", + minify: { + identifiers: true, + keepNames: true, + }, +}); +``` + +This preserves the `.name` property on functions and classes while still minifying the actual identifier names in the code. + +## Combined Example + +Using all three minification modes together: + +```ts#input.ts (158 bytes) +const myVariable = 42; + +const myFunction = () => { + const isValid = true; + const result = undefined; + return isValid ? myVariable : result; +}; + +const output = myFunction(); +``` + +```ts#output.ts +// Output with --minify (49 bytes, 69% reduction) +const a=42,b=()=>{const c=!0,d=void 0;return c?a:d},e=b(); +``` + +## When to Use Minification + +**Use `--minify` for:** + +- Production bundles +- Reducing CDN bandwidth costs +- Improving page load times + +**Use individual modes for:** + +- **`--minify-whitespace`:** Quick size reduction without semantic changes +- **`--minify-syntax`:** Smaller output while keeping readable identifiers for debugging +- **`--minify-identifiers`:** Maximum size reduction (combine with `--keep-names` for better stack traces) + +**Avoid minification for:** + +- Development builds (harder to debug) +- When you need readable error messages +- Libraries where consumers may read the source diff --git a/docs/bundler/plugins.mdx b/docs/bundler/plugins.mdx new file mode 100644 index 0000000000..49caa67bae --- /dev/null +++ b/docs/bundler/plugins.mdx @@ -0,0 +1,411 @@ +--- +title: Plugins +description: Universal plugin API for extending Bun's runtime and bundler +--- + +Bun provides a universal plugin API that can be used to extend both the runtime and bundler. + +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. + +## Lifecycle hooks + +Plugins can register callbacks to be run at various points in the lifecycle of a bundle: + +- `onStart()`: Run once the bundler has started a bundle +- `onResolve()`: Run before a module is resolved +- `onLoad()`: Run before a module is loaded +- `onBeforeParse()`: Run zero-copy native addons in the parser thread before a file is parsed + +## Reference + +A rough overview of the types (please refer to Bun's `bun.d.ts` for the full type definitions): + +```ts title="bun.d.ts" icon="/icons/typescript.svg" +type PluginBuilder = { + onStart(callback: () => void): void; + onResolve: ( + args: { filter: RegExp; namespace?: string }, + callback: (args: { path: string; importer: string }) => { + path: string; + namespace?: string; + } | void, + ) => void; + onLoad: ( + args: { filter: RegExp; namespace?: string }, + defer: () => Promise, + callback: (args: { path: string }) => { + loader?: Loader; + contents?: string; + exports?: Record; + }, + ) => void; + config: BuildConfig; +}; + +type Loader = "js" | "jsx" | "ts" | "tsx" | "css" | "json" | "toml"; +``` + +## Usage + +A plugin is defined as simple JavaScript object containing a `name` property and a `setup` function. + +```ts title="myPlugin.ts" icon="/icons/typescript.svg" +import type { BunPlugin } from "bun"; + +const myPlugin: BunPlugin = { + name: "Custom loader", + setup(build) { + // implementation + }, +}; +``` + +This plugin can be passed into the `plugins` array when calling `Bun.build`. + +```ts title="index.ts" icon="/icons/typescript.svg" +await Bun.build({ + entrypoints: ["./app.ts"], + outdir: "./out", + plugins: [myPlugin], +}); +``` + +## Plugin lifecycle + +### Namespaces + +`onLoad` and `onResolve` accept an optional `namespace` string. What is a namespace? + +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`. + +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"`. + +Other common namespaces are: + +- `"bun"`: for Bun-specific modules (e.g. `"bun:test"`, `"bun:sqlite"`) +- `"node"`: for Node.js modules (e.g. `"node:fs"`, `"node:path"`) + +### onStart + +```ts +onStart(callback: () => void): Promise | void; +``` + +Registers a callback to be run when the bundler starts a new bundle. + +```ts title="index.ts" icon="/icons/typescript.svg" +import { plugin } from "bun"; + +plugin({ + name: "onStart example", + + setup(build) { + build.onStart(() => { + console.log("Bundle started!"); + }); + }, +}); +``` + +The callback can return a Promise. After the bundle process has initialized, the bundler waits until all `onStart()` callbacks have completed before continuing. + +For example: + +```ts title="index.ts" icon="/icons/typescript.svg" +const result = await Bun.build({ + entrypoints: ["./app.ts"], + outdir: "./dist", + sourcemap: "external", + plugins: [ + { + name: "Sleep for 10 seconds", + setup(build) { + build.onStart(async () => { + await Bun.sleep(10_000); + }); + }, + }, + { + name: "Log bundle time to a file", + setup(build) { + build.onStart(async () => { + const now = Date.now(); + await Bun.$`echo ${now} > bundle-time.txt`; + }); + }, + }, + ], +}); +``` + +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). + + + `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. + + +### onResolve + +```ts +onResolve( + args: { filter: RegExp; namespace?: string }, + callback: (args: { path: string; importer: string }) => { + path: string; + namespace?: string; + } | void, +): void; +``` + +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. + +The `onResolve()` plugin lifecycle callback allows you to configure how a module is resolved. + +The first argument to `onResolve()` is an object with a `filter` and `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. + +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. + +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. + +For example, redirecting all imports to `images/` to `./public/images/`: + +```ts title="index.ts" icon="/icons/typescript.svg" +import { plugin } from "bun"; + +plugin({ + name: "onResolve example", + setup(build) { + build.onResolve({ filter: /.*/, namespace: "file" }, args => { + if (args.path.startsWith("images/")) { + return { + path: args.path.replace("images/", "./public/images/"), + }; + } + }); + }, +}); +``` + +### onLoad + +```ts +onLoad( + args: { filter: RegExp; namespace?: string }, + defer: () => Promise, + callback: (args: { path: string, importer: string, namespace: string, kind: ImportKind }) => { + loader?: Loader; + contents?: string; + exports?: Record; + }, +): void; +``` + +After Bun's bundler has resolved a module, it needs to read the contents of the module and parse it. + +The `onLoad()` plugin lifecycle callback allows you to modify the contents of a module before it is read and parsed by Bun. + +Like `onResolve()`, the first argument to `onLoad()` allows you to filter which modules this invocation of `onLoad()` will apply to. + +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. + +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. + +The callback can return a new `contents` string for the module as well as a new `loader`. + +For example: + +```ts title="index.ts" icon="/icons/typescript.svg" +import { plugin } from "bun"; + +const envPlugin: BunPlugin = { + name: "env plugin", + setup(build) { + build.onLoad({ filter: /env/, namespace: "file" }, args => { + return { + contents: `export default ${JSON.stringify(process.env)}`, + loader: "js", + }; + }); + }, +}); + +Bun.build({ + entrypoints: ["./app.ts"], + outdir: "./dist", + plugins: [envPlugin], +}); + +// import env from "env" +// env.FOO === "bar" +``` + +This plugin will transform all imports of the form `import env from "env"` into a JavaScript module that exports the current environment variables. + +#### .defer() + +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. + +This allows you to delay execution of the `onLoad` callback until all other modules have been loaded. + +This is useful for returning contents of a module that depends on other modules. + + + +```ts title="index.ts" icon="/icons/typescript.svg" +import { plugin } from "bun"; + +plugin({ + name: "track imports", + setup(build) { + const transpiler = new Bun.Transpiler(); + + let trackedImports: Record = {}; + + // Each module that goes through this onLoad callback + // will record its imports in `trackedImports` + build.onLoad({ filter: /\.ts/ }, async ({ path }) => { + const contents = await Bun.file(path).arrayBuffer(); + + const imports = transpiler.scanImports(contents); + + for (const i of imports) { + trackedImports[i.path] = (trackedImports[i.path] || 0) + 1; + } + + return undefined; + }); + + build.onLoad({ filter: /stats\.json/ }, async ({ defer }) => { + // Wait for all files to be loaded, ensuring + // that every file goes through the above `onLoad()` function + // and their imports tracked + await defer(); + + // Emit JSON containing the stats of each import + return { + contents: `export default ${JSON.stringify(trackedImports)}`, + loader: "json", + }; + }); + }, +}); +``` + + + + + The `.defer()` function currently has the limitation that it can only be called once per `onLoad` callback. + + +## Native plugins + +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. + +However, one limitation of plugins written in JavaScript is that JavaScript itself is single-threaded. + +Native plugins are written as NAPI modules and can be run on multiple threads. This allows native plugins to run much faster than JavaScript plugins. + +In addition, native plugins can skip unnecessary work such as the UTF-8 -> UTF-16 conversion needed to pass strings to JavaScript. + +These are the following lifecycle hooks which are available to native plugins: + +- `onBeforeParse()`: Called on any thread before a file is parsed by Bun's bundler. + +Native plugins are NAPI modules which expose lifecycle hooks as C ABI functions. + +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. + +### Creating a native plugin in Rust + +Native plugins are NAPI modules which expose lifecycle hooks as C ABI functions. + +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. + +```bash terminal icon="terminal" +bun add -g @napi-rs/cli +napi new +``` + +Then install this crate: + +```bash terminal icon="terminal" +cargo add bun-native-plugin +``` + +Now, inside the `lib.rs` file, we'll use the `bun_native_plugin::bun` proc macro to define a function which will implement our native plugin. + +Here's an example implementing the `onBeforeParse` hook: + +```rust title="lib.rs" icon="/icons/rust.svg" +use bun_native_plugin::{define_bun_plugin, OnBeforeParse, bun, Result, anyhow, BunLoader}; +use napi_derive::napi; + +/// Define the plugin and its name +define_bun_plugin!("replace-foo-with-bar"); + +/// Here we'll implement `onBeforeParse` with code that replaces all occurrences of +/// `foo` with `bar`. +/// +/// We use the #[bun] macro to generate some of the boilerplate code. +/// +/// The argument of the function (`handle: &mut OnBeforeParse`) tells +/// the macro that this function implements the `onBeforeParse` hook. +#[bun] +pub fn replace_foo_with_bar(handle: &mut OnBeforeParse) -> Result<()> { + // Fetch the input source code. + let input_source_code = handle.input_source_code()?; + + // Get the Loader for the file + let loader = handle.output_loader(); + + let output_source_code = input_source_code.replace("foo", "bar"); + + handle.set_output_source_code(output_source_code, BunLoader::BUN_LOADER_JSX); + + Ok(()) +} +``` + +And to use it in `Bun.build()`: + +```ts title="index.ts" icon="/icons/typescript.svg" +import myNativeAddon from "./my-native-addon"; + +Bun.build({ + entrypoints: ["./app.tsx"], + plugins: [ + { + name: "my-plugin", + + setup(build) { + build.onBeforeParse( + { + namespace: "file", + filter: "**/*.tsx", + }, + { + napiModule: myNativeAddon, + symbol: "replace_foo_with_bar", + // external: myNativeAddon.getSharedState() + }, + ); + }, + }, + ], +}); +``` + +### onBeforeParse + +```ts +onBeforeParse( + args: { filter: RegExp; namespace?: string }, + callback: { napiModule: NapiModule; symbol: string; external?: unknown }, +): void; +``` + +This lifecycle callback is run immediately before a file is parsed by Bun's bundler. + +As input, it receives the file's contents and can optionally return new source code. + +This callback can be called from any thread and so the napi module implementation must be thread-safe. diff --git a/docs/bundler/vs-esbuild.md b/docs/bundler/vs-esbuild.md deleted file mode 100644 index 1387950d61..0000000000 --- a/docs/bundler/vs-esbuild.md +++ /dev/null @@ -1,1127 +0,0 @@ -Bun's bundler API is inspired heavily by [esbuild](https://esbuild.github.io/). Migrating to Bun's bundler from esbuild should be relatively painless. This guide will briefly explain why you might consider migrating to Bun's bundler and provide a side-by-side API comparison reference for those who are already familiar with esbuild's API. - -There are a few behavioral differences to note. - -- **Bundling by default**. Unlike esbuild, Bun _always bundles by default_. This is why the `--bundle` flag isn't necessary in the Bun example. To transpile each file individually, use [`Bun.Transpiler`](https://bun.com/docs/api/transpiler). -- **It's just a bundler**. Unlike esbuild, Bun's bundler does not include a built-in development server or file watcher. It's just a bundler. The bundler is intended for use in conjunction with `Bun.serve` and other runtime APIs to achieve the same effect. As such, all options relating to HTTP/file watching are not applicable. - -## Performance - -With a performance-minded API coupled with the extensively optimized Zig-based JS/TS parser, Bun's bundler is 1.75x faster than esbuild on esbuild's [three.js benchmark](https://github.com/oven-sh/bun/tree/main/bench/bundle). - -{% image src="/images/bundler-speed.png" caption="Bundling 10 copies of three.js from scratch, with sourcemaps and minification" /%} - -## CLI API - -Bun and esbuild both provide a command-line interface. - -```bash -$ esbuild --outdir=out --bundle -$ bun build --outdir=out -``` - -In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Other flags like `--outdir ` do accept an argument; these flags can be written as `--outdir out` or `--outdir=out`. Some flags like `--define` can be specified several times: `--define foo=bar --define bar=baz`. - -{% table %} - -- `esbuild` -- `bun build` - ---- - -- `--bundle` -- n/a -- Bun always bundles, use `--no-bundle` to disable this behavior. - ---- - -- `--define:K=V` -- `--define K=V` -- Small syntax difference; no colon. - - ```bash - $ esbuild --define:foo=bar - $ bun build --define foo=bar - ``` - ---- - -- `--external:` -- `--external ` -- Small syntax difference; no colon. - - ```bash - $ esbuild --external:react - $ bun build --external react - ``` - ---- - -- `--format` -- `--format` -- Bun supports `"esm"` and `"cjs"` currently, but more module formats are planned. esbuild defaults to `"iife"`. - ---- - -- `--loader:.ext=loader` -- `--loader .ext:loader` -- Bun supports a different set of built-in loaders than esbuild; see [Bundler > Loaders](https://bun.com/docs/bundler/loaders) for a complete reference. The esbuild loaders `dataurl`, `binary`, `base64`, `copy`, and `empty` are not yet implemented. - - The syntax for `--loader` is slightly different. - - ```bash - $ esbuild app.ts --bundle --loader:.svg=text - $ bun build app.ts --loader .svg:text - ``` - ---- - -- `--minify` -- `--minify` -- No differences - ---- - -- `--outdir` -- `--outdir` -- No differences - ---- - -- `--outfile` -- `--outfile` - ---- - -- `--packages` -- `--packages` -- No differences - ---- - -- `--platform` -- `--target` -- Renamed to `--target` for consistency with tsconfig. Does not support `neutral`. - ---- - -- `--serve` -- n/a -- Not applicable - ---- - -- `--sourcemap` -- `--sourcemap` -- No differences - ---- - -- `--splitting` -- `--splitting` -- No differences - ---- - -- `--target` -- n/a -- Not supported. Bun's bundler performs no syntactic down-leveling at this time. - ---- - -- `--watch` -- `--watch` -- No differences - ---- - -- `--allow-overwrite` -- n/a -- Overwriting is never allowed - ---- - -- `--analyze` -- n/a -- Not supported - ---- - -- `--asset-names` -- `--asset-naming` -- Renamed for consistency with `naming` in JS API - ---- - -- `--banner` -- `--banner` -- Only applies to js bundles - ---- - -- `--footer` -- `--footer` -- Only applies to js bundles - ---- - -- `--certfile` -- n/a -- Not applicable - ---- - -- `--charset=utf8` -- n/a -- Not supported - ---- - -- `--chunk-names` -- `--chunk-naming` -- Renamed for consistency with `naming` in JS API - ---- - -- `--color` -- n/a -- Always enabled - ---- - -- `--drop` -- `--drop` - ---- - -- `--entry-names` -- `--entry-naming` -- Renamed for consistency with `naming` in JS API - ---- - -- `--global-name` -- n/a -- Not applicable, Bun does not support `iife` output at this time - ---- - -- `--ignore-annotations` -- `--ignore-dce-annotations` - ---- - -- `--inject` -- n/a -- Not supported - ---- - -- `--jsx` -- `--jsx-runtime ` -- Supports `"automatic"` (uses `jsx` transform) and `"classic"` (uses `React.createElement`) - ---- - -- `--jsx-dev` -- n/a -- Bun reads `compilerOptions.jsx` from `tsconfig.json` to determine a default. If `compilerOptions.jsx` is `"react-jsx"`, or if `NODE_ENV=production`, Bun will use the `jsx` transform. Otherwise, it uses `jsxDEV`. For any to Bun uses `jsxDEV`. The bundler does not support `preserve`. - ---- - -- `--jsx-factory` -- `--jsx-factory` - ---- - -- `--jsx-fragment` -- `--jsx-fragment` - ---- - -- `--jsx-import-source` -- `--jsx-import-source` - ---- - -- `--jsx-side-effects` -- `--jsx-side-effects` -- Controls whether JSX expressions are marked as `/* @__PURE__ */` for dead code elimination. Default is `false` (JSX marked as pure). - ---- - -- `--keep-names` -- n/a -- Not supported - ---- - -- `--keyfile` -- n/a -- Not applicable - ---- - -- `--legal-comments` -- n/a -- Not supported - ---- - -- `--log-level` -- n/a -- Not supported. This can be set in `bunfig.toml` as `logLevel`. - ---- - -- `--log-limit` -- n/a -- Not supported - ---- - -- `--log-override:X=Y` -- n/a -- Not supported - ---- - -- `--main-fields` -- n/a -- Not supported - ---- - -- `--mangle-cache` -- n/a -- Not supported - ---- - -- `--mangle-props` -- n/a -- Not supported - ---- - -- `--mangle-quoted` -- n/a -- Not supported - ---- - -- `--metafile` -- n/a -- Not supported - ---- - -- `--minify-whitespace` -- `--minify-whitespace` - ---- - -- `--minify-identifiers` -- `--minify-identifiers` - ---- - -- `--minify-syntax` -- `--minify-syntax` - ---- - -- `--out-extension` -- n/a -- Not supported - ---- - -- `--outbase` -- `--root` - ---- - -- `--preserve-symlinks` -- n/a -- Not supported - ---- - -- `--public-path` -- `--public-path` - ---- - -- `--pure` -- n/a -- Not supported - ---- - -- `--reserve-props` -- n/a -- Not supported - ---- - -- `--resolve-extensions` -- n/a -- Not supported - ---- - -- `--servedir` -- n/a -- Not applicable - ---- - -- `--source-root` -- n/a -- Not supported - ---- - -- `--sourcefile` -- n/a -- Not supported. Bun does not support `stdin` input yet. - ---- - -- `--sourcemap` -- `--sourcemap` -- No differences - ---- - -- `--sources-content` -- n/a -- Not supported - ---- - -- `--supported` -- n/a -- Not supported - ---- - -- `--tree-shaking` -- n/a -- Always `true` - ---- - -- `--tsconfig` -- `--tsconfig-override` - ---- - -- `--version` -- n/a -- Run `bun --version` to see the version of Bun. - -{% /table %} - -## JavaScript API - -{% table %} - -- `esbuild.build()` -- `Bun.build()` - ---- - -- `absWorkingDir` -- n/a -- Always set to `process.cwd()` - ---- - -- `alias` -- n/a -- Not supported - ---- - -- `allowOverwrite` -- n/a -- Always `false` - ---- - -- `assetNames` -- `naming.asset` -- Uses same templating syntax as esbuild, but `[ext]` must be included explicitly. - - ```ts - Bun.build({ - entrypoints: ["./index.tsx"], - naming: { - asset: "[name].[ext]", - }, - }); - ``` - ---- - -- `banner` -- n/a -- Not supported - ---- - -- `bundle` -- n/a -- Always `true`. Use [`Bun.Transpiler`](https://bun.com/docs/api/transpiler) to transpile without bundling. - ---- - -- `charset` -- n/a -- Not supported - ---- - -- `chunkNames` -- `naming.chunk` -- Uses same templating syntax as esbuild, but `[ext]` must be included explicitly. - - ```ts - Bun.build({ - entrypoints: ["./index.tsx"], - naming: { - chunk: "[name].[ext]", - }, - }); - ``` - ---- - -- `color` -- n/a -- Bun returns logs in the `logs` property of the build result. - ---- - -- `conditions` -- n/a -- Not supported. Export conditions priority is determined by `target`. - ---- - -- `define` -- `define` - ---- - -- `drop` -- n/a -- Not supported - ---- - -- `entryNames` -- `naming` or `naming.entry` -- Bun supports a `naming` key that can either be a string or an object. Uses same templating syntax as esbuild, but `[ext]` must be included explicitly. - - ```ts - Bun.build({ - entrypoints: ["./index.tsx"], - // when string, this is equivalent to entryNames - naming: "[name].[ext]", - - // granular naming options - naming: { - entry: "[name].[ext]", - asset: "[name].[ext]", - chunk: "[name].[ext]", - }, - }); - ``` - ---- - -- `entryPoints` -- `entrypoints` -- Capitalization difference - ---- - -- `external` -- `external` -- No differences - ---- - -- `footer` -- n/a -- Not supported - ---- - -- `format` -- `format` -- Only supports `"esm"` currently. Support for `"cjs"` and `"iife"` is planned. - ---- - -- `globalName` -- n/a -- Not supported - ---- - -- `ignoreAnnotations` -- n/a -- Not supported - ---- - -- `inject` -- n/a -- Not supported - ---- - -- `jsx` -- `jsx` -- Not supported in JS API, configure in `tsconfig.json` - ---- - -- `jsxDev` -- `jsxDev` -- Not supported in JS API, configure in `tsconfig.json` - ---- - -- `jsxFactory` -- `jsxFactory` -- Not supported in JS API, configure in `tsconfig.json` - ---- - -- `jsxFragment` -- `jsxFragment` -- Not supported in JS API, configure in `tsconfig.json` - ---- - -- `jsxImportSource` -- `jsxImportSource` -- Not supported in JS API, configure in `tsconfig.json` - ---- - -- `jsxSideEffects` -- `jsxSideEffects` -- Controls whether JSX expressions are marked as pure for dead code elimination - ---- - -- `keepNames` -- n/a -- Not supported - ---- - -- `legalComments` -- n/a -- Not supported - ---- - -- `loader` -- `loader` -- Bun supports a different set of built-in loaders than esbuild; see [Bundler > Loaders](https://bun.com/docs/bundler/loaders) for a complete reference. The esbuild loaders `dataurl`, `binary`, `base64`, `copy`, and `empty` are not yet implemented. - ---- - -- `logLevel` -- n/a -- Not supported - ---- - -- `logLimit` -- n/a -- Not supported - ---- - -- `logOverride` -- n/a -- Not supported - ---- - -- `mainFields` -- n/a -- Not supported - ---- - -- `mangleCache` -- n/a -- Not supported - ---- - -- `mangleProps` -- n/a -- Not supported - ---- - -- `mangleQuoted` -- n/a -- Not supported - ---- - -- `metafile` -- n/a -- Not supported - - - ---- - -- `minify` -- `minify` -- In Bun, `minify` can be a boolean or an object. - - ```ts - await Bun.build({ - entrypoints: ['./index.tsx'], - // enable all minification - minify: true - - // granular options - minify: { - identifiers: true, - syntax: true, - whitespace: true - } - }) - ``` - ---- - -- `minifyIdentifiers` -- `minify.identifiers` -- See `minify` - ---- - -- `minifySyntax` -- `minify.syntax` -- See `minify` - ---- - -- `minifyWhitespace` -- `minify.whitespace` -- See `minify` - ---- - -- `nodePaths` -- n/a -- Not supported - ---- - -- `outExtension` -- n/a -- Not supported - ---- - -- `outbase` -- `root` -- Different name - ---- - -- `outdir` -- `outdir` -- No differences - ---- - -- `outfile` -- `outfile` -- No differences - ---- - -- `packages` -- n/a -- Not supported, use `external` - ---- - -- `platform` -- `target` -- Supports `"bun"`, `"node"` and `"browser"` (the default). Does not support `"neutral"`. - ---- - -- `plugins` -- `plugins` -- Bun's plugin API is a subset of esbuild's. Some esbuild plugins will work out of the box with Bun. - ---- - -- `preserveSymlinks` -- n/a -- Not supported - ---- - -- `publicPath` -- `publicPath` -- No differences - ---- - -- `pure` -- n/a -- Not supported - ---- - -- `reserveProps` -- n/a -- Not supported - ---- - -- `resolveExtensions` -- n/a -- Not supported - ---- - -- `sourceRoot` -- n/a -- Not supported - ---- - -- `sourcemap` -- `sourcemap` -- Supports `"inline"`, `"external"`, and `"none"` - ---- - -- `sourcesContent` -- n/a -- Not supported - ---- - -- `splitting` -- `splitting` -- No differences - ---- - -- `stdin` -- n/a -- Not supported - ---- - -- `supported` -- n/a -- Not supported - ---- - -- `target` -- n/a -- No support for syntax downleveling - ---- - -- `treeShaking` -- n/a -- Always `true` - ---- - -- `tsconfig` -- n/a -- Not supported - ---- - -- `write` -- n/a -- Set to `true` if `outdir`/`outfile` is set, otherwise `false` - ---- - -{% /table %} - -## Plugin API - -Bun's plugin API is designed to be esbuild compatible. Bun doesn't support esbuild's entire plugin API surface, but the core functionality is implemented. Many third-party `esbuild` plugins will work out of the box with Bun. - -{% callout %} -Long term, we aim for feature parity with esbuild's API, so if something doesn't work please file an issue to help us prioritize. - -{% /callout %} - -Plugins in Bun and esbuild are defined with a `builder` object. - -```ts -import type { BunPlugin } from "bun"; - -const myPlugin: BunPlugin = { - name: "my-plugin", - setup(builder) { - // define plugin - }, -}; -``` - -The `builder` object provides some methods for hooking into parts of the bundling process. Bun implements `onResolve` and `onLoad`; it does not yet implement the esbuild hooks `onStart`, `onEnd`, and `onDispose`, and `resolve` utilities. `initialOptions` is partially implemented, being read-only and only having a subset of esbuild's options; use [`config`](https://bun.com/docs/bundler/plugins) (same thing but with Bun's `BuildConfig` format) instead. - -```ts -import type { BunPlugin } from "bun"; -const myPlugin: BunPlugin = { - name: "my-plugin", - setup(builder) { - builder.onResolve( - { - /* onResolve.options */ - }, - args => { - return { - /* onResolve.results */ - }; - }, - ); - builder.onLoad( - { - /* onLoad.options */ - }, - args => { - return { - /* onLoad.results */ - }; - }, - ); - }, -}; -``` - -### `onResolve` - -#### `options` - -{% table %} - -- 🟢 -- `filter` - ---- - -- 🟢 -- `namespace` - -{% /table %} - -#### `arguments` - -{% table %} - -- 🟢 -- `path` - ---- - -- 🟢 -- `importer` - ---- - -- 🔴 -- `namespace` - ---- - -- 🔴 -- `resolveDir` - ---- - -- 🔴 -- `kind` - ---- - -- 🔴 -- `pluginData` - -{% /table %} - -#### `results` - -{% table %} - -- 🟢 -- `namespace` - ---- - -- 🟢 -- `path` - ---- - -- 🔴 -- `errors` - ---- - -- 🔴 -- `external` - ---- - -- 🔴 -- `pluginData` - ---- - -- 🔴 -- `pluginName` - ---- - -- 🔴 -- `sideEffects` - ---- - -- 🔴 -- `suffix` - ---- - -- 🔴 -- `warnings` - ---- - -- 🔴 -- `watchDirs` - ---- - -- 🔴 -- `watchFiles` - -{% /table %} - -### `onLoad` - -#### `options` - -{% table %} - ---- - -- 🟢 -- `filter` - ---- - -- 🟢 -- `namespace` - -{% /table %} - -#### `arguments` - -{% table %} - ---- - -- 🟢 -- `path` - ---- - -- 🔴 -- `namespace` - ---- - -- 🔴 -- `suffix` - ---- - -- 🔴 -- `pluginData` - -{% /table %} - -#### `results` - -{% table %} - ---- - -- 🟢 -- `contents` - ---- - -- 🟢 -- `loader` - ---- - -- 🔴 -- `errors` - ---- - -- 🔴 -- `pluginData` - ---- - -- 🔴 -- `pluginName` - ---- - -- 🔴 -- `resolveDir` - ---- - -- 🔴 -- `warnings` - ---- - -- 🔴 -- `watchDirs` - ---- - -- 🔴 -- `watchFiles` - -{% /table %} diff --git a/docs/cli/bun-completions.md b/docs/cli/bun-completions.md deleted file mode 100644 index 68dcb946d5..0000000000 --- a/docs/cli/bun-completions.md +++ /dev/null @@ -1,3 +0,0 @@ -This command installs completions for `zsh` and/or `fish`. It runs automatically on every `bun upgrade` and on install. It reads from `$SHELL` to determine which shell to install for. It tries several common shell completion directories for your shell and OS. - -If you want to copy the completions manually, run `bun completions > path-to-file`. If you know the completions directory to install them to, run `bun completions /path/to/directory`. diff --git a/docs/cli/bun-install.md b/docs/cli/bun-install.md deleted file mode 100644 index c3311cd1a7..0000000000 --- a/docs/cli/bun-install.md +++ /dev/null @@ -1,349 +0,0 @@ -### `bun install` - -bun install is a fast package manager & npm client. - -bun install can be configured via `bunfig.toml`, environment variables, and CLI flags. - -#### Configuring `bun install` with `bunfig.toml` - -`bunfig.toml` is searched for in the following paths on `bun install`, `bun remove`, and `bun add`: - -1. `$XDG_CONFIG_HOME/.bunfig.toml` or `$HOME/.bunfig.toml` -2. `./bunfig.toml` - -If both are found, the results are merged together. - -Configuring with `bunfig.toml` is optional. Bun tries to be zero configuration in general, but that's not always possible. - -```toml -# Using scoped packages with bun install -[install.scopes] - -# Scope name The value can be a URL string or an object -"@mybigcompany" = { token = "123456", url = "https://registry.mybigcompany.com" } -# URL is optional and falls back to the default registry - -# The "@" in the scope is optional -mybigcompany2 = { token = "123456" } - -# Environment variables can be referenced as a string that starts with $ and it will be replaced -mybigcompany3 = { token = "$npm_config_token" } - -# Setting username and password turns it into a Basic Auth header by taking base64("username:password") -mybigcompany4 = { username = "myusername", password = "$npm_config_password", url = "https://registry.yarnpkg.com/" } -# You can set username and password in the registry URL. This is the same as above. -mybigcompany5 = "https://username:password@registry.yarnpkg.com/" - -# You can set a token for a registry URL: -mybigcompany6 = "https://:$NPM_CONFIG_TOKEN@registry.yarnpkg.com/" - -[install] -# Default registry -# can be a URL string or an object -registry = "https://registry.yarnpkg.com/" -# as an object -#registry = { url = "https://registry.yarnpkg.com/", token = "123456" } - -# Install for production? This is the equivalent to the "--production" CLI argument -production = false - -# Save a text-based lockfile? This is equivalent to the "--save-text-lockfile" CLI argument -saveTextLockfile = false - -# Disallow changes to lockfile? This is the equivalent to the "--frozen-lockfile" CLI argument -frozenLockfile = false - -# Don't actually install -dryRun = true - -# Install optionalDependencies (default: true) -# Setting this to false is equivalent to the `--omit=optional` CLI argument -optional = true - -# Install local devDependencies (default: true) -# Setting this to false is equivalent to the `--omit=dev` CLI argument -dev = true - -# Install peerDependencies (default: true) -# Setting this to false is equivalent to the `--omit=peer` CLI argument -peer = true - -# Max number of concurrent lifecycle scripts (default: (cpu count or GOMAXPROCS) x2) -concurrentScripts = 16 - -# When using `bun install -g`, install packages here -globalDir = "~/.bun/install/global" - -# When using `bun install -g`, link package bins here -globalBinDir = "~/.bun/bin" - -# cache-related configuration -[install.cache] -# The directory to use for the cache -dir = "~/.bun/install/cache" - -# Don't load from the global cache. -# Note: Bun may still write to node_modules/.cache -disable = false - - -# Always resolve the latest versions from the registry -disableManifest = false - - -# Lockfile-related configuration -[install.lockfile] - -# Print a yarn v1 lockfile -# Note: it does not load the lockfile, it just converts bun.lock into a yarn.lock -print = "yarn" - -# Save the lockfile to disk -save = true - -``` - -If it's easier to read as TypeScript types: - -```ts -export interface Root { - install: Install; -} - -export interface Install { - scopes: Scopes; - registry: Registry; - production: boolean; - saveTextLockfile: boolean; - frozenLockfile: boolean; - dryRun: boolean; - optional: boolean; - dev: boolean; - peer: boolean; - globalDir: string; - globalBinDir: string; - cache: Cache; - lockfile: Lockfile; - logLevel: "debug" | "error" | "warn"; -} - -type Registry = - | string - | { - url?: string; - token?: string; - username?: string; - password?: string; - }; - -type Scopes = Record; - -export interface Cache { - dir: string; - disable: boolean; - disableManifest: boolean; -} - -export interface Lockfile { - print?: "yarn"; - save: boolean; -} -``` - -## Configuring with environment variables - -Environment variables have a higher priority than `bunfig.toml`. - -| Name | Description | -| -------------------------------- | ------------------------------------------------------------- | -| BUN_CONFIG_REGISTRY | Set an npm registry (default: ) | -| BUN_CONFIG_TOKEN | Set an auth token (currently does nothing) | -| BUN_CONFIG_YARN_LOCKFILE | Save a Yarn v1-style yarn.lock | -| BUN_CONFIG_LINK_NATIVE_BINS | Point `bin` in package.json to a platform-specific dependency | -| BUN_CONFIG_SKIP_SAVE_LOCKFILE | Don’t save a lockfile | -| BUN_CONFIG_SKIP_LOAD_LOCKFILE | Don’t load a lockfile | -| BUN_CONFIG_SKIP_INSTALL_PACKAGES | Don’t install any packages | - -Bun always tries to use the fastest available installation method for the target platform. On macOS, that’s `clonefile` and on Linux, that’s `hardlink`. You can change which installation method is used with the `--backend` flag. When unavailable or on error, `clonefile` and `hardlink` fallsback to a platform-specific implementation of copying files. - -Bun stores installed packages from npm in `~/.bun/install/cache/${name}@${version}`. Note that if the semver version has a `build` or a `pre` tag, it is replaced with a hash of that value instead. This is to reduce the chances of errors from long file paths, but unfortunately complicates figuring out where a package was installed on disk. - -When the `node_modules` folder exists, before installing, Bun checks if the `"name"` and `"version"` in `package/package.json` in the expected node_modules folder matches the expected `name` and `version`. This is how it determines whether it should install. It uses a custom JSON parser which stops parsing as soon as it finds `"name"` and `"version"`. - -When a `bun.lock` doesn’t exist or `package.json` has changed dependencies, tarballs are downloaded & extracted eagerly while resolving. - -When a `bun.lock` exists and `package.json` hasn’t changed, Bun downloads missing dependencies lazily. If the package with a matching `name` & `version` already exists in the expected location within `node_modules`, Bun won’t attempt to download the tarball. - -## Platform-specific dependencies? - -bun stores normalized `cpu` and `os` values from npm in the lockfile, along with the resolved packages. It skips downloading, extracting, and installing packages disabled for the current target at runtime. This means the lockfile won't change between platforms/architectures even if the packages ultimately installed do change. - -### `--cpu` and `--os` flags - -You can override the target platform for package selection: - -```bash -bun install --cpu=x64 --os=linux -``` - -This installs packages for the specified platform instead of the current system. Useful for cross-platform builds or when preparing deployments for different environments. - -**Accepted values for `--cpu`**: `arm64`, `x64`, `ia32`, `ppc64`, `s390x` - -**Accepted values for `--os`**: `linux`, `darwin`, `win32`, `freebsd`, `openbsd`, `sunos`, `aix` - -## Peer dependencies? - -Peer dependencies are handled similarly to yarn. `bun install` will automatically install peer dependencies. If the dependency is marked optional in `peerDependenciesMeta`, an existing dependency will be chosen if possible. - -## Lockfile - -`bun.lock` is Bun’s lockfile format. See [our blogpost about the text lockfile](https://bun.com/blog/bun-lock-text-lockfile). - -Prior to Bun 1.2, the lockfile was binary and called `bun.lockb`. Old lockfiles can be upgraded to the new format by running `bun install --save-text-lockfile --frozen-lockfile --lockfile-only`, and then deleting `bun.lockb`. - -## Cache - -To delete the cache: - -```bash -$ rm -rf ~/.bun/install/cache -``` - -## Platform-specific backends - -`bun install` uses different system calls to install dependencies depending on the platform. This is a performance optimization. You can force a specific backend with the `--backend` flag. - -**`hardlink`** is the default backend on Linux. Benchmarking showed it to be the fastest on Linux. - -```bash -$ rm -rf node_modules -$ bun install --backend hardlink -``` - -**`clonefile`** is the default backend on macOS. Benchmarking showed it to be the fastest on macOS. It is only available on macOS. - -```bash -$ rm -rf node_modules -$ bun install --backend clonefile -``` - -**`clonefile_each_dir`** is similar to `clonefile`, except it clones each file individually per directory. It is only available on macOS and tends to perform slower than `clonefile`. Unlike `clonefile`, this does not recursively clone subdirectories in one system call. - -```bash -$ rm -rf node_modules -$ bun install --backend clonefile_each_dir -``` - -**`copyfile`** is the fallback used when any of the above fail, and is the slowest. on macOS, it uses `fcopyfile()` and on linux it uses `copy_file_range()`. - -```bash -$ rm -rf node_modules -$ bun install --backend copyfile -``` - -**`symlink`** is typically only used for `file:` dependencies (and eventually `link:`) internally. To prevent infinite loops, it skips symlinking the `node_modules` folder. - -If you install with `--backend=symlink`, Node.js won't resolve node_modules of dependencies unless each dependency has its own node_modules folder or you pass `--preserve-symlinks` to `node` or `bun`. See [Node.js documentation on `--preserve-symlinks`](https://nodejs.org/api/cli.html#--preserve-symlinks). - -```bash -$ rm -rf node_modules -$ bun install --backend symlink -$ bun --preserve-symlinks ./my-file.js -$ node --preserve-symlinks ./my-file.js # https://nodejs.org/api/cli.html#--preserve-symlinks -``` - -## npm registry metadata - -bun uses a binary format for caching NPM registry responses. This loads much faster than JSON and tends to be smaller on disk. -You will see these files in `~/.bun/install/cache/*.npm`. The filename pattern is `${hash(packageName)}.npm`. It’s a hash so that extra directories don’t need to be created for scoped packages. - -Bun's usage of `Cache-Control` ignores `Age`. This improves performance, but means bun may be about 5 minutes out of date to receive the latest package version metadata from npm. - -## pnpm migration - -Bun automatically migrates projects from pnpm to bun. When a `pnpm-lock.yaml` file is detected and no `bun.lock` file exists, Bun will automatically migrate the lockfile to `bun.lock` during installation. The original `pnpm-lock.yaml` file remains unmodified. - -```bash -bun install -``` - -**Note**: Migration only runs when `bun.lock` is absent. There is currently no opt-out flag for pnpm migration. - -The migration process handles: - -### Lockfile Migration - -- Converts `pnpm-lock.yaml` to `bun.lock` format -- Preserves package versions and resolution information -- Maintains dependency relationships and peer dependencies -- Handles patched dependencies with integrity hashes - -### Workspace Configuration - -When a `pnpm-workspace.yaml` file exists, Bun migrates workspace settings to your root `package.json`: - -```yaml -# pnpm-workspace.yaml -packages: - - "apps/*" - - "packages/*" - -catalog: - react: ^18.0.0 - typescript: ^5.0.0 - -catalogs: - build: - webpack: ^5.0.0 - babel: ^7.0.0 -``` - -The workspace packages list and catalogs are moved to the `workspaces` field in `package.json`: - -```json -{ - "workspaces": { - "packages": ["apps/*", "packages/*"], - "catalog": { - "react": "^18.0.0", - "typescript": "^5.0.0" - }, - "catalogs": { - "build": { - "webpack": "^5.0.0", - "babel": "^7.0.0" - } - } - } -} -``` - -### Catalog Dependencies - -Dependencies using pnpm's `catalog:` protocol are preserved: - -```json -{ - "dependencies": { - "react": "catalog:", - "webpack": "catalog:build" - } -} -``` - -### Configuration Migration - -The following pnpm configuration is migrated from both `pnpm-lock.yaml` and `pnpm-workspace.yaml`: - -- **Overrides**: Moved from `pnpm.overrides` to root-level `overrides` in `package.json` -- **Patched Dependencies**: Moved from `pnpm.patchedDependencies` to root-level `patchedDependencies` in `package.json` -- **Workspace Overrides**: Applied from `pnpm-workspace.yaml` to root `package.json` - -### Requirements - -- Requires pnpm lockfile version 7 or higher -- Workspace packages must have a `name` field in their `package.json` -- All catalog entries referenced by dependencies must exist in the catalogs definition - -After migration, you can safely remove `pnpm-lock.yaml` and `pnpm-workspace.yaml` files. diff --git a/docs/cli/bun-upgrade.md b/docs/cli/bun-upgrade.md deleted file mode 100644 index 5ce49fa9e1..0000000000 --- a/docs/cli/bun-upgrade.md +++ /dev/null @@ -1,39 +0,0 @@ -To upgrade Bun, run `bun upgrade`. - -It automatically downloads the latest version of Bun and overwrites the currently-running version. - -This works by checking the latest version of Bun in [bun-releases-for-updater](https://github.com/Jarred-Sumner/bun-releases-for-updater/releases) and unzipping it using the system-provided `unzip` library (so that Gatekeeper works on macOS) - -If for any reason you run into issues, you can also use the curl install script: - -```bash -$ curl https://bun.com/install | bash -``` - -It will still work when Bun is already installed. - -Bun is distributed as a single binary file, so you can also do this manually: - -- Download the latest version of Bun for your platform in [bun-releases-for-updater](https://github.com/Jarred-Sumner/bun-releases-for-updater/releases/latest) (`darwin` == macOS) -- Unzip the folder -- Move the `bun` binary to `~/.bun/bin` (or anywhere) - -## `--canary` - -[Canary](https://github.com/oven-sh/bun/releases/tag/canary) builds are generated on every commit. - -To install a [canary](https://github.com/oven-sh/bun/releases/tag/canary) build of Bun, run: - -```bash -$ bun upgrade --canary -``` - -This flag is not persistent (though that might change in the future). If you want to always run the canary build of Bun, set the `BUN_CANARY` environment variable to `1` in your shell's startup script. - -This will download the release zip from https://github.com/oven-sh/bun/releases/tag/canary. - -To revert to the latest published version of Bun, run: - -```bash -$ bun upgrade -``` diff --git a/docs/cli/info.md b/docs/cli/info.md deleted file mode 100644 index d1fb1801f8..0000000000 --- a/docs/cli/info.md +++ /dev/null @@ -1,65 +0,0 @@ -`bun info` displays package metadata from the npm registry. - -## Usage - -```bash -$ bun info react -``` - -This will display information about the `react` package, including its latest version, description, homepage, dependencies, and more. - -## Viewing specific versions - -To view information about a specific version: - -```bash -$ bun info react@18.0.0 -``` - -## Viewing specific properties - -You can also query specific properties from the package metadata: - -```bash -$ bun info react version -$ bun info react dependencies -$ bun info react repository.url -``` - -## JSON output - -To get the output in JSON format, use the `--json` flag: - -```bash -$ bun info react --json -``` - -## Alias - -`bun pm view` is an alias for `bun info`: - -```bash -$ bun pm view react # equivalent to: bun info react -``` - -## Examples - -```bash -# View basic package information -$ bun info is-number - -# View a specific version -$ bun info is-number@7.0.0 - -# View all available versions -$ bun info is-number versions - -# View package dependencies -$ bun info express dependencies - -# View package homepage -$ bun info lodash homepage - -# Get JSON output -$ bun info react --json -``` diff --git a/docs/cli/patch-commit.md b/docs/cli/patch-commit.md deleted file mode 100644 index acee94b238..0000000000 --- a/docs/cli/patch-commit.md +++ /dev/null @@ -1,11 +0,0 @@ -An alias for `bun patch --commit` to maintain compatibility with pnpm. - -To get started with patch, first prepare the package for patching with [`bun patch `](https://bun.com/docs/install/patch). - -### `--patches-dir` - -By default, `bun patch-commit` will use the `patches` directory in the temporary directory. - -You can specify a different directory with the `--patches-dir` flag. - -{% bunCLIUsage command="patch-commit" /%} diff --git a/docs/cli/remove.md b/docs/cli/remove.md deleted file mode 100644 index 118ce44c35..0000000000 --- a/docs/cli/remove.md +++ /dev/null @@ -1,7 +0,0 @@ -To remove a dependency: - -```bash -$ bun remove ts-node -``` - -{% bunCLIUsage command="remove" /%} diff --git a/docs/cli/unlink.md b/docs/cli/unlink.md deleted file mode 100644 index c841651b30..0000000000 --- a/docs/cli/unlink.md +++ /dev/null @@ -1,9 +0,0 @@ -Use `bun unlink` in the root directory to unregister a local package. - -```bash -$ cd /path/to/cool-pkg -$ bun unlink -bun unlink v1.x (7416672e) -``` - -{% bunCLIUsage command="unlink" /%} diff --git a/docs/contributing/upgrading-webkit.md b/docs/contributing/upgrading-webkit.md deleted file mode 100644 index 637adb4c1e..0000000000 --- a/docs/contributing/upgrading-webkit.md +++ /dev/null @@ -1,57 +0,0 @@ -Bun uses [a fork](https://github.com/oven-sh/WebKit) of WebKit with a small number of changes. - -It's important to periodically update WebKit for many reasons: - -- Security -- Performance -- Compatibility -- …and many more. - -To upgrade, first find the commit in **Bun's WebKit fork** (not Bun!) between when we last upgraded and now. - -```bash -$ cd src/bun.js/WebKit # In the WebKit directory! not bun -$ git checkout $COMMIT -``` - -This is the main command to run: - -```bash -$ git merge upstream main -# If you get an error saying histories are unrelated, run this and try again: -$ git fetch --unshallow -``` - -Then, you will likely see some silly merge conflicts. Fix them and then run: - -```bash -# You might have to run this multiple times. -$ rm -rf WebKitBuild - -# Go to Bun's directory! Not WebKit. -cd ../../../../ -make jsc-build-mac-compile -``` - -Make sure that JSC's CLI is able to load successfully. This verifies that the build is working. - -You know this worked when it printed help options. If it complains about symbols, crashes, or anything else that looks wrong, something is wrong. - -```bash -src/bun.js/WebKit/WebKitBuild/Release/bin/jsc --help -``` - -Then, clear out our bindings and regenerate the C++<>Zig headers: - -```bash -make clean-bindings headers builtins -``` - -Now update Bun's bindings wherever there are compiler errors: - -```bash -# It will take awhile if you don't pass -j here -make bindings -j10 -``` - -This is the hard part. It might involve digging through WebKit's commit history to figure out what changed and why. Fortunately, WebKit contributors write great commit messages. diff --git a/docs/docs.json b/docs/docs.json new file mode 100644 index 0000000000..f9791f4081 --- /dev/null +++ b/docs/docs.json @@ -0,0 +1,618 @@ +{ + "$schema": "https://mintlify.com/docs.json", + "theme": "aspen", + "name": "Bun", + "seo": { + "metatags": { + "canonical": "https://bun.com/docs" + } + }, + "colors": { + "light": "#ff73a8", + "primary": "#ff73a8", + "dark": "#ff73a8" + }, + "background": { + "decoration": "gradient" + }, + "favicon": "/logo/bun.png", + "icons": { + "library": "lucide" + }, + "fonts": { + "heading": { + "family": "Inter Display Bold", + "source": "https://mintlify-assets.b-cdn.net/fonts/InterDisplay-Bold.woff2", + "format": "woff2" + } + }, + "appearance": { + "default": "system" + }, + "logo": { + "light": "/logo/logo-with-wordmark-dark.svg", + "dark": "/logo/logo-with-wordmark-light.svg" + }, + "navbar": { + "links": [ + { + "label": "Install Bun", + "href": "https://www.bun.com/docs/installation", + "icon": "download", + "primary": true + } + ] + }, + "contextual": { + "options": ["copy", "view", "chatgpt", "claude", "perplexity", "mcp", "cursor", "vscode"] + }, + "styling": { + "codeblocks": { + "theme": { + "light": "github-light", + "dark": "dracula" + } + } + }, + "navigation": { + "tabs": [ + { + "tab": "Runtime", + "icon": "cog", + "groups": [ + { + "group": "Get Started", + "icon": "terminal", + "pages": [ + "/index", + "/installation", + "/quickstart", + "/typescript", + "/runtime/templating/init", + "/runtime/templating/create" + ] + }, + { + "group": "Core Runtime", + "icon": "cog", + "pages": ["/runtime/index", "/runtime/watch-mode", "/runtime/debugger", "/runtime/bunfig"] + }, + { + "group": "File & Module System", + "icon": "file", + "pages": [ + "/runtime/file-types", + "/runtime/module-resolution", + "/runtime/jsx", + "/runtime/auto-install", + "/runtime/plugins", + "/runtime/file-system-router" + ] + }, + { + "group": "HTTP server", + "icon": "server", + "pages": [ + "/runtime/http/server", + "/runtime/http/routing", + "/runtime/http/cookies", + "/runtime/http/tls", + "/runtime/http/error-handling", + "/runtime/http/metrics" + ] + }, + { + "group": "Networking", + "icon": "globe", + "expanded": true, + "pages": [ + "/runtime/networking/fetch", + "/runtime/http/websockets", + "/runtime/networking/tcp", + "/runtime/networking/udp", + "/runtime/networking/dns" + ] + }, + { + "group": "Data & Storage", + "icon": "database", + "pages": [ + "/runtime/cookies", + "/runtime/file-io", + "/runtime/streams", + "/runtime/binary-data", + "/runtime/sql", + "/runtime/sqlite", + "/runtime/s3", + "/runtime/redis" + ] + }, + { + "group": "Concurrency", + "icon": "split", + "pages": ["/runtime/workers"] + }, + { + "group": "Process & System", + "icon": "computer", + "pages": ["/runtime/environment-variables", "/runtime/shell", "/runtime/child-process"] + }, + { + "group": "Interop & Tooling", + "icon": "puzzle", + "pages": ["/runtime/node-api", "/runtime/ffi", "/runtime/c-compiler", "/runtime/transpiler"] + }, + { + "group": "Utilities", + "icon": "wrench", + "pages": [ + "/runtime/secrets", + "/runtime/console", + "/runtime/yaml", + "/runtime/html-rewriter", + "/runtime/hashing", + "/runtime/glob", + "/runtime/semver", + "/runtime/color", + "/runtime/utils" + ] + }, + { + "group": "Standards & Compatibility", + "icon": "badge-check", + "pages": ["/runtime/globals", "/runtime/bun-apis", "/runtime/web-apis", "/runtime/nodejs-compat"] + }, + { + "group": "Contributing", + "icon": "heart", + "pages": [ + "/project/roadmap", + "/project/benchmarking", + "/project/contributing", + "/project/building-windows", + "/project/bindgen", + "/project/license" + ] + } + ] + }, + { + "tab": "Package Manager", + "icon": "box", + "groups": [ + { + "group": "Core Commands", + "icon": "terminal", + "pages": ["/pm/cli/install", "/pm/cli/add", "/pm/cli/remove", "/pm/cli/update", "/pm/bunx"] + }, + { + "group": "Publishing & Analysis", + "icon": "upload", + "pages": ["/pm/cli/publish", "/pm/cli/outdated", "/pm/cli/why", "/pm/cli/audit"] + }, + { + "group": "Workspace Management", + "icon": "folders", + "pages": ["/pm/workspaces", "/pm/catalogs", "/pm/cli/link", "/pm/cli/pm"] + }, + { + "group": "Advanced Configuration", + "icon": "settings", + "pages": [ + "/pm/cli/patch", + "/pm/filter", + "/pm/global-cache", + "/pm/isolated-installs", + "/pm/lockfile", + "/pm/lifecycle", + "/pm/scopes-registries", + "/pm/overrides", + "/pm/security-scanner-api", + "/pm/npmrc" + ] + } + ] + }, + { + "tab": "Bundler", + "icon": "combine", + "groups": [ + { + "group": "Core", + "icon": "package", + "pages": ["/bundler/index"] + }, + { + "group": "Development Server", + "icon": "monitor", + "pages": ["/bundler/fullstack", "/bundler/hot-reloading"] + }, + { + "group": "Asset Processing", + "icon": "image", + "pages": ["/bundler/html-static", "/bundler/css", "/bundler/loaders"] + }, + { + "group": "Single File Executable", + "icon": "binary", + "pages": ["/bundler/executables"] + }, + { + "group": "Extensions", + "icon": "plug", + "pages": ["/bundler/plugins", "/bundler/macros"] + }, + { + "group": "Optimization", + "icon": "zap", + "pages": ["/bundler/bytecode", "/bundler/minifier"] + }, + { + "group": "Migration", + "icon": "arrow-right", + "pages": ["/bundler/esbuild"] + } + ] + }, + { + "tab": "Test Runner", + "icon": "flask-conical", + "groups": [ + { + "group": "Getting Started", + "icon": "circle-play", + "pages": ["/test/index", "/test/writing-tests", "/test/configuration"] + }, + { + "group": "Test Execution", + "icon": "zap", + "pages": ["/test/runtime-behavior", "/test/discovery"] + }, + { + "group": "Test Features", + "icon": "sparkles", + "pages": ["/test/lifecycle", "/test/mocks", "/test/snapshots", "/test/dates-times"] + }, + { + "group": "Specialized Testing", + "icon": "microscope", + "pages": ["/test/dom"] + }, + { + "group": "Reporting", + "icon": "file-text", + "pages": ["/test/code-coverage", "/test/reporters"] + } + ] + }, + + { + "tab": "Guides", + "icon": "map", + "groups": [ + { + "group": "Overview", + "icon": "globe", + "pages": ["/guides/index"] + }, + { + "group": "Deployment", + "icon": "rocket", + "pages": ["/guides/deployment/vercel", "/guides/deployment/railway", "/guides/deployment/render"] + }, + { + "group": "Runtime & Debugging", + "icon": "bug", + "pages": [ + "/guides/runtime/typescript", + "/guides/runtime/tsconfig-paths", + "/guides/runtime/vscode-debugger", + "/guides/runtime/web-debugger", + "/guides/runtime/heap-snapshot", + "/guides/runtime/build-time-constants", + "/guides/runtime/define-constant", + "/guides/runtime/cicd", + "/guides/runtime/codesign-macos-executable" + ] + }, + { + "group": "Utilities", + "icon": "wrench", + "pages": [ + "/guides/util/detect-bun", + "/guides/util/version", + "/guides/util/hash-a-password", + "/guides/util/javascript-uuid", + "/guides/util/base64", + "/guides/util/gzip", + "/guides/util/deflate", + "/guides/util/escape-html", + "/guides/util/deep-equals", + "/guides/util/sleep", + "/guides/util/file-url-to-path", + "/guides/util/path-to-file-url", + "/guides/util/which-path-to-executable-bin", + "/guides/util/import-meta-dir", + "/guides/util/import-meta-file", + "/guides/util/import-meta-path", + "/guides/util/entrypoint", + "/guides/util/main" + ] + }, + { + "group": "Ecosystem & Frameworks", + "icon": "puzzle", + "pages": [ + "/guides/ecosystem/astro", + "/guides/ecosystem/discordjs", + "/guides/ecosystem/docker", + "/guides/ecosystem/drizzle", + "/guides/ecosystem/edgedb", + "/guides/ecosystem/elysia", + "/guides/ecosystem/express", + "/guides/ecosystem/hono", + "/guides/ecosystem/mongoose", + "/guides/ecosystem/neon-drizzle", + "/guides/ecosystem/neon-serverless-postgres", + "/guides/ecosystem/nextjs", + "/guides/ecosystem/nuxt", + "/guides/ecosystem/pm2", + "/guides/ecosystem/prisma", + "/guides/ecosystem/prisma-postgres", + "/guides/ecosystem/qwik", + "/guides/ecosystem/react", + "/guides/ecosystem/remix", + "/guides/ecosystem/sentry", + "/guides/ecosystem/solidstart", + "/guides/ecosystem/ssr-react", + "/guides/ecosystem/stric", + "/guides/ecosystem/sveltekit", + "/guides/ecosystem/systemd", + "/guides/ecosystem/vite" + ] + }, + { + "group": "HTTP & Networking", + "icon": "globe", + "pages": [ + "/guides/http/server", + "/guides/http/simple", + "/guides/http/fetch", + "/guides/http/hot", + "/guides/http/cluster", + "/guides/http/tls", + "/guides/http/proxy", + "/guides/http/stream-file", + "/guides/http/file-uploads", + "/guides/http/fetch-unix", + "/guides/http/stream-iterator", + "/guides/http/stream-node-streams-in-bun" + ] + }, + { + "group": "WebSocket", + "icon": "radio", + "pages": [ + "/guides/websocket/simple", + "/guides/websocket/pubsub", + "/guides/websocket/context", + "/guides/websocket/compression" + ] + }, + { + "group": "Processes & System", + "icon": "cpu", + "pages": [ + "/guides/process/spawn", + "/guides/process/spawn-stdout", + "/guides/process/spawn-stderr", + "/guides/process/argv", + "/guides/process/stdin", + "/guides/process/ipc", + "/guides/process/ctrl-c", + "/guides/process/os-signals", + "/guides/process/nanoseconds", + "/guides/runtime/shell", + "/guides/runtime/timezone", + "/guides/runtime/set-env", + "/guides/runtime/read-env" + ] + }, + { + "group": "Package Manager", + "icon": "package", + "pages": [ + "/guides/install/add", + "/guides/install/add-dev", + "/guides/install/add-optional", + "/guides/install/add-peer", + "/guides/install/add-git", + "/guides/install/add-tarball", + "/guides/install/npm-alias", + "/guides/install/workspaces", + "/guides/install/custom-registry", + "/guides/install/registry-scope", + "/guides/install/azure-artifacts", + "/guides/install/jfrog-artifactory", + "/guides/install/trusted", + "/guides/install/yarnlock", + "/guides/install/from-npm-install-to-bun-install", + "/guides/install/git-diff-bun-lockfile", + "/guides/install/cicd" + ] + }, + { + "group": "Test Runner", + "icon": "flask-conical", + "pages": [ + "/guides/test/run-tests", + "/guides/test/watch-mode", + "/guides/test/migrate-from-jest", + "/guides/test/mock-functions", + "/guides/test/spy-on", + "/guides/test/mock-clock", + "/guides/test/snapshot", + "/guides/test/update-snapshots", + "/guides/test/coverage", + "/guides/test/coverage-threshold", + "/guides/test/skip-tests", + "/guides/test/todo-tests", + "/guides/test/timeout", + "/guides/test/bail", + "/guides/test/rerun-each", + "/guides/test/testing-library", + "/guides/test/happy-dom", + "/guides/test/svelte-test" + ] + }, + { + "group": "Runtime & Debugging", + "icon": "bug", + "pages": [ + "/guides/runtime/vscode-debugger", + "/guides/runtime/web-debugger", + "/guides/runtime/heap-snapshot", + "/guides/runtime/build-time-constants", + "/guides/runtime/define-constant", + "/guides/runtime/cicd", + "/guides/runtime/codesign-macos-executable" + ] + }, + { + "group": "Module System", + "icon": "box", + "pages": [ + "/guides/runtime/import-json", + "/guides/runtime/import-toml", + "/guides/runtime/import-yaml", + "/guides/runtime/import-html", + "/guides/util/import-meta-dir", + "/guides/util/import-meta-file", + "/guides/util/import-meta-path", + "/guides/util/entrypoint", + "/guides/util/main" + ] + }, + { + "group": "File System", + "icon": "folder", + "pages": [ + "/guides/read-file/string", + "/guides/read-file/buffer", + "/guides/read-file/uint8array", + "/guides/read-file/arraybuffer", + "/guides/read-file/json", + "/guides/read-file/mime", + "/guides/read-file/exists", + "/guides/read-file/watch", + "/guides/read-file/stream", + "/guides/write-file/basic", + "/guides/write-file/blob", + "/guides/write-file/response", + "/guides/write-file/append", + "/guides/write-file/filesink", + "/guides/write-file/stream", + "/guides/write-file/stdout", + "/guides/write-file/cat", + "/guides/write-file/file-cp", + "/guides/write-file/unlink", + "/guides/runtime/delete-file", + "/guides/runtime/delete-directory" + ] + }, + { + "group": "Utilities", + "icon": "wrench", + "pages": [ + "/guides/util/hash-a-password", + "/guides/util/javascript-uuid", + "/guides/util/base64", + "/guides/util/gzip", + "/guides/util/deflate", + "/guides/util/escape-html", + "/guides/util/deep-equals", + "/guides/util/sleep", + "/guides/util/file-url-to-path", + "/guides/util/path-to-file-url", + "/guides/util/which-path-to-executable-bin" + ] + }, + { + "group": "HTML Processing", + "icon": "file-code", + "pages": ["/guides/html-rewriter/extract-links", "/guides/html-rewriter/extract-social-meta"] + }, + { + "group": "Binary Data", + "icon": "binary", + "pages": [ + "/guides/binary/arraybuffer-to-string", + "/guides/binary/arraybuffer-to-buffer", + "/guides/binary/arraybuffer-to-blob", + "/guides/binary/arraybuffer-to-array", + "/guides/binary/arraybuffer-to-typedarray", + "/guides/binary/buffer-to-string", + "/guides/binary/buffer-to-arraybuffer", + "/guides/binary/buffer-to-blob", + "/guides/binary/buffer-to-typedarray", + "/guides/binary/buffer-to-readablestream", + "/guides/binary/blob-to-string", + "/guides/binary/blob-to-arraybuffer", + "/guides/binary/blob-to-typedarray", + "/guides/binary/blob-to-dataview", + "/guides/binary/blob-to-stream", + "/guides/binary/typedarray-to-string", + "/guides/binary/typedarray-to-arraybuffer", + "/guides/binary/typedarray-to-buffer", + "/guides/binary/typedarray-to-blob", + "/guides/binary/typedarray-to-dataview", + "/guides/binary/typedarray-to-readablestream", + "/guides/binary/dataview-to-string" + ] + }, + { + "group": "Streams", + "icon": "waves", + "pages": [ + "/guides/streams/to-string", + "/guides/streams/to-json", + "/guides/streams/to-blob", + "/guides/streams/to-buffer", + "/guides/streams/to-arraybuffer", + "/guides/streams/to-typedarray", + "/guides/streams/to-array", + "/guides/streams/node-readable-to-string", + "/guides/streams/node-readable-to-json", + "/guides/streams/node-readable-to-blob", + "/guides/streams/node-readable-to-uint8array", + "/guides/streams/node-readable-to-arraybuffer" + ] + } + ] + }, + { + "tab": "Reference", + "icon": "book", + "href": "https://bun.com/reference" + }, + { + "tab": "Blog", + "icon": "newspaper", + "href": "https://bun.com/blog" + }, + { + "tab": "Feedback", + "icon": "lightbulb", + "pages": ["/feedback"] + } + ] + }, + "footer": { + "socials": { + "x": "https://x.com/bunjavascript", + "github": "https://github.com/oven-sh/bun", + "discord": "https://bun.com/discord", + "youtube": "https://www.youtube.com/@bunjs" + } + } +} diff --git a/docs/ecosystem/elysia.md b/docs/ecosystem/elysia.md deleted file mode 100644 index 60dafe9812..0000000000 --- a/docs/ecosystem/elysia.md +++ /dev/null @@ -1,24 +0,0 @@ -[Elysia](https://elysiajs.com) is a Bun-first performance focused web framework that takes full advantage of Bun's HTTP, file system, and hot reloading APIs. -Designed with TypeScript in mind, you don't need to understand TypeScript to gain the benefit of TypeScript with Elysia. The library understands what you want and automatically infers the type from your code. - -⚡️ Elysia is [one of the fastest Bun web frameworks](https://github.com/SaltyAom/bun-http-framework-benchmark) - -```ts#server.ts -import { Elysia } from 'elysia' - -const app = new Elysia() - .get('/', () => 'Hello Elysia') - .listen(8080) - -console.log(`🦊 Elysia is running at on port ${app.server.port}...`) -``` - -Get started with `bun create`. - -```bash -$ bun create elysia ./myapp -$ cd myapp -$ bun run dev -``` - -Refer to the Elysia [documentation](https://elysiajs.com/quick-start.html) for more information. diff --git a/docs/ecosystem/express.md b/docs/ecosystem/express.md deleted file mode 100644 index b172cc3c3f..0000000000 --- a/docs/ecosystem/express.md +++ /dev/null @@ -1,37 +0,0 @@ -Projects that use Express and other major Node.js HTTP libraries should work out of the box. - -{% callout %} -If you run into bugs, [please file an issue](https://bun.com/issues) _in Bun's repo_, not the library. It is Bun's responsibility to address Node.js compatibility issues. -{% /callout %} - -```ts -import express from "express"; - -const app = express(); -const port = 8080; - -app.get("/", (req, res) => { - res.send("Hello World!"); -}); - -app.listen(port, () => { - console.log(`Listening on port ${port}...`); -}); -``` - -Bun implements the [`node:http`](https://nodejs.org/api/http.html) and [`node:https`](https://nodejs.org/api/https.html) modules that these libraries rely on. These modules can also be used directly, though [`Bun.serve`](https://bun.com/docs/api/http) is recommended for most use cases. - -{% callout %} -**Note** — Refer to the [Runtime > Node.js APIs](https://bun.com/docs/runtime/nodejs-apis#node-http) page for more detailed compatibility information. -{% /callout %} - -```ts -import * as http from "node:http"; - -http - .createServer(function (req, res) { - res.write("Hello World!"); - res.end(); - }) - .listen(8080); -``` diff --git a/docs/ecosystem/hono.md b/docs/ecosystem/hono.md deleted file mode 100644 index e96fbc5c91..0000000000 --- a/docs/ecosystem/hono.md +++ /dev/null @@ -1,18 +0,0 @@ -[Hono](https://github.com/honojs/hono) is a lightweight ultrafast web framework designed for the edge. - -```ts -import { Hono } from "hono"; -const app = new Hono(); - -app.get("/", c => c.text("Hono!")); - -export default app; -``` - -Get started with `bun create` or follow Hono's [Bun quickstart](https://hono.dev/getting-started/bun). - -```bash -$ bun create hono ./myapp -$ cd myapp -$ bun run start -``` diff --git a/docs/ecosystem/react.md b/docs/ecosystem/react.md deleted file mode 100644 index 570e8e3c88..0000000000 --- a/docs/ecosystem/react.md +++ /dev/null @@ -1,65 +0,0 @@ -Bun supports `.jsx` and `.tsx` files out of the box. Bun's internal transpiler converts JSX syntax into vanilla JavaScript before execution. - -```tsx#react.tsx -function Component(props: {message: string}) { - return ( - -

{props.message}

- - ); -} - -console.log(); -``` - -Bun implements special logging for JSX to make debugging easier. - -```bash -$ bun run react.tsx - -``` - -### Prop punning - -The Bun runtime also supports "prop punning" for JSX. This is a shorthand syntax useful for assigning a variable to a prop with the same name. - -```tsx -function Div(props: {className: string;}) { - const {className} = props; - - // without punning - return
; - // with punning - return
; -} -``` - -### Server-side rendering - -To server-side render (SSR) React in an [HTTP server](https://bun.com/docs/api/http): - -```tsx#ssr.tsx -import {renderToReadableStream} from 'react-dom/server'; - -function Component(props: {message: string}) { - return ( - -

{props.message}

- - ); -} - -Bun.serve({ - port: 4000, - async fetch() { - const stream = await renderToReadableStream( - - ); - return new Response(stream, { - headers: {'Content-Type': 'text/html'}, - }); - }, -}); -``` - -React `18.3` and later includes an [SSR optimization](https://github.com/facebook/react/pull/25597) that takes advantage of Bun's "direct" `ReadableStream` implementation. diff --git a/docs/ecosystem/stric.md b/docs/ecosystem/stric.md deleted file mode 100644 index bba1b0ae23..0000000000 --- a/docs/ecosystem/stric.md +++ /dev/null @@ -1,38 +0,0 @@ -[Stric](https://github.com/bunsvr) is a minimalist, fast web framework for Bun. - -```ts#index.ts -import { Router } from '@stricjs/router'; - -// Export the fetch handler and serve with Bun -export default new Router() - // Return 'Hi' on every request - .get('/', () => new Response('Hi')); -``` - -Stric provides support for [ArrowJS](https://www.arrow-js.com), a library for building reactive interfaces. - -{% codetabs %} - -```ts#src/App.ts -import { html } from '@stricjs/arrow/utils'; - -// Code inside this function can use web APIs -export function render() { - // Render a

element with text 'Hi' - html`

Hi

`; -}; - -// Set the path to handle -export const path = '/'; -``` - -```ts#index.ts -import { PageRouter } from '@stricjs/arrow'; - -// Create a page router, build and serve directly -new PageRouter().serve(); -``` - -{% /codetabs %} - -For more info, see Stric's [documentation](https://stricjs.gitbook.io/docs). diff --git a/docs/feedback.mdx b/docs/feedback.mdx new file mode 100644 index 0000000000..710cb89b7a --- /dev/null +++ b/docs/feedback.mdx @@ -0,0 +1,85 @@ +--- +title: Feedback +description: Share feedback, bug reports, and feature requests +mode: center +--- + +import Feedback from "/snippets/cli/feedback.mdx"; + +Whether you've found a bug, have a performance issue, or just want to suggest an improvement, here's how you can open a helpful issue: + + + For general questions, please join our [Discord](https://discord.com/invite/CXdq2DP29u). + + +## Reporting Issues + + + + Try upgrading Bun to the latest version with `bun upgrade`. This might fix your problem without having to open an issue. + + ```bash terminal icon="terminal" + bun upgrade + ``` + + You can also try the latest canary release, which includes the most recent changes and bug fixes that haven't been released in a stable version yet. + + ```bash terminal icon="terminal" + bun upgrade --canary + + # To revert back to the stable + bun upgrade --stable + ``` + + If the issue still persists after upgrading, continue to the next step. + + + First take a minute to check if the issue has already been reported. Don't open a new issue if it has already been reported, it saves time for everyone and helps us focus on fixing things faster. + + - 🔍 [**Search existing issues**](https://github.com/oven-sh/bun/issues) + - 💬 [**Check discussions**](https://github.com/oven-sh/bun/discussions) + + If you find a related issue, add a 👍 reaction or comment with extra details instead of opening a new one. + + + If no one has reported the issue, please open a new issue or suggest an improvement. + + - 🐞 [**Report a Bug**](https://github.com/oven-sh/bun/issues/new?template=2-bug-report.yml) + - ⚡ [**Suggest an Improvement**](https://github.com/oven-sh/bun/issues/new?template=4-feature-request.yml) + + Please provide as much detail as possible, including: + - A clear and concise title + - A code example or steps to reproduce the issue + - The version of Bun you are using (run `bun --version`) + - A detailed description of the issue (what happened, what you expected to happen, and what actually happened) + - The operating system and version you are using + + - For MacOS and Linux: copy the output of `uname -mprs` + - For Windows: copy the output of this command in the powershell console: + ```powershell + "$([Environment]::OSVersion | ForEach-Object VersionString) $(if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" })" + ``` + + + + + +The Bun team will review the issue and get back to you as soon as possible! + +--- + +## Use `bun feedback` + +Alternatively, you can use `bun feedback` to share feedback, bug reports, and feature requests directly with the Bun team. + +```bash terminal icon="terminal" +bun feedback "Love the new release!" +bun feedback report.txt details.log +echo "please document X" | bun feedback --email you@example.com +``` + +You can provide feedback as text arguments, file paths, or piped input. + +--- + + diff --git a/docs/guides/binary/arraybuffer-to-array.md b/docs/guides/binary/arraybuffer-to-array.mdx similarity index 90% rename from docs/guides/binary/arraybuffer-to-array.md rename to docs/guides/binary/arraybuffer-to-array.mdx index 0ad56f7deb..6141431fae 100644 --- a/docs/guides/binary/arraybuffer-to-array.md +++ b/docs/guides/binary/arraybuffer-to-array.mdx @@ -1,5 +1,7 @@ --- -name: Convert an ArrayBuffer to an array of numbers +title: Convert an ArrayBuffer to an array of numbers +sidebarTitle: "ArrayBuffer to Array" +mode: center --- To retrieve the contents of an `ArrayBuffer` as an array of numbers, create a [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) over of the buffer. and use the [`Array.from()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) method to convert it to an array. diff --git a/docs/guides/binary/arraybuffer-to-blob.md b/docs/guides/binary/arraybuffer-to-blob.mdx similarity index 88% rename from docs/guides/binary/arraybuffer-to-blob.md rename to docs/guides/binary/arraybuffer-to-blob.mdx index 14fa68013d..d3b4bef160 100644 --- a/docs/guides/binary/arraybuffer-to-blob.md +++ b/docs/guides/binary/arraybuffer-to-blob.mdx @@ -1,5 +1,7 @@ --- -name: Convert an ArrayBuffer to a Blob +title: Convert an ArrayBuffer to a Blob +sidebarTitle: "ArrayBuffer to Blob" +mode: center --- A [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) can be constructed from an array of "chunks", where each chunk is a string, binary data structure, or another `Blob`. diff --git a/docs/guides/binary/arraybuffer-to-buffer.md b/docs/guides/binary/arraybuffer-to-buffer.mdx similarity index 88% rename from docs/guides/binary/arraybuffer-to-buffer.md rename to docs/guides/binary/arraybuffer-to-buffer.mdx index 3d9f1cd495..ba448c7456 100644 --- a/docs/guides/binary/arraybuffer-to-buffer.md +++ b/docs/guides/binary/arraybuffer-to-buffer.mdx @@ -1,5 +1,7 @@ --- -name: Convert an ArrayBuffer to a Buffer +title: Convert an ArrayBuffer to a Buffer +sidebarTitle: "ArrayBuffer to Buffer" +mode: center --- The Node.js [`Buffer`](https://nodejs.org/api/buffer.html) API predates the introduction of `ArrayBuffer` into the JavaScript language. Bun implements both. diff --git a/docs/guides/binary/arraybuffer-to-string.md b/docs/guides/binary/arraybuffer-to-string.mdx similarity index 82% rename from docs/guides/binary/arraybuffer-to-string.md rename to docs/guides/binary/arraybuffer-to-string.mdx index 551347f03e..67d8559f93 100644 --- a/docs/guides/binary/arraybuffer-to-string.md +++ b/docs/guides/binary/arraybuffer-to-string.mdx @@ -1,5 +1,7 @@ --- -name: Convert an ArrayBuffer to a string +title: Convert an ArrayBuffer to a string +sidebarTitle: "ArrayBuffer to string" +mode: center --- Bun implements the Web-standard [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder) class for converting between binary data types and strings. diff --git a/docs/guides/binary/arraybuffer-to-typedarray.md b/docs/guides/binary/arraybuffer-to-typedarray.mdx similarity index 90% rename from docs/guides/binary/arraybuffer-to-typedarray.md rename to docs/guides/binary/arraybuffer-to-typedarray.mdx index 495caae90b..194c26c05b 100644 --- a/docs/guides/binary/arraybuffer-to-typedarray.md +++ b/docs/guides/binary/arraybuffer-to-typedarray.mdx @@ -1,5 +1,7 @@ --- -name: Convert an ArrayBuffer to a Uint8Array +title: Convert an ArrayBuffer to a Uint8Array +sidebarTitle: "ArrayBuffer to Uint8Array" +mode: center --- A `Uint8Array` is a _typed array_, meaning it is a mechanism for viewing the data in an underlying `ArrayBuffer`. diff --git a/docs/guides/binary/blob-to-arraybuffer.md b/docs/guides/binary/blob-to-arraybuffer.mdx similarity index 82% rename from docs/guides/binary/blob-to-arraybuffer.md rename to docs/guides/binary/blob-to-arraybuffer.mdx index bef23da95e..6edfa274c1 100644 --- a/docs/guides/binary/blob-to-arraybuffer.md +++ b/docs/guides/binary/blob-to-arraybuffer.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Blob to an ArrayBuffer +title: Convert a Blob to an ArrayBuffer +sidebarTitle: "Blob to ArrayBuffer" +mode: center --- The [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) class provides a number of methods for consuming its contents in different formats, including `.arrayBuffer()`. diff --git a/docs/guides/binary/blob-to-dataview.md b/docs/guides/binary/blob-to-dataview.mdx similarity index 86% rename from docs/guides/binary/blob-to-dataview.md rename to docs/guides/binary/blob-to-dataview.mdx index 75a5d553c1..9b9a77d281 100644 --- a/docs/guides/binary/blob-to-dataview.md +++ b/docs/guides/binary/blob-to-dataview.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Blob to a DataView +title: Convert a Blob to a DataView +sidebarTitle: "Blob to DataView" +mode: center --- The [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) class provides a number of methods for consuming its contents in different formats. This snippets reads the contents to an `ArrayBuffer`, then creates a `DataView` from the buffer. diff --git a/docs/guides/binary/blob-to-stream.md b/docs/guides/binary/blob-to-stream.mdx similarity index 83% rename from docs/guides/binary/blob-to-stream.md rename to docs/guides/binary/blob-to-stream.mdx index 9da968a160..bd433d7fb2 100644 --- a/docs/guides/binary/blob-to-stream.md +++ b/docs/guides/binary/blob-to-stream.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Blob to a ReadableStream +title: Convert a Blob to a ReadableStream +sidebarTitle: "Blob to ReadableStream" +mode: center --- The [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) class provides a number of methods for consuming its contents in different formats, including `.stream()`. This returns `Promise`. diff --git a/docs/guides/binary/blob-to-string.md b/docs/guides/binary/blob-to-string.mdx similarity index 84% rename from docs/guides/binary/blob-to-string.md rename to docs/guides/binary/blob-to-string.mdx index 9382311786..af1bb71bc3 100644 --- a/docs/guides/binary/blob-to-string.md +++ b/docs/guides/binary/blob-to-string.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Blob to a string +title: Convert a Blob to a string +sidebarTitle: "Blob to string" +mode: center --- The [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) class provides a number of methods for consuming its contents in different formats, including `.text()`. diff --git a/docs/guides/binary/blob-to-typedarray.md b/docs/guides/binary/blob-to-typedarray.mdx similarity index 85% rename from docs/guides/binary/blob-to-typedarray.md rename to docs/guides/binary/blob-to-typedarray.mdx index 9a99705f71..80bfb2f4c4 100644 --- a/docs/guides/binary/blob-to-typedarray.md +++ b/docs/guides/binary/blob-to-typedarray.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Blob to a Uint8Array +title: Convert a Blob to a Uint8Array +sidebarTitle: "Blob to Uint8Array" +mode: center --- The [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) class provides a number of methods for consuming its contents in different formats. This snippets reads the contents to an `ArrayBuffer`, then creates a `Uint8Array` from the buffer. diff --git a/docs/guides/binary/buffer-to-arraybuffer.md b/docs/guides/binary/buffer-to-arraybuffer.mdx similarity index 81% rename from docs/guides/binary/buffer-to-arraybuffer.md rename to docs/guides/binary/buffer-to-arraybuffer.mdx index 6a0ae979c6..57c5c13608 100644 --- a/docs/guides/binary/buffer-to-arraybuffer.md +++ b/docs/guides/binary/buffer-to-arraybuffer.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Buffer to an ArrayBuffer +title: Convert a Buffer to an ArrayBuffer +sidebarTitle: "Buffer to ArrayBuffer" +mode: center --- The Node.js [`Buffer`](https://nodejs.org/api/buffer.html) class provides a way to view and manipulate data in an underlying `ArrayBuffer`, which is available via the `buffer` property. diff --git a/docs/guides/binary/buffer-to-blob.md b/docs/guides/binary/buffer-to-blob.mdx similarity index 84% rename from docs/guides/binary/buffer-to-blob.md rename to docs/guides/binary/buffer-to-blob.mdx index 8a888686b3..325bb457f7 100644 --- a/docs/guides/binary/buffer-to-blob.md +++ b/docs/guides/binary/buffer-to-blob.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Buffer to a blob +title: Convert a Buffer to a blob +sidebarTitle: "Buffer to Blob" +mode: center --- A [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) can be constructed from an array of "chunks", where each chunk is a string, binary data structure (including `Buffer`), or another `Blob`. diff --git a/docs/guides/binary/buffer-to-readablestream.md b/docs/guides/binary/buffer-to-readablestream.mdx similarity index 93% rename from docs/guides/binary/buffer-to-readablestream.md rename to docs/guides/binary/buffer-to-readablestream.mdx index 4b8ae9f73d..6456dd9792 100644 --- a/docs/guides/binary/buffer-to-readablestream.md +++ b/docs/guides/binary/buffer-to-readablestream.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Buffer to a ReadableStream +title: Convert a Buffer to a ReadableStream +sidebarTitle: "Buffer to ReadableStream" +mode: center --- The naive approach to creating a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) from a [`Buffer`](https://nodejs.org/api/buffer.html) is to use the `ReadableStream` constructor and enqueue the entire array as a single chunk. For a large buffer, this may be undesirable as this approach does not "streaming" the data in smaller chunks. diff --git a/docs/guides/binary/buffer-to-string.md b/docs/guides/binary/buffer-to-string.mdx similarity index 87% rename from docs/guides/binary/buffer-to-string.md rename to docs/guides/binary/buffer-to-string.mdx index 41fc01b8a2..cf761d54a4 100644 --- a/docs/guides/binary/buffer-to-string.md +++ b/docs/guides/binary/buffer-to-string.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Buffer to a string +title: Convert a Buffer to a string +sidebarTitle: "Buffer to string" +mode: center --- The [`Buffer`](https://nodejs.org/api/buffer.html) class provides a built-in `.toString()` method that converts a `Buffer` to a string. diff --git a/docs/guides/binary/buffer-to-typedarray.md b/docs/guides/binary/buffer-to-typedarray.mdx similarity index 85% rename from docs/guides/binary/buffer-to-typedarray.md rename to docs/guides/binary/buffer-to-typedarray.mdx index 967ed42ca7..6a9678c2cb 100644 --- a/docs/guides/binary/buffer-to-typedarray.md +++ b/docs/guides/binary/buffer-to-typedarray.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Buffer to a Uint8Array +title: Convert a Buffer to a Uint8Array +sidebarTitle: "Buffer to Uint8Array" +mode: center --- The Node.js [`Buffer`](https://nodejs.org/api/buffer.html) class extends [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array), so no conversion is needed. All properties and methods on `Uint8Array` are available on `Buffer`. diff --git a/docs/guides/binary/dataview-to-string.md b/docs/guides/binary/dataview-to-string.mdx similarity index 85% rename from docs/guides/binary/dataview-to-string.md rename to docs/guides/binary/dataview-to-string.mdx index 2e4de99d9b..a1147cf2d6 100644 --- a/docs/guides/binary/dataview-to-string.md +++ b/docs/guides/binary/dataview-to-string.mdx @@ -1,5 +1,7 @@ --- -name: Convert a DataView to a string +title: Convert a DataView to a string +sidebarTitle: "DataView to string" +mode: center --- If a [`DataView`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView) contains ASCII-encoded text, you can convert it to a string using the [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder) class. diff --git a/docs/guides/binary/index.json b/docs/guides/binary/index.json deleted file mode 100644 index 0ddf3da495..0000000000 --- a/docs/guides/binary/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Binary data", - "description": "A collection of guides for converting between binary data formats with Bun" -} diff --git a/docs/guides/binary/typedarray-to-arraybuffer.md b/docs/guides/binary/typedarray-to-arraybuffer.mdx similarity index 89% rename from docs/guides/binary/typedarray-to-arraybuffer.md rename to docs/guides/binary/typedarray-to-arraybuffer.mdx index 3431e37715..a746d2007f 100644 --- a/docs/guides/binary/typedarray-to-arraybuffer.md +++ b/docs/guides/binary/typedarray-to-arraybuffer.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Uint8Array to an ArrayBuffer +title: Convert a Uint8Array to an ArrayBuffer +sidebarTitle: "Uint8Array to ArrayBuffer" +mode: center --- A [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) is a _typed array_ class, meaning it is a mechanism for viewing data in an underlying `ArrayBuffer`. The underlying `ArrayBuffer` is accessible via the `buffer` property. diff --git a/docs/guides/binary/typedarray-to-blob.md b/docs/guides/binary/typedarray-to-blob.mdx similarity index 85% rename from docs/guides/binary/typedarray-to-blob.md rename to docs/guides/binary/typedarray-to-blob.mdx index 4d634279b7..5ac00a6779 100644 --- a/docs/guides/binary/typedarray-to-blob.md +++ b/docs/guides/binary/typedarray-to-blob.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Uint8Array to a Blob +title: Convert a Uint8Array to a Blob +sidebarTitle: "Uint8Array to Blob" +mode: center --- A [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) can be constructed from an array of "chunks", where each chunk is a string, binary data structure (including `Uint8Array`), or another `Blob`. diff --git a/docs/guides/binary/typedarray-to-buffer.md b/docs/guides/binary/typedarray-to-buffer.mdx similarity index 85% rename from docs/guides/binary/typedarray-to-buffer.md rename to docs/guides/binary/typedarray-to-buffer.mdx index 227fea89cd..daa782a159 100644 --- a/docs/guides/binary/typedarray-to-buffer.md +++ b/docs/guides/binary/typedarray-to-buffer.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Uint8Array to a Buffer +title: Convert a Uint8Array to a Buffer +sidebarTitle: "Uint8Array to Buffer" +mode: center --- The [`Buffer`](https://nodejs.org/api/buffer.html) class extends [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) with a number of additional methods. Use `Buffer.from()` to create a `Buffer` instance from a `Uint8Array`. diff --git a/docs/guides/binary/typedarray-to-dataview.md b/docs/guides/binary/typedarray-to-dataview.mdx similarity index 86% rename from docs/guides/binary/typedarray-to-dataview.md rename to docs/guides/binary/typedarray-to-dataview.mdx index 4e4367f3cd..3a7603e74b 100644 --- a/docs/guides/binary/typedarray-to-dataview.md +++ b/docs/guides/binary/typedarray-to-dataview.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Uint8Array to a DataView +title: Convert a Uint8Array to a DataView +sidebarTitle: "Uint8Array to DataView" +mode: center --- A [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) is a _typed array_ class, meaning it is a mechanism for viewing data in an underlying `ArrayBuffer`. The following snippet creates a [`DataView`] instance over the same range of data as the `Uint8Array`. diff --git a/docs/guides/binary/typedarray-to-readablestream.md b/docs/guides/binary/typedarray-to-readablestream.mdx similarity index 92% rename from docs/guides/binary/typedarray-to-readablestream.md rename to docs/guides/binary/typedarray-to-readablestream.mdx index 3dec40ea91..dec46b02d9 100644 --- a/docs/guides/binary/typedarray-to-readablestream.md +++ b/docs/guides/binary/typedarray-to-readablestream.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Uint8Array to a ReadableStream +title: Convert a Uint8Array to a ReadableStream +sidebarTitle: "Uint8Array to ReadableStream" +mode: center --- The naive approach to creating a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) from a [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) is to use the [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) constructor and enqueue the entire array as a single chunk. For larger chunks, this may be undesirable as it isn't actually "streaming" the data. diff --git a/docs/guides/binary/typedarray-to-string.md b/docs/guides/binary/typedarray-to-string.mdx similarity index 84% rename from docs/guides/binary/typedarray-to-string.md rename to docs/guides/binary/typedarray-to-string.mdx index a6c567a678..5e6e5726a0 100644 --- a/docs/guides/binary/typedarray-to-string.md +++ b/docs/guides/binary/typedarray-to-string.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Uint8Array to a string +title: Convert a Uint8Array to a string +sidebarTitle: "Uint8Array to string" +mode: center --- Bun implements the Web-standard [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder) class for converting from binary data types like `Uint8Array` and strings. diff --git a/docs/guides/deployment/index.json b/docs/guides/deployment/index.json deleted file mode 100644 index 401c4c56b0..0000000000 --- a/docs/guides/deployment/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Deployment", - "description": "A collection of guides for deploying Bun to providers" -} diff --git a/docs/guides/deployment/railway.md b/docs/guides/deployment/railway.mdx similarity index 73% rename from docs/guides/deployment/railway.md rename to docs/guides/deployment/railway.mdx index 54f8942700..c095c76929 100644 --- a/docs/guides/deployment/railway.md +++ b/docs/guides/deployment/railway.mdx @@ -1,6 +1,8 @@ --- -name: Deploy a Bun application on Railway +title: Deploy a Bun application on Railway description: Deploy Bun applications to Railway with this step-by-step guide covering CLI and dashboard methods, optional PostgreSQL setup, and automatic SSL configuration. +sidebarTitle: Deploy on Railway +mode: center --- Railway is an infrastructure platform where you can provision infrastructure, develop with that infrastructure locally, and then deploy to the cloud. It enables instant deployments from GitHub with zero configuration, automatic SSL, and built-in database provisioning. @@ -9,14 +11,13 @@ This guide walks through deploying a Bun application with a PostgreSQL database You can either follow this guide step-by-step or simply deploy the pre-configured template with one click: -{% raw %} - - + Deploy on Railway -{% /raw %} - --- **Prerequisites**: @@ -30,88 +31,78 @@ You can either follow this guide step-by-step or simply deploy the pre-configure ## Method 1: Deploy via CLI ---- - -#### Step 1 - + + Ensure sure you have the Railway CLI installed. -```bash +```bash terminal icon="terminal" bun install -g @railway/cli ``` ---- - -#### Step 2 - + + Log into your Railway account. -```bash +```bash terminal icon="terminal" railway login ``` ---- - -#### Step 3 - + + After successfully authenticating, initialize a new project. -```bash -# Initialize project -bun-react-postgres$ railway init +```bash terminal icon="terminal" +railway init ``` ---- - -#### Step 4 - + + After initializing the project, add a new database and service. -> **Note:** Step 4 is only necessary if your application uses a database. If you don't need PostgreSQL, skip to Step 5. +Step 4 is only necessary if your application uses a database. If you don't need PostgreSQL, skip to Step 5. -```bash +```bash terminal icon="terminal" # Add PostgreSQL database. Make sure to add this first! -bun-react-postgres$ railway add --database postgres +railway add --database postgres # Add your application service. -bun-react-postgres$ railway add --service bun-react-db --variables DATABASE_URL=\${{Postgres.DATABASE_URL}} +railway add --service bun-react-db --variables DATABASE_URL=\${{Postgres.DATABASE_URL}} ``` ---- - -#### Step 5 - + + After the services have been created and connected, deploy the application to Railway. By default, services are only accessible within Railway's private network. To make your app publicly accessible, you need to generate a public domain. -```bash +```bash terminal icon="terminal" # Deploy your application -bun-nextjs-starter$ railway up +railway up # Generate public domain -bun-nextjs-starter$ railway domain +railway domain ``` + + + +Your app is now live! Railway auto-deploys on every GitHub push. + --- ## Method 2: Deploy via Dashboard ---- - -#### Step 1 - + + Create a new project 1. Go to [Railway Dashboard](http://railway.com/dashboard?utm_medium=integration&utm_source=docs&utm_campaign=bun) 2. Click **"+ New"** → **"GitHub repo"** 3. Choose your repository ---- - -#### Step 2 - + + Add a PostgreSQL database, and connect this database to the service -> **Note:** Step 2 is only necessary if your application uses a database. If you don't need PostgreSQL, skip to Step 3. +Step 2 is only necessary if your application uses a database. If you don't need PostgreSQL, skip to Step 3. 1. Click **"+ New"** → **"Database"** → **"Add PostgreSQL"** 2. After the database has been created, select your service (not the database) @@ -119,17 +110,16 @@ Add a PostgreSQL database, and connect this database to the service 4. Click **"+ New Variable"** → **"Add Reference"** 5. Select `DATABASE_URL` from postgres ---- - -#### Step 3 - + + Generate a public domain 1. Select your service 2. Go to **"Settings"** tab 3. Under **"Networking"**, click **"Generate Domain"** ---- + + Your app is now live! Railway auto-deploys on every GitHub push. @@ -137,15 +127,13 @@ Your app is now live! Railway auto-deploys on every GitHub push. ## Configuration (Optional) ---- - By default, Railway uses [Nixpacks](https://docs.railway.com/guides/build-configuration#nixpacks-options) to automatically detect and build your Bun application with zero configuration. However, using the [Railpack](https://docs.railway.com/guides/build-configuration#railpack) application builder provides better Bun support, and will always support the latest version of Bun. The pre-configured templates use Railpack by default. To enable Railpack in a custom project, add the following to your `railway.json`: -```json +```json railway.json icon="file-json" { "$schema": "https://railway.com/railway.schema.json", "build": { diff --git a/docs/guides/deployment/render.mdx b/docs/guides/deployment/render.mdx new file mode 100644 index 0000000000..a652bf6635 --- /dev/null +++ b/docs/guides/deployment/render.mdx @@ -0,0 +1,82 @@ +--- +title: Deploy a Bun application on Render +sidebarTitle: Deploy on Render +mode: center +--- + +[Render](https://render.com/) is a cloud platform that lets you flexibly build, deploy, and scale your apps. + +It offers features like auto deploys from GitHub, a global CDN, private networks, automatic HTTPS setup, and managed PostgreSQL and Redis. + +Render supports Bun natively. You can deploy Bun apps as web services, background workers, cron jobs, and more. + +--- + +As an example, let's deploy a simple Express HTTP server to Render. + + + + Create a new GitHub repo named `myapp`. Git clone it locally. + + ```sh + git clone git@github.com:my-github-username/myapp.git + cd myapp + ``` + + + Add the Express library. + + ```sh + bun add express + ``` + + + + + Define a simple server with Express: + + ```ts app.ts icon="/icons/typescript.svg" + import express from "express"; + + const app = express(); + const port = process.env.PORT || 3001; + + app.get("/", (req, res) => { + res.send("Hello World!"); + }); + + app.listen(port, () => { + console.log(`Listening on port ${port}...`); + }); + ``` + + + + Commit your changes and push to GitHub. + + ```sh terminal icon="terminal" + git add app.ts bun.lock package.json + git commit -m "Create simple Express app" + git push origin main + ``` + + + In your [Render Dashboard](https://dashboard.render.com/), click `New` > `Web Service` and connect your `myapp` repo. + + + + In the Render UI, provide the following values during web service creation: + + | | | + | ----------------- | ------------- | + | **Runtime** | `Node` | + | **Build Command** | `bun install` | + | **Start Command** | `bun app.ts` | + + + + + +That's it! Your web service will be live at its assigned `onrender.com` URL as soon as the build finishes. + +You can view the [deploy logs](https://docs.render.com/logging#logs-for-an-individual-deploy-or-job) for details. Refer to [Render's documentation](https://docs.render.com/deploys) for a complete overview of deploying on Render. diff --git a/docs/guides/deployment/vercel.mdx b/docs/guides/deployment/vercel.mdx new file mode 100644 index 0000000000..edd9011a9f --- /dev/null +++ b/docs/guides/deployment/vercel.mdx @@ -0,0 +1,99 @@ +--- +title: Deploy a Bun application on Vercel +sidebarTitle: Deploy on Vercel +mode: center +--- + +import { ProductCard } from "/snippets/product-card.mdx"; + +[Vercel](https://vercel.com/) is a cloud platform that lets you build, deploy, and scale your apps. + + + The Bun runtime is in Beta; certain features (e.g., automatic source maps, byte-code caching, metrics on + `node:http/https`) are not yet supported. + + + + `Bun.serve` is currently not supported on Vercel Functions. Use Bun with frameworks supported by Vercel, like Next.js, + Express, Hono, or Nitro. + + +--- + + + + To enable the Bun runtime for your Functions, add a `bunVersion` field in your `vercel.json` file: + + ```json vercel.json icon="file-json" + { + "bunVersion": "1.x" // [!code ++] + } + ``` + + Vercel automatically detects this configuration and runs your application on Bun. The value has to be `"1.x"`, Vercel handles the minor version internally. + + For best results, match your local Bun version with the version used by Vercel. **Currently, Bun version `1.2.23` is supported**. + + + + If you’re deploying a **Next.js** project (including ISR), update your `package.json` scripts to use the Bun runtime: + + ```json package.json icon="file-json" + { + "scripts": { + "dev": "bun --bun next dev", // [!code ++] + "build": "bun --bun next build" // [!code ++] + } + } + ``` + + + The `--bun` flag runs the Next.js CLI under Bun. Bundling (via Turbopack or Webpack) remains unchanged, but all commands execute within the Bun runtime. + + + This ensures both local development and builds use Bun. + + + + Connect your repository to Vercel, or deploy from the CLI: + + ```bash terminal icon="terminal" + # Using bunx (no global install) + bunx vercel login + bunx vercel deploy + ``` + + Or install the Vercel CLI globally: + + ```bash terminal icon="terminal" + bun i -g vercel + vercel login + vercel deploy + ``` + + [Learn more in the Vercel Deploy CLI documentation →](https://vercel.com/docs/cli/deploy) + + + + To confirm your deployment uses Bun, log the Bun version: + + ```ts index.ts icon="/icons/typescript.svg" + console.log("runtime", process.versions.bun); + ``` + ```txt + runtime 1.2.23 + ``` + + [See the Vercel Bun Runtime documentation for feature support →](https://vercel.com/docs/functions/runtimes/bun#feature-support) + + + + +--- + +- [Fluid compute](https://vercel.com/docs/fluid-compute): Both Bun and Node.js runtimes run on Fluid compute and support the same core Vercel Functions features. +- [Middleware](https://vercel.com/docs/routing-middleware): To run Routing Middleware with Bun, set the runtime to `nodejs`: + +```ts middleware.ts icon="/icons/typescript.svg" +export const config = { runtime: "nodejs" }; // [!code ++] +``` diff --git a/docs/guides/ecosystem/astro.md b/docs/guides/ecosystem/astro.mdx similarity index 84% rename from docs/guides/ecosystem/astro.md rename to docs/guides/ecosystem/astro.mdx index 04be120129..6609b1aa7b 100644 --- a/docs/guides/ecosystem/astro.md +++ b/docs/guides/ecosystem/astro.mdx @@ -1,11 +1,16 @@ --- -name: Build an app with Astro and Bun +title: Build an app with Astro and Bun +sidebarTitle: "Astro with Bun" +mode: center --- Initialize a fresh Astro app with `bun create astro`. The `create-astro` package detects when you are using `bunx` and will automatically install dependencies using `bun`. -```sh -$ bun create astro +```sh terminal icon="terminal" +bun create astro +``` + +```txt ╭─────╮ Houston: │ ◠ ◡ ◠ We're glad to have you on board. ╰─────╯ @@ -53,8 +58,11 @@ Start the dev server with `bunx`. By default, Bun will run the dev server with Node.js. To use the Bun runtime instead, use the `--bun` flag. -```sh -$ bunx --bun astro dev +```sh terminal icon="terminal" +bunx --bun astro dev +``` + +```txt 🚀 astro v3.1.4 started in 200ms ┃ Local http://localhost:4321/ @@ -65,7 +73,9 @@ $ bunx --bun astro dev Open [http://localhost:4321](http://localhost:4321) with your browser to see the result. Astro will hot-reload your app as you edit your source files. -{% image src="https://i.imgur.com/Dswiu6w.png" caption="An Astro v3 starter app running on Bun" %} + + + --- diff --git a/docs/guides/ecosystem/discordjs.md b/docs/guides/ecosystem/discordjs.mdx similarity index 73% rename from docs/guides/ecosystem/discordjs.md rename to docs/guides/ecosystem/discordjs.mdx index db13548e35..7ab36e2727 100644 --- a/docs/guides/ecosystem/discordjs.md +++ b/docs/guides/ecosystem/discordjs.mdx @@ -1,21 +1,23 @@ --- -name: Create a Discord bot +title: Create a Discord bot +sidebarTitle: "Discord.js with Bun" +mode: center --- Discord.js works out of the box with Bun. Let's write a simple bot. First create a directory and initialize it with `bun init`. -```sh -$ mkdir my-bot -$ cd my-bot -$ bun init +```sh terminal icon="terminal" +mkdir my-bot +cd my-bot +bun init ``` --- Now install Discord.js. -```sh -$ bun add discord.js +```sh terminal icon="terminal" +bun add discord.js ``` --- @@ -26,11 +28,9 @@ Before we go further, we need to go to the [Discord developer portal](https://di Once complete, you'll be presented with your bot's _private key_. Let's add this to a file called `.env.local`. Bun automatically reads this file and loads it into `process.env`. -{% callout %} -This is an example token that has already been invalidated. -{% /callout %} +This is an example token that has already been invalidated. -```txt#.env.local +```txt .env.local icon="settings" DISCORD_TOKEN=NzkyNzE1NDU0MTk2MDg4ODQy.X-hvzA.Ovy4MCQywSkoMRRclStW4xAYK7I ``` @@ -38,7 +38,7 @@ DISCORD_TOKEN=NzkyNzE1NDU0MTk2MDg4ODQy.X-hvzA.Ovy4MCQywSkoMRRclStW4xAYK7I Be sure to add `.env.local` to your `.gitignore`! It is dangerous to check your bot's private key into version control. -```txt#.gitignore +```txt .gitignore icon="file-code" node_modules .env.local ``` @@ -47,15 +47,15 @@ node_modules Now let's actually write our bot in a new file called `bot.ts`. -```ts#bot.ts +```ts bot.ts icon="/icons/typescript.svg" // import discord.js -import {Client, Events, GatewayIntentBits} from 'discord.js'; +import { Client, Events, GatewayIntentBits } from "discord.js"; // create a new Client instance -const client = new Client({intents: [GatewayIntentBits.Guilds]}); +const client = new Client({ intents: [GatewayIntentBits.Guilds] }); // listen for the client to be ready -client.once(Events.ClientReady, (c) => { +client.once(Events.ClientReady, c => { console.log(`Ready! Logged in as ${c.user.tag}`); }); @@ -67,8 +67,11 @@ client.login(process.env.DISCORD_TOKEN); Now we can run our bot with `bun run`. It may take a several seconds for the client to initialize the first time you run the file. -```sh -$ bun run bot.ts +```sh terminal icon="terminal" +bun run bot.ts +``` + +```txt Ready! Logged in as my-bot#1234 ``` diff --git a/docs/guides/ecosystem/docker.md b/docs/guides/ecosystem/docker.mdx similarity index 89% rename from docs/guides/ecosystem/docker.md rename to docs/guides/ecosystem/docker.mdx index ad8f8907ea..4c50202e12 100644 --- a/docs/guides/ecosystem/docker.md +++ b/docs/guides/ecosystem/docker.mdx @@ -1,10 +1,12 @@ --- -name: Containerize a Bun application with Docker +title: Containerize a Bun application with Docker +sidebarTitle: Docker with Bun +mode: center --- -{% callout %} -This guide assumes you already have [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed. -{% /callout %} + + This guide assumes you already have [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed. + [Docker](https://www.docker.com) is a platform for packaging and running an application as a lightweight, portable _container_ that encapsulates all the necessary dependencies. @@ -12,7 +14,7 @@ This guide assumes you already have [Docker Desktop](https://www.docker.com/prod To _containerize_ our application, we define a `Dockerfile`. This file contains a list of instructions to initialize the container, copy our local project files into it, install dependencies, and starts the application. -```docker#Dockerfile +```docker Dockerfile icon="docker" # use the official Bun image # see all versions at https://hub.docker.com/r/oven/bun/tags FROM oven/bun:1 AS base @@ -57,7 +59,7 @@ ENTRYPOINT [ "bun", "run", "index.ts" ] Now that you have your docker image, let's look at `.dockerignore` which has the same syntax as `.gitignore`, here you need to specify the files/directories that must not go in any stage of the docker build. An example for a ignore file is -```txt#.dockerignore +```txt .dockerignore icon="docker" node_modules Dockerfile* docker-compose* @@ -81,8 +83,11 @@ We'll now use `docker build` to convert this `Dockerfile` into a _Docker image_, The `-t` flag lets us specify a name for the image, and `--pull` tells Docker to automatically download the latest version of the base image (`oven/bun`). The initial build will take longer, as Docker will download all the base images and dependencies. -```bash -$ docker build --pull -t bun-hello-world . +```bash terminal icon="terminal" +docker build --pull -t bun-hello-world . +``` + +```txt [+] Building 0.9s (21/21) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 37B 0.0s @@ -108,8 +113,11 @@ We'll use `docker run` to start a new container using the `bun-hello-world` imag The `run` command prints a string representing the _container ID_. -```sh -$ docker run -d -p 3000:3000 bun-hello-world +```sh terminal icon="terminal" +docker run -d -p 3000:3000 bun-hello-world +``` + +```txt 7f03e212a15ede8644379bce11a13589f563d3909a9640446c5bbefce993678d ``` @@ -121,16 +129,19 @@ The container is now running in the background. Visit [localhost:3000](http://lo To stop the container, we'll use `docker stop `. -```sh -$ docker stop 7f03e212a15ede8644379bce11a13589f563d3909a9640446c5bbefce993678d +```sh terminal icon="terminal" +docker stop 7f03e212a15ede8644379bce11a13589f563d3909a9640446c5bbefce993678d ``` --- If you can't find the container ID, you can use `docker ps` to list all running containers. -```sh -$ docker ps +```sh terminal icon="terminal" +docker ps +``` + +```txt CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 7f03e212a15e bun-hello-world "bun run index.ts" 2 minutes ago Up 2 minutes 0.0.0.0:3000->3000/tcp flamboyant_cerf ``` diff --git a/docs/guides/ecosystem/drizzle.md b/docs/guides/ecosystem/drizzle.mdx similarity index 81% rename from docs/guides/ecosystem/drizzle.md rename to docs/guides/ecosystem/drizzle.mdx index ef2f102580..58b6e90b87 100644 --- a/docs/guides/ecosystem/drizzle.md +++ b/docs/guides/ecosystem/drizzle.mdx @@ -1,5 +1,7 @@ --- -name: Use Drizzle ORM with Bun +title: Use Drizzle ORM with Bun +sidebarTitle: Drizzle with Bun +mode: center --- Drizzle is an ORM that supports both a SQL-like "query builder" API and an ORM-like [Queries API](https://orm.drizzle.team/docs/rqb). It supports the `bun:sqlite` built-in module. @@ -8,17 +10,17 @@ Drizzle is an ORM that supports both a SQL-like "query builder" API and an ORM-l Let's get started by creating a fresh project with `bun init` and installing Drizzle. -```sh -$ bun init -y -$ bun add drizzle-orm -$ bun add -D drizzle-kit +```sh terminal icon="terminal" +bun init -y +bun add drizzle-orm +bun add -D drizzle-kit ``` --- Then we'll connect to a SQLite database using the `bun:sqlite` module and create the Drizzle database instance. -```ts#db.ts +```ts db.ts icon="/icons/typescript.svg" import { drizzle } from "drizzle-orm/bun-sqlite"; import { Database } from "bun:sqlite"; @@ -30,7 +32,7 @@ export const db = drizzle(sqlite); To see the database in action, add these lines to `index.ts`. -```ts#index.ts +```ts index.ts icon="/icons/typescript.svg" import { db } from "./db"; import { sql } from "drizzle-orm"; @@ -43,8 +45,11 @@ console.log(result); Then run `index.ts` with Bun. Bun will automatically create `sqlite.db` and execute the query. -```sh -$ bun run index.ts +```sh terminal icon="terminal" +bun run index.ts +``` + +```txt { text: "hello world" } @@ -54,7 +59,7 @@ $ bun run index.ts Lets give our database a proper schema. Create a `schema.ts` file and define a `movies` table. -```ts#schema.ts +```ts schema.ts icon="/icons/typescript.svg" import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"; export const movies = sqliteTable("movies", { @@ -68,15 +73,15 @@ export const movies = sqliteTable("movies", { We can use the `drizzle-kit` CLI to generate an initial SQL migration. -```sh -$ bunx drizzle-kit generate --dialect sqlite --schema ./schema.ts +```sh terminal icon="terminal" +bunx drizzle-kit generate --dialect sqlite --schema ./schema.ts ``` --- This creates a new `drizzle` directory containing a `.sql` migration file and `meta` directory. -```txt +```txt File Tree icon="folder-tree" drizzle ├── 0000_ordinary_beyonder.sql └── meta @@ -90,7 +95,7 @@ We can execute these migrations with a simple `migrate.ts` script. This script creates a new connection to a SQLite database that writes to `sqlite.db`, then executes all unexecuted migrations in the `drizzle` directory. -```ts#migrate.ts +```ts migrate.ts icon="/icons/typescript.svg" import { migrate } from "drizzle-orm/bun-sqlite/migrator"; import { drizzle } from "drizzle-orm/bun-sqlite"; @@ -105,15 +110,15 @@ migrate(db, { migrationsFolder: "./drizzle" }); We can run this script with `bun` to execute the migration. -```sh -$ bun run migrate.ts +```sh terminal icon="terminal" +bun run migrate.ts ``` --- Now that we have a database, let's add some data to it. Create a `seed.ts` file with the following contents. -```ts#seed.ts +```ts seed.ts icon="/icons/typescript.svg" import { db } from "./db"; import * as schema from "./schema"; @@ -139,8 +144,11 @@ console.log(`Seeding complete.`); Then run this file. -```sh -$ bun run seed.ts +```sh terminal icon="terminal" +bun run seed.ts +``` + +```txt Seeding complete. ``` @@ -148,7 +156,7 @@ Seeding complete. We finally have a database with a schema and some sample data. Let's use Drizzle to query it. Replace the contents of `index.ts` with the following. -```ts#index.ts +```ts index.ts icon="/icons/typescript.svg" import * as schema from "./schema"; import { db } from "./db"; @@ -160,9 +168,11 @@ console.log(result); Then run the file. You should see the three movies we inserted. -```sh -$ bun run index.ts +```sh terminal icon="terminal" bun run index.ts +``` + +```txt [ { id: 1, diff --git a/docs/guides/ecosystem/edgedb.md b/docs/guides/ecosystem/edgedb.mdx similarity index 84% rename from docs/guides/ecosystem/edgedb.md rename to docs/guides/ecosystem/edgedb.mdx index 48c76438ad..6b621f9139 100644 --- a/docs/guides/ecosystem/edgedb.md +++ b/docs/guides/ecosystem/edgedb.mdx @@ -1,5 +1,7 @@ --- -name: Use EdgeDB with Bun +title: Use EdgeDB with Bun +sidebarTitle: EdgeDB with Bun +mode: center --- EdgeDB is a graph-relational database powered by Postgres under the hood. It provides a declarative schema language, migrations system, and object-oriented query language, in addition to supporting raw SQL queries. It solves the object-relational mapping problem at the database layer, eliminating the need for an ORM library in your application code. @@ -8,34 +10,37 @@ EdgeDB is a graph-relational database powered by Postgres under the hood. It pro First, [install EdgeDB](https://www.edgedb.com/install) if you haven't already. -{% codetabs %} + -```sh#Linux/macOS -$ curl --proto '=https' --tlsv1.2 -sSf https://sh.edgedb.com | sh +```sh Linux/macOS terminal icon="terminal" +curl --proto '=https' --tlsv1.2 -sSf https://sh.edgedb.com | sh ``` -```sh#Windows -$ iwr https://ps1.edgedb.com -useb | iex +```sh Windows terminal icon="windows" +iwr https://ps1.edgedb.com -useb | iex ``` -{% /codetabs %} + --- Use `bun init` to create a fresh project. -```sh -$ mkdir my-edgedb-app -$ cd my-edgedb-app -$ bun init -y +```sh terminal icon="terminal" +mkdir my-edgedb-app +cd my-edgedb-app +bun init -y ``` --- We'll use the EdgeDB CLI to initialize an EdgeDB instance for our project. This creates an `edgedb.toml` file in our project root. -```sh -$ edgedb project init +```sh terminal icon="terminal" +edgedb project init +``` + +```txt No `edgedb.toml` found in `/Users/colinmcd94/Documents/bun/fun/examples/my-edgedb-app` or above Do you want to initialize a new project? [Y/n] > Y @@ -64,12 +69,18 @@ To connect to my_edgedb_app, run `edgedb` To see if the database is running, let's open a REPL and run a simple query. +```sh terminal icon="terminal" +edgedb +edgedb> select 1 + 1; +``` + +```txt +2 +``` + Then run `\quit` to exit the REPL. -```sh -$ edgedb -edgedb> select 1 + 1; -2 +```sh terminal icon="terminal" edgedb> \quit ``` @@ -77,7 +88,7 @@ edgedb> \quit With the project initialized, we can define a schema. The `edgedb project init` command already created a `dbschema/default.esdl` file to contain our schema. -```txt +```txt File Tree icon="folder-tree" dbschema ├── default.esdl └── migrations @@ -87,7 +98,7 @@ dbschema Open that file and paste the following contents. -```txt +```ts default.esdl icon="file-code" module default { type Movie { required title: str; @@ -100,10 +111,19 @@ module default { Then generate and apply an initial migration. -```sh -$ edgedb migration create +```sh terminal icon="terminal" +edgedb migration create +``` + +```txt Created /Users/colinmcd94/Documents/bun/fun/examples/my-edgedb-app/dbschema/migrations/00001.edgeql, id: m1uwekrn4ni4qs7ul7hfar4xemm5kkxlpswolcoyqj3xdhweomwjrq -$ edgedb migrate +``` + +```sh terminal icon="terminal" +edgedb migrate +``` + +```txt Applied m1uwekrn4ni4qs7ul7hfar4xemm5kkxlpswolcoyqj3xdhweomwjrq (00001.edgeql) ``` @@ -111,10 +131,10 @@ Applied m1uwekrn4ni4qs7ul7hfar4xemm5kkxlpswolcoyqj3xdhweomwjrq (00001.edgeql) With our schema applied, let's execute some queries using EdgeDB's JavaScript client library. We'll install the client library and EdgeDB's codegen CLI, and create a `seed.ts`.file. -```sh -$ bun add edgedb -$ bun add -D @edgedb/generate -$ touch seed.ts +```sh terminal icon="terminal" +bun add edgedb +bun add -D @edgedb/generate +touch seed.ts ``` --- @@ -123,7 +143,7 @@ Paste the following code into `seed.ts`. The client auto-connects to the database. We insert a couple movies using the `.execute()` method. We will use EdgeQL's `for` expression to turn this bulk insert into a single optimized query. -```ts +```ts seed.ts icon="/icons/typescript.svg" import { createClient } from "edgedb"; const client = createClient(); @@ -154,8 +174,11 @@ process.exit(); Then run this file with Bun. -```sh -$ bun run seed.ts +```sh terminal icon="terminal" +bun run seed.ts +``` + +```txt Seeding complete. ``` @@ -163,8 +186,11 @@ Seeding complete. EdgeDB implements a number of code generation tools for TypeScript. To query our newly seeded database in a typesafe way, we'll use `@edgedb/generate` to code-generate the EdgeQL query builder. -```sh -$ bunx @edgedb/generate edgeql-js +```sh terminal icon="terminal" +bunx @edgedb/generate edgeql-js +``` + +```txt Generating query builder... Detected tsconfig.json, generating TypeScript files. To override this, use the --target flag. @@ -186,7 +212,7 @@ the query builder directory? The following line will be added: In `index.ts`, we can import the generated query builder from `./dbschema/edgeql-js` and write a simple select query. -```ts +```ts index.ts icon="/icons/typescript.svg" import { createClient } from "edgedb"; import e from "./dbschema/edgeql-js"; @@ -207,8 +233,11 @@ results; // { title: string, releaseYear: number | null }[] Running the file with Bun, we can see the list of movies we inserted. -```sh -$ bun run index.ts +```sh terminal icon="terminal" +bun run index.ts +``` + +```txt [ { title: "The Matrix", diff --git a/docs/guides/ecosystem/elysia.md b/docs/guides/ecosystem/elysia.mdx similarity index 68% rename from docs/guides/ecosystem/elysia.md rename to docs/guides/ecosystem/elysia.mdx index 019686a636..e28fb1b94e 100644 --- a/docs/guides/ecosystem/elysia.md +++ b/docs/guides/ecosystem/elysia.mdx @@ -1,27 +1,27 @@ --- -name: Build an HTTP server using Elysia and Bun +title: Build an HTTP server using Elysia and Bun +sidebarTitle: Elysia with Bun +mode: center --- [Elysia](https://elysiajs.com) is a Bun-first performance focused web framework that takes full advantage of Bun's HTTP, file system, and hot reloading APIs. Get started with `bun create`. -```bash -$ bun create elysia myapp -$ cd myapp -$ bun run dev +```bash terminal icon="terminal" +bun create elysia myapp +cd myapp +bun run dev ``` --- To define a simple HTTP route and start a server with Elysia: -```ts#server.ts -import { Elysia } from 'elysia' +```ts server.ts icon="/icons/typescript.svg" +import { Elysia } from "elysia"; -const app = new Elysia() - .get('/', () => 'Hello Elysia') - .listen(8080) +const app = new Elysia().get("/", () => "Hello Elysia").listen(8080); -console.log(`🦊 Elysia is running at on port ${app.server?.port}...`) +console.log(`🦊 Elysia is running at on port ${app.server?.port}...`); ``` --- diff --git a/docs/guides/ecosystem/express.md b/docs/guides/ecosystem/express.mdx similarity index 59% rename from docs/guides/ecosystem/express.md rename to docs/guides/ecosystem/express.mdx index efa8c163f2..e61fe1fbf3 100644 --- a/docs/guides/ecosystem/express.md +++ b/docs/guides/ecosystem/express.mdx @@ -1,22 +1,25 @@ --- -name: Build an HTTP server using Express and Bun +title: Build an HTTP server using Express and Bun +sidebarTitle: Express with Bun +mode: center --- Express and other major Node.js HTTP libraries should work out of the box. Bun implements the [`node:http`](https://nodejs.org/api/http.html) and [`node:https`](https://nodejs.org/api/https.html) modules that these libraries rely on. -{% callout %} -Refer to the [Runtime > Node.js APIs](https://bun.com/docs/runtime/nodejs-apis#node-http) page for more detailed compatibility information. -{% /callout %} + + Refer to the [Runtime > Node.js APIs](https://bun.com/docs/runtime/nodejs-apis#node-http) page for more detailed + compatibility information. + -```sh -$ bun add express +```sh terminal icon="terminal" +bun add express ``` --- To define a simple HTTP route and start a server with Express: -```ts#server.ts +```ts server.ts icon="/icons/typescript.svg" import express from "express"; const app = express(); @@ -35,6 +38,6 @@ app.listen(port, () => { To start the server on `localhost`: -```sh -$ bun server.ts +```sh terminal icon="terminal" +bun server.ts ``` diff --git a/docs/guides/ecosystem/hono.md b/docs/guides/ecosystem/hono.mdx similarity index 69% rename from docs/guides/ecosystem/hono.md rename to docs/guides/ecosystem/hono.mdx index fe067a1840..a5a5cdceaa 100644 --- a/docs/guides/ecosystem/hono.md +++ b/docs/guides/ecosystem/hono.mdx @@ -1,10 +1,12 @@ --- -name: Build an HTTP server using Hono and Bun +title: Build an HTTP server using Hono and Bun +sidebarTitle: Hono with Bun +mode: center --- [Hono](https://github.com/honojs/hono) is a lightweight ultrafast web framework designed for the edge. -```ts +```ts server.ts icon="/icons/typescript.svg" import { Hono } from "hono"; const app = new Hono(); @@ -17,21 +19,27 @@ export default app; Use `create-hono` to get started with one of Hono's project templates. Select `bun` when prompted for a template. -```sh -$ bun create hono myapp +```sh terminal icon="terminal" +bun create hono myapp +``` + +```txt ✔ Which template do you want to use? › bun cloned honojs/starter#main to /path/to/myapp ✔ Copied project files -$ cd myapp -$ bun install +``` + +```sh terminal icon="terminal" +cd myapp +bun install ``` --- Then start the dev server and visit [localhost:3000](http://localhost:3000). -```sh -$ bun run dev +```sh terminal icon="terminal" +bun run dev ``` --- diff --git a/docs/guides/ecosystem/index.json b/docs/guides/ecosystem/index.json deleted file mode 100644 index 4099acec33..0000000000 --- a/docs/guides/ecosystem/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Ecosystem", - "description": "A collection of guides for using various tools and frameworks with Bun" -} diff --git a/docs/guides/ecosystem/mongoose.md b/docs/guides/ecosystem/mongoose.mdx similarity index 64% rename from docs/guides/ecosystem/mongoose.md rename to docs/guides/ecosystem/mongoose.mdx index 0a6d2e24e0..3555838789 100644 --- a/docs/guides/ecosystem/mongoose.md +++ b/docs/guides/ecosystem/mongoose.mdx @@ -1,5 +1,7 @@ --- -name: Read and write data to MongoDB using Mongoose and Bun +title: Read and write data to MongoDB using Mongoose and Bun +sidebarTitle: Mongoose with Bun +mode: center --- MongoDB and Mongoose work out of the box with Bun. This guide assumes you've already installed MongoDB and are running it as background process/service on your development machine. Follow [this guide](https://www.mongodb.com/docs/manual/installation/) for details. @@ -8,31 +10,31 @@ MongoDB and Mongoose work out of the box with Bun. This guide assumes you've alr Once MongoDB is running, create a directory and initialize it with `bun init`. -```sh -$ mkdir mongoose-app -$ cd mongoose-app -$ bun init +```sh terminal icon="terminal" +mkdir mongoose-app +cd mongoose-app +bun init ``` --- Then add Mongoose as a dependency. -```sh -$ bun add mongoose +```sh terminal icon="terminal" +bun add mongoose ``` --- In `schema.ts` we'll declare and export a simple `Animal` model. -```ts#schema.ts -import * as mongoose from 'mongoose'; +```ts schema.ts icon="/icons/typescript.svg" +import * as mongoose from "mongoose"; const animalSchema = new mongoose.Schema( { - name: {type: String, required: true}, - sound: {type: String, required: true}, + title: { type: String, required: true }, + sound: { type: String, required: true }, }, { methods: { @@ -40,28 +42,28 @@ const animalSchema = new mongoose.Schema( console.log(`${this.sound}!`); }, }, - } + }, ); export type Animal = mongoose.InferSchemaType; -export const Animal = mongoose.model('Animal', animalSchema); +export const Animal = mongoose.model("Animal", animalSchema); ``` --- Now from `index.ts` we can import `Animal`, connect to MongoDB, and add some data to our database. -```ts#index.ts -import * as mongoose from 'mongoose'; -import {Animal} from './schema'; +```ts index.ts icon="/icons/typescript.svg" +import * as mongoose from "mongoose"; +import { Animal } from "./schema"; // connect to database -await mongoose.connect('mongodb://127.0.0.1:27017/mongoose-app'); +await mongoose.connect("mongodb://127.0.0.1:27017/mongoose-app"); // create new Animal const cow = new Animal({ - name: 'Cow', - sound: 'Moo', + title: "Cow", + sound: "Moo", }); await cow.save(); // saves to the database @@ -77,8 +79,11 @@ await mongoose.disconnect(); Let's run this with `bun run`. -```bash -$ bun run index.ts +```bash terminal icon="terminal" +bun run index.ts +``` + +```txt Moo! ``` diff --git a/docs/guides/ecosystem/neon-drizzle.md b/docs/guides/ecosystem/neon-drizzle.mdx similarity index 77% rename from docs/guides/ecosystem/neon-drizzle.md rename to docs/guides/ecosystem/neon-drizzle.mdx index 5bd2311f58..5cef85e7f6 100644 --- a/docs/guides/ecosystem/neon-drizzle.md +++ b/docs/guides/ecosystem/neon-drizzle.mdx @@ -1,34 +1,36 @@ --- -name: Use Neon Postgres through Drizzle ORM +title: Use Neon Postgres through Drizzle ORM +sidebarTitle: Neon Drizzle with Bun +mode: center --- [Neon](https://neon.tech/) is a fully managed serverless Postgres, separating compute and storage to offer features like autoscaling, branching and bottomless storage. Neon can be used from Bun directly using the `@neondatabase/serverless` driver or through an ORM like `Drizzle`. Drizzle ORM supports both a SQL-like "query builder" API and an ORM-like [Queries API](https://orm.drizzle.team/docs/rqb). Get started by creating a project directory, initializing the directory using `bun init`, and installing Drizzle and the [Neon serverless driver](https://github.com/neondatabase/serverless/). -```sh -$ mkdir bun-drizzle-neon -$ cd bun-drizzle-neon -$ bun init -y -$ bun add drizzle-orm @neondatabase/serverless -$ bun add -D drizzle-kit +```sh terminal icon="terminal" +mkdir bun-drizzle-neon +cd bun-drizzle-neon +bun init -y +bun add drizzle-orm @neondatabase/serverless +bun add -D drizzle-kit ``` --- Create a `.env.local` file and add your [Neon Postgres connection string](https://neon.tech/docs/connect/connect-from-any-app) to it. -```sh -DATABASE_URL=postgresql://username:password@ep-adj-noun-guid.us-east-1.aws.neon.tech/neondb?sslmode=require +```txt .env.local icon="settings" +DATABASE_URL=postgresql://usertitle:password@ep-adj-noun-guid.us-east-1.aws.neon.tech/neondb?sslmode=require ``` --- We will connect to the Neon database using the Neon serverless driver, wrapped in a Drizzle database instance. -```ts#db.ts -import { neon } from '@neondatabase/serverless'; -import { drizzle } from 'drizzle-orm/neon-http'; +```ts db.ts icon="/icons/typescript.svg" +import { neon } from "@neondatabase/serverless"; +import { drizzle } from "drizzle-orm/neon-http"; // Bun automatically loads the DATABASE_URL from .env.local // Refer to: https://bun.com/docs/runtime/env for more information @@ -41,7 +43,7 @@ export const db = drizzle(sql); To see the database in action, add these lines to `index.ts`. -```ts#index.ts +```ts index.ts icon="/icons/typescript.svg" import { db } from "./db"; import { sql } from "drizzle-orm"; @@ -54,8 +56,11 @@ console.log(result.rows); Then run `index.ts` with Bun. -```sh -$ bun run index.ts +```sh terminal icon="terminal" +bun run index.ts +``` + +```txt [ { text: "hello world", @@ -67,12 +72,12 @@ $ bun run index.ts We can define a schema for our database using Drizzle ORM primitives. Create a `schema.ts` file and add this code. -```ts#schema.ts +```ts schema.ts icon="/icons/typescript.svg" import { pgTable, integer, serial, text, timestamp } from "drizzle-orm/pg-core"; export const authors = pgTable("authors", { id: serial("id").primaryKey(), - name: text("name").notNull(), + title: text("name").notNull(), bio: text("bio"), createdAt: timestamp("created_at").notNull().defaultNow(), }); @@ -83,14 +88,14 @@ export const authors = pgTable("authors", { We then use the `drizzle-kit` CLI to generate an initial SQL migration. ```sh -$ bunx drizzle-kit generate --dialect postgresql --schema ./schema.ts --out ./drizzle +bunx drizzle-kit generate --dialect postgresql --schema ./schema.ts --out ./drizzle ``` --- This creates a new `drizzle` directory containing a `.sql` migration file and `meta` directory. -```txt +```txt File Tree icon="folder-tree" drizzle ├── 0000_aspiring_post.sql └── meta @@ -102,8 +107,8 @@ drizzle We can execute these migrations with a simple `migrate.ts` script. This script creates a new connection to the Neon database and executes all unexecuted migrations in the `drizzle` directory. -```ts#migrate.ts -import { db } from './db'; +```ts migrate.ts +import { db } from "./db"; import { migrate } from "drizzle-orm/neon-http/migrator"; const main = async () => { @@ -123,8 +128,11 @@ main(); We can run this script with `bun` to execute the migration. -```sh -$ bun run migrate.ts +```sh terminal icon="terminal" +bun run migrate.ts +``` + +```txt Migration completed ``` @@ -132,22 +140,22 @@ Migration completed We can now add some data to our database. Create a `seed.ts` file with the following contents. -```ts#seed.ts +```ts seed.ts icon="/icons/typescript.svg" import { db } from "./db"; import * as schema from "./schema"; async function seed() { await db.insert(schema.authors).values([ { - name: "J.R.R. Tolkien", + title: "J.R.R. Tolkien", bio: "The creator of Middle-earth and author of The Lord of the Rings.", }, { - name: "George R.R. Martin", + title: "George R.R. Martin", bio: "The author of the epic fantasy series A Song of Ice and Fire.", }, { - name: "J.K. Rowling", + title: "J.K. Rowling", bio: "The creator of the Harry Potter series.", }, ]); @@ -170,8 +178,11 @@ main(); Then run this file. -```sh -$ bun run seed.ts +```sh terminal icon="terminal" +bun run seed.ts +``` + +```txt Seeding completed ``` @@ -179,7 +190,7 @@ Seeding completed We now have a database with a schema and sample data. We can use Drizzle to query it. Replace the contents of `index.ts` with the following. -```ts#index.ts +```ts index.ts icon="/icons/typescript.svg" import * as schema from "./schema"; import { db } from "./db"; @@ -191,22 +202,25 @@ console.log(result); Then run the file. You should see the three authors we inserted. -```sh -$ bun run index.ts +```sh terminal icon="terminal" +bun run index.ts +``` + +```txt [ { id: 1, - name: "J.R.R. Tolkien", + title: "J.R.R. Tolkien", bio: "The creator of Middle-earth and author of The Lord of the Rings.", createdAt: 2024-05-11T10:28:46.029Z, }, { id: 2, - name: "George R.R. Martin", + title: "George R.R. Martin", bio: "The author of the epic fantasy series A Song of Ice and Fire.", createdAt: 2024-05-11T10:28:46.029Z, }, { id: 3, - name: "J.K. Rowling", + title: "J.K. Rowling", bio: "The creator of the Harry Potter series.", createdAt: 2024-05-11T10:28:46.029Z, } diff --git a/docs/guides/ecosystem/neon-serverless-postgres.md b/docs/guides/ecosystem/neon-serverless-postgres.mdx similarity index 76% rename from docs/guides/ecosystem/neon-serverless-postgres.md rename to docs/guides/ecosystem/neon-serverless-postgres.mdx index 3c04c0adee..84cd4d870e 100644 --- a/docs/guides/ecosystem/neon-serverless-postgres.md +++ b/docs/guides/ecosystem/neon-serverless-postgres.mdx @@ -1,5 +1,7 @@ --- -name: Use Neon's Serverless Postgres with Bun +title: Use Neon's Serverless Postgres with Bun +sidebarTitle: Neon Serverless Postgres with Bun +mode: center --- [Neon](https://neon.tech/) is a fully managed serverless Postgres. Neon separates compute and storage to offer modern developer features such as autoscaling, branching, bottomless storage, and more. @@ -8,26 +10,26 @@ name: Use Neon's Serverless Postgres with Bun Get started by creating a project directory, initializing the directory using `bun init`, and adding the [Neon serverless driver](https://github.com/neondatabase/serverless/) as a project dependency. -```sh -$ mkdir bun-neon-postgres -$ cd bun-neon-postgres -$ bun init -y -$ bun add @neondatabase/serverless +```sh terminal icon="terminal" +mkdir bun-neon-postgres +cd bun-neon-postgres +bun init -y +bun add @neondatabase/serverless ``` --- Create a `.env.local` file and add your [Neon Postgres connection string](https://neon.tech/docs/connect/connect-from-any-app) to it. -```sh -DATABASE_URL=postgresql://username:password@ep-adj-noun-guid.us-east-1.aws.neon.tech/neondb?sslmode=require +```sh .env.local icon="settings" +DATABASE_URL=postgresql://usertitle:password@ep-adj-noun-guid.us-east-1.aws.neon.tech/neondb?sslmode=require ``` --- Paste the following code into your project's `index.ts` file. -```ts +```ts index.ts icon="/icons/typescript.svg" import { neon } from "@neondatabase/serverless"; // Bun automatically loads the DATABASE_URL from .env.local @@ -43,8 +45,11 @@ console.log(rows[0].version); Start the program using `bun ./index.ts`. The Postgres version should be printed to the console. -```sh -$ bun ./index.ts +```sh terminal icon="terminal" +bun ./index.ts +``` + +```txt PostgreSQL 16.2 on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit ``` diff --git a/docs/guides/ecosystem/nextjs.md b/docs/guides/ecosystem/nextjs.mdx similarity index 77% rename from docs/guides/ecosystem/nextjs.md rename to docs/guides/ecosystem/nextjs.mdx index 6d0e3ae66e..2a3f4188cb 100644 --- a/docs/guides/ecosystem/nextjs.md +++ b/docs/guides/ecosystem/nextjs.mdx @@ -1,11 +1,16 @@ --- -name: Build an app with Next.js and Bun +title: Build an app with Next.js and Bun +sidebarTitle: Next.js with Bun +mode: center --- Initialize a Next.js app with `create-next-app`. This will scaffold a new Next.js project and automatically install dependencies. -```sh -$ bun create next-app +```sh terminal icon="terminal" +bun create next-app +``` + +```txt ✔ What is your project named? … my-app ✔ Would you like to use TypeScript with this project? … No / Yes ✔ Would you like to use ESLint with this project? … No / Yes @@ -21,7 +26,10 @@ Creating a new Next.js app in /path/to/my-app. You can specify a starter template using the `--example` flag. ```sh -$ bun create next-app --example with-supabase +bun create next-app --example with-supabase +``` + +```txt ✔ What is your project named? … my-app ... ``` @@ -30,18 +38,18 @@ $ bun create next-app --example with-supabase To start the dev server with Bun, run `bun --bun run dev` from the project root. -```sh -$ cd my-app -$ bun --bun run dev +```sh terminal icon="terminal" +cd my-app +bun --bun run dev ``` --- To run the dev server with Node.js instead, omit `--bun`. -```sh -$ cd my-app -$ bun run dev +```sh terminal icon="terminal" +cd my-app +bun run dev ``` --- diff --git a/docs/guides/ecosystem/nuxt.md b/docs/guides/ecosystem/nuxt.mdx similarity index 51% rename from docs/guides/ecosystem/nuxt.md rename to docs/guides/ecosystem/nuxt.mdx index 9eeded94d0..4a7a9b889c 100644 --- a/docs/guides/ecosystem/nuxt.md +++ b/docs/guides/ecosystem/nuxt.mdx @@ -1,15 +1,20 @@ --- -name: Build an app with Nuxt and Bun +title: Build an app with Nuxt and Bun +sidebarTitle: Nuxt with Bun +mode: center --- Bun supports [Nuxt](https://nuxt.com) out of the box. Initialize a Nuxt app with official `nuxi` CLI. -```sh -$ bunx nuxi init my-nuxt-app +```sh terminal icon="terminal" +bunx nuxi init my-nuxt-app +``` + +```txt ✔ Which package manager would you like to use? bun ◐ Installing dependencies... -bun install v$BUN_LATEST_VERSION (16b4bf34) +bun install v1.3.1 (16b4bf34) + @nuxt/devtools@0.8.2 + nuxt@3.7.0 785 packages installed [2.67s] @@ -24,14 +29,17 @@ bun install v$BUN_LATEST_VERSION (16b4bf34) To start the dev server, run `bun --bun run dev` from the project root. This will execute the `nuxt dev` command (as defined in the `"dev"` script in `package.json`). -{% callout %} -The `nuxt` CLI uses Node.js by default; passing the `--bun` flag forces the dev server to use the Bun runtime instead. -{% /callout %} + + The `nuxt` CLI uses Node.js by default; passing the `--bun` flag forces the dev server to use the Bun runtime instead. + +```sh terminal icon="terminal" +cd my-nuxt-app +bun --bun run dev ``` -$ cd my-nuxt-app -$ bun --bun run dev - $ nuxt dev + +```txt +nuxt dev Nuxi 3.6.5 Nuxt 3.6.5 with Nitro 2.5.2 > Local: http://localhost:3000/ @@ -49,7 +57,33 @@ Once the dev server spins up, open [http://localhost:3000](http://localhost:3000 To start developing your app, replace `` in `app.vue` with your own UI. -{% image src="https://github.com/oven-sh/bun/assets/3084745/2c683ecc-3298-4bb0-b8c0-cf4cfaea1daa" caption="Demo Nuxt app running on localhost" /%} + + ![Demo Nuxt app running on + localhost](https://github.com/oven-sh/bun/assets/3084745/2c683ecc-3298-4bb0-b8c0-cf4cfaea1daa) + + +--- + +For production build, while the default preset is already compatible with Bun, you can also use [Bun preset](https://nitro.build/deploy/runtimes/bun) to generate better optimized builds. + +```ts nuxt.config.ts icon="/icons/typescript.svg" +export default defineNuxtConfig({ + nitro: { + preset: "bun", // [!code ++] + }, +}); +``` + + + Some packages provide Bun-specific exports that Nitro will not bundle correctly using the default preset. In this + case, you need to use Bun preset so that the packages will work correctly in production builds. + + +After building with bun, run: + +```sh terminal icon="terminal" +bun run ./.output/server/index.mjs +``` --- diff --git a/docs/guides/ecosystem/pm2.md b/docs/guides/ecosystem/pm2.mdx similarity index 86% rename from docs/guides/ecosystem/pm2.md rename to docs/guides/ecosystem/pm2.mdx index 41a06b7bd1..70d4242b06 100644 --- a/docs/guides/ecosystem/pm2.md +++ b/docs/guides/ecosystem/pm2.mdx @@ -1,5 +1,7 @@ --- -name: Run Bun as a daemon with PM2 +title: Run Bun as a daemon with PM2 +sidebarTitle: PM2 with Bun +mode: center --- [PM2](https://pm2.keymetrics.io/) is a popular process manager that manages and runs your applications as daemons (background processes). @@ -17,11 +19,9 @@ You can use PM2 with Bun in two ways: as a CLI option or in a configuration file ### With `--interpreter` ---- - To start your application with PM2 and Bun as the interpreter, open your terminal and run the following command: -```bash +```bash terminal icon="terminal" pm2 start --interpreter ~/.bun/bin/bun index.ts ``` @@ -29,13 +29,11 @@ pm2 start --interpreter ~/.bun/bin/bun index.ts ### With a configuration file ---- - Alternatively, you can create a PM2 configuration file. Create a file named `pm2.config.js` in your project directory and add the following content. -```javascript +```js pm2.config.js icon="file-code" module.exports = { - name: "app", // Name of your application + title: "app", // Name of your application script: "index.ts", // Entry point of your application interpreter: "bun", // Bun interpreter env: { @@ -48,7 +46,7 @@ module.exports = { After saving the file, you can start your application with PM2 -```bash +```bash terminal icon="terminal" pm2 start pm2.config.js ``` diff --git a/docs/guides/ecosystem/prisma-postgres.mdx b/docs/guides/ecosystem/prisma-postgres.mdx new file mode 100644 index 0000000000..d8bc15ca45 --- /dev/null +++ b/docs/guides/ecosystem/prisma-postgres.mdx @@ -0,0 +1,169 @@ +--- +title: Use Prisma Postgres with Bun +sidebarTitle: Prisma Postgres with Bun +mode: center +--- + + + **Note** — At the moment Prisma needs Node.js to be installed to run certain generation code. Make sure Node.js is + installed in the environment where you're running `bunx prisma` commands. + + + + + First, create a directory and initialize it with `bun init`. + + ```bash terminal icon="terminal" + mkdir prisma-postgres-app + cd prisma-postgres-app + bun init + ``` + + + + Then install the Prisma CLI (`prisma`), Prisma Client (`@prisma/client`), and the accelerate extension as dependencies. + + ```bash terminal icon="terminal" + bun add -d prisma + bun add @prisma/client @prisma/extension-accelerate + ``` + + + + We'll use the Prisma CLI with `bunx` to initialize our schema and migration directory. We'll be using PostgreSQL as our database. + + ```bash terminal icon="terminal" + bunx --bun prisma init --db + ``` + + This creates a basic schema. We need to update it to use the new Rust-free client with Bun optimization. Open `prisma/schema.prisma` and modify the generator block, then add a simple `User` model. + + ```prisma prisma/schema.prisma icon="/icons/ecosystem/prisma.svg" + generator client { + provider = "prisma-client" + output = "./generated" // [!code ++] + engineType = "client" // [!code ++] + runtime = "bun" // [!code ++] + } + + datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + } + + model User { // [!code ++] + id Int @id @default(autoincrement()) // [!code ++] + email String @unique // [!code ++] + name String? // [!code ++] + } // [!code ++] + ``` + + + + Set up your Postgres database URL in the `.env` file. + + ```env .env icon="settings" + DATABASE_URL="postgresql://username:password@localhost:5432/mydb?schema=public" + ``` + + + + Then generate and run initial migration. + + This will generate a `.sql` migration file in `prisma/migrations`, and execute the migration against your Postgres database. + + ```bash terminal icon="terminal" + bunx --bun prisma migrate dev --name init + ``` + + ```txt + Environment variables loaded from .env + Prisma schema loaded from prisma/schema.prisma + Datasource "db": PostgreSQL database "mydb", schema "public" at "localhost:5432" + + Applying migration `20250114141233_init` + + The following migration(s) have been created and applied from new schema changes: + + prisma/migrations/ + └─ 20250114141233_init/ + └─ migration.sql + + Your database is now in sync with your schema. + + ✔ Generated Prisma Client (6.17.1) to ./generated in 18ms + ``` + + + + As indicated in the output, Prisma re-generates our _Prisma client_ whenever we execute a new migration. The client provides a fully typed API for reading and writing from our database. You can manually re-generate the client with the Prisma CLI. + + ```sh terminal icon="terminal" + bunx --bun prisma generate + ``` + + + + Now we need to create a Prisma client instance. Create a new file `prisma/db.ts` to initialize the PrismaClient with the Postgres adapter. + + ```ts prisma/db.ts icon="/icons/typescript.svg" + import { PrismaClient } from "./generated/client"; + import { withAccelerate } from '@prisma/extension-accelerate' + + export const prisma = new PrismaClient().$extends(withAccelerate()) + ``` + + + + Let's write a simple script to create a new user, then count the number of users in the database. + + ```ts index.ts icon="/icons/typescript.svg" + import { prisma } from "./prisma/db"; + + // create a new user + await prisma.user.create({ + data: { + name: "John Dough", + email: `john-${Math.random()}@example.com`, + }, + }); + + // count the number of users + const count = await prisma.user.count(); + console.log(`There are ${count} users in the database.`); + ``` + + + + Let's run this script with `bun run`. Each time we run it, a new user is created. + + ```bash terminal icon="terminal" + bun run index.ts + ``` + + ```txt + There are 1 users in the database. + ``` + + ```bash terminal icon="terminal" + bun run index.ts + ``` + + ```txt + There are 2 users in the database. + ``` + + ```bash terminal icon="terminal" + bun run index.ts + ``` + + ```txt + There are 3 users in the database. + ``` + + + + +--- + +That's it! Now that you've set up Prisma Postgres using Bun, we recommend referring to the [official Prisma Postgres docs](https://www.prisma.io/docs/postgres) as you continue to develop your application. diff --git a/docs/guides/ecosystem/prisma.md b/docs/guides/ecosystem/prisma.md deleted file mode 100644 index 349558f61c..0000000000 --- a/docs/guides/ecosystem/prisma.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -name: Use Prisma with Bun ---- - -{% callout %} -**Note** — At the moment Prisma needs Node.js to be installed to run certain generation code. Make sure Node.js is installed in the environment where you're running `bunx prisma` commands. -{% /callout %} - ---- - -Prisma works out of the box with Bun. First, create a directory and initialize it with `bun init`. - -```bash -$ mkdir prisma-app -$ cd prisma-app -$ bun init -``` - ---- - -Then install the Prisma CLI (`prisma`) and Prisma Client (`@prisma/client`) as dependencies. - -```bash -$ bun add -d prisma -$ bun add @prisma/client -``` - ---- - -We'll use the Prisma CLI with `bunx` to initialize our schema and migration directory. For simplicity we'll be using an in-memory SQLite database. - -```bash -$ bunx --bun prisma init --datasource-provider sqlite -``` - ---- - -Open `prisma/schema.prisma` and add a simple `User` model. - -```prisma-diff#prisma/schema.prisma - generator client { - provider = "prisma-client-js" - output = "../generated/prisma" - } - - datasource db { - provider = "sqlite" - url = env("DATABASE_URL") - } - -+ model User { -+ id Int @id @default(autoincrement()) -+ email String @unique -+ name String? -+ } -``` - ---- - -Then generate and run initial migration. - -This will generate a `.sql` migration file in `prisma/migrations`, create a new SQLite instance, and execute the migration against the new instance. - -```bash -$ bunx prisma migrate dev --name init -Environment variables loaded from .env -Prisma schema loaded from prisma/schema.prisma -Datasource "db": SQLite database "dev.db" at "file:./dev.db" - -SQLite database dev.db created at file:./dev.db - -Applying migration `20230928182242_init` - -The following migration(s) have been created and applied from new schema changes: - -migrations/ - └─ 20230928182242_init/ - └─ migration.sql - -Your database is now in sync with your schema. - -✔ Generated Prisma Client (v6.11.1) to ./generated/prisma in 41ms -``` - ---- - -As indicated in the output, Prisma re-generates our _Prisma client_ whenever we execute a new migration. The client provides a fully typed API for reading and writing from our database. You can manually re-generate the client with the Prisma CLI. - -```sh -$ bunx prisma generate -``` - ---- - -We can import the generated client from `@prisma/client`. - -```ts#src/index.ts -import {PrismaClient} from "@prisma/client"; -``` - ---- - -Let's write a simple script to create a new user, then count the number of users in the database. - -```ts#index.ts -import { PrismaClient } from "@prisma/client"; - -const prisma = new PrismaClient(); - -// create a new user -await prisma.user.create({ - data: { - name: "John Dough", - email: `john-${Math.random()}@example.com`, - }, -}); - -// count the number of users -const count = await prisma.user.count(); -console.log(`There are ${count} users in the database.`); -``` - ---- - -Let's run this script with `bun run`. Each time we run it, a new user is created. - -```bash -$ bun run index.ts -Created john-0.12802932895402364@example.com -There are 1 users in the database. -$ bun run index.ts -Created john-0.8671308799782803@example.com -There are 2 users in the database. -$ bun run index.ts -Created john-0.4465968383115295@example.com -There are 3 users in the database. -``` - ---- - -That's it! Now that you've set up Prisma using Bun, we recommend referring to the [official Prisma docs](https://www.prisma.io/docs/concepts/components/prisma-client) as you continue to develop your application. diff --git a/docs/guides/ecosystem/prisma.mdx b/docs/guides/ecosystem/prisma.mdx new file mode 100644 index 0000000000..a762159423 --- /dev/null +++ b/docs/guides/ecosystem/prisma.mdx @@ -0,0 +1,164 @@ +--- +title: Use Prisma with Bun +sidebarTitle: Prisma ORM with Bun +mode: center +--- + + + **Note** — Prisma's dynamic subcommand loading system currently requires npm to be installed alongside Bun. This + affects certain CLI commands like `prisma init`, `prisma migrate`, etc. Generated code works perfectly with Bun using + the new `prisma-client` generator. + + + + + Prisma works out of the box with Bun. First, create a directory and initialize it with `bun init`. + + ```bash terminal icon="terminal" + mkdir prisma-app + cd prisma-app + bun init + ``` + + + + Then install the Prisma CLI (`prisma`), Prisma Client (`@prisma/client`), and the LibSQL adapter as dependencies. + + ```bash terminal icon="terminal" + bun add -d prisma + bun add @prisma/client @prisma/adapter-libsql + ``` + + + + We'll use the Prisma CLI with `bunx` to initialize our schema and migration directory. For simplicity we'll be using an in-memory SQLite database. + + ```bash terminal icon="terminal" + bunx --bun prisma init --datasource-provider sqlite + ``` + + This creates a basic schema. We need to update it to use the new Rust-free client with Bun optimization. Open `prisma/schema.prisma` and modify the generator block, then add a simple `User` model. + + ```prisma prisma/schema.prisma icon="/icons/ecosystem/prisma.svg" + generator client { + provider = "prisma-client" // [!code ++] + output = "./generated" // [!code ++] + engineType = "client" // [!code ++] + runtime = "bun" // [!code ++] + } + + datasource db { + provider = "sqlite" + url = env("DATABASE_URL") + } + + model User { // [!code ++] + id Int @id @default(autoincrement()) // [!code ++] + email String @unique // [!code ++] + name String? // [!code ++] + } // [!code ++] + ``` + + + + Then generate and run initial migration. + + This will generate a `.sql` migration file in `prisma/migrations`, create a new SQLite instance, and execute the migration against the new instance. + + ```bash terminal icon="terminal" + bunx --bun prisma migrate dev --name init + ``` + ```txt + Environment variables loaded from .env + Prisma schema loaded from prisma/schema.prisma + Datasource "db": SQLite database "dev.db" at "file:./dev.db" + + SQLite database dev.db created at file:./dev.db + + Applying migration `20251014141233_init` + + The following migration(s) have been created and applied from new schema changes: + + prisma/migrations/ + └─ 20251014141233_init/ + └─ migration.sql + + Your database is now in sync with your schema. + + ✔ Generated Prisma Client (6.17.1) to ./generated in 18ms + ``` + + + + As indicated in the output, Prisma re-generates our _Prisma client_ whenever we execute a new migration. The client provides a fully typed API for reading and writing from our database. You can manually re-generate the client with the Prisma CLI. + + ```sh terminal icon="terminal" + bunx --bun prisma generate + ``` + + + + Now we need to create a Prisma client instance. Create a new file `prisma/db.ts` to initialize the PrismaClient with the LibSQL adapter. + + ```ts prisma/db.ts icon="/icons/typescript.svg" + import { PrismaClient } from "./generated/client"; + import { PrismaLibSQL } from "@prisma/adapter-libsql"; + + const adapter = new PrismaLibSQL({ url: process.env.DATABASE_URL || "" }); + export const prisma = new PrismaClient({ adapter }); + ``` + + + + Let's write a simple script to create a new user, then count the number of users in the database. + + ```ts index.ts icon="/icons/typescript.svg" + import { prisma } from "./prisma/db"; + + // create a new user + await prisma.user.create({ + data: { + name: "John Dough", + email: `john-${Math.random()}@example.com`, + }, + }); + + // count the number of users + const count = await prisma.user.count(); + console.log(`There are ${count} users in the database.`); + ``` + + + + Let's run this script with `bun run`. Each time we run it, a new user is created. + + ```bash terminal icon="terminal" + bun run index.ts + ``` + ```txg + Created john-0.12802932895402364@example.com + There are 1 users in the database. + ``` + + ```bash terminal icon="terminal" + bun run index.ts + ``` + ```txt + Created john-0.8671308799782803@example.com + There are 2 users in the database. + ``` + + ```bash terminal icon="terminal" + bun run index.ts + ``` + ```txt + Created john-0.4465968383115295@example.com + There are 3 users in the database. + ``` + + + + +--- + +That's it! Now that you've set up Prisma using Bun, we recommend referring to the [official Prisma docs](https://www.prisma.io/docs/concepts/components/prisma-client) as you continue to develop your application. diff --git a/docs/guides/ecosystem/qwik.md b/docs/guides/ecosystem/qwik.mdx similarity index 88% rename from docs/guides/ecosystem/qwik.md rename to docs/guides/ecosystem/qwik.mdx index 45d3f9c1aa..3a5f38695c 100644 --- a/docs/guides/ecosystem/qwik.md +++ b/docs/guides/ecosystem/qwik.mdx @@ -1,14 +1,18 @@ --- -name: Build an app with Qwik and Bun +title: Build an app with Qwik and Bun +sidebarTitle: Qwik with Bun +mode: center --- Initialize a new Qwik app with `bunx create-qwik`. The `create-qwik` package detects when you are using `bunx` and will automatically install dependencies using `bun`. -```sh -$ bun create qwik +```sh terminal icon="terminal" +bun create qwik +``` +```txts ............ .::: :--------:. .:::: .:-------:. @@ -85,22 +89,25 @@ $ bun create qwik Run `bun run dev` to start the development server. -```sh -$ bun run dev - $ vite--mode ssr +```sh terminal icon="terminal" +bun run dev +``` - VITE v4.4.7 ready in 1190 ms +```txt +$ vite--mode ssr - ➜ Local: http://localhost:5173/ - ➜ Network: use --host to expose - ➜ press h to show help +VITE v4.4.7 ready in 1190 ms + +➜ Local: http://localhost:5173/ +➜ Network: use --host to expose +➜ press h to show help ``` --- Open [http://localhost:5173](http://localhost:5173) with your browser to see the result. Qwik will hot-reload your app as you edit your source files. -{% image src="https://github.com/oven-sh/bun/assets/3084745/ec35f2f7-03dd-4c90-851e-fb4ad150bb28" alt="Qwik screenshot" /%} +![Qwik screenshot](https://github.com/oven-sh/bun/assets/3084745/ec35f2f7-03dd-4c90-851e-fb4ad150bb28) --- diff --git a/docs/guides/ecosystem/react.md b/docs/guides/ecosystem/react.mdx similarity index 87% rename from docs/guides/ecosystem/react.md rename to docs/guides/ecosystem/react.mdx index 5538226b53..d27d674d1f 100644 --- a/docs/guides/ecosystem/react.md +++ b/docs/guides/ecosystem/react.mdx @@ -1,23 +1,25 @@ --- -name: Build a React app with Bun +title: Build a React app with Bun +sidebarTitle: React with Bun +mode: center --- Bun supports `.jsx` and `.tsx` files out of the box. React just works with Bun. Create a new React app with `bun init --react`. This gives you a template with a simple React app and a simple API server together in one full-stack app. -```bash +```bash terminal icon="terminal" # Create a new React app -$ bun init --react +bun init --react # Run the app in development mode -$ bun dev +bun dev # Build as a static site for production -$ bun run build +bun run build # Run the server in production -$ bun start +bun start ``` --- @@ -34,7 +36,7 @@ Run `bun start` to start the API server and frontend together in one process. Run `bun run build` to build the app as a static site. This will create a `dist` directory with the built app and all the assets. -``` +```txt File Tree icon="folder-tree" ├── src/ │ ├── index.tsx # Server entry point with API routes │ ├── frontend.tsx # React app entry point with HMR diff --git a/docs/guides/ecosystem/remix.md b/docs/guides/ecosystem/remix.mdx similarity index 50% rename from docs/guides/ecosystem/remix.md rename to docs/guides/ecosystem/remix.mdx index 5adf648ec8..8dc405f131 100644 --- a/docs/guides/ecosystem/remix.md +++ b/docs/guides/ecosystem/remix.mdx @@ -1,18 +1,23 @@ --- -name: Build an app with Remix and Bun +title: Build an app with Remix and Bun +sidebarTitle: Remix with Bun +mode: center --- -{% callout %} -Currently the Remix development server (`remix dev`) relies on Node.js APIs that Bun does not yet implement. The guide below uses Bun to initialize a project and install dependencies, but it uses Node.js to run the dev server. -{% /callout %} + + Currently the Remix development server (`remix dev`) relies on Node.js APIs that Bun does not yet implement. The guide + below uses Bun to initialize a project and install dependencies, but it uses Node.js to run the dev server. + --- Initialize a Remix app with `create-remix`. -```sh -$ bun create remix +```sh terminal icon="terminal" +bun create remix +``` +```txt remix v1.19.3 💿 Let's build a better website... dir Where should we create your new project? @@ -28,11 +33,9 @@ $ bun create remix Yes ✔ Dependencies installed - ✔ Git initialized done That's it! - Enter your project directory using cd ./my-app Check out README.md for development and deploy instructions. ``` @@ -41,36 +44,52 @@ $ bun create remix To start the dev server, run `bun run dev` from the project root. This will start the dev server using the `remix dev` command. Note that Node.js will be used to run the dev server. -```sh -$ cd my-app -$ bun run dev - $ remix dev +```sh terminal icon="terminal" +cd my-app +bun run dev +``` - 💿 remix dev +```txt +$ remix dev - info building... - info built (263ms) - Remix App Server started at http://localhost:3000 (http://172.20.0.143:3000) +💿 remix dev + +info building... +info built (263ms) +Remix App Server started at http://localhost:3000 (http://172.20.0.143:3000) ``` --- Open [http://localhost:3000](http://localhost:3000) to see the app. Any changes you make to `app/routes/_index.tsx` will be hot-reloaded in the browser. -{% image src="https://github.com/oven-sh/bun/assets/3084745/c26f1059-a5d4-4c0b-9a88-d9902472fd77" caption="Remix app running on localhost" /%} + + ![Remix app running on localhost](https://github.com/oven-sh/bun/assets/3084745/c26f1059-a5d4-4c0b-9a88-d9902472fd77) + --- -To build and start your app, run `bun run build` then `bun run start` from the project root. +To build and start your app, run `bun run build` -```sh -$ bun run build - $ remix build - info building... (NODE_ENV=production) - info built (158ms) -$ bun start - $ remix-serve ./build/index.js - [remix-serve] http://localhost:3000 (http://192.168.86.237:3000) +```sh terminal icon="terminal" +bun run build +``` + +```txt +$ remix build +info building... (NODE_ENV=production) +info built (158ms) +``` + +Then `bun run start` from the project root. + +```sh terminal icon="terminal" +bun start +``` + +```txt +$ remix-serve ./build/index.js +[remix-serve] http://localhost:3000 (http://192.168.86.237:3000) ``` --- diff --git a/docs/guides/ecosystem/render.md b/docs/guides/ecosystem/render.md deleted file mode 100644 index 338c6a2158..0000000000 --- a/docs/guides/ecosystem/render.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -name: Deploy a Bun application on Render ---- - -[Render](https://render.com/) is a cloud platform that lets you flexibly build, deploy, and scale your apps. - -It offers features like auto deploys from GitHub, a global CDN, private networks, automatic HTTPS setup, and managed PostgreSQL and Redis. - -Render supports Bun natively. You can deploy Bun apps as web services, background workers, cron jobs, and more. - ---- - -As an example, let's deploy a simple Express HTTP server to Render. - ---- - -Create a new GitHub repo named `myapp`. Git clone it locally. - -```sh -$ git clone git@github.com:my-github-username/myapp.git -$ cd myapp -``` - ---- - -Add the Express library. - -```sh -$ bun add express -``` - ---- - -Define a simple server with Express: - -```ts#app.ts -import express from "express"; - -const app = express(); -const port = process.env.PORT || 3001; - -app.get("/", (req, res) => { - res.send("Hello World!"); -}); - -app.listen(port, () => { - console.log(`Listening on port ${port}...`); -}); -``` - ---- - -Commit your changes and push to GitHub. - -```sh -$ git add app.ts bun.lock package.json -$ git commit -m "Create simple Express app" -$ git push origin main -``` - ---- - -In your [Render Dashboard](https://dashboard.render.com/), click `New` > `Web Service` and connect your `myapp` repo. - ---- - -In the Render UI, provide the following values during web service creation: - -| | | -| ----------------- | ------------- | -| **Runtime** | `Node` | -| **Build Command** | `bun install` | -| **Start Command** | `bun app.ts` | - ---- - -That's it! Your web service will be live at its assigned `onrender.com` URL as soon as the build finishes. - -You can view the [deploy logs](https://docs.render.com/logging#logs-for-an-individual-deploy-or-job) for details. Refer to [Render's documentation](https://docs.render.com/deploys) for a complete overview of deploying on Render. diff --git a/docs/guides/ecosystem/sentry.md b/docs/guides/ecosystem/sentry.mdx similarity index 87% rename from docs/guides/ecosystem/sentry.md rename to docs/guides/ecosystem/sentry.mdx index a513720ed5..a7bc1353cd 100644 --- a/docs/guides/ecosystem/sentry.md +++ b/docs/guides/ecosystem/sentry.mdx @@ -1,5 +1,7 @@ --- -name: Add Sentry to a Bun app +title: Add Sentry to a Bun app +sidebarTitle: Sentry with Bun +mode: center --- [Sentry](https://sentry.io) is a developer-first error tracking and performance monitoring platform. Sentry has a first-class SDK for Bun, `@sentry/bun`, that instruments your Bun application to automatically collect error and performance data. @@ -10,15 +12,15 @@ Don't already have an account and Sentry project established? Head over to [sent To start using Sentry with Bun, first install the Sentry Bun SDK. -```sh -$ bun add @sentry/bun +```sh terminal icon="terminal" +bun add @sentry/bun ``` --- Then, initialize the Sentry SDK with your Sentry DSN in your app's entry file. You can find your DSN in your Sentry project settings. -```js +```js sentry.ts icon="/icons/typescript.svg" import * as Sentry from "@sentry/bun"; // Ensure to call this before importing any other modules! @@ -35,7 +37,7 @@ Sentry.init({ You can verify that Sentry is working by capturing a test error: -```js +```js sentry.ts icon="/icons/typescript.svg" setTimeout(() => { try { foo(); diff --git a/docs/guides/ecosystem/solidstart.md b/docs/guides/ecosystem/solidstart.mdx similarity index 60% rename from docs/guides/ecosystem/solidstart.md rename to docs/guides/ecosystem/solidstart.mdx index cd99e8e1ad..b958187817 100644 --- a/docs/guides/ecosystem/solidstart.md +++ b/docs/guides/ecosystem/solidstart.mdx @@ -1,17 +1,23 @@ --- -name: Build an app with SolidStart and Bun +title: Build an app with SolidStart and Bun +sidebarTitle: "SolidStart with Bun" +mode: center --- -{% callout %} -SolidStart currently relies on Node.js APIs that Bun does not yet implement. The guide below uses Bun to initialize a project and install dependencies, but it uses Node.js to run the dev server. -{% /callout %} + + SolidStart currently relies on Node.js APIs that Bun does not yet implement. The guide below uses Bun to initialize a + project and install dependencies, but it uses Node.js to run the dev server. + --- Initialize a SolidStart app with `create-solid`. -```sh -$ bun create solid my-app +```sh terminal icon="terminal" +bun create solid my-app +``` + +```txt create-solid version 0.2.31 Welcome to the SolidStart setup wizard! @@ -32,26 +38,28 @@ cloned solidjs/solid-start#main to /path/to/my-app/.solid-start As instructed by the `create-solid` CLI, let's install our dependencies. -```sh -$ cd my-app -$ bun install +```sh terminal icon="terminal" +cd my-app +bun install ``` --- Then run the development server. -```sh -$ bun run dev +```sh terminal icon="terminal" +bun run dev # or, equivalently -$ bunx solid-start dev +bunx solid-start dev ``` --- Open [localhost:3000](http://localhost:3000). Any changes you make to `src/routes/index.tsx` will be hot-reloaded automatically. -{% image src="https://github.com/oven-sh/bun/assets/3084745/1e8043c4-49d1-498c-9add-c1eaab6c7167" alt="SolidStart demo app" /%} + + ![SolidStart demo app](https://github.com/oven-sh/bun/assets/3084745/1e8043c4-49d1-498c-9add-c1eaab6c7167) + --- diff --git a/docs/guides/ecosystem/ssr-react.md b/docs/guides/ecosystem/ssr-react.mdx similarity index 71% rename from docs/guides/ecosystem/ssr-react.md rename to docs/guides/ecosystem/ssr-react.mdx index 56ac703fd9..1a5b46c68c 100644 --- a/docs/guides/ecosystem/ssr-react.md +++ b/docs/guides/ecosystem/ssr-react.mdx @@ -1,19 +1,21 @@ --- -name: Server-side render (SSR) a React component +title: Server-side render (SSR) a React component +sidebarTitle: "SSR React with Bun" +mode: center --- To get started, install `react` & `react-dom`: -```sh +```sh terminal icon="terminal" # Any package manager can be used -$ bun add react react-dom +bun add react react-dom ``` --- To render a React component to an HTML stream server-side (SSR): -```tsx +```tsx ssr-react.tsx icon="file-code" import { renderToReadableStream } from "react-dom/server"; function Component(props: { message: string }) { @@ -24,21 +26,17 @@ function Component(props: { message: string }) { ); } -const stream = await renderToReadableStream( - , -); +const stream = await renderToReadableStream(); ``` --- Combining this with `Bun.serve()`, we get a simple SSR HTTP server: -```tsx +```tsx server.ts icon="/icons/typescript.svg" Bun.serve({ async fetch() { - const stream = await renderToReadableStream( - , - ); + const stream = await renderToReadableStream(); return new Response(stream, { headers: { "Content-Type": "text/html" }, }); diff --git a/docs/guides/ecosystem/stric.md b/docs/guides/ecosystem/stric.mdx similarity index 60% rename from docs/guides/ecosystem/stric.md rename to docs/guides/ecosystem/stric.mdx index 2d12bb0baa..359b52abc1 100644 --- a/docs/guides/ecosystem/stric.md +++ b/docs/guides/ecosystem/stric.mdx @@ -1,5 +1,7 @@ --- -name: Build an HTTP server using StricJS and Bun +title: Build an HTTP server using StricJS and Bun +sidebarTitle: "StricJS with Bun" +mode: center --- [StricJS](https://github.com/bunsvr) is a Bun framework for building high-performance web applications and APIs. @@ -12,42 +14,39 @@ name: Build an HTTP server using StricJS and Bun Use `bun init` to create an empty project. -```bash -$ mkdir myapp -$ cd myapp -$ bun init -$ bun add @stricjs/router @stricjs/utils +```bash terminal icon="terminal" +mkdir myapp +cd myapp +bun init +bun add @stricjs/router @stricjs/utils ``` --- To implement a simple HTTP server with StricJS: -```ts#index.ts -import { Router } from '@stricjs/router'; +```ts index.ts icon="file-code" +import { Router } from "@stricjs/router"; -export default new Router() - .get('/', () => new Response('Hi')); +export default new Router().get("/", () => new Response("Hi")); ``` --- To serve static files from `/public`: -```ts#index.ts -import { dir } from '@stricjs/utils'; +```ts index.ts icon="file-code" +import { dir } from "@stricjs/utils"; -export default new Router() - .get('/', () => new Response('Hi')) - .get('/*', dir('./public')); +export default new Router().get("/", () => new Response("Hi")).get("/*", dir("./public")); ``` --- Run the file in watch mode to start the development server. -```bash -$ bun --watch run index.ts +```bash terminal icon="terminal" +bun --watch run index.ts ``` --- diff --git a/docs/guides/ecosystem/sveltekit.md b/docs/guides/ecosystem/sveltekit.mdx similarity index 75% rename from docs/guides/ecosystem/sveltekit.md rename to docs/guides/ecosystem/sveltekit.mdx index 61e0c3b31c..9b0d892d38 100644 --- a/docs/guides/ecosystem/sveltekit.md +++ b/docs/guides/ecosystem/sveltekit.mdx @@ -1,11 +1,16 @@ --- -name: Build an app with SvelteKit and Bun +title: Build an app with SvelteKit and Bun +sidebarTitle: "SvelteKit with Bun" +mode: center --- Use `sv create my-app` to create a SvelteKit project with SvelteKit CLI. Answer the prompts to select a template and set up your development environment. -```sh -$ bunx sv create my-app +```sh terminal icon="terminal" +bunx sv create my-app +``` + +```txt ┌ Welcome to the Svelte CLI! (v0.5.7) │ ◇ Which template would you like? @@ -47,9 +52,12 @@ Then start the development server with `bun --bun run dev`. To run the dev server with Node.js instead of Bun, you can omit the `--bun` flag. -```sh -$ cd my-app -$ bun --bun run dev +```sh terminal icon="terminal" +cd my-app +bun --bun run dev +``` + +```txt $ vite dev Forced re-optimization of dependencies @@ -64,7 +72,9 @@ $ bun --bun run dev Visit [http://localhost:5173](http://localhost:5173/) in a browser to see the template app. -{% image src="https://github.com/oven-sh/bun/assets/3084745/7c76eae8-78f9-44fa-9f15-1bd3ca1a47c0" /%} + + ![SvelteKit app running](https://github.com/oven-sh/bun/assets/3084745/7c76eae8-78f9-44fa-9f15-1bd3ca1a47c0) + --- @@ -78,34 +88,37 @@ To build for production, you'll need to add the right SvelteKit adapter. Current Now, make the following changes to your `svelte.config.js`. -```ts-diff -- import adapter from "@sveltejs/adapter-auto"; -+ import adapter from "svelte-adapter-bun"; - import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; +```ts svelte.config.js icon="file-code" +import adapter from "@sveltejs/adapter-auto"; // [!code --] +import adapter from "svelte-adapter-bun"; // [!code ++] +import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; - /** @type {import('@sveltejs/kit').Config} */ - const config = { - // Consult https://svelte.dev/docs/kit/integrations#preprocessors - // for more information about preprocessors - preprocess: vitePreprocess(), +/** @type {import('@sveltejs/kit').Config} */ +const config = { + // Consult https://svelte.dev/docs/kit/integrations#preprocessors + // for more information about preprocessors + preprocess: vitePreprocess(), - kit: { - // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. - // If your environment is not supported, or you settled on a specific environment, switch out the adapter. - // See https://svelte.dev/docs/kit/adapters for more information about adapters. - adapter: adapter() - } - }; + kit: { + // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. + // If your environment is not supported, or you settled on a specific environment, switch out the adapter. + // See https://svelte.dev/docs/kit/adapters for more information about adapters. + adapter: adapter(), + }, +}; - export default config; +export default config; ``` --- To build a production bundle: -```sh -$ bun --bun run build +```sh terminal icon="terminal" +bun --bun run build +``` + +```txt $ vite build vite v5.4.10 building SSR bundle for production... "confetti" is imported from external module "@neoconfetti/svelte" but never used in "src/routes/sverdle/+page.svelte". diff --git a/docs/guides/ecosystem/systemd.md b/docs/guides/ecosystem/systemd.mdx similarity index 65% rename from docs/guides/ecosystem/systemd.md rename to docs/guides/ecosystem/systemd.mdx index c22fc9ae29..17a599d932 100644 --- a/docs/guides/ecosystem/systemd.md +++ b/docs/guides/ecosystem/systemd.mdx @@ -1,20 +1,18 @@ --- -name: Run Bun as a daemon with systemd +title: Run Bun as a daemon with systemd +sidebarTitle: "systemd with Bun" +mode: center --- [systemd](https://systemd.io) is an init system and service manager for Linux operating systems that manages the startup and control of system processes and services. - - - - --- To run a Bun application as a daemon using **systemd** you'll need to create a _service file_ in `/lib/systemd/system/`. -```sh -$ cd /lib/systemd/system -$ touch my-app.service +```sh terminal icon="terminal" +cd /lib/systemd/system +touch my-app.service ``` --- @@ -23,7 +21,7 @@ Here is a typical service file that runs an application on system start. You can Refer to the [systemd documentation](https://www.freedesktop.org/software/systemd/man/systemd.service.html) for more information on each setting. -```ini#my-app.service +```ini my-app.service icon="file-code" [Unit] # describe the app Description=My App @@ -54,32 +52,35 @@ WantedBy=multi-user.target If your application starts a webserver, note that non-`root` users are not able to listen on ports 80 or 443 by default. To permanently allow Bun to listen on these ports when executed by a non-`root` user, use the following command. This step isn't necessary when running as `root`. -```bash -$ sudo setcap CAP_NET_BIND_SERVICE=+eip ~/.bun/bin/bun +```bash terminal icon="terminal" +setcap CAP_NET_BIND_SERVICE=+eip ~/.bun/bin/bun ``` --- With the service file configured, you can now _enable_ the service. Once enabled, it will start automatically on reboot. This requires `sudo` permissions. -```bash -$ sudo systemctl enable my-app +```bash terminal icon="terminal" +systemctl enable my-app ``` --- To start the service without rebooting, you can manually _start_ it. -```bash -$ sudo systemctl start my-app +```bash terminal icon="terminal" +systemctl start my-app ``` --- Check the status of your application with `systemctl status`. If you've started your app successfully, you should see something like this: -```bash -$ sudo systemctl status my-app +```bash terminal icon="terminal" +systemctl status my-app +``` + +```txt ● my-app.service - My App Loaded: loaded (/lib/systemd/system/my-app.service; enabled; preset: enabled) Active: active (running) since Thu 2023-10-12 11:34:08 UTC; 1h 8min ago @@ -95,19 +96,19 @@ $ sudo systemctl status my-app To update the service, edit the contents of the service file, then reload the daemon. -```bash -$ sudo systemctl daemon-reload +```bash terminal icon="terminal" +systemctl daemon-reload ``` --- For a complete guide on the service unit configuration, you can check [this page](https://www.freedesktop.org/software/systemd/man/systemd.service.html). Or refer to this cheatsheet of common commands: -```bash -$ sudo systemctl daemon-reload # tell systemd that some files got changed -$ sudo systemctl enable my-app # enable the app (to allow auto-start) -$ sudo systemctl disable my-app # disable the app (turns off auto-start) -$ sudo systemctl start my-app # start the app if is stopped -$ sudo systemctl stop my-app # stop the app -$ sudo systemctl restart my-app # restart the app +```bash terminal icon="terminal" +systemctl daemon-reload # tell systemd that some files got changed +systemctl enable my-app # enable the app (to allow auto-start) +systemctl disable my-app # disable the app (turns off auto-start) +systemctl start my-app # start the app if is stopped +systemctl stop my-app # stop the app +systemctl restart my-app # restart the app ``` diff --git a/docs/guides/ecosystem/vite.md b/docs/guides/ecosystem/vite.mdx similarity index 64% rename from docs/guides/ecosystem/vite.md rename to docs/guides/ecosystem/vite.mdx index 0fd551400b..64f3c89f3d 100644 --- a/docs/guides/ecosystem/vite.md +++ b/docs/guides/ecosystem/vite.mdx @@ -1,17 +1,23 @@ --- -name: Build a frontend using Vite and Bun +title: Build a frontend using Vite and Bun +sidebarTitle: "Vite with Bun" +mode: center --- -{% callout %} -You can use Vite with Bun, but many projects get faster builds & drop hundreds of dependencies by switching to [HTML imports](/docs/bundler/html). -{% /callout %} + + You can use Vite with Bun, but many projects get faster builds & drop hundreds of dependencies by switching to [HTML + imports](/bundler/html-static). + --- Vite works out of the box with Bun. Get started with one of Vite's templates. -```bash -$ bun create vite my-app +```bash terminal icon="terminal" +bun create vite my-app +``` + +```txt ✔ Select a framework: › React ✔ Select a variant: › TypeScript + SWC Scaffolding project in /path/to/my-app... @@ -21,7 +27,7 @@ Scaffolding project in /path/to/my-app... Then `cd` into the project directory and install dependencies. -```bash +```bash terminal icon="terminal" cd my-app bun install ``` @@ -32,7 +38,7 @@ Start the development server with the `vite` CLI using `bunx`. The `--bun` flag tells Bun to run Vite's CLI using `bun` instead of `node`; by default Bun respects Vite's `#!/usr/bin/env node` [shebang line](). -```bash +```bash terminal icon="terminal" bunx --bun vite ``` @@ -40,10 +46,10 @@ bunx --bun vite To simplify this command, update the `"dev"` script in `package.json` to the following. -```json-diff#package.json +```json package.json icon="file-json" "scripts": { -- "dev": "vite", -+ "dev": "bunx --bun vite", + "dev": "vite", // [!code --] + "dev": "bunx --bun vite", // [!code ++] "build": "vite build", "serve": "vite preview" }, @@ -54,7 +60,7 @@ To simplify this command, update the `"dev"` script in `package.json` to the fol Now you can start the development server with `bun run dev`. -```bash +```bash terminal icon="terminal" bun run dev ``` @@ -62,8 +68,8 @@ bun run dev The following command will build your app for production. -```sh -$ bunx --bun vite build +```sh terminal icon="terminal" +bunx --bun vite build ``` --- diff --git a/docs/guides/html-rewriter/extract-links.md b/docs/guides/html-rewriter/extract-links.mdx similarity index 79% rename from docs/guides/html-rewriter/extract-links.md rename to docs/guides/html-rewriter/extract-links.mdx index 37fe561240..d2b8e2962f 100644 --- a/docs/guides/html-rewriter/extract-links.md +++ b/docs/guides/html-rewriter/extract-links.mdx @@ -1,12 +1,14 @@ --- -name: Extract links from a webpage using HTMLRewriter +title: Extract links from a webpage using HTMLRewriter +sidebarTitle: Extract links using HTMLRewriter +mode: center --- ## Extract links from a webpage Bun's [HTMLRewriter](https://bun.com/docs/api/html-rewriter) API can be used to efficiently extract links from HTML content. It works by chaining together CSS selectors to match the elements, text, and attributes you want to process. This is a simple example of how to extract links from a webpage. You can pass `.transform` a `Response`, `Blob`, or `string`. -```ts +```ts extract-links.ts icon="/icons/typescript.svg" async function extractLinks(url: string) { const links = new Set(); const response = await fetch(url); @@ -35,7 +37,7 @@ await extractLinks("https://bun.com"); When scraping websites, you often want to convert relative URLs (like `/docs`) to absolute URLs. Here's how to handle URL resolution: -```ts +```ts extract-links.ts icon="/icons/typescript.svg" async function extractLinksFromURL(url: string) { const response = await fetch(url); const links = new Set(); @@ -44,13 +46,15 @@ async function extractLinksFromURL(url: string) { element(el) { const href = el.getAttribute("href"); if (href) { - // Convert relative URLs to absolute + // Convert relative URLs to absolute // [!code ++] try { - const absoluteURL = new URL(href, url).href; - links.add(absoluteURL); + // [!code ++] + const absoluteURL = new URL(href, url).href; // [!code ++] + links.add(absoluteURL); // [!code ++] } catch { + // [!code ++] links.add(href); - } + } // [!code ++] } }, }); diff --git a/docs/guides/html-rewriter/extract-social-meta.md b/docs/guides/html-rewriter/extract-social-meta.mdx similarity index 93% rename from docs/guides/html-rewriter/extract-social-meta.md rename to docs/guides/html-rewriter/extract-social-meta.mdx index 2930d2853f..455c75739d 100644 --- a/docs/guides/html-rewriter/extract-social-meta.md +++ b/docs/guides/html-rewriter/extract-social-meta.mdx @@ -1,12 +1,14 @@ --- -name: Extract social share images and Open Graph tags +title: Extract social share images and Open Graph tags +sidebarTitle: OpenGraph tags +mode: center --- ## Extract social share images and Open Graph tags Bun's [HTMLRewriter](https://bun.com/docs/api/html-rewriter) API can be used to efficiently extract social share images and Open Graph metadata from HTML content. This is particularly useful for building link preview features, social media cards, or web scrapers. We can use HTMLRewriter to match CSS selectors to HTML elements, text, and attributes we want to process. -```ts +```ts extract-social-meta.ts icon="/icons/typescript.svg" interface SocialMetadata { title?: string; description?: string; @@ -79,7 +81,9 @@ async function extractSocialMetadata(url: string): Promise { return metadata; } +``` +```ts Example Usage icon="/icons/typescript.svg" // Example usage const metadata = await extractSocialMetadata("https://bun.com"); console.log(metadata); diff --git a/docs/guides/html-rewriter/index.json b/docs/guides/html-rewriter/index.json deleted file mode 100644 index 412ecb8ce5..0000000000 --- a/docs/guides/html-rewriter/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "HTMLRewriter", - "description": "A collection of guides for using the HTMLRewriter streaming HTML parser with Bun" -} diff --git a/docs/guides/http/cluster.md b/docs/guides/http/cluster.mdx similarity index 80% rename from docs/guides/http/cluster.md rename to docs/guides/http/cluster.mdx index c434337d79..5fbde83524 100644 --- a/docs/guides/http/cluster.md +++ b/docs/guides/http/cluster.mdx @@ -1,13 +1,15 @@ --- -name: Start a cluster of HTTP servers +title: Start a cluster of HTTP servers description: Run multiple HTTP servers concurrently via the "reusePort" option to share the same port across multiple processes +sidebarTitle: Start a cluster of HTTP servers +mode: center --- To run multiple HTTP servers concurrently, use the `reusePort` option in `Bun.serve()` which shares the same port across multiple processes. This automatically load balances incoming requests across multiple instances of Bun. -```ts#server.ts +```ts server.ts icon="/icons/typescript.svg" import { serve } from "bun"; const id = Math.random().toString(36).slice(2); @@ -22,21 +24,22 @@ serve({ async fetch(request) { return new Response("Hello from Bun #" + id + "!\n"); - } + }, }); ``` --- -{% callout %} -**Linux only** — Windows and macOS ignore the `reusePort` option. This is an operating system limitation with `SO_REUSEPORT`, unfortunately. -{% /callout %} + + **Linux only** — Windows and macOS ignore the `reusePort` option. This is an operating system limitation with + `SO_REUSEPORT`, unfortunately. + After saving the file, start your servers on the same port. Under the hood, this uses the Linux `SO_REUSEPORT` and `SO_REUSEADDR` socket options to ensure fair load balancing across multiple processes. [Learn more about `SO_REUSEPORT` and `SO_REUSEADDR`](https://lwn.net/Articles/542629/) -```ts#cluster.ts +```ts cluster.ts icon="/icons/typescript.svg" import { spawn } from "bun"; const cpus = navigator.hardwareConcurrency; // Number of CPU cores diff --git a/docs/guides/http/fetch-unix.md b/docs/guides/http/fetch-unix.mdx similarity index 83% rename from docs/guides/http/fetch-unix.md rename to docs/guides/http/fetch-unix.mdx index 68306d31bd..c48e39b297 100644 --- a/docs/guides/http/fetch-unix.md +++ b/docs/guides/http/fetch-unix.mdx @@ -1,10 +1,12 @@ --- -name: fetch with unix domain sockets in Bun +title: fetch with unix domain sockets in Bun +sidebarTitle: Fetch with unix domain sockets +mode: center --- In Bun, the `unix` option in `fetch()` lets you send HTTP requests over a [unix domain socket](https://en.wikipedia.org/wiki/Unix_domain_socket). -```ts +```ts fetch-unix.ts icon="/icons/typescript.svg" const unix = "/var/run/docker.sock"; const response = await fetch("http://localhost/info", { unix }); @@ -19,7 +21,7 @@ The `unix` option is a string that specifies the local file path to a unix domai To send a `POST` request to an API endpoint over a unix domain socket: -```ts +```ts fetch-unix.ts icon="/icons/typescript.svg" const response = await fetch("https://hostname/a/path", { unix: "/var/run/path/to/unix.sock", method: "POST", diff --git a/docs/guides/http/fetch.md b/docs/guides/http/fetch.mdx similarity index 76% rename from docs/guides/http/fetch.md rename to docs/guides/http/fetch.mdx index b9f4063278..f5c2e0ed72 100644 --- a/docs/guides/http/fetch.md +++ b/docs/guides/http/fetch.mdx @@ -1,10 +1,12 @@ --- -name: Send an HTTP request using fetch +title: Send an HTTP request using fetch +sidebarTitle: Fetch with Bun +mode: center --- Bun implements the Web-standard [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) API for sending HTTP requests. To send a simple `GET` request to a URL: -```ts +```ts fetch.ts icon="/icons/typescript.svg" const response = await fetch("https://bun.com"); const html = await response.text(); // HTML string ``` @@ -13,7 +15,7 @@ const html = await response.text(); // HTML string To send a `POST` request to an API endpoint. -```ts +```ts fetch.ts icon="/icons/typescript.svg" const response = await fetch("https://bun.com/api", { method: "POST", body: JSON.stringify({ message: "Hello from Bun!" }), diff --git a/docs/guides/http/file-uploads.md b/docs/guides/http/file-uploads.mdx similarity index 73% rename from docs/guides/http/file-uploads.md rename to docs/guides/http/file-uploads.mdx index 6630934548..b704a7f82d 100644 --- a/docs/guides/http/file-uploads.md +++ b/docs/guides/http/file-uploads.mdx @@ -1,10 +1,12 @@ --- -name: Upload files via HTTP using FormData +title: Upload files via HTTP using FormData +sidebarTitle: Upload files via HTTP using FormData +mode: center --- To upload files via HTTP with Bun, use the [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) API. Let's start with a HTTP server that serves a simple HTML web form. -```ts#index.ts +```ts index.ts icon="/icons/typescript.svg" const server = Bun.serve({ port: 4000, async fetch(req) { @@ -29,7 +31,7 @@ console.log(`Listening on http://localhost:${server.port}`); We can define our HTML form in another file, `index.html`. -```html#index.html +```html index.html icon="file-code" @@ -51,7 +53,7 @@ We can define our HTML form in another file, `index.html`. At this point, we can run the server and visit [`localhost:4000`](http://localhost:4000) to see our form. ```bash -$ bun run index.ts +bun run index.ts Listening on http://localhost:4000 ``` @@ -63,7 +65,7 @@ First we use the [`.formData()`](https://developer.mozilla.org/en-US/docs/Web/AP Finally, we write the `Blob` to disk using [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write). -```ts-diff#index.ts +```ts index.ts icon="/icons/typescript.svg" const server = Bun.serve({ port: 4000, async fetch(req) { @@ -77,16 +79,17 @@ const server = Bun.serve({ }, }); -+ // parse formdata at /action -+ if (url.pathname === '/action') { -+ const formdata = await req.formData(); -+ const name = formdata.get('name'); -+ const profilePicture = formdata.get('profilePicture'); -+ if (!profilePicture) throw new Error('Must upload a profile picture.'); -+ // write profilePicture to disk -+ await Bun.write('profilePicture.png', profilePicture); -+ return new Response("Success"); -+ } + // parse formdata at /action // [!code ++] + if (url.pathname === "/action") { + // [!code ++] + const formdata = await req.formData(); // [!code ++] + const name = formdata.get("name"); // [!code ++] + const profilePicture = formdata.get("profilePicture"); // [!code ++] + if (!profilePicture) throw new Error("Must upload a profile picture."); // [!code ++] + // write profilePicture to disk // [!code ++] + await Bun.write("profilePicture.png", profilePicture); // [!code ++] + return new Response("Success"); // [!code ++] + } // [!code ++] return new Response("Not Found", { status: 404 }); }, diff --git a/docs/guides/http/hot.md b/docs/guides/http/hot.mdx similarity index 73% rename from docs/guides/http/hot.md rename to docs/guides/http/hot.mdx index 8e2c70796a..ecc6e04627 100644 --- a/docs/guides/http/hot.md +++ b/docs/guides/http/hot.mdx @@ -1,22 +1,24 @@ --- -name: Hot reload an HTTP server +title: Hot reload an HTTP server +sidebarTitle: Hot reload an HTTP server +mode: center --- Bun supports the [`--hot`](https://bun.com/docs/runtime/hot#hot-mode) flag to run a file with hot reloading enabled. When any module or file changes, Bun re-runs the file. -```sh -$ bun --hot run index.ts +```sh terminal icon="terminal" +bun --hot run index.ts ``` --- Bun detects when you are running an HTTP server with `Bun.serve()`. It reloads your fetch handler when source files change, _without_ restarting the `bun` process. This makes hot reloads nearly instantaneous. -{% callout %} + Note that this doesn't reload the page on your browser. -{% /callout %} - -```ts + + +```ts index.ts icon="/icons/typescript.svg" Bun.serve({ port: 3000, fetch(req) { diff --git a/docs/guides/http/index.json b/docs/guides/http/index.json deleted file mode 100644 index d018e1bdd3..0000000000 --- a/docs/guides/http/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "HTTP", - "description": "A collection of guides for building HTTP servers with Bun" -} diff --git a/docs/guides/http/proxy.md b/docs/guides/http/proxy.mdx similarity index 66% rename from docs/guides/http/proxy.md rename to docs/guides/http/proxy.mdx index e8aafaf2ea..c75434a3f9 100644 --- a/docs/guides/http/proxy.md +++ b/docs/guides/http/proxy.mdx @@ -1,13 +1,15 @@ --- -name: Proxy HTTP requests using fetch() +title: Proxy HTTP requests using fetch() +sidebarTitle: Proxy HTTP requests using fetch() +mode: center --- In Bun, `fetch` supports sending requests through an HTTP or HTTPS proxy. This is useful on corporate networks or when you need to ensure a request is sent through a specific IP address. -```ts +```ts proxy.ts icon="/icons/typescript.svg" await fetch("https://example.com", { // The URL of the proxy server - proxy: "https://username:password@proxy.example.com:8080", + proxy: "https://usertitle:password@proxy.example.com:8080", }); ``` @@ -19,6 +21,6 @@ The `proxy` option is a URL string that specifies the proxy server. It can inclu You can also set the `$HTTP_PROXY` or `$HTTPS_PROXY` environment variable to the proxy URL. This is useful when you want to use the same proxy for all requests. -```sh -HTTPS_PROXY=https://username:password@proxy.example.com:8080 bun run index.ts +```sh terminal icon="terminal" +HTTPS_PROXY=https://usertitle:password@proxy.example.com:8080 bun run index.ts ``` diff --git a/docs/guides/http/server.md b/docs/guides/http/server.mdx similarity index 91% rename from docs/guides/http/server.md rename to docs/guides/http/server.mdx index d857066537..ec75f1635e 100644 --- a/docs/guides/http/server.md +++ b/docs/guides/http/server.mdx @@ -1,12 +1,14 @@ --- -name: Common HTTP server usage +title: Common HTTP server usage +sidebarTitle: HTTP Server with Bun +mode: center --- This starts an HTTP server listening on port `3000`. It demonstrates basic routing with a number of common responses and also handles POST data from standard forms or as JSON. See [`Bun.serve`](https://bun.com/docs/api/http) for details. -```ts +```ts server.ts icon="/icons/typescript.svg" const server = Bun.serve({ async fetch(req) { const path = new URL(req.url).pathname; diff --git a/docs/guides/http/simple.md b/docs/guides/http/simple.mdx similarity index 73% rename from docs/guides/http/simple.md rename to docs/guides/http/simple.mdx index 5f9858ac6a..91e862465c 100644 --- a/docs/guides/http/simple.md +++ b/docs/guides/http/simple.mdx @@ -1,12 +1,14 @@ --- -name: Write a simple HTTP server +title: Write a simple HTTP server +sidebarTitle: Simple HTTP Server with Bun +mode: center --- This starts an HTTP server listening on port `3000`. It responds to all requests with a `Response` with status `200` and body `"Welcome to Bun!"`. See [`Bun.serve`](https://bun.com/docs/api/http) for details. -```ts +```ts server.ts icon="/icons/typescript.svg" const server = Bun.serve({ port: 3000, fetch(request) { diff --git a/docs/guides/http/stream-file.md b/docs/guides/http/stream-file.mdx similarity index 84% rename from docs/guides/http/stream-file.md rename to docs/guides/http/stream-file.mdx index ac4c93e912..b5a31639a2 100644 --- a/docs/guides/http/stream-file.md +++ b/docs/guides/http/stream-file.mdx @@ -1,10 +1,12 @@ --- -name: Stream a file as an HTTP Response +title: Stream a file as an HTTP Response +sidebarTitle: Stream file response +mode: center --- This snippet reads a file from disk using [`Bun.file()`](https://bun.com/docs/api/file-io#reading-files-bun-file). This returns a `BunFile` instance, which can be passed directly into the `new Response` constructor. -```ts +```ts server.ts icon="/icons/typescript.svg" const path = "/path/to/file.txt"; const file = Bun.file(path); const resp = new Response(file); @@ -14,7 +16,7 @@ const resp = new Response(file); The `Content-Type` is read from the file and automatically set on the `Response`. -```ts +```ts server.ts icon="/icons/typescript.svg" new Response(Bun.file("./package.json")).headers.get("Content-Type"); // => application/json;charset=utf-8 @@ -32,7 +34,7 @@ new Response(Bun.file("./img.png")).headers.get("Content-Type"); Putting it all together with [`Bun.serve()`](https://bun.com/docs/api/http#bun-serve). -```ts +```ts server.ts icon="/icons/typescript.svg" // static file server Bun.serve({ async fetch(req) { diff --git a/docs/guides/http/stream-iterator.md b/docs/guides/http/stream-iterator.mdx similarity index 83% rename from docs/guides/http/stream-iterator.md rename to docs/guides/http/stream-iterator.mdx index f3ece8c683..37d04d7304 100644 --- a/docs/guides/http/stream-iterator.md +++ b/docs/guides/http/stream-iterator.mdx @@ -1,10 +1,12 @@ --- -name: Streaming HTTP Server with Async Iterators +title: Streaming HTTP Server with Async Iterators +sidebarTitle: Stream with iterators +mode: center --- In Bun, [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects can accept an async generator function as their body. This allows you to stream data to the client as it becomes available, rather than waiting for the entire response to be ready. -```ts +```ts stream-iterator.ts icon="/icons/typescript.svg" Bun.serve({ port: 3000, fetch(req) { @@ -28,7 +30,7 @@ Bun.serve({ You can pass any async iterable directly to `Response`: -```ts +```ts stream-iterator.ts icon="/icons/typescript.svg" Bun.serve({ port: 3000, fetch(req) { diff --git a/docs/guides/http/stream-node-streams-in-bun.md b/docs/guides/http/stream-node-streams-in-bun.mdx similarity index 80% rename from docs/guides/http/stream-node-streams-in-bun.md rename to docs/guides/http/stream-node-streams-in-bun.mdx index 0b5e31d744..b32f3263e0 100644 --- a/docs/guides/http/stream-node-streams-in-bun.md +++ b/docs/guides/http/stream-node-streams-in-bun.mdx @@ -1,12 +1,14 @@ --- -name: Streaming HTTP Server with Node.js Streams +title: Streaming HTTP Server with Node.js Streams +sidebarTitle: Stream with Node.js +mode: center --- In Bun, [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects can accept a Node.js [`Readable`](https://nodejs.org/api/stream.html#stream_readable_streams). This works because Bun's `Response` object allows any async iterable as its body. Node.js streams are async iterables, so you can pass them directly to `Response`. -```ts +```ts server.ts icon="/icons/typescript.svg" import { Readable } from "stream"; import { serve } from "bun"; serve({ diff --git a/docs/guides/http/tls.md b/docs/guides/http/tls.mdx similarity index 82% rename from docs/guides/http/tls.md rename to docs/guides/http/tls.mdx index d0882fadfe..14bb44c447 100644 --- a/docs/guides/http/tls.md +++ b/docs/guides/http/tls.mdx @@ -1,10 +1,12 @@ --- -name: Configure TLS on an HTTP server +title: Configure TLS on an HTTP server +sidebarTitle: Configure TLS +mode: center --- Set the `tls` key to configure TLS. Both `key` and `cert` are required. The `key` should be the contents of your private key; `cert` should be the contents of your issued certificate. Use [`Bun.file()`](https://bun.com/docs/api/file-io#reading-files-bun-file) to read the contents. -```ts +```ts server.ts icon="/icons/typescript.svg" const server = Bun.serve({ fetch: request => new Response("Welcome to Bun!"), tls: { @@ -18,7 +20,7 @@ const server = Bun.serve({ By default Bun trusts the default Mozilla-curated list of well-known root CAs. To override this list, pass an array of certificates as `ca`. -```ts +```ts server.ts icon="/icons/typescript.svg" const server = Bun.serve({ fetch: request => new Response("Welcome to Bun!"), tls: { diff --git a/docs/guides/index.mdx b/docs/guides/index.mdx new file mode 100644 index 0000000000..107c894c0a --- /dev/null +++ b/docs/guides/index.mdx @@ -0,0 +1,10 @@ +--- +title: Guides +description: A collection of code samples and walkthroughs for performing common tasks with Bun. +contextual: false +mode: center +--- + +import { GuidesList } from "/snippets/guides.jsx"; + + diff --git a/docs/guides/install/add-dev.md b/docs/guides/install/add-dev.mdx similarity index 61% rename from docs/guides/install/add-dev.md rename to docs/guides/install/add-dev.mdx index 8855c7e673..7e0a501f91 100644 --- a/docs/guides/install/add-dev.md +++ b/docs/guides/install/add-dev.mdx @@ -1,22 +1,24 @@ --- -name: Add a development dependency +title: Add a development dependency +sidebarTitle: Add a dev dependency +mode: center --- To add an npm package as a development dependency, use `bun add --development`. -```sh -$ bun add zod --dev -$ bun add zod -d # shorthand +```sh terminal icon="terminal" +bun add zod --dev +bun add zod -d # shorthand ``` --- This will add the package to `devDependencies` in `package.json`. -```json-diff +```json { "devDependencies": { -+ "zod": "^3.0.0" + "zod": "^3.0.0" // [!code ++] } } ``` diff --git a/docs/guides/install/add-git.md b/docs/guides/install/add-git.md deleted file mode 100644 index 388b696547..0000000000 --- a/docs/guides/install/add-git.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -name: Add a Git dependency ---- - -Bun supports directly adding GitHub repositories as dependencies of your project. - -```sh -$ bun add github:lodash/lodash -``` - ---- - -This will add the following line to your `package.json`: - -```json-diff#package.json -{ - "dependencies": { -+ "lodash": "github:lodash/lodash" - } -} -``` - ---- - -Bun supports a number of protocols for specifying Git dependencies. - -```sh -$ bun add git+https://github.com/lodash/lodash.git -$ bun add git+ssh://github.com/lodash/lodash.git#4.17.21 -$ bun add git@github.com:lodash/lodash.git -$ bun add github:colinhacks/zod -``` - ---- - -See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. diff --git a/docs/guides/install/add-git.mdx b/docs/guides/install/add-git.mdx new file mode 100644 index 0000000000..70950e1a63 --- /dev/null +++ b/docs/guides/install/add-git.mdx @@ -0,0 +1,38 @@ +--- +title: Add a Git dependency +sidebarTitle: Add a Git dependency +mode: center +--- + +Bun supports directly adding GitHub repositories as dependencies of your project. + +```sh terminal icon="terminal" +bun add github:lodash/lodash +``` + +--- + +This will add the following line to your `package.json`: + +```json package.json icon="file-json" +{ + "dependencies": { + "lodash": "github:lodash/lodash" + } +} +``` + +--- + +Bun supports a number of protocols for specifying Git dependencies. + +```sh terminal icon="terminal" +bun add git+https://github.com/lodash/lodash.git +bun add git+ssh://github.com/lodash/lodash.git#4.17.21 +bun add git@github.com:lodash/lodash.git +bun add github:colinhacks/zod +``` + +--- + +See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager. diff --git a/docs/guides/install/add-optional.md b/docs/guides/install/add-optional.mdx similarity index 60% rename from docs/guides/install/add-optional.md rename to docs/guides/install/add-optional.mdx index cbaf60e966..568ebeba4e 100644 --- a/docs/guides/install/add-optional.md +++ b/docs/guides/install/add-optional.mdx @@ -1,21 +1,23 @@ --- -name: Add an optional dependency +title: Add an optional dependency +sidebarTitle: Add an optional dependency +mode: center --- To add an npm package as an optional dependency, use the `--optional` flag. -```sh -$ bun add zod --optional +```sh terminal icon="terminal" +bun add zod --optional ``` --- This will add the package to `optionalDependencies` in `package.json`. -```json-diff +```json package.json icon="file-json" { "optionalDependencies": { -+ "zod": "^3.0.0" + "zod": "^3.0.0" // [!code ++] } } ``` diff --git a/docs/guides/install/add-peer.md b/docs/guides/install/add-peer.mdx similarity index 57% rename from docs/guides/install/add-peer.md rename to docs/guides/install/add-peer.mdx index a60f347a99..66b15174f2 100644 --- a/docs/guides/install/add-peer.md +++ b/docs/guides/install/add-peer.mdx @@ -1,21 +1,23 @@ --- -name: Add a peer dependency +title: Add a peer dependency +sidebarTitle: Add a peer dependency +mode: center --- To add an npm package as a peer dependency, use the `--peer` flag. -```sh -$ bun add @types/bun --peer +```sh terminal icon="terminal" +bun add @types/bun --peer ``` --- This will add the package to `peerDependencies` in `package.json`. -```json-diff +```json package.json icon="file-json" { "peerDependencies": { -+ "@types/bun": "^$BUN_LATEST_VERSION" + "@types/bun": "^1.3.1" // [!code ++] } } ``` @@ -24,17 +26,17 @@ This will add the package to `peerDependencies` in `package.json`. Running `bun install` will install peer dependencies by default, unless marked optional in `peerDependenciesMeta`. -```json-diff +```json package.json icon="file-json" { "peerDependencies": { - "@types/bun": "^$BUN_LATEST_VERSION" + "@types/bun": "^1.3.1" }, "peerDependenciesMeta": { -+ "@types/bun": { -+ "optional": true -+ } + "@types/bun": { + // [!code ++] + "optional": true // [!code ++] + } // [!code ++] } - } ``` diff --git a/docs/guides/install/add-tarball.md b/docs/guides/install/add-tarball.mdx similarity index 65% rename from docs/guides/install/add-tarball.md rename to docs/guides/install/add-tarball.mdx index 87aadb1fdb..36abdd66b0 100644 --- a/docs/guides/install/add-tarball.md +++ b/docs/guides/install/add-tarball.mdx @@ -1,21 +1,23 @@ --- -name: Add a tarball dependency +title: Add a tarball dependency +sidebarTitle: Add a tarball dependency +mode: center --- Bun's package manager can install any publicly available tarball URL as a dependency of your project. -```sh -$ bun add zod@https://registry.npmjs.org/zod/-/zod-3.21.4.tgz +```sh terminal icon="terminal" +bun add zod@https://registry.npmjs.org/zod/-/zod-3.21.4.tgz ``` --- Running this command will download, extract, and install the tarball to your project's `node_modules` directory. It will also add the following line to your `package.json`: -```json-diff#package.json +```json package.json icon="file-json" { "dependencies": { -+ "zod": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz" + "zod": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz" // [!code ++] } } ``` diff --git a/docs/guides/install/add.md b/docs/guides/install/add.mdx similarity index 68% rename from docs/guides/install/add.md rename to docs/guides/install/add.mdx index 06035586c4..bb69bd2753 100644 --- a/docs/guides/install/add.md +++ b/docs/guides/install/add.mdx @@ -1,21 +1,23 @@ --- -name: Add a dependency +title: Add a dependency +sidebarTitle: Add a dependency +mode: center --- To add an npm package as a dependency, use `bun add`. -```sh -$ bun add zod +```sh terminal icon="terminal" +bun add zod ``` --- This will add the package to `dependencies` in `package.json`. By default, the `^` range specifier will be used, to indicate that any future minor or patch versions are acceptable. -```json-diff +```json package.json icon="file-json" { "dependencies": { -+ "zod": "^3.0.0" + "zod": "^3.0.0" // [!code ++] } } ``` @@ -24,17 +26,17 @@ This will add the package to `dependencies` in `package.json`. By default, the ` To "pin" to an exact version of the package, use `--exact`. This will add the package to `dependencies` without the `^`, pinning your project to the exact version you installed. -```sh -$ bun add zod --exact +```sh terminal icon="terminal" +bun add zod --exact ``` --- To specify an exact version or a tag: -```sh -$ bun add zod@3.0.0 -$ bun add zod@next +```sh terminal icon="terminal" +bun add zod@3.0.0 +bun add zod@next ``` --- diff --git a/docs/guides/install/azure-artifacts.md b/docs/guides/install/azure-artifacts.mdx similarity index 76% rename from docs/guides/install/azure-artifacts.md rename to docs/guides/install/azure-artifacts.mdx index 155e5d4a27..31543ca634 100644 --- a/docs/guides/install/azure-artifacts.md +++ b/docs/guides/install/azure-artifacts.mdx @@ -1,10 +1,15 @@ --- -name: Using bun install with an Azure Artifacts npm registry +title: Using bun install with an Azure Artifacts npm registry +sidebarTitle: Azure Artifacts with Bun +mode: center --- -{% callout %} -In [Azure Artifact's](https://learn.microsoft.com/en-us/azure/devops/artifacts/npm/npmrc?view=azure-devops&tabs=windows%2Cclassic) instructions for `.npmrc`, they say to base64 encode the password. Do not do this for `bun install`. Bun will automatically base64 encode the password for you if needed. -{% /callout %} + + In [Azure + Artifact's](https://learn.microsoft.com/en-us/azure/devops/artifacts/npm/npmrc?view=azure-devops&tabs=windows%2Cclassic) + instructions for `.npmrc`, they say to base64 encode the password. Do not do this for `bun install`. Bun will + automatically base64 encode the password for you if needed. + [Azure Artifacts](https://azure.microsoft.com/en-us/products/devops/artifacts) is a package management system for Azure DevOps. It allows you to host your own private npm registry, npm packages, and other types of packages as well. @@ -16,7 +21,7 @@ In [Azure Artifact's](https://learn.microsoft.com/en-us/azure/devops/artifacts/n To use it with `bun install`, add a `bunfig.toml` file to your project with the following contents. Make sure to replace `my-azure-artifacts-user` with your Azure Artifacts username, such as `jarred1234`. -```toml#bunfig.toml +```toml bunfig.toml icon="settings" [install.registry] url = "https://pkgs.dev.azure.com/my-azure-artifacts-user/_packaging/my-azure-artifacts-user/npm/registry" username = "my-azure-artifacts-user" @@ -28,7 +33,7 @@ password = "$NPM_PASSWORD" Then assign your Azure Personal Access Token to the `NPM_PASSWORD` environment variable. Bun [automatically reads](https://bun.com/docs/runtime/env) `.env` files, so create a file called `.env` in your project root. There is no need to base-64 encode this token! Bun will do this for you. -```txt#.env +```txt .env icon="settings" NPM_PASSWORD= ``` @@ -40,7 +45,7 @@ NPM_PASSWORD= To configure Azure Artifacts without `bunfig.toml`, you can set the `NPM_CONFIG_REGISTRY` environment variable. The URL should include `:username` and `:_password` as query parameters. Replace `` and `` with the apprropriate values. -```bash#shell +```bash terminal icon="terminal" NPM_CONFIG_REGISTRY=https://pkgs.dev.azure.com/my-azure-artifacts-user/_packaging/my-azure-artifacts-user/npm/registry/:username=:_password= ``` @@ -52,15 +57,13 @@ NPM_CONFIG_REGISTRY=https://pkgs.dev.azure.com/my-azure-artifacts-user/_packagin In [Azure Artifact's](https://learn.microsoft.com/en-us/azure/devops/artifacts/npm/npmrc?view=azure-devops&tabs=windows%2Cclassic) instructions for `.npmrc`, they say to base64 encode the password. Do not do this for `bun install`. Bun will automatically base64 encode the password for you if needed. -{% callout %} -**Tip** — If it ends with `==`, it probably is base64 encoded. -{% /callout %} +**Tip** — If it ends with `==`, it probably is base64 encoded. --- To decode a base64-encoded password, open your browser console and run: -```js +```js browser icon="computer" atob(""); ``` @@ -68,6 +71,6 @@ atob(""); Alternatively, use the `base64` command line tool, but doing so means it may be saved in your terminal history which is not recommended: -```bash +```bash terminal icon="terminal" echo "base64-encoded-password" | base64 --decode ``` diff --git a/docs/guides/runtime/cicd.md b/docs/guides/install/cicd.mdx similarity index 56% rename from docs/guides/runtime/cicd.md rename to docs/guides/install/cicd.mdx index c536a4dc0d..8dd09475db 100644 --- a/docs/guides/runtime/cicd.md +++ b/docs/guides/install/cicd.mdx @@ -1,41 +1,41 @@ --- -name: Install and run Bun in GitHub Actions +title: Install dependencies with Bun in GitHub Actions +sidebarTitle: Install Bun in GitHub Actions +mode: center --- Use the official [`setup-bun`](https://github.com/oven-sh/setup-bun) GitHub Action to install `bun` in your GitHub Actions runner. -```yaml-diff#workflow.yml -name: my-workflow +```yaml workflow.yml icon="file-code" +title: my-workflow jobs: my-job: - name: my-job + title: my-job runs-on: ubuntu-latest steps: # ... - uses: actions/checkout@v4 -+ - uses: oven-sh/setup-bun@v2 + - uses: oven-sh/setup-bun@v2 // [!code ++] # run any `bun` or `bunx` command -+ - run: bun install -+ - run: bun index.ts -+ - run: bun run build + - run: bun install // [!code ++] ``` --- To specify a version of Bun to install: -```yaml-diff#workflow.yml -name: my-workflow +```yaml workflow.yml icon="file-code" +title: my-workflow jobs: my-job: - name: my-job + title: my-job runs-on: ubuntu-latest steps: # ... - uses: oven-sh/setup-bun@v2 -+ with: -+ bun-version: 1.2.0 # or "latest", "canary", + with: # [!code ++] + version: "latest" # or "canary" # [!code ++] ``` --- diff --git a/docs/guides/install/custom-registry.md b/docs/guides/install/custom-registry.mdx similarity index 75% rename from docs/guides/install/custom-registry.md rename to docs/guides/install/custom-registry.mdx index 1cda26cc75..ce5cd1426b 100644 --- a/docs/guides/install/custom-registry.md +++ b/docs/guides/install/custom-registry.mdx @@ -1,10 +1,12 @@ --- -name: Override the default npm registry for bun install +title: Override the default npm registry for bun install +sidebarTitle: Override the default npm registry +mode: center --- The default registry is `registry.npmjs.org`. This can be globally configured in `bunfig.toml`. -```toml#bunfig.toml +```toml bunfig.toml icon="settings" [install] # set default registry as a string registry = "https://registry.npmjs.org" @@ -13,14 +15,14 @@ registry = "https://registry.npmjs.org" registry = { url = "https://registry.npmjs.org", token = "123456" } # if needed, set a username/password -registry = "https://username:password@registry.npmjs.org" +registry = "https://usertitle:password@registry.npmjs.org" ``` --- Your `bunfig.toml` can reference environment variables. Bun automatically loads environment variables from `.env.local`, `.env.[NODE_ENV]`, and `.env`. See [Docs > Environment variables](https://bun.com/docs/runtime/env) for more information. -```toml#bunfig.toml +```toml bunfig.toml icon="settings" [install] registry = { url = "https://registry.npmjs.org", token = "$npm_token" } ``` diff --git a/docs/guides/install/from-npm-install-to-bun-install.md b/docs/guides/install/from-npm-install-to-bun-install.mdx similarity index 90% rename from docs/guides/install/from-npm-install-to-bun-install.md rename to docs/guides/install/from-npm-install-to-bun-install.mdx index c8aebecdb7..477eaa6c5c 100644 --- a/docs/guides/install/from-npm-install-to-bun-install.md +++ b/docs/guides/install/from-npm-install-to-bun-install.mdx @@ -1,5 +1,7 @@ --- -name: Migrate from npm install to bun install +title: Migrate from npm install to bun install +sidebarTitle: Migrate from npm to bun +mode: center --- `bun install` is a Node.js compatible npm client designed to be an incredibly fast successor to npm. @@ -11,18 +13,18 @@ We've put a lot of work into making sure that the migration path from `npm insta - **`.npmrc` compatible**: bun install reads npm registry configuration from npm's `.npmrc`, so you can use the same configuration for both npm and Bun. - **Hardlinks**: On Windows and Linux, `bun install` uses hardlinks to conserve disk space and install times. -```bash +```bash terminal icon="terminal" # It only takes one command to migrate -$ bun i +bun i # To add dependencies: -$ bun i @types/bun +bun i @types/bun # To add devDependencies: -$ bun i -d @types/bun +bun i -d @types/bun # To remove a dependency: -$ bun rm @types/bun +bun rm @types/bun ``` --- @@ -40,17 +42,17 @@ Run scripts from package.json, executables from `node_modules/.bin` (sort of lik When you use `bun run `, it will choose the locally-installed executable -```sh +```sh terminal icon="terminal" # Run a package.json script: -$ bun my-script -$ bun run my-script +bun my-script +bun run my-script # Run an executable in node_modules/.bin: -$ bun my-executable # such as tsc, esbuild, etc. -$ bun run my-executable +bun my-executable # such as tsc, esbuild, etc. +bun run my-executable # Run a JavaScript/TypeScript file: -$ bun ./index.ts +bun ./index.ts ``` --- @@ -61,7 +63,7 @@ $ bun ./index.ts In package.json, you can set `"workspaces"` to an array of relative paths. -```json#package.json +```json package.json icon="file-json" { "name": "my-app", "workspaces": ["packages/*", "apps/*"] @@ -74,8 +76,8 @@ In package.json, you can set `"workspaces"` to an array of relative paths. In Bun, the `--filter` flag accepts a glob pattern, and will run the command concurrently for all workspace packages with a `name` that matches the pattern, respecting dependency order. -```sh -$ bun --filter 'lib-*' my-script +```sh terminal icon="terminal" +bun --filter 'lib-*' my-script # instead of: # npm run --workspace lib-foo --workspace lib-bar my-script ``` @@ -86,21 +88,21 @@ $ bun --filter 'lib-*' my-script To update a dependency, you can use `bun update `. This will update the dependency to the latest version that satisfies the semver range specified in package.json. -```sh +```sh terminal icon="terminal" # Update a single dependency -$ bun update @types/bun +bun update @types/bun # Update all dependencies -$ bun update +bun update # Ignore semver, update to the latest version -$ bun update @types/bun --latest +bun update @types/bun --latest # Update a dependency to a specific version -$ bun update @types/bun@$BUN_LATEST_VERSION +bun update @types/bun@1.3.1 # Update all dependencies to the latest versions -$ bun update --latest +bun update --latest ``` --- @@ -109,8 +111,11 @@ $ bun update --latest To view outdated dependencies, run `bun outdated`. This is like `npm outdated` but with more compact output. -```sh -$ bun outdated +```sh terminal icon="terminal" +bun outdated +``` + +```txt ┌────────────────────────────────────────┬─────────┬────────┬────────┐ │ Package │ Current │ Update │ Latest │ ├────────────────────────────────────────┼─────────┼────────┼────────┤ @@ -144,18 +149,27 @@ $ bun outdated To list installed packages, you can use `bun pm ls`. This will list all the packages that are installed in the `node_modules` folder using Bun's lockfile as the source of truth. You can pass the `-a` flag to list all installed packages, including transitive dependencies. -```sh +```sh terminal icon="terminal" # List top-level installed packages: -$ bun pm ls +bun pm ls +``` + +```txt my-pkg node_modules (781) ├── @types/node@20.16.5 ├── @types/react@18.3.8 ├── @types/react-dom@18.3.0 ├── eslint@8.57.1 ├── eslint-config-next@14.2.8 +... +``` +```sh terminal icon="terminal" # List all installed packages: -$ bun pm ls -a +bun pm ls -a +``` + +```txt my-pkg node_modules ├── @alloc/quick-lru@5.2.0 ├── @isaacs/cliui@8.0.2 @@ -172,10 +186,12 @@ my-pkg node_modules To create a package tarball, you can use `bun pm pack`. This will create a tarball of the package in the current directory. -```sh +```sh terminal icon="terminal" # Create a tarball -$ bun pm pack +bun pm pack +``` +```txt Total files: 46 Shasum: 2ee19b6f0c6b001358449ca0eadead703f326216 Integrity: sha512-ZV0lzWTEkGAMz[...]Gl4f8lA9sl97g== @@ -191,12 +207,12 @@ If the package references `node` in the `#!/usr/bin/env node` shebang, `bun run` When you pass `--bun` to `bun run`, we create a symlink to the locally-installed Bun executable named `"node"` in a temporary directory and add that to your `PATH` for the duration of the script's execution. -```sh +```sh terminal icon="terminal" # Force using Bun's runtime instead of node -$ bun --bun my-script +bun --bun my-script # This also works: -$ bun run --bun my-script +bun run --bun my-script ``` --- @@ -205,10 +221,10 @@ $ bun run --bun my-script You can install packages globally using `bun i -g `. This will install into a `.bun/install/global/node_modules` folder inside your home directory by default. -```sh +```sh terminal icon="terminal" # Install a package globally -$ bun i -g eslint +bun i -g eslint # Run a globally-installed package without the `bun run` prefix -$ eslint --init +eslint --init ``` diff --git a/docs/guides/install/git-diff-bun-lockfile.md b/docs/guides/install/git-diff-bun-lockfile.mdx similarity index 54% rename from docs/guides/install/git-diff-bun-lockfile.md rename to docs/guides/install/git-diff-bun-lockfile.mdx index 32ab443021..b92fe38035 100644 --- a/docs/guides/install/git-diff-bun-lockfile.md +++ b/docs/guides/install/git-diff-bun-lockfile.mdx @@ -1,16 +1,19 @@ --- -name: Configure git to diff Bun's lockb lockfile +title: Configure git to diff Bun's lockb lockfile +sidebarTitle: Configure git to diff Bun's lockfile +mode: center --- -{% callout %} -Bun v1.1.39 introduced `bun.lock`, a JSONC formatted lockfile. `bun.lock` is human-readable and git-diffable without configuration, at no cost to performance. [**Learn more.**](https://bun.com/docs/install/lockfile#text-based-lockfile) -{% /callout %} + + Bun v1.1.39 introduced `bun.lock`, a JSONC formatted lockfile. `bun.lock` is human-readable and git-diffable without + configuration, at no cost to performance. [**Learn more.**](https://bun.com/docs/install/lockfile#text-based-lockfile) + --- To teach `git` how to generate a human-readable diff of Bun's binary lockfile format (`.lockb`), add the following to your local or global `.gitattributes` file: -```js +```js gitattributes icon="file-code" *.lockb binary diff=lockb ``` @@ -18,18 +21,18 @@ To teach `git` how to generate a human-readable diff of Bun's binary lockfile fo Then add the following to you local git config with: -```sh -$ git config diff.lockb.textconv bun -$ git config diff.lockb.binary true +```sh terminal icon="terminal" +git config diff.lockb.textconv bun +git config diff.lockb.binary true ``` --- To globally configure git to diff Bun's lockfile, add the following to your global git config with: -```sh -$ git config --global diff.lockb.textconv bun -$ git config --global diff.lockb.binary true +```sh terminal icon="terminal" +git config --global diff.lockb.textconv bun +git config --global diff.lockb.binary true ``` --- diff --git a/docs/guides/install/index.json b/docs/guides/install/index.json deleted file mode 100644 index 017ef2845d..0000000000 --- a/docs/guides/install/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Package manager", - "description": "A collection of guides for managing dependencies with Bun's package manager" -} diff --git a/docs/guides/install/jfrog-artifactory.md b/docs/guides/install/jfrog-artifactory.mdx similarity index 87% rename from docs/guides/install/jfrog-artifactory.md rename to docs/guides/install/jfrog-artifactory.mdx index e4872982bc..5ca52102ef 100644 --- a/docs/guides/install/jfrog-artifactory.md +++ b/docs/guides/install/jfrog-artifactory.mdx @@ -1,5 +1,7 @@ --- -name: Using bun install with Artifactory +title: Using bun install with Artifactory +sidebarTitle: JFrog Artifactory with Bun +mode: center --- [JFrog Artifactory](https://jfrog.com/artifactory/) is a package management system for npm, Docker, Maven, NuGet, Ruby, Helm, and more. It allows you to host your own private npm registry, npm packages, and other types of packages as well. @@ -12,7 +14,7 @@ To use it with `bun install`, add a `bunfig.toml` file to your project with the Make sure to replace `MY_SUBDOMAIN` with your JFrog Artifactory subdomain, such as `jarred1234` and MY_TOKEN with your JFrog Artifactory token. -```toml#bunfig.toml +```toml bunfig.toml icon="settings" [install.registry] url = "https://MY_SUBDOMAIN.jfrog.io/artifactory/api/npm/npm/_auth=MY_TOKEN" # Bun v1.0.3+ supports using an environment variable here @@ -24,5 +26,3 @@ url = "https://MY_SUBDOMAIN.jfrog.io/artifactory/api/npm/npm/_auth=MY_TOKEN" ### Configure with `$NPM_CONFIG_REGISTRY` Like with npm, you can use the `NPM_CONFIG_REGISTRY` environment variable to configure JFrog Artifactory with bun install. - ---- diff --git a/docs/guides/install/npm-alias.md b/docs/guides/install/npm-alias.mdx similarity index 59% rename from docs/guides/install/npm-alias.md rename to docs/guides/install/npm-alias.mdx index 45ff622c3b..e6201aeb4c 100644 --- a/docs/guides/install/npm-alias.md +++ b/docs/guides/install/npm-alias.mdx @@ -1,18 +1,20 @@ --- -name: Install a package under a different name +title: Install a package under a different name +sidebarTitle: Install with alias +mode: center --- To install an npm package under an alias: -```sh -$ bun add my-custom-name@npm:zod +```sh terminal icon="terminal" +bun add my-custom-name@npm:zod ``` --- The `zod` package can now be imported as `my-custom-name`. -```ts +```ts index.ts icon="/icons/typescript.svg" import { z } from "my-custom-name"; z.string(); diff --git a/docs/guides/install/registry-scope.md b/docs/guides/install/registry-scope.mdx similarity index 80% rename from docs/guides/install/registry-scope.md rename to docs/guides/install/registry-scope.mdx index 0dfb527aeb..1c9496a0d6 100644 --- a/docs/guides/install/registry-scope.md +++ b/docs/guides/install/registry-scope.mdx @@ -1,15 +1,17 @@ --- -name: Configure a private registry for an organization scope with bun install +title: Configure a private registry for an organization scope with bun install +sidebarTitle: Configure a scoped registry +mode: center --- Private registries can be configured using either [`.npmrc`](https://bun.com/docs/install/npmrc) or [`bunfig.toml`](https://bun.com/docs/runtime/bunfig#install-registry). While both are supported, we recommend using **bunfig.toml** for enhanced flexibility and Bun-specific options. To configure a registry for a particular npm scope: -```toml#bunfig.toml +```toml bunfig.toml icon="settings" [install.scopes] # as a string -"@myorg1" = "https://username:password@registry.myorg.com/" +"@myorg1" = "https://usertitle:password@registry.myorg.com/" # as an object with username/password # you can reference environment variables @@ -28,7 +30,7 @@ To configure a registry for a particular npm scope: Your `bunfig.toml` can reference environment variables. Bun automatically loads environment variables from `.env.local`, `.env.[NODE_ENV]`, and `.env`. See [Docs > Environment variables](https://bun.com/docs/runtime/env) for more information. -```toml#bunfig.toml +```toml bunfig.toml icon="settings" [install.scopes] "@myorg3" = { token = "$npm_token", url = "https://registry.myorg.com/" } ``` diff --git a/docs/guides/install/trusted.md b/docs/guides/install/trusted.mdx similarity index 57% rename from docs/guides/install/trusted.md rename to docs/guides/install/trusted.mdx index 692e799e99..49efb33e5f 100644 --- a/docs/guides/install/trusted.md +++ b/docs/guides/install/trusted.mdx @@ -1,12 +1,15 @@ --- -name: Add a trusted dependency +title: Add a trusted dependency +sidebarTitle: Add a trusted dependency +mode: center --- Unlike other npm clients, Bun does not execute arbitrary lifecycle scripts for installed dependencies, such as `postinstall` and `node-gyp` builds. These scripts represent a potential security risk, as they can execute arbitrary code on your machine. -{% callout %} -Bun includes a default allowlist of popular packages containing `postinstall` scripts that are known to be safe. You can see this list [here](https://github.com/oven-sh/bun/blob/main/src/install/default-trusted-dependencies.txt). -{% /callout %} + + Bun includes a default allowlist of popular packages containing `postinstall` scripts that are known to be safe. You + can see this list [here](https://github.com/oven-sh/bun/blob/main/src/install/default-trusted-dependencies.txt). + --- @@ -19,26 +22,27 @@ If you are seeing one of the following errors, you are probably trying to use a To allow Bun to execute lifecycle scripts for a specific package, add the package to `trustedDependencies` in your package.json file. You can do this automatically by running the command `bun pm trust `. -{% callout %} -Note that this only allows lifecycle scripts for the specific package listed in `trustedDependencies`, _not_ the dependencies of that dependency! -{% /callout %} + + Note that this only allows lifecycle scripts for the specific package listed in `trustedDependencies`, _not_ the + dependencies of that dependency! + -```json-diff - { - "name": "my-app", - "version": "1.0.0", -+ "trustedDependencies": ["my-trusted-package"] - } +```json package.json icon="file-json" +{ + "name": "my-app", + "version": "1.0.0", + "trustedDependencies": ["my-trusted-package"] // [!code ++] +} ``` --- Once this is added, run a fresh install. Bun will re-install your dependencies and properly install -```sh -$ rm -rf node_modules -$ rm bun.lock -$ bun install +```sh terminal icon="terminal" +rm -rf node_modules +rm bun.lock +bun install ``` --- diff --git a/docs/guides/install/workspaces.md b/docs/guides/install/workspaces.mdx similarity index 81% rename from docs/guides/install/workspaces.md rename to docs/guides/install/workspaces.mdx index d2aa9a75da..6e789d2eb1 100644 --- a/docs/guides/install/workspaces.md +++ b/docs/guides/install/workspaces.mdx @@ -1,5 +1,7 @@ --- -name: Configuring a monorepo using workspaces +title: Configuring a monorepo using workspaces +sidebarTitle: Workspaces with Bun +mode: center --- Bun's package manager supports npm `"workspaces"`. This allows you to split a codebase into multiple distinct "packages" that live in the same repository, can depend on each other, and (when possible) share a `node_modules` directory. @@ -10,13 +12,11 @@ Clone [this sample project](https://github.com/colinhacks/bun-workspaces) to exp The root `package.json` should not contain any `"dependencies"`, `"devDependencies"`, etc. Each individual package should be self-contained and declare its own dependencies. Similarly, it's conventional to declare `"private": true` to avoid accidentally publishing the root package to `npm`. -```json#package.json +```json package.json icon="file-json" { "name": "my-monorepo", "private": true, - "workspaces": [ - "packages/*" - ] + "workspaces": ["packages/*"] } ``` @@ -24,7 +24,7 @@ The root `package.json` should not contain any `"dependencies"`, `"devDependenci It's common to place all packages in a `packages` directory. The `"workspaces"` field in package.json supports glob patterns, so you can use `packages/*` to indicate that each subdirectory of `packages` should be considered separate _package_ (also known as a workspace). -```txt +```txt File Tree icon="folder-tree" . ├── package.json ├── node_modules @@ -39,11 +39,11 @@ It's common to place all packages in a `packages` directory. The `"workspaces"` To add dependencies between workspaces, use the `"workspace:*"` syntax. Here we're adding `stuff-a` as a dependency of `stuff-b`. -```json-diff#packages/stuff-b/package.json +```json packages/stuff-b/package.json icon="file-json" { "name": "stuff-b", "dependencies": { -+ "stuff-a": "workspace:*" + "stuff-a": "workspace:*" // [!code ++] } } ``` @@ -52,17 +52,17 @@ To add dependencies between workspaces, use the `"workspace:*"` syntax. Here we' Once added, run `bun install` from the project root to install dependencies for all workspaces. -```sh -$ bun install +```sh terminal icon="terminal" +bun install ``` --- To add npm dependencies to a particular workspace, just `cd` to the appropriate directory and run `bun add` commands as you would normally. Bun will detect that you are in a workspace and hoist the dependency as needed. -```sh -$ cd packages/stuff-a -$ bun add zod +```sh terminal icon="terminal" +cd packages/stuff-a +bun add zod ``` --- diff --git a/docs/guides/install/yarnlock.md b/docs/guides/install/yarnlock.mdx similarity index 61% rename from docs/guides/install/yarnlock.md rename to docs/guides/install/yarnlock.mdx index f5303c5164..534033bc1e 100644 --- a/docs/guides/install/yarnlock.md +++ b/docs/guides/install/yarnlock.mdx @@ -1,24 +1,27 @@ --- -name: Generate a yarn-compatible lockfile +title: Generate a yarn-compatible lockfile +sidebarTitle: Generate a yarn-compatible lockfile +mode: center --- -{% callout %} -Bun v1.1.39 introduced `bun.lock`, a JSONC formatted lockfile. `bun.lock` is human-readable and git-diffable without configuration, at no cost to performance. [**Learn more.**](https://bun.com/docs/install/lockfile#text-based-lockfile) -{% /callout %} + + Bun v1.1.39 introduced `bun.lock`, a JSONC formatted lockfile. `bun.lock` is human-readable and git-diffable without + configuration, at no cost to performance. [**Learn more.**](https://bun.com/docs/install/lockfile#text-based-lockfile) + --- Use the `--yarn` flag to generate a Yarn-compatible `yarn.lock` file (in addition to `bun.lock`). -```sh -$ bun install --yarn +```sh terminal icon="terminal" +bun install --yarn ``` --- To set this as the default behavior, add the following to your `bunfig.toml` file. -```toml#bunfig.toml +```toml bunfig.toml icon="settings" [install.lockfile] print = "yarn" ``` @@ -27,8 +30,11 @@ print = "yarn" To print a Yarn lockfile to your console without writing it to disk, "run" your `bun.lockb` with `bun`. -```sh -$ bun bun.lockb +```sh terminal icon="terminal" +bun bun.lockb +``` + +```txt # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 # bun ./bun.lockb --hash: 9BFBF11D86084AAB-9418b03ff880c569-390CE6459EACEC9A... diff --git a/docs/guides/process/argv.md b/docs/guides/process/argv.mdx similarity index 66% rename from docs/guides/process/argv.md rename to docs/guides/process/argv.mdx index 7e3a60e564..4395aff478 100644 --- a/docs/guides/process/argv.md +++ b/docs/guides/process/argv.mdx @@ -1,10 +1,12 @@ --- -name: Parse command-line arguments +title: Parse command-line arguments +sidebarTitle: Parse command-line arguments +mode: center --- The _argument vector_ is the list of arguments passed to the program when it is run. It is available as `Bun.argv`. -```ts#cli.ts +```ts cli.ts icon="/icons/typescript.svg" console.log(Bun.argv); ``` @@ -12,8 +14,11 @@ console.log(Bun.argv); Running this file with arguments results in the following: -```sh -$ bun run cli.ts --flag1 --flag2 value +```sh terminal icon="terminal" +bun run cli.ts --flag1 --flag2 value +``` + +```txt [ '/path/to/bun', '/path/to/cli.ts', '--flag1', '--flag2', 'value' ] ``` @@ -23,17 +28,17 @@ To parse `argv` into a more useful format, `util.parseArgs` would be helpful. Example: -```ts#cli.ts +```ts cli.ts icon="/icons/typescript.svg" import { parseArgs } from "util"; const { values, positionals } = parseArgs({ args: Bun.argv, options: { flag1: { - type: 'boolean', + type: "boolean", }, flag2: { - type: 'string', + type: "string", }, }, strict: true, @@ -48,8 +53,11 @@ console.log(positionals); then it outputs -```sh -$ bun run cli.ts --flag1 --flag2 value +```sh terminal icon="terminal" +bun run cli.ts --flag1 --flag2 value +``` + +```txt { flag1: true, flag2: "value", diff --git a/docs/guides/process/ctrl-c.md b/docs/guides/process/ctrl-c.mdx similarity index 77% rename from docs/guides/process/ctrl-c.md rename to docs/guides/process/ctrl-c.mdx index ab3c5f9d4b..cffee3085f 100644 --- a/docs/guides/process/ctrl-c.md +++ b/docs/guides/process/ctrl-c.mdx @@ -1,10 +1,12 @@ --- -name: Listen for CTRL+C +title: Listen for CTRL+C +sidebarTitle: Listen for CTRL+C +mode: center --- The `ctrl+c` shortcut sends an _interrupt signal_ to the running process. This signal can be intercepted by listening for the `SIGINT` event. If you want to close the process, you must explicitly call `process.exit()`. -```ts +```ts process.ts icon="/icons/typescript.svg" process.on("SIGINT", () => { console.log("Ctrl-C was pressed"); process.exit(); diff --git a/docs/guides/process/index.json b/docs/guides/process/index.json deleted file mode 100644 index df0384544b..0000000000 --- a/docs/guides/process/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Processes", - "description": "A collection of guides for inspecting the current process and creating child processes with Bun" -} diff --git a/docs/guides/process/ipc.md b/docs/guides/process/ipc.mdx similarity index 76% rename from docs/guides/process/ipc.md rename to docs/guides/process/ipc.mdx index 4ca95c7625..1df59fcb04 100644 --- a/docs/guides/process/ipc.md +++ b/docs/guides/process/ipc.mdx @@ -1,14 +1,17 @@ --- -name: Spawn a child process and communicate using IPC +title: Spawn a child process and communicate using IPC +sidebarTitle: Spawn a child process and communicate using IPC +mode: center --- Use [`Bun.spawn()`](https://bun.com/docs/api/spawn) to spawn a child process. When spawning a second `bun` process, you can open a direct inter-process communication (IPC) channel between the two processes. -{%callout%} -**Note** — This API is only compatible with other `bun` processes. Use `process.execPath` to get a path to the currently running `bun` executable. -{%/callout%} + + This API is only compatible with other `bun` processes. Use `process.execPath` to get a path to the currently running + `bun` executable. + -```ts#parent.ts +```ts parent.ts icon="/icons/typescript.svg" const child = Bun.spawn(["bun", "child.ts"], { ipc(message) { /** @@ -22,13 +25,13 @@ const child = Bun.spawn(["bun", "child.ts"], { The parent process can send messages to the subprocess using the `.send()` method on the returned `Subprocess` instance. A reference to the sending subprocess is also available as the second argument in the `ipc` handler. -```ts#parent.ts +```ts parent.ts icon="/icons/typescript.svg" const childProc = Bun.spawn(["bun", "child.ts"], { ipc(message, childProc) { /** * The message received from the sub process **/ - childProc.send("Respond to child") + childProc.send("Respond to child"); }, }); @@ -39,11 +42,11 @@ childProc.send("I am your father"); // The parent can send messages to the child Meanwhile the child process can send messages to its parent using with `process.send()` and receive messages with `process.on("message")`. This is the same API used for `child_process.fork()` in Node.js. -```ts#child.ts +```ts child.ts icon="/icons/typescript.svg" process.send("Hello from child as string"); process.send({ message: "Hello from child as object" }); -process.on("message", (message) => { +process.on("message", message => { // print message from parent console.log(message); }); @@ -53,7 +56,7 @@ process.on("message", (message) => { All messages are serialized using the JSC `serialize` API, which allows for the same set of [transferrable types](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects) supported by `postMessage` and `structuredClone`, including strings, typed arrays, streams, and objects. -```ts#child.ts +```ts child.ts icon="/icons/typescript.svg" // send a string process.send("Hello from child as string"); diff --git a/docs/guides/process/nanoseconds.md b/docs/guides/process/nanoseconds.mdx similarity index 72% rename from docs/guides/process/nanoseconds.md rename to docs/guides/process/nanoseconds.mdx index ab3d9822ab..8f974a84fb 100644 --- a/docs/guides/process/nanoseconds.md +++ b/docs/guides/process/nanoseconds.mdx @@ -1,5 +1,7 @@ --- -name: Get the process uptime in nanoseconds +title: Get the process uptime in nanoseconds +sidebarTitle: Process uptime +mode: center --- Use `Bun.nanoseconds()` to get the total number of nanoseconds the `bun` process has been alive. diff --git a/docs/guides/process/os-signals.md b/docs/guides/process/os-signals.mdx similarity index 92% rename from docs/guides/process/os-signals.md rename to docs/guides/process/os-signals.mdx index 51cfbdae5a..eff7d3f0ab 100644 --- a/docs/guides/process/os-signals.md +++ b/docs/guides/process/os-signals.mdx @@ -1,5 +1,7 @@ --- -name: Listen to OS signals +title: Listen to OS signals +sidebarTitle: OS signals +mode: center --- Bun supports the Node.js `process` global, including the `process.on()` method for listening to OS signals. diff --git a/docs/guides/process/spawn-stderr.md b/docs/guides/process/spawn-stderr.mdx similarity index 89% rename from docs/guides/process/spawn-stderr.md rename to docs/guides/process/spawn-stderr.mdx index c5bcbd072c..cadfdf0f13 100644 --- a/docs/guides/process/spawn-stderr.md +++ b/docs/guides/process/spawn-stderr.mdx @@ -1,5 +1,7 @@ --- -name: Read stderr from a child process +title: Read stderr from a child process +sidebarTitle: Read stderr +mode: center --- When using [`Bun.spawn()`](https://bun.com/docs/api/spawn), the child process inherits the `stderr` of the spawning process. If instead you'd prefer to read and handle `stderr`, set the `stderr` option to `"pipe"`. @@ -8,6 +10,7 @@ When using [`Bun.spawn()`](https://bun.com/docs/api/spawn), the child process in const proc = Bun.spawn(["echo", "hello"], { stderr: "pipe", }); + proc.stderr; // => ReadableStream ``` diff --git a/docs/guides/process/spawn-stdout.md b/docs/guides/process/spawn-stdout.mdx similarity index 87% rename from docs/guides/process/spawn-stdout.md rename to docs/guides/process/spawn-stdout.mdx index 3afcbd5062..ab64f2a2cf 100644 --- a/docs/guides/process/spawn-stdout.md +++ b/docs/guides/process/spawn-stdout.mdx @@ -1,5 +1,7 @@ --- -name: Read stdout from a child process +title: Read stdout from a child process +sidebarTitle: Read stdout +mode: center --- When using [`Bun.spawn()`](https://bun.com/docs/api/spawn), the `stdout` of the child process can be consumed as a `ReadableStream` via `proc.stdout`. diff --git a/docs/guides/process/spawn.md b/docs/guides/process/spawn.mdx similarity index 90% rename from docs/guides/process/spawn.md rename to docs/guides/process/spawn.mdx index e9a992ed7e..a03ac5eaea 100644 --- a/docs/guides/process/spawn.md +++ b/docs/guides/process/spawn.mdx @@ -1,5 +1,7 @@ --- -name: Spawn a child process +title: Spawn a child process +sidebarTitle: Spawn child process +mode: center --- Use [`Bun.spawn()`](https://bun.com/docs/api/spawn) to spawn a child process. diff --git a/docs/guides/process/stdin.md b/docs/guides/process/stdin.mdx similarity index 79% rename from docs/guides/process/stdin.md rename to docs/guides/process/stdin.mdx index d7e297df11..b9fb18f614 100644 --- a/docs/guides/process/stdin.md +++ b/docs/guides/process/stdin.mdx @@ -1,10 +1,12 @@ --- -name: Read from stdin +title: Read from stdin +sidebarTitle: Read from stdin +mode: center --- For CLI tools, it's often useful to read from `stdin`. In Bun, the `console` object is an `AsyncIterable` that yields lines from `stdin`. -```ts#index.ts +```ts index.ts icon="/icons/typescript.svg" const prompt = "Type something: "; process.stdout.write(prompt); for await (const line of console) { @@ -17,8 +19,11 @@ for await (const line of console) { Running this file results in a never-ending interactive prompt that echoes whatever the user types. -```sh -$ bun run index.ts +```sh terminal icon="terminal" +bun run index.ts +``` + +```txt Type something: hello You typed: hello Type something: hello again @@ -31,7 +36,7 @@ Bun also exposes stdin as a `BunFile` via `Bun.stdin`. This is useful for increm There is no guarantee that the chunks will be split line-by-line. -```ts#stdin.ts +```ts stdin.ts icon="/icons/typescript.svg" for await (const chunk of Bun.stdin.stream()) { // chunk is Uint8Array // this converts it to text (assumes ASCII encoding) @@ -44,8 +49,11 @@ for await (const chunk of Bun.stdin.stream()) { This will print the input that is piped into the `bun` process. -```sh -$ echo "hello" | bun run stdin.ts +```sh terminal icon="terminal" +echo "hello" | bun run stdin.ts +``` + +```txt Chunk: hello ``` diff --git a/docs/guides/read-file/arraybuffer.md b/docs/guides/read-file/arraybuffer.mdx similarity index 81% rename from docs/guides/read-file/arraybuffer.md rename to docs/guides/read-file/arraybuffer.mdx index 7bbd5d438d..563ca71d1d 100644 --- a/docs/guides/read-file/arraybuffer.md +++ b/docs/guides/read-file/arraybuffer.mdx @@ -1,10 +1,12 @@ --- -name: Read a file to an ArrayBuffer +title: Read a file to an ArrayBuffer +sidebarTitle: Read to ArrayBuffer +mode: center --- The `Bun.file()` function accepts a path and returns a `BunFile` instance. The `BunFile` class extends `Blob` and allows you to lazily read the file in a variety of formats. Use `.arrayBuffer()` to read the file as an `ArrayBuffer`. -```ts +```ts index.ts icon="/icons/typescript.svg" const path = "/path/to/package.json"; const file = Bun.file(path); @@ -15,7 +17,7 @@ const buffer = await file.arrayBuffer(); The binary content in the `ArrayBuffer` can then be read as a typed array, such as `Int8Array`. For `Uint8Array`, use [`.bytes()`](./uint8array). -```ts +```ts index.ts icon="/icons/typescript.svg" const buffer = await file.arrayBuffer(); const bytes = new Int8Array(buffer); diff --git a/docs/guides/read-file/buffer.md b/docs/guides/read-file/buffer.mdx similarity index 85% rename from docs/guides/read-file/buffer.md rename to docs/guides/read-file/buffer.mdx index 784b89182d..5b24eda0a4 100644 --- a/docs/guides/read-file/buffer.md +++ b/docs/guides/read-file/buffer.mdx @@ -1,12 +1,14 @@ --- -name: Read a file to a Buffer +title: Read a file to a Buffer +sidebarTitle: Read to Buffer +mode: center --- The `Bun.file()` function accepts a path and returns a `BunFile` instance. The `BunFile` class extends `Blob` and allows you to lazily read the file in a variety of formats. To read the file into a `Buffer` instance, first use `.arrayBuffer()` to consume the file as an `ArrayBuffer`, then use `Buffer.from()` to create a `Buffer` from the `ArrayBuffer`. -```ts +```ts index.ts icon="/icons/typescript.svg" const path = "/path/to/package.json"; const file = Bun.file(path); diff --git a/docs/guides/read-file/exists.md b/docs/guides/read-file/exists.mdx similarity index 75% rename from docs/guides/read-file/exists.md rename to docs/guides/read-file/exists.mdx index b7ef178bd0..320e659252 100644 --- a/docs/guides/read-file/exists.md +++ b/docs/guides/read-file/exists.mdx @@ -1,10 +1,12 @@ --- -name: Check if a file exists +title: Check if a file exists +sidebarTitle: Check file exists +mode: center --- The `Bun.file()` function accepts a path and returns a `BunFile` instance. Use the `.exists()` method to check if a file exists at the given path. -```ts +```ts index.ts icon="/icons/typescript.svg" const path = "/path/to/package.json"; const file = Bun.file(path); diff --git a/docs/guides/read-file/index.json b/docs/guides/read-file/index.json deleted file mode 100644 index 321fd96e29..0000000000 --- a/docs/guides/read-file/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Reading files", - "description": "A collection of guides for reading files with Bun" -} diff --git a/docs/guides/read-file/json.md b/docs/guides/read-file/json.mdx similarity index 82% rename from docs/guides/read-file/json.md rename to docs/guides/read-file/json.mdx index e14fd560af..e599afa0b8 100644 --- a/docs/guides/read-file/json.md +++ b/docs/guides/read-file/json.mdx @@ -1,12 +1,14 @@ --- -name: Read a JSON file +title: Read a JSON file +sidebarTitle: Read JSON file +mode: center --- The `Bun.file()` function accepts a path and returns a `BunFile` instance. The `BunFile` class extends `Blob` and allows you to lazily read the file in a variety of formats. Use `.json()` to read and parse the contents of a `.json` file as a plain object. The MIME type of the `BunFile` will be set accordingly. -```ts +```ts index.ts icon="/icons/typescript.svg" const path = "/path/to/package.json"; const file = Bun.file(path); diff --git a/docs/guides/read-file/mime.md b/docs/guides/read-file/mime.mdx similarity index 86% rename from docs/guides/read-file/mime.md rename to docs/guides/read-file/mime.mdx index debd581941..c3a369a013 100644 --- a/docs/guides/read-file/mime.md +++ b/docs/guides/read-file/mime.mdx @@ -1,5 +1,7 @@ --- -name: Get the MIME type of a file +title: Get the MIME type of a file +sidebarTitle: Get MIME type +mode: center --- The `Bun.file()` function accepts a path and returns a `BunFile` instance. The `BunFile` class extends `Blob`, so use the `.type` property to read the MIME type. diff --git a/docs/guides/read-file/stream.md b/docs/guides/read-file/stream.mdx similarity index 90% rename from docs/guides/read-file/stream.md rename to docs/guides/read-file/stream.mdx index 632000ec5a..2724ad2f6a 100644 --- a/docs/guides/read-file/stream.md +++ b/docs/guides/read-file/stream.mdx @@ -1,5 +1,7 @@ --- -name: Read a file as a ReadableStream +title: Read a file as a ReadableStream +sidebarTitle: Read as stream +mode: center --- The `Bun.file()` function accepts a path and returns a `BunFile` instance. The `BunFile` class extends `Blob` and allows you to lazily read the file in a variety of formats. Use `.stream()` to consume the file incrementally as a `ReadableStream`. diff --git a/docs/guides/read-file/string.md b/docs/guides/read-file/string.mdx similarity index 88% rename from docs/guides/read-file/string.md rename to docs/guides/read-file/string.mdx index 08cdfd9522..9d26535db5 100644 --- a/docs/guides/read-file/string.md +++ b/docs/guides/read-file/string.mdx @@ -1,5 +1,7 @@ --- -name: Read a file as a string +title: Read a file as a string +sidebarTitle: Read as string +mode: center --- The `Bun.file()` function accepts a path and returns a `BunFile` instance. The `BunFile` class extends `Blob` and allows you to lazily read the file in a variety of formats. Use `.text()` to read the contents as a string. diff --git a/docs/guides/read-file/uint8array.md b/docs/guides/read-file/uint8array.mdx similarity index 89% rename from docs/guides/read-file/uint8array.md rename to docs/guides/read-file/uint8array.mdx index dcabdc1646..53c902e682 100644 --- a/docs/guides/read-file/uint8array.md +++ b/docs/guides/read-file/uint8array.mdx @@ -1,5 +1,7 @@ --- -name: Read a file to a Uint8Array +title: Read a file to a Uint8Array +sidebarTitle: Read to Uint8Array +mode: center --- The `Bun.file()` function accepts a path and returns a `BunFile` instance. The `BunFile` class extends `Blob` and allows you to lazily read the file in a variety of formats. diff --git a/docs/guides/read-file/watch.md b/docs/guides/read-file/watch.mdx similarity index 87% rename from docs/guides/read-file/watch.md rename to docs/guides/read-file/watch.mdx index c1a792903f..ab27043c88 100644 --- a/docs/guides/read-file/watch.md +++ b/docs/guides/read-file/watch.mdx @@ -1,5 +1,7 @@ --- -name: Watch a directory for changes +title: Watch a directory for changes +sidebarTitle: Watch directory +mode: center --- Bun implements the `node:fs` module, including the `fs.watch` function for listening for file system changes. @@ -21,13 +23,9 @@ To listen to changes in subdirectories, pass the `recursive: true` option to `fs ```ts import { watch } from "fs"; -const watcher = watch( - import.meta.dir, - { recursive: true }, - (event, relativePath) => { - console.log(`Detected ${event} in ${relativePath}`); - }, -); +const watcher = watch(import.meta.dir, { recursive: true }, (event, filename) => { + console.log(`Detected ${event} in ${filename}`); +}); ``` --- diff --git a/docs/guides/runtime/build-time-constants.md b/docs/guides/runtime/build-time-constants.mdx similarity index 80% rename from docs/guides/runtime/build-time-constants.md rename to docs/guides/runtime/build-time-constants.mdx index 511e473ba8..f6a122b168 100644 --- a/docs/guides/runtime/build-time-constants.md +++ b/docs/guides/runtime/build-time-constants.mdx @@ -1,11 +1,13 @@ --- -name: Build-time constants with --define +title: Build-time constants with --define +sidebarTitle: Build-time constants +mode: center --- The `--define` flag can be used with `bun build` and `bun build --compile` to inject build-time constants into your application. This is especially useful for embedding metadata like build versions, timestamps, or configuration flags directly into your compiled executables. -```sh -$ bun build --compile --define BUILD_VERSION='"1.2.3"' --define BUILD_TIME='"2024-01-15T10:30:00Z"' src/index.ts --outfile myapp +```sh terminal icon="terminal" +bun build --compile --define BUILD_VERSION='"1.2.3"' --define BUILD_TIME='"2024-01-15T10:30:00Z"' src/index.ts --outfile myapp ``` --- @@ -27,21 +29,21 @@ This is similar to `gcc -D` or `#define` in C/C++, but for JavaScript/TypeScript ### With `bun build` -```sh +```sh terminal icon="terminal" # Bundle with build-time constants -$ bun build --define BUILD_VERSION='"1.0.0"' --define NODE_ENV='"production"' src/index.ts --outdir ./dist +bun build --define BUILD_VERSION='"1.0.0"' --define NODE_ENV='"production"' src/index.ts --outdir ./dist ``` ### With `bun build --compile` -```sh +```sh terminal icon="terminal" # Compile to executable with build-time constants -$ bun build --compile --define BUILD_VERSION='"1.0.0"' --define BUILD_TIME='"2024-01-15T10:30:00Z"' src/cli.ts --outfile mycli +bun build --compile --define BUILD_VERSION='"1.0.0"' --define BUILD_TIME='"2024-01-15T10:30:00Z"' src/cli.ts --outfile mycli ``` ### JavaScript API -```ts +```ts build.ts icon="/icons/typescript.svg" await Bun.build({ entrypoints: ["./src/index.ts"], outdir: "./dist", @@ -61,9 +63,9 @@ await Bun.build({ Embed version and build metadata directly into your executable: -{% codetabs %} + -```ts#src/version.ts +```ts src/version.ts icon="/icons/typescript.svg" // These constants are replaced at build time declare const BUILD_VERSION: string; declare const BUILD_TIME: string; @@ -78,21 +80,21 @@ export function getVersion() { } ``` -```sh#Build command -$ bun build --compile \ +```sh Build command +bun build --compile \ --define BUILD_VERSION='"1.2.3"' \ --define BUILD_TIME='"2024-01-15T10:30:00Z"' \ --define GIT_COMMIT='"abc123"' \ src/cli.ts --outfile mycli ``` -{% /codetabs %} + ### Feature flags Use build-time constants to enable/disable features: -```ts +```ts src/version.ts icon="/icons/typescript.svg" // Replaced at build time declare const ENABLE_ANALYTICS: boolean; declare const ENABLE_DEBUG: boolean; @@ -111,17 +113,17 @@ if (ENABLE_DEBUG) { ```sh # Production build - analytics enabled, debug disabled -$ bun build --compile --define ENABLE_ANALYTICS=true --define ENABLE_DEBUG=false src/app.ts --outfile app-prod +bun build --compile --define ENABLE_ANALYTICS=true --define ENABLE_DEBUG=false src/app.ts --outfile app-prod # Development build - both enabled -$ bun build --compile --define ENABLE_ANALYTICS=false --define ENABLE_DEBUG=true src/app.ts --outfile app-dev +bun build --compile --define ENABLE_ANALYTICS=false --define ENABLE_DEBUG=true src/app.ts --outfile app-dev ``` ### Configuration Replace configuration objects at build time: -```ts +```ts src/version.ts icon="/icons/typescript.svg" declare const CONFIG: { apiUrl: string; timeout: number; @@ -135,7 +137,7 @@ const response = await fetch(CONFIG.apiUrl, { ``` ```sh -$ bun build --compile --define 'CONFIG={"apiUrl":"https://api.example.com","timeout":5000,"retries":3}' src/app.ts --outfile app +bun build --compile --define 'CONFIG={"apiUrl":"https://api.example.com","timeout":5000,"retries":3}' src/app.ts --outfile app ``` --- @@ -162,7 +164,7 @@ Generate build-time constants from shell commands: ```sh # Use git to get current commit and timestamp -$ bun build --compile \ +bun build --compile \ --define BUILD_VERSION="\"$(git describe --tags --always)\"" \ --define BUILD_TIME="\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"" \ --define GIT_COMMIT="\"$(git rev-parse HEAD)\"" \ @@ -275,13 +277,13 @@ When building for multiple platforms, constants work the same way: ```sh # Linux -$ bun build --compile --target=bun-linux-x64 --define PLATFORM='"linux"' src/app.ts --outfile app-linux +bun build --compile --target=bun-linux-x64 --define PLATFORM='"linux"' src/app.ts --outfile app-linux # macOS -$ bun build --compile --target=bun-darwin-x64 --define PLATFORM='"darwin"' src/app.ts --outfile app-macos +bun build --compile --target=bun-darwin-x64 --define PLATFORM='"darwin"' src/app.ts --outfile app-macos # Windows -$ bun build --compile --target=bun-windows-x64 --define PLATFORM='"windows"' src/app.ts --outfile app-windows.exe +bun build --compile --target=bun-windows-x64 --define PLATFORM='"windows"' src/app.ts --outfile app-windows.exe ``` --- diff --git a/docs/guides/install/cicd.md b/docs/guides/runtime/cicd.mdx similarity index 59% rename from docs/guides/install/cicd.md rename to docs/guides/runtime/cicd.mdx index 1e5b60b711..6c68246f2e 100644 --- a/docs/guides/install/cicd.md +++ b/docs/guides/runtime/cicd.mdx @@ -1,10 +1,12 @@ --- -name: Install dependencies with Bun in GitHub Actions +title: Install and run Bun in GitHub Actions +sidebarTitle: GitHub Actions +mode: center --- Use the official [`setup-bun`](https://github.com/oven-sh/setup-bun) GitHub Action to install `bun` in your GitHub Actions runner. -```yaml-diff#workflow.yml +```yaml workflow.yml icon="file-code" name: my-workflow jobs: my-job: @@ -13,17 +15,19 @@ jobs: steps: # ... - uses: actions/checkout@v4 -+ - uses: oven-sh/setup-bun@v2 + - uses: oven-sh/setup-bun@v2 // [!code ++] # run any `bun` or `bunx` command -+ - run: bun install + - run: bun install // [!code ++] + - run: bun index.ts // [!code ++] + - run: bun run build // [!code ++] ``` --- To specify a version of Bun to install: -```yaml-diff#workflow.yml +```yaml workflow.yml icon="file-code" name: my-workflow jobs: my-job: @@ -32,8 +36,8 @@ jobs: steps: # ... - uses: oven-sh/setup-bun@v2 -+ with: -+ version: "latest" # or "canary" + with: // [!code ++] + bun-version: 1.2.0 # or "latest", "canary", // [!code ++] ``` --- diff --git a/docs/guides/runtime/codesign-macos-executable.md b/docs/guides/runtime/codesign-macos-executable.mdx similarity index 72% rename from docs/guides/runtime/codesign-macos-executable.md rename to docs/guides/runtime/codesign-macos-executable.mdx index 308506f48e..553856024f 100644 --- a/docs/guides/runtime/codesign-macos-executable.md +++ b/docs/guides/runtime/codesign-macos-executable.mdx @@ -1,20 +1,25 @@ --- -name: Codesign a single-file JavaScript executable on macOS +title: Codesign a single-file JavaScript executable on macOS description: Fix the "can't be opened because it is from an unidentified developer" Gatekeeper warning when running your JavaScript executable. +sidebarTitle: Codesign on macOS +mode: center --- Compile your executable using the `--compile` flag. ```sh -$ bun build --compile ./path/to/entry.ts --outfile myapp +bun build --compile ./path/to/entry.ts --outfile myapp ``` --- List your available signing identities. One of these will be your signing identity that you pass to the `codesign` command. This command requires macOS. -```sh -$ security find-identity -v -p codesigning +```sh terminal icon="terminal" +security find-identity -v -p codesigning +``` + +```txt 1. XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX "Developer ID Application: Your Name (ZZZZZZZZZZ)" 1 valid identities found ``` @@ -23,7 +28,7 @@ $ security find-identity -v -p codesigning Optional, but recommended: create an `entitlements.plist` file with the necessary permissions for the JavaScript engine to work correctly. -```xml#entitlements.plist +```xml entitlements.plist icon="file-code" @@ -46,11 +51,11 @@ Optional, but recommended: create an `entitlements.plist` file with the necessar Sign your executable using the `codesign` command and verify it works. -```bash -$ codesign --entitlements entitlements.plist -vvvv --deep --sign "XXXXXXXXXX" ./myapp --force -$ codesign -vvv --verify ./myapp +```bash terminal icon="terminal" +codesign --entitlements entitlements.plist -vvvv --deep --sign "XXXXXXXXXX" ./myapp --force +codesign -vvv --verify ./myapp ``` --- -For more information on macOS codesigning, refer to [Apple's Code Signing documentation](https://developer.apple.com/documentation/security/code_signing_services). For details about creating single-file executables with Bun, see [Standalone Executables](/docs/bundler/executables). This guide requires Bun v1.2.4 or newer. +For more information on macOS codesigning, refer to [Apple's Code Signing documentation](https://developer.apple.com/documentation/security/code_signing_services). For details about creating single-file executables with Bun, see [Standalone Executables](/bundler/executables). This guide requires Bun v1.2.4 or newer. diff --git a/docs/guides/runtime/define-constant.md b/docs/guides/runtime/define-constant.mdx similarity index 87% rename from docs/guides/runtime/define-constant.md rename to docs/guides/runtime/define-constant.mdx index 8915dc4501..ad9c24f2ed 100644 --- a/docs/guides/runtime/define-constant.md +++ b/docs/guides/runtime/define-constant.mdx @@ -1,12 +1,14 @@ --- -name: Define and replace static globals & constants +title: Define and replace static globals & constants +sidebarTitle: Define constants +mode: center --- The `--define` flag lets you declare statically-analyzable constants and globals. It replace all usages of an identifier or property in a JavaScript or TypeScript file with a constant value. This feature is supported at runtime and also in `bun build`. This is sort of similar to `#define` in C/C++, except for JavaScript. -```sh -$ bun --define process.env.NODE_ENV="'production'" src/index.ts # Runtime -$ bun build --define process.env.NODE_ENV="'production'" src/index.ts # Build +```sh terminal icon="terminal" +bun --define process.env.NODE_ENV="'production'" src/index.ts # Runtime +bun build --define process.env.NODE_ENV="'production'" src/index.ts # Build ``` --- @@ -25,12 +27,13 @@ if (process.env.NODE_ENV === "production") { Before the code reaches the JavaScript engine, Bun replaces `process.env.NODE_ENV` with `"production"`. -```ts-diff -+ if ("production" === "production") { - console.log("Production mode"); - } else { - console.log("Development mode"); - } +```ts +if ("production" === "production") { + // [!code ++] + console.log("Production mode"); +} else { + console.log("Development mode"); +} ``` --- @@ -39,12 +42,13 @@ It doesn't stop there. Bun's optimizing transpiler is smart enough to do some ba Since `"production" === "production"` is always `true`, Bun replaces the entire expression with the `true` value. -```ts-diff -+ if (true) { - console.log("Production mode"); - } else { - console.log("Development mode"); - } +```ts +if (true) { + // [!code ++] + console.log("Production mode"); +} else { + console.log("Development mode"); +} ``` --- diff --git a/docs/guides/runtime/delete-directory.md b/docs/guides/runtime/delete-directory.mdx similarity index 82% rename from docs/guides/runtime/delete-directory.md rename to docs/guides/runtime/delete-directory.mdx index 3ffa344ba3..bcc8055051 100644 --- a/docs/guides/runtime/delete-directory.md +++ b/docs/guides/runtime/delete-directory.mdx @@ -1,10 +1,12 @@ --- -name: Delete directories +title: Delete directories +sidebarTitle: Delete directories +mode: center --- To recursively delete a directory and all its contents, use `rm` from `node:fs/promises`. This is like running `rm -rf` in JavaScript. -```ts +```ts delete-directory.ts icon="/icons/typescript.svg" import { rm } from "node:fs/promises"; // Delete a directory and all its contents @@ -20,7 +22,7 @@ These options configure the deletion behavior: You can also use it without `force` to ensure the directory exists: -```ts +```ts delete-directory.ts icon="/icons/typescript.svg" try { await rm("path/to/directory", { recursive: true }); } catch (error) { diff --git a/docs/guides/runtime/delete-file.md b/docs/guides/runtime/delete-file.mdx similarity index 74% rename from docs/guides/runtime/delete-file.md rename to docs/guides/runtime/delete-file.mdx index e8827b20ae..e8e24390e5 100644 --- a/docs/guides/runtime/delete-file.md +++ b/docs/guides/runtime/delete-file.mdx @@ -1,10 +1,12 @@ --- -name: Delete files +title: Delete files +sidebarTitle: Delete files +mode: center --- To delete a file, use `Bun.file(path).delete()`. -```ts +```ts delete-file.ts icon="/icons/typescript.svg" // Delete a file const file = Bun.file("path/to/file.txt"); await file.delete(); diff --git a/docs/guides/runtime/heap-snapshot.md b/docs/guides/runtime/heap-snapshot.mdx similarity index 74% rename from docs/guides/runtime/heap-snapshot.md rename to docs/guides/runtime/heap-snapshot.mdx index 8e5849cfb1..4d97988c27 100644 --- a/docs/guides/runtime/heap-snapshot.md +++ b/docs/guides/runtime/heap-snapshot.mdx @@ -1,10 +1,12 @@ --- -name: Inspect memory usage using V8 heap snapshots +title: Inspect memory usage using V8 heap snapshots +sidebarTitle: Heap snapshots +mode: center --- Bun implements V8's heap snapshot API, which allows you to create snapshots of the heap at runtime. This helps debug memory leaks in your JavaScript/TypeScript application. -```ts#snapshot.ts +```ts snapshot.ts icon="/icons/typescript.svg" import v8 from "node:v8"; // Creates a heap snapshot file with an auto-generated name @@ -23,4 +25,4 @@ To view V8 heap snapshots in Chrome DevTools: 3. Click the "Load" button (folder icon) 4. Select your `.heapsnapshot` file -{% image src="/images/chrome-devtools-memory.png" alt="Chrome DevTools Memory Tab" width="100%" /%} +![Chrome DevTools Memory Tab](/images/chrome-devtools-memory.png) diff --git a/docs/guides/runtime/import-html.md b/docs/guides/runtime/import-html.mdx similarity index 77% rename from docs/guides/runtime/import-html.md rename to docs/guides/runtime/import-html.mdx index 60a839e872..7e18476a26 100644 --- a/docs/guides/runtime/import-html.md +++ b/docs/guides/runtime/import-html.mdx @@ -1,10 +1,12 @@ --- -name: Import a HTML file as text +title: Import a HTML file as text +sidebarTitle: Import HTML +mode: center --- To import a `.html` file in Bun as a text file, use the `type: "text"` attribute in the import statement. -```ts +```ts file.ts icon="/icons/typescript.svg" import html from "./file.html" with { type: "text" }; console.log(html); // ... diff --git a/docs/guides/runtime/import-json.md b/docs/guides/runtime/import-json.mdx similarity index 80% rename from docs/guides/runtime/import-json.md rename to docs/guides/runtime/import-json.mdx index 1a42e45466..1ed290a601 100644 --- a/docs/guides/runtime/import-json.md +++ b/docs/guides/runtime/import-json.mdx @@ -1,10 +1,12 @@ --- -name: Import a JSON file +title: Import a JSON file +sidebarTitle: Import JSON +mode: center --- Bun natively supports `.json` imports. -```json#package.json +```json package.json icon="file-json" { "name": "bun", "version": "1.0.0", @@ -19,7 +21,7 @@ Bun natively supports `.json` imports. Import the file like any other source file. -```ts +```ts data.ts icon="/icons/typescript.svg" import data from "./package.json"; data.name; // => "bun" @@ -31,7 +33,7 @@ data.author.name; // => "John Dough" Bun also supports [Import Attributes](https://github.com/tc39/proposal-import-attributes/) and [JSON modules](https://github.com/tc39/proposal-json-modules) syntax. -```ts +```ts data.ts icon="/icons/typescript.svg" import data from "./package.json" with { type: "json" }; data.name; // => "bun" diff --git a/docs/guides/runtime/import-toml.md b/docs/guides/runtime/import-toml.mdx similarity index 76% rename from docs/guides/runtime/import-toml.md rename to docs/guides/runtime/import-toml.mdx index 9a509d845a..991e12e049 100644 --- a/docs/guides/runtime/import-toml.md +++ b/docs/guides/runtime/import-toml.mdx @@ -1,10 +1,12 @@ --- -name: Import a TOML file +title: Import a TOML file +sidebarTitle: Import TOML +mode: center --- Bun natively supports importing `.toml` files. -```toml#data.toml +```toml data.toml icon="file-code" name = "bun" version = "1.0.0" @@ -17,7 +19,7 @@ email = "john@dough.com" Import the file like any other source file. -```ts +```ts data.ts icon="/icons/typescript.svg" import data from "./data.toml"; data.name; // => "bun" diff --git a/docs/guides/runtime/import-yaml.md b/docs/guides/runtime/import-yaml.mdx similarity index 83% rename from docs/guides/runtime/import-yaml.md rename to docs/guides/runtime/import-yaml.mdx index 53faf29019..e4ff38155b 100644 --- a/docs/guides/runtime/import-yaml.md +++ b/docs/guides/runtime/import-yaml.mdx @@ -1,10 +1,12 @@ --- -name: Import a YAML file +title: Import a YAML file +sidebarTitle: Import YAML +mode: center --- Bun natively supports `.yaml` and `.yml` imports. -```yaml#config.yaml +```yaml config.yaml icon="file-code" database: host: localhost port: 5432 @@ -23,7 +25,7 @@ features: Import the file like any other source file. -```ts +```ts config.ts icon="/icons/typescript.svg" import config from "./config.yaml"; config.database.host; // => "localhost" @@ -35,7 +37,7 @@ config.features.auth; // => true You can also use named imports to destructure top-level properties: -```ts +```ts config.ts icon="/icons/typescript.svg" import { database, server, features } from "./config.yaml"; console.log(database.name); // => "myapp" @@ -47,7 +49,7 @@ console.log(features.rateLimit); // => true Bun also supports [Import Attributes](https://github.com/tc39/proposal-import-attributes) syntax: -```ts +```ts config.ts icon="/icons/typescript.svg" import config from "./config.yaml" with { type: "yaml" }; config.database.port; // => 5432 @@ -57,7 +59,7 @@ config.database.port; // => 5432 For parsing YAML strings at runtime, use `Bun.YAML.parse()`: -```ts +```ts config.ts icon="/icons/typescript.svg" const yamlString = ` name: John Doe age: 30 @@ -77,7 +79,7 @@ console.log(data.hobbies); // => ["reading", "coding"] To add TypeScript support for your YAML imports, create a declaration file with `.d.ts` appended to the YAML filename (e.g., `config.yaml` → `config.yaml.d.ts`); -```ts#config.yaml.d.ts +```ts config.yaml.d.ts icon="/icons/typescript.svg" const contents: { database: { host: string; diff --git a/docs/guides/runtime/index.json b/docs/guides/runtime/index.json deleted file mode 100644 index fad27c69de..0000000000 --- a/docs/guides/runtime/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Runtime", - "description": "A collection of guides for executing code with the Bun runtime" -} diff --git a/docs/guides/runtime/read-env.md b/docs/guides/runtime/read-env.mdx similarity index 71% rename from docs/guides/runtime/read-env.md rename to docs/guides/runtime/read-env.mdx index 017fdd763c..7e3294e246 100644 --- a/docs/guides/runtime/read-env.md +++ b/docs/guides/runtime/read-env.mdx @@ -1,10 +1,12 @@ --- -name: Read environment variables +title: Read environment variables +sidebarTitle: Read env variables +mode: center --- The current environment variables can be accessed via `process.env`. -```ts +```ts index.ts icon="/icons/typescript.svg" process.env.API_TOKEN; // => "secret" ``` @@ -12,7 +14,7 @@ process.env.API_TOKEN; // => "secret" Bun also exposes these variables via `Bun.env`, which is a simple alias of `process.env`. -```ts +```ts index.ts icon="/icons/typescript.svg" Bun.env.API_TOKEN; // => "secret" ``` @@ -20,8 +22,11 @@ Bun.env.API_TOKEN; // => "secret" To print all currently-set environment variables to the command line, run `bun --print process.env`. This is useful for debugging. -```sh -$ bun --print process.env +```sh terminal icon="terminal" +bun --print process.env +``` + +```txt BAZ=stuff FOOBAR=aaaaaa diff --git a/docs/guides/runtime/set-env.md b/docs/guides/runtime/set-env.mdx similarity index 67% rename from docs/guides/runtime/set-env.md rename to docs/guides/runtime/set-env.mdx index cac8935b1d..27ed10b32c 100644 --- a/docs/guides/runtime/set-env.md +++ b/docs/guides/runtime/set-env.mdx @@ -1,10 +1,12 @@ --- -name: Set environment variables +title: Set environment variables +sidebarTitle: Set env variables +mode: center --- The current environment variables can be accessed via `process.env` or `Bun.env`. -```ts +```ts index.ts icon="/icons/typescript.svg" Bun.env.API_TOKEN; // => "secret" process.env.API_TOKEN; // => "secret" ``` @@ -19,7 +21,7 @@ Bun reads the following files automatically (listed in order of increasing prece - `.env.production`, `.env.development`, `.env.test` (depending on value of `NODE_ENV`) - `.env.local` (not loaded when `NODE_ENV=test`) -```txt#.env +```txt .env icon="settings" FOO=hello BAR=world ``` @@ -28,20 +30,22 @@ BAR=world Variables can also be set via the command line. -{% codetabs %} + -```sh#Linux/macOS -$ FOO=helloworld bun run dev +```sh Linux/macOS icon="terminal" +FOO=helloworld bun run dev ``` -```sh#Windows +```sh Windows icon="windows" # Using CMD -$ set FOO=helloworld && bun run dev +set FOO=helloworld && bun run dev # Using PowerShell -$ $env:FOO="helloworld"; bun run dev +$env:FOO="helloworld"; bun run dev ``` -## {% /codetabs %} + + +--- See [Docs > Runtime > Environment variables](https://bun.com/docs/runtime/env) for more information on using environment variables with Bun. diff --git a/docs/guides/runtime/shell.md b/docs/guides/runtime/shell.mdx similarity index 80% rename from docs/guides/runtime/shell.md rename to docs/guides/runtime/shell.mdx index 8aeb9655ad..b845a625a5 100644 --- a/docs/guides/runtime/shell.md +++ b/docs/guides/runtime/shell.mdx @@ -1,12 +1,14 @@ --- -name: Run a Shell Command +title: Run a Shell Command +sidebarTitle: Run shell command +mode: center --- Bun Shell is a cross-platform bash-like shell built in to Bun. It provides a simple way to run shell commands in JavaScript and TypeScript. To get started, import the `$` function from the `bun` package and use it to run shell commands. -```ts#foo.ts +```ts foo.ts icon="/icons/typescript.svg" import { $ } from "bun"; await $`echo Hello, world!`; // => "Hello, world!" @@ -16,7 +18,7 @@ await $`echo Hello, world!`; // => "Hello, world!" The `$` function is a tagged template literal that runs the command and returns a promise that resolves with the command's output. -```ts#foo.ts +```ts foo.ts icon="/icons/typescript.svg" import { $ } from "bun"; const output = await $`ls -l`.text(); @@ -27,7 +29,7 @@ console.log(output); To get each line of the output as an array, use the `lines` method. -```ts#foo.ts +```ts foo.ts icon="/icons/typescript.svg" import { $ } from "bun"; for await (const line of $`ls -l`.lines()) { diff --git a/docs/guides/runtime/timezone.md b/docs/guides/runtime/timezone.mdx similarity index 77% rename from docs/guides/runtime/timezone.md rename to docs/guides/runtime/timezone.mdx index 528f759fa2..a4da3119b5 100644 --- a/docs/guides/runtime/timezone.md +++ b/docs/guides/runtime/timezone.mdx @@ -1,16 +1,19 @@ --- -name: Set a time zone in Bun +title: Set a time zone in Bun +sidebarTitle: Set time zone +mode: center --- Bun supports programmatically setting a default time zone for the lifetime of the `bun` process. To do set, set the value of the `TZ` environment variable to a [valid timezone identifier](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). -{% callout %} + When running a file with `bun`, the timezone defaults to your system's configured local time zone. When running tests with `bun test`, the timezone is set to `UTC` to make tests more deterministic. -{% /callout %} -```ts + + +```ts process.ts icon="/icons/typescript.svg" process.env.TZ = "America/New_York"; ``` @@ -18,15 +21,15 @@ process.env.TZ = "America/New_York"; Alternatively, this can be set from the command line when running a Bun command. -```sh -$ TZ=America/New_York bun run dev +```sh terminal icon="terminal" +TZ=America/New_York bun run dev ``` --- Once `TZ` is set, any `Date` instances will have that time zone. By default all dates use your system's configured time zone. -```ts +```ts process.ts icon="/icons/typescript.svg" new Date().getHours(); // => 18 process.env.TZ = "America/New_York"; diff --git a/docs/guides/runtime/tsconfig-paths.md b/docs/guides/runtime/tsconfig-paths.mdx similarity index 80% rename from docs/guides/runtime/tsconfig-paths.md rename to docs/guides/runtime/tsconfig-paths.mdx index ceb0bf96ab..6c102d483b 100644 --- a/docs/guides/runtime/tsconfig-paths.md +++ b/docs/guides/runtime/tsconfig-paths.mdx @@ -1,10 +1,12 @@ --- -name: Re-map import paths +title: Re-map import paths +sidebarTitle: Re-map import paths +mode: center --- Bun reads the `paths` field in your `tsconfig.json` to re-write import paths. This is useful for aliasing package names or avoiding long relative paths. -```json +```json tsconfig.json icon="file-json" { "compilerOptions": { "paths": { @@ -19,7 +21,7 @@ Bun reads the `paths` field in your `tsconfig.json` to re-write import paths. Th With the above `tsconfig.json`, the following imports will be re-written: -```ts +```ts tsconfig.ts icon="/icons/typescript.svg" import { z } from "my-custom-name"; // imports from "zod" import { Button } from "@components/Button"; // imports from "./src/components/Button" ``` diff --git a/docs/guides/runtime/typescript.md b/docs/guides/runtime/typescript.mdx similarity index 81% rename from docs/guides/runtime/typescript.md rename to docs/guides/runtime/typescript.mdx index 2881b59888..1c5ee7d32e 100644 --- a/docs/guides/runtime/typescript.md +++ b/docs/guides/runtime/typescript.mdx @@ -1,18 +1,20 @@ --- -name: Install TypeScript declarations for Bun +title: Install TypeScript declarations for Bun +sidebarTitle: TypeScript types +mode: center --- To install TypeScript definitions for Bun's built-in APIs in your project, install `@types/bun`. -```sh -$ bun add -d @types/bun # dev dependency +```sh terminal icon="terminal" +bun add -d @types/bun # dev dependency ``` --- Below is the full set of recommended `compilerOptions` for a Bun project. With this `tsconfig.json`, you can use top-level await, extensioned or extensionless imports, and JSX. -```jsonc +```json package.json icon="file-json" { "compilerOptions": { // Environment setup & latest features @@ -39,8 +41,8 @@ Below is the full set of recommended `compilerOptions` for a Bun project. With t // Some stricter flags (disabled by default) "noUnusedLocals": false, "noUnusedParameters": false, - "noPropertyAccessFromIndexSignature": false, - }, + "noPropertyAccessFromIndexSignature": false + } } ``` diff --git a/docs/guides/runtime/vscode-debugger.md b/docs/guides/runtime/vscode-debugger.mdx similarity index 62% rename from docs/guides/runtime/vscode-debugger.md rename to docs/guides/runtime/vscode-debugger.mdx index 7b5c4f06dd..d254ee4ab9 100644 --- a/docs/guides/runtime/vscode-debugger.md +++ b/docs/guides/runtime/vscode-debugger.mdx @@ -1,12 +1,13 @@ --- -name: Debugging Bun with the VS Code extension +title: Debugging Bun with the VS Code extension +sidebarTitle: VS Code debugger +mode: center --- -{% note %} - -VSCode extension support is currently buggy. We recommend the [Web Debugger](https://bun.com/guides/runtime/web-debugger) for now. - -{% /note %} + + VSCode extension support is currently buggy. We recommend the [Web + Debugger](https://bun.com/guides/runtime/web-debugger) for now. + Bun speaks the [WebKit Inspector Protocol](https://github.com/oven-sh/bun/blob/main/packages/bun-inspector-protocol/src/protocol/jsc/index.d.ts) so you can debug your code with an interactive debugger. @@ -14,19 +15,19 @@ Bun speaks the [WebKit Inspector Protocol](https://github.com/oven-sh/bun/blob/m To install the extension, visit the [Bun for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=oven.bun-vscode) page on the VS Code marketplace website, then click Install. -{% image src="https://github.com/oven-sh/bun/assets/3084745/7c8c80e6-d49e-457a-a45e-45ebed946d56" /%} +![VS Code extension](https://github.com/oven-sh/bun/assets/3084745/7c8c80e6-d49e-457a-a45e-45ebed946d56) --- Alternatively, search `bun-vscode` in the Extensions tab of VS Code. -{% image src="https://github.com/oven-sh/bun/assets/3084745/664b4c40-944c-4076-a4c2-f812aebd3dc9" /%} +![VS Code extension](https://github.com/oven-sh/bun/assets/3084745/664b4c40-944c-4076-a4c2-f812aebd3dc9) --- Make sure you are installing the extension published by the verified Oven organization. -{% image src="https://github.com/oven-sh/bun/assets/3084745/73e6b09f-9ff1-4d85-b725-c5eb7215b6ae" /%} +![VS Code extension](https://github.com/oven-sh/bun/assets/3084745/73e6b09f-9ff1-4d85-b725-c5eb7215b6ae) --- @@ -36,7 +37,7 @@ Once installed, two new Bun-specific commands will appear in the Command Palette The `Bun: Run File` command will execute your code and print the output to the Debug Console in VS Code. Breakpoints will be ignored; this is similar to executing the file with `bun ` from the command line. -{% image src="https://github.com/oven-sh/bun/assets/3084745/1b2c7fd9-fbb9-486a-84d0-eb7ec135ded3" /%} +![VS Code extension](https://github.com/oven-sh/bun/assets/3084745/1b2c7fd9-fbb9-486a-84d0-eb7ec135ded3) --- @@ -44,4 +45,4 @@ The `Bun: Debug File` command will execute your code and print the output to the When you run the file with `Bun: Debug File`, execution will pause at the breakpoint. You can inspect the variables in scope and step through the code line-by-line using the VS Code controls. -{% image src="https://github.com/oven-sh/bun/assets/3084745/c579a36c-eb21-4a58-bc9c-74612aad82af" /%} +![VS Code extension](https://github.com/oven-sh/bun/assets/3084745/c579a36c-eb21-4a58-bc9c-74612aad82af) diff --git a/docs/guides/runtime/web-debugger.md b/docs/guides/runtime/web-debugger.mdx similarity index 59% rename from docs/guides/runtime/web-debugger.md rename to docs/guides/runtime/web-debugger.mdx index d41d93b4a2..46cc4d7e63 100644 --- a/docs/guides/runtime/web-debugger.md +++ b/docs/guides/runtime/web-debugger.mdx @@ -1,16 +1,18 @@ --- -name: Debugging Bun with the web debugger +title: Debugging Bun with the web debugger +sidebarTitle: Web debugger +mode: center --- Bun speaks the [WebKit Inspector Protocol](https://github.com/oven-sh/bun/blob/main/packages/bun-vscode/types/jsc.d.ts). To enable debugging when running code with Bun, use the `--inspect` flag. For demonstration purposes, consider the following simple web server. -```ts#server.ts +```ts server.ts icon="/icons/typescript.svg" Bun.serve({ - fetch(req){ + fetch(req) { console.log(req.url); return new Response("Hello, world!"); - } -}) + }, +}); ``` --- @@ -21,8 +23,11 @@ This automatically starts a WebSocket server on an available port that can be us Bun hosts a web-based debugger at [debug.bun.sh](https://debug.bun.sh). It is a modified version of WebKit's [Web Inspector Interface](https://webkit.org/web-inspector/web-inspector-interface/), which will look familiar to Safari users. -```sh -$ bun --inspect server.ts +```sh terminal icon="terminal" +bun --inspect server.ts +``` + +```txt ------------------ Bun Inspector ------------------ Listening at: ws://localhost:6499/0tqxs9exrgrm @@ -36,13 +41,18 @@ Inspect in browser: Open the provided `debug.bun.sh` URL in your browser to start a debugging session. From this interface, you'll be able to view the source code of the running file, view and set breakpoints, and execute code with the built-in console. -{% image src="https://github.com/oven-sh/bun/assets/3084745/e6a976a8-80cc-4394-8925-539025cc025d" alt="Screenshot of Bun debugger, Console tab" /%} + + ![Screenshot of Bun debugger, Console + tab](https://github.com/oven-sh/bun/assets/3084745/e6a976a8-80cc-4394-8925-539025cc025d) + --- Let's set a breakpoint. Navigate to the Sources tab; you should see the code from earlier. Click on the line number `3` to set a breakpoint on our `console.log(req.url)` statement. -{% image src="https://github.com/oven-sh/bun/assets/3084745/3b69c7e9-25ff-4f9d-acc4-caa736862935" alt="screenshot of Bun debugger" /%} + + ![screenshot of Bun debugger](https://github.com/oven-sh/bun/assets/3084745/3b69c7e9-25ff-4f9d-acc4-caa736862935) + --- @@ -50,33 +60,44 @@ Then visit [`http://localhost:3000`](http://localhost:3000) in your web browser. Note how the UI has changed. -{% image src="https://github.com/oven-sh/bun/assets/3084745/8b565e58-5445-4061-9bc4-f41090dfe769" alt="screenshot of Bun debugger" /%} + + ![screenshot of Bun debugger](https://github.com/oven-sh/bun/assets/3084745/8b565e58-5445-4061-9bc4-f41090dfe769) + --- At this point there's a lot we can do to introspect the current execution environment. We can use the console at the bottom to run arbitrary code in the context of the program, with full access to the variables in scope at our breakpoint. -{% image src="https://github.com/oven-sh/bun/assets/3084745/f4312b76-48ba-4a7d-b3b6-6205968ac681" /%} + + ![Bun debugger console](https://github.com/oven-sh/bun/assets/3084745/f4312b76-48ba-4a7d-b3b6-6205968ac681) + --- On the right side of the Sources pane, we can see all local variables currently in scope, and drill down to see their properties and methods. Here, we're inspecting the `req` variable. -{% image src="https://github.com/oven-sh/bun/assets/3084745/63d7f843-5180-489c-aa94-87c486e68646" /%} + + ![Bun debugger variables panel](https://github.com/oven-sh/bun/assets/3084745/63d7f843-5180-489c-aa94-87c486e68646) + --- In the upper left of the Sources pane, we can control the execution of the program. -{% image src="https://github.com/oven-sh/bun/assets/3084745/41b76deb-7371-4461-9d5d-81b5a6d2f7a4" /%} + + ![Bun debugger controls](https://github.com/oven-sh/bun/assets/3084745/41b76deb-7371-4461-9d5d-81b5a6d2f7a4) + --- Here's a cheat sheet explaining the functions of the control flow buttons. -- _Continue script execution_ — continue running the program until the next breakpoint or exception. -- _Step over_ — The program will continue to the next line. -- _Step into_ — If the current statement contains a function call, the debugger will "step into" the called function. -- _Step out_ — If the current statement is a function call, the debugger will finish executing the call, then "step out" of the function to the location where it was called. +- _Continue script execution_ — continue running the program until the next breakpoint or exception. +- _Step over_ — The program will continue to the next line. +- _Step into_ — If the current statement contains a function call, the debugger will "step into" the called function. +- _Step out_ — If the current statement is a function call, the debugger will finish executing the call, then "step out" of the function to the location where it was called. -{% image src="https://github-production-user-asset-6210df.s3.amazonaws.com/3084745/261510346-6a94441c-75d3-413a-99a7-efa62365f83d.png" /%} + + ![Debugger control buttons cheat + sheet](https://github-production-user-asset-6210df.s3.amazonaws.com/3084745/261510346-6a94441c-75d3-413a-99a7-efa62365f83d.png) + diff --git a/docs/guides/streams/index.json b/docs/guides/streams/index.json deleted file mode 100644 index da6ff26297..0000000000 --- a/docs/guides/streams/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Streams", - "description": "A collection of guides for manipulating streams with Bun" -} diff --git a/docs/guides/streams/node-readable-to-arraybuffer.md b/docs/guides/streams/node-readable-to-arraybuffer.mdx similarity index 77% rename from docs/guides/streams/node-readable-to-arraybuffer.md rename to docs/guides/streams/node-readable-to-arraybuffer.mdx index ebfa2e7edc..645f12308e 100644 --- a/docs/guides/streams/node-readable-to-arraybuffer.md +++ b/docs/guides/streams/node-readable-to-arraybuffer.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Node.js Readable to an ArrayBuffer +title: Convert a Node.js Readable to an ArrayBuffer +sidebarTitle: Readable to ArrayBuffer +mode: center --- To convert a Node.js `Readable` stream to an `ArrayBuffer` in Bun, you can create a new `Response` object with the stream as the body, then use `arrayBuffer()` to read the stream into an `ArrayBuffer`. diff --git a/docs/guides/streams/node-readable-to-blob.md b/docs/guides/streams/node-readable-to-blob.mdx similarity index 86% rename from docs/guides/streams/node-readable-to-blob.md rename to docs/guides/streams/node-readable-to-blob.mdx index cb6e0ff76e..f5136589d1 100644 --- a/docs/guides/streams/node-readable-to-blob.md +++ b/docs/guides/streams/node-readable-to-blob.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Node.js Readable to a Blob +title: Convert a Node.js Readable to a Blob +sidebarTitle: Readable to Blob +mode: center --- To convert a Node.js `Readable` stream to a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) in Bun, you can create a new [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object with the stream as the body, then use [`response.blob()`](https://developer.mozilla.org/en-US/docs/Web/API/Response/blob) to read the stream into a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob). diff --git a/docs/guides/streams/node-readable-to-json.md b/docs/guides/streams/node-readable-to-json.mdx similarity index 86% rename from docs/guides/streams/node-readable-to-json.md rename to docs/guides/streams/node-readable-to-json.mdx index 1b9425e96c..5a75caa388 100644 --- a/docs/guides/streams/node-readable-to-json.md +++ b/docs/guides/streams/node-readable-to-json.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Node.js Readable to JSON +title: Convert a Node.js Readable to JSON +sidebarTitle: Readable to JSON +mode: center --- To convert a Node.js `Readable` stream to a JSON object in Bun, you can create a new [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object with the stream as the body, then use [`response.json()`](https://developer.mozilla.org/en-US/docs/Web/API/Response/json) to read the stream into a JSON object. diff --git a/docs/guides/streams/node-readable-to-string.md b/docs/guides/streams/node-readable-to-string.mdx similarity index 84% rename from docs/guides/streams/node-readable-to-string.md rename to docs/guides/streams/node-readable-to-string.mdx index 492fcb59df..5884fbdf4a 100644 --- a/docs/guides/streams/node-readable-to-string.md +++ b/docs/guides/streams/node-readable-to-string.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Node.js Readable to a string +title: Convert a Node.js Readable to a string +sidebarTitle: Readable to string +mode: center --- To convert a Node.js `Readable` stream to a string in Bun, you can create a new [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object with the stream as the body, then use [`response.text()`](https://developer.mozilla.org/en-US/docs/Web/API/Response/text) to read the stream into a string. diff --git a/docs/guides/streams/node-readable-to-uint8array.md b/docs/guides/streams/node-readable-to-uint8array.mdx similarity index 77% rename from docs/guides/streams/node-readable-to-uint8array.md rename to docs/guides/streams/node-readable-to-uint8array.mdx index e354e21700..869905a497 100644 --- a/docs/guides/streams/node-readable-to-uint8array.md +++ b/docs/guides/streams/node-readable-to-uint8array.mdx @@ -1,5 +1,7 @@ --- -name: Convert a Node.js Readable to an Uint8Array +title: Convert a Node.js Readable to an Uint8Array +sidebarTitle: Readable to Uint8Array +mode: center --- To convert a Node.js `Readable` stream to an `Uint8Array` in Bun, you can create a new `Response` object with the stream as the body, then use `bytes()` to read the stream into an `Uint8Array`. diff --git a/docs/guides/streams/to-array.md b/docs/guides/streams/to-array.mdx similarity index 85% rename from docs/guides/streams/to-array.md rename to docs/guides/streams/to-array.mdx index 16dd63f8b3..3fc1547231 100644 --- a/docs/guides/streams/to-array.md +++ b/docs/guides/streams/to-array.mdx @@ -1,5 +1,7 @@ --- -name: Convert a ReadableStream to an array of chunks +title: Convert a ReadableStream to an array of chunks +sidebarTitle: Stream to array +mode: center --- Bun provides a number of convenience functions for reading the contents of a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) into different formats. The `Bun.readableStreamToArray` function reads the contents of a `ReadableStream` to an array of chunks. diff --git a/docs/guides/streams/to-arraybuffer.md b/docs/guides/streams/to-arraybuffer.mdx similarity index 82% rename from docs/guides/streams/to-arraybuffer.md rename to docs/guides/streams/to-arraybuffer.mdx index 11c234ec41..4e05cb8cee 100644 --- a/docs/guides/streams/to-arraybuffer.md +++ b/docs/guides/streams/to-arraybuffer.mdx @@ -1,5 +1,7 @@ --- -name: Convert a ReadableStream to an ArrayBuffer +title: Convert a ReadableStream to an ArrayBuffer +sidebarTitle: Stream to ArrayBuffer +mode: center --- Bun provides a number of convenience functions for reading the contents of a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) into different formats. diff --git a/docs/guides/streams/to-blob.md b/docs/guides/streams/to-blob.mdx similarity index 84% rename from docs/guides/streams/to-blob.md rename to docs/guides/streams/to-blob.mdx index 75d81cb72e..df604513c6 100644 --- a/docs/guides/streams/to-blob.md +++ b/docs/guides/streams/to-blob.mdx @@ -1,5 +1,7 @@ --- -name: Convert a ReadableStream to a Blob +title: Convert a ReadableStream to a Blob +sidebarTitle: Stream to Blob +mode: center --- Bun provides a number of convenience functions for reading the contents of a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) into different formats. diff --git a/docs/guides/streams/to-buffer.md b/docs/guides/streams/to-buffer.mdx similarity index 89% rename from docs/guides/streams/to-buffer.md rename to docs/guides/streams/to-buffer.mdx index c7267652cd..2befca63cd 100644 --- a/docs/guides/streams/to-buffer.md +++ b/docs/guides/streams/to-buffer.mdx @@ -1,5 +1,7 @@ --- -name: Convert a ReadableStream to a Buffer +title: Convert a ReadableStream to a Buffer +sidebarTitle: Stream to Buffer +mode: center --- Bun provides a number of convenience functions for reading the contents of a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) into different formats. This snippet reads the contents of a `ReadableStream` to an [`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer), then creates a [`Buffer`](https://nodejs.org/api/buffer.html) that points to it. diff --git a/docs/guides/streams/to-json.md b/docs/guides/streams/to-json.mdx similarity index 77% rename from docs/guides/streams/to-json.md rename to docs/guides/streams/to-json.mdx index 4882fead61..567decf586 100644 --- a/docs/guides/streams/to-json.md +++ b/docs/guides/streams/to-json.mdx @@ -1,12 +1,14 @@ --- -name: Convert a ReadableStream to JSON +title: Convert a ReadableStream to JSON +sidebarTitle: Stream to JSON +mode: center --- Bun provides a number of convenience functions for reading the contents of a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) into different formats. ```ts const stream = new ReadableStream(); -const json = await Bun.readableStreamToJSON(stream); +const json = await stream.json(); ``` --- diff --git a/docs/guides/streams/to-string.md b/docs/guides/streams/to-string.mdx similarity index 83% rename from docs/guides/streams/to-string.md rename to docs/guides/streams/to-string.mdx index 33f9246e6a..c07f8a3991 100644 --- a/docs/guides/streams/to-string.md +++ b/docs/guides/streams/to-string.mdx @@ -1,5 +1,7 @@ --- -name: Convert a ReadableStream to a string +title: Convert a ReadableStream to a string +sidebarTitle: Stream to string +mode: center --- Bun provides a number of convenience functions for reading the contents of a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) into different formats. diff --git a/docs/guides/streams/to-typedarray.md b/docs/guides/streams/to-typedarray.mdx similarity index 91% rename from docs/guides/streams/to-typedarray.md rename to docs/guides/streams/to-typedarray.mdx index c43c212869..4925910af4 100644 --- a/docs/guides/streams/to-typedarray.md +++ b/docs/guides/streams/to-typedarray.mdx @@ -1,5 +1,7 @@ --- -name: Convert a ReadableStream to a Uint8Array +title: Convert a ReadableStream to a Uint8Array +sidebarTitle: Stream to Uint8Array +mode: center --- Bun provides a number of convenience functions for reading the contents of a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) into different formats. This snippet reads the contents of a `ReadableStream` to an [`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer), then creates a [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) that points to the buffer. diff --git a/docs/guides/test/bail.md b/docs/guides/test/bail.mdx similarity index 69% rename from docs/guides/test/bail.md rename to docs/guides/test/bail.mdx index 8b657c5739..c69f8f0fbb 100644 --- a/docs/guides/test/bail.md +++ b/docs/guides/test/bail.mdx @@ -1,20 +1,22 @@ --- -name: Bail early with the Bun test runner +title: Bail early with the Bun test runner +sidebarTitle: Bail early +mode: center --- Use the `--bail` flag to bail on a test run after a single failure. This is useful for aborting as soon as possible in a continuous integration environment. -```sh -$ bun test --bail +```sh terminal icon="terminal" +bun test --bail ``` --- To bail after a certain threshold of failures, optionally specify a number after the flag. -```sh +```sh terminal icon="terminal" # bail after 10 failures -$ bun test --bail=10 +bun test --bail=10 ``` --- diff --git a/docs/guides/test/coverage-threshold.md b/docs/guides/test/coverage-threshold.mdx similarity index 81% rename from docs/guides/test/coverage-threshold.md rename to docs/guides/test/coverage-threshold.mdx index ac8a582389..560e38756b 100644 --- a/docs/guides/test/coverage-threshold.md +++ b/docs/guides/test/coverage-threshold.mdx @@ -1,12 +1,16 @@ --- -name: Set a code coverage threshold with the Bun test runner +title: Set a code coverage threshold with the Bun test runner +sidebarTitle: Coverage threshold +mode: center --- Bun's test runner supports built-in code coverage reporting via the `--coverage` flag. -```sh -$ bun test --coverage +```sh terminal icon="terminal" +bun test --coverage +``` +```txt test.test.ts: ✓ math > add [0.71ms] ✓ math > multiply [0.03ms] @@ -28,7 +32,7 @@ All files | 66.67 | 77.78 | To set a minimum coverage threshold, add the following line to your `bunfig.toml`. This requires that 90% of your codebase is covered by tests. -```toml +```toml bunfig.toml icon="settings" [test] # to require 90% line-level and function-level coverage coverageThreshold = 0.9 @@ -38,8 +42,11 @@ coverageThreshold = 0.9 If your test suite does not meet this threshold, `bun test` will exit with a non-zero exit code to signal a failure. -```sh -$ bun test --coverage +```sh terminal icon="terminal" +bun test --coverage +``` + +```txt $ echo $? 1 # this is the exit code of the previous command @@ -49,7 +56,7 @@ $ echo $? Different thresholds can be set for line-level and function-level coverage. -```toml +```toml bunfig.toml icon="settings" [test] # to set different thresholds for lines and functions coverageThreshold = { lines = 0.5, functions = 0.7 } diff --git a/docs/guides/test/coverage.md b/docs/guides/test/coverage.mdx similarity index 85% rename from docs/guides/test/coverage.md rename to docs/guides/test/coverage.mdx index 5eaec581ae..7c2315c297 100644 --- a/docs/guides/test/coverage.md +++ b/docs/guides/test/coverage.mdx @@ -1,5 +1,7 @@ --- -name: Generate code coverage reports with the Bun test runner +title: Generate code coverage reports with the Bun test runner +sidebarTitle: Coverage reports +mode: center --- Bun's test runner supports built-in _code coverage reporting_. This makes it easy to see how much of the codebase is covered by tests and find areas that are not currently well-tested. @@ -10,8 +12,11 @@ Pass the `--coverage` flag to `bun test` to enable this feature. This will print The coverage report lists the source files that were executed during the test run, the percentage of functions and lines that were executed, and the line ranges that were not executed during the run. -```sh -$ bun test --coverage +```sh terminal icon="terminal" +bun test --coverage +``` + +```txt test.test.ts: ✓ math > add [0.71ms] @@ -34,7 +39,7 @@ All files | 66.67 | 77.78 | To always enable coverage reporting by default, add the following line to your `bunfig.toml`: -```toml +```toml bunfig.toml icon="settings" [test] coverage = true # always enable coverage ``` diff --git a/docs/guides/test/happy-dom.md b/docs/guides/test/happy-dom.mdx similarity index 82% rename from docs/guides/test/happy-dom.md rename to docs/guides/test/happy-dom.mdx index 90475ded15..14a40db2f0 100644 --- a/docs/guides/test/happy-dom.md +++ b/docs/guides/test/happy-dom.mdx @@ -1,5 +1,7 @@ --- -name: Write browser DOM tests with Bun and happy-dom +title: Write browser DOM tests with Bun and happy-dom +sidebarTitle: DOM tests +mode: center --- You can write and run browser tests with Bun's test runner in conjunction with [Happy DOM](https://github.com/capricorn86/happy-dom). Happy DOM implements mocked versions of browser APIs like `document` and `location`. @@ -8,15 +10,15 @@ You can write and run browser tests with Bun's test runner in conjunction with [ To get started, install `happy-dom`. -```sh -$ bun add -d @happy-dom/global-registrator +```sh terminal icon="terminal" +bun add -d @happy-dom/global-registrator ``` --- This module exports a "registrator" that injects the mocked browser APIs to the global scope. -```ts#happydom.ts +```ts happydom.ts icon="/icons/typescript.svg" import { GlobalRegistrator } from "@happy-dom/global-registrator"; GlobalRegistrator.register(); @@ -28,7 +30,7 @@ We need to make sure this file is executed before any of our test files. That's The `./happydom.ts` file should contain the registration code above. -```toml#bunfig.toml +```toml bunfig.toml icon="settings" [test] preload = "./happydom.ts" ``` @@ -37,7 +39,7 @@ preload = "./happydom.ts" Now running `bun test` inside our project will automatically execute `happydom.ts` first. We can start writing tests that use browser APIs. -```ts +```ts dom.test.ts icon="/icons/typescript.svg" import { test, expect } from "bun:test"; test("set button text", () => { @@ -51,8 +53,11 @@ test("set button text", () => { With Happy DOM properly configured, this test runs as expected. -```sh -$ bun test +```sh terminal icon="terminal" +bun test +``` + +```txt dom.test.ts: ✓ set button text [0.82ms] diff --git a/docs/guides/test/index.json b/docs/guides/test/index.json deleted file mode 100644 index d990068acc..0000000000 --- a/docs/guides/test/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Test runner", - "description": "A collection of guides for writing, running, and configuring tests in Bun" -} diff --git a/docs/guides/test/migrate-from-jest.md b/docs/guides/test/migrate-from-jest.mdx similarity index 83% rename from docs/guides/test/migrate-from-jest.md rename to docs/guides/test/migrate-from-jest.mdx index 59ccebc647..af0ebc0daf 100644 --- a/docs/guides/test/migrate-from-jest.md +++ b/docs/guides/test/migrate-from-jest.mdx @@ -1,13 +1,15 @@ --- -name: Migrate from Jest to Bun's test runner +title: Migrate from Jest to Bun's test runner +sidebarTitle: Migrate from Jest +mode: center --- In many cases, Bun's test runner can run Jest test suites with no code changes. Just run `bun test` instead of `npx jest`, `yarn test`, etc. -```sh -- $ npx jest -- $ yarn test -+ $ bun test +```sh terminal icon="terminal" +npx jest # [!code --] +yarn test # [!code --] +bun test # [!code ++] ``` --- @@ -19,9 +21,9 @@ There's often no need for code changes. But if you'd rather switch to the `bun:test` imports, you can do that too. -```ts-diff -- import {test, expect} from "@jest/globals"; -+ import {test, expect} from "bun:test"; +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect } from "@jest/globals"; // [!code --] +import { test, expect } from "bun:test"; // [!code ++] ``` --- @@ -34,7 +36,7 @@ Add this directive to _just one file_ in your project, such as: - Your test `preload.ts` setup file (if using `preload` in bunfig.toml) - Any single `.ts` file that TypeScript includes in your compilation -```ts +```ts title="global.d.ts" icon="/icons/typescript.svg" /// ``` @@ -42,7 +44,7 @@ Add this directive to _just one file_ in your project, such as: Once added, all test files in your project automatically get TypeScript support for Jest globals: -```ts#math.test.ts +```ts math.test.ts icon="/icons/typescript.svg" describe("my test suite", () => { test("should work", () => { expect(1 + 1).toBe(2); @@ -72,7 +74,7 @@ If you're using `testEnvironment: "jsdom"` to run your tests in a browser-like e At the moment jsdom does not work in Bun due to its internal use of V8 APIs. Track support for it [here](https://github.com/oven-sh/bun/issues/3554). -```toml#bunfig.toml +```toml bunfig.toml [test] preload = ["./happy-dom.ts"] ``` @@ -81,44 +83,24 @@ preload = ["./happy-dom.ts"] Replace `bail` in your Jest config with the `--bail` CLI flag. - - -```sh -$ bun test --bail=3 +```sh terminal icon="terminal" +bun test --bail=3 ``` --- Replace `collectCoverage` with the `--coverage` CLI flag. - - -```sh -$ bun test --coverage +```sh terminal icon="terminal" +bun test --coverage ``` --- Replace `testTimeout` with the `--test-timeout` CLI flag. -```sh -$ bun test --timeout 10000 +```sh terminal icon="terminal" +bun test --timeout 10000 ``` --- diff --git a/docs/guides/test/mock-clock.md b/docs/guides/test/mock-clock.mdx similarity index 93% rename from docs/guides/test/mock-clock.md rename to docs/guides/test/mock-clock.mdx index 4a154d59a7..fd862254fa 100644 --- a/docs/guides/test/mock-clock.md +++ b/docs/guides/test/mock-clock.mdx @@ -1,5 +1,7 @@ --- -name: Set the system time in Bun's test runner +title: Set the system time in Bun's test runner +sidebarTitle: Mock system time +mode: center --- Bun's test runner supports setting the system time programmatically with the `setSystemTime` function. diff --git a/docs/guides/test/mock-functions.md b/docs/guides/test/mock-functions.mdx similarity index 85% rename from docs/guides/test/mock-functions.md rename to docs/guides/test/mock-functions.mdx index c84ef39936..93b14c953e 100644 --- a/docs/guides/test/mock-functions.md +++ b/docs/guides/test/mock-functions.mdx @@ -1,10 +1,12 @@ --- -name: Mock functions in `bun test` +title: Mock functions in `bun test` +sidebarTitle: Mock functions +mode: center --- Create mocks with the `mock` function from `bun:test`. -```ts +```ts test.ts icon="/icons/typescript.svg" import { test, expect, mock } from "bun:test"; const random = mock(() => Math.random()); @@ -14,7 +16,7 @@ const random = mock(() => Math.random()); The mock function can accept arguments. -```ts +```ts test.ts icon="/icons/typescript.svg" import { test, expect, mock } from "bun:test"; const random = mock((multiplier: number) => multiplier * Math.random()); @@ -24,7 +26,7 @@ const random = mock((multiplier: number) => multiplier * Math.random()); The result of `mock()` is a new function that's been decorated with some additional properties. -```ts +```ts test.ts icon="/icons/typescript.svg" import { mock } from "bun:test"; const random = mock((multiplier: number) => multiplier * Math.random()); @@ -46,7 +48,7 @@ random.mock.results; These extra properties make it possible to write `expect` assertions about usage of the mock function, including how many times it was called, the arguments, and the return values. -```ts +```ts test.ts icon="/icons/typescript.svg" import { test, expect, mock } from "bun:test"; const random = mock((multiplier: number) => multiplier * Math.random()); diff --git a/docs/guides/test/rerun-each.md b/docs/guides/test/rerun-each.mdx similarity index 65% rename from docs/guides/test/rerun-each.md rename to docs/guides/test/rerun-each.mdx index be2755a2a4..12046aebbd 100644 --- a/docs/guides/test/rerun-each.md +++ b/docs/guides/test/rerun-each.mdx @@ -1,12 +1,14 @@ --- -name: Re-run tests multiple times with the Bun test runner +title: Re-run tests multiple times with the Bun test runner +sidebarTitle: Re-run tests +mode: center --- Use the `--rerun-each` flag to re-run every test multiple times with the Bun test runner. This is useful for finding flaky or non-deterministic tests. -```sh +```sh terminal icon="terminal" # re-run each test 10 times -$ bun test --rerun-each 10 +bun test --rerun-each 10 ``` --- diff --git a/docs/guides/test/run-tests.md b/docs/guides/test/run-tests.mdx similarity index 88% rename from docs/guides/test/run-tests.md rename to docs/guides/test/run-tests.mdx index f813ea944e..54638b950e 100644 --- a/docs/guides/test/run-tests.md +++ b/docs/guides/test/run-tests.mdx @@ -1,5 +1,7 @@ --- -name: Run your tests with the Bun test runner +title: Run your tests with the Bun test runner +sidebarTitle: Run tests +mode: center --- Bun has a built-in [test runner](https://bun.sh/docs/cli/test) with a Jest-like `expect` API. @@ -8,7 +10,7 @@ Bun has a built-in [test runner](https://bun.sh/docs/cli/test) with a Jest-like To use it, run the `bun test` command from your project directory. The test runner will recursively search for all files in the directory that match the following patterns and execute the tests they contain. -```txt +```txt File Tree icon="folder-tree" *.test.{js|jsx|ts|tsx} *_test.{js|jsx|ts|tsx} *.spec.{js|jsx|ts|tsx} @@ -19,10 +21,11 @@ To use it, run the `bun test` command from your project directory. The test runn Here's what the output of a typical test run looks like. In this case, there are three tests files (`test.test.js`, `test2.test.js`, and `test3.test.js`) containing two tests each (`add` and `multiply`). -```sh -$ bun test -bun test v$BUN_LATEST_VERSION (9c68abdb) +```sh terminal icon="terminal" +bun test +``` +```txt test.test.js: ✓ add [0.87ms] ✓ multiply [0.02ms] @@ -45,10 +48,11 @@ Ran 6 tests across 3 files. [9.00ms] To only run certain test files, pass a positional argument to `bun test`. The runner will only execute files that contain that argument in their path. -```sh -$ bun test test3 -bun test v$BUN_LATEST_VERSION (9c68abdb) +```sh terminal icon="terminal" +bun test test3 +``` +```txt test3.test.js: ✓ add [1.40ms] ✓ multiply [0.03ms] @@ -83,10 +87,11 @@ To filter which tests are executed by name, use the `-t`/`--test-name-pattern` f Adding `-t add` will only run tests with "add" in the name. This works with test names defined with `test` or test suite names defined with `describe`. -```sh -$ bun test -t add -bun test v$BUN_LATEST_VERSION (9c68abdb) +```sh terminal icon="terminal" +bun test -t add +``` +```txt test.test.js: ✓ add [1.79ms] » multiply diff --git a/docs/guides/test/skip-tests.md b/docs/guides/test/skip-tests.mdx similarity index 77% rename from docs/guides/test/skip-tests.md rename to docs/guides/test/skip-tests.mdx index e5b43be3b7..135e516efb 100644 --- a/docs/guides/test/skip-tests.md +++ b/docs/guides/test/skip-tests.mdx @@ -1,10 +1,12 @@ --- -name: Skip tests with the Bun test runner +title: Skip tests with the Bun test runner +sidebarTitle: Skip tests +mode: center --- To skip a test with the Bun test runner, use the `test.skip` function. -```ts +```ts test.ts icon="/icons/typescript.svg" import { test } from "bun:test"; test.skip("unimplemented feature", () => { @@ -16,9 +18,11 @@ test.skip("unimplemented feature", () => { Running `bun test` will not execute this test. It will be marked as skipped in the terminal output. -```sh -$ bun test +```sh terminal icon="terminal" +bun test +``` +```txt test.test.ts: ✓ add [0.03ms] ✓ multiply [0.02ms] diff --git a/docs/guides/test/snapshot.md b/docs/guides/test/snapshot.mdx similarity index 80% rename from docs/guides/test/snapshot.md rename to docs/guides/test/snapshot.mdx index 3c62c16abf..da461952c8 100644 --- a/docs/guides/test/snapshot.md +++ b/docs/guides/test/snapshot.mdx @@ -1,10 +1,12 @@ --- -name: Use snapshot testing in `bun test` +title: Use snapshot testing in `bun test` +sidebarTitle: Snapshot testing +mode: center --- Bun's test runner supports Jest-style snapshot testing via `.toMatchSnapshot()`. -```ts#snap.test.ts +```ts snap.test.ts icon="/icons/typescript.svg" import { test, expect } from "bun:test"; test("snapshot", () => { @@ -16,10 +18,11 @@ test("snapshot", () => { The first time this test is executed, Bun will evaluate the value passed into `expect()` and write it to disk in a directory called `__snapshots__` that lives alongside the test file. (Note the `snapshots: +1 added` line in the output.) -```sh -$ bun test test/snap -bun test v$BUN_LATEST_VERSION (9c68abdb) +```sh terminal icon="terminal" +bun test test/snap +``` +```txt test/snap.test.ts: ✓ snapshot [1.48ms] @@ -34,7 +37,7 @@ Ran 1 tests across 1 files. [82.00ms] The `__snapshots__` directory contains a `.snap` file for each test file in the directory. -```txt +```txt File Tree icon="folder-tree" test ├── __snapshots__ │   └── snap.test.ts.snap @@ -45,7 +48,7 @@ test The `snap.test.ts.snap` file is a JavaScript file that exports a serialized version of the value passed into `expect()`. The `{foo: "bar"}` object has been serialized to JSON. -```js +```js snap.test.ts.snap icon="file-code" // Bun Snapshot v1, https://bun.sh/docs/test/snapshots exports[`snapshot 1`] = ` @@ -59,10 +62,12 @@ exports[`snapshot 1`] = ` Later, when this test file is executed again, Bun will read the snapshot file and compare it to the value passed into `expect()`. If the values are different, the test will fail. -```sh -$ bun test -bun test v$BUN_LATEST_VERSION (9c68abdb) +```sh terminal icon="terminal" +bun test +bun test v1.3.1 (9c68abdb) +``` +```txt test/snap.test.ts: ✓ snapshot [1.05ms] @@ -76,10 +81,12 @@ Ran 1 tests across 1 files. [101.00ms] To update snapshots, use the `--update-snapshots` flag. -```sh -$ bun test --update-snapshots -bun test v$BUN_LATEST_VERSION (9c68abdb) +```sh terminal icon="terminal" +bun test --update-snapshots +bun test v1.3.1 (9c68abdb) +``` +```txt test/snap.test.ts: ✓ snapshot [0.86ms] diff --git a/docs/guides/test/spy-on.md b/docs/guides/test/spy-on.md deleted file mode 100644 index 7df2c5c8f9..0000000000 --- a/docs/guides/test/spy-on.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -name: Spy on methods in `bun test` ---- - -Use the `spyOn` utility to track method calls with Bun's test runner. - -```ts -import { test, expect, spyOn } from "bun:test"; - -const leo = { - name: "Leonardo", - sayHi(thing: string) { - console.log(`Sup I'm ${this.name} and I like ${thing}`); - }, -}; - -const spy = spyOn(leo, "sayHi"); -``` - ---- - -Once the spy is created, it can be used to write `expect` assertions relating to method calls. - -```ts-diff - import { test, expect, spyOn } from "bun:test"; - - const leo = { - name: "Leonardo", - sayHi(thing: string) { - console.log(`Sup I'm ${this.name} and I like ${thing}`); - }, - }; - - const spy = spyOn(leo, "sayHi"); - -+ test("turtles", ()=>{ -+ expect(spy).toHaveBeenCalledTimes(0); -+ leo.sayHi("pizza"); -+ expect(spy).toHaveBeenCalledTimes(1); -+ expect(spy.mock.calls).toEqual([[ "pizza" ]]); -+ }) -``` - ---- - -See [Docs > Test Runner > Mocks](https://bun.sh/docs/test/mocks) for complete documentation on mocking with the Bun test runner. diff --git a/docs/guides/test/spy-on.mdx b/docs/guides/test/spy-on.mdx new file mode 100644 index 0000000000..b5dea537e2 --- /dev/null +++ b/docs/guides/test/spy-on.mdx @@ -0,0 +1,49 @@ +--- +title: Spy on methods in `bun test` +sidebarTitle: Spy on methods +mode: center +--- + +Use the `spyOn` utility to track method calls with Bun's test runner. + +```ts +import { test, expect, spyOn } from "bun:test"; + +const leo = { + name: "Leonardo", + sayHi(thing: string) { + console.log(`Sup I'm ${this.name} and I like ${thing}`); + }, +}; + +const spy = spyOn(leo, "sayHi"); +``` + +--- + +Once the spy is created, it can be used to write `expect` assertions relating to method calls. + +```ts +import { test, expect, spyOn } from "bun:test"; + +const leo = { + name: "Leonardo", + sayHi(thing: string) { + console.log(`Sup I'm ${this.name} and I like ${thing}`); + }, +}; + +const spy = spyOn(leo, "sayHi"); + +test("turtles", () => { + // [!code ++] + expect(spy).toHaveBeenCalledTimes(0); // [!code ++] + leo.sayHi("pizza"); // [!code ++] + expect(spy).toHaveBeenCalledTimes(1); // [!code ++] + expect(spy.mock.calls).toEqual([["pizza"]]); // [!code ++] +}); // [!code ++] +``` + +--- + +See [Docs > Test Runner > Mocks](https://bun.sh/docs/test/mocks) for complete documentation on mocking with the Bun test runner. diff --git a/docs/guides/test/svelte-test.md b/docs/guides/test/svelte-test.mdx similarity index 70% rename from docs/guides/test/svelte-test.md rename to docs/guides/test/svelte-test.mdx index 062cd3312d..54e555ae76 100644 --- a/docs/guides/test/svelte-test.md +++ b/docs/guides/test/svelte-test.mdx @@ -1,18 +1,20 @@ --- -name: "import, require, and test Svelte components with bun test" +title: "import, require, and test Svelte components with bun test" +sidebarTitle: Test Svelte +mode: center --- -Bun's [Plugin API](/docs/runtime/plugins) lets you add custom loaders to your project. The `test.preload` option in `bunfig.toml` lets you configure your loader to start before your tests run. +Bun's [Plugin API](/runtime/plugins) lets you add custom loaders to your project. The `test.preload` option in `bunfig.toml` lets you configure your loader to start before your tests run. Firstly, install `@testing-library/svelte`, `svelte`, and `@happy-dom/global-registrator`. -```bash -$ bun add @testing-library/svelte svelte@4 @happy-dom/global-registrator +```bash terminal icon="terminal" +bun add @testing-library/svelte svelte@4 @happy-dom/global-registrator ``` Then, save this plugin in your project. -```ts#svelte-loader.js +```ts svelte-loader.js icon="/icons/typescript.svg" import { plugin } from "bun"; import { compile } from "svelte/compiler"; import { readFileSync } from "fs"; @@ -28,20 +30,14 @@ afterEach(async () => { }); plugin({ - name: "svelte loader", + title: "svelte loader", setup(builder) { builder.onLoad({ filter: /\.svelte(\?[^.]+)?$/ }, ({ path }) => { try { - const source = readFileSync( - path.substring( - 0, - path.includes("?") ? path.indexOf("?") : path.length - ), - "utf-8" - ); + const source = readFileSync(path.substring(0, path.includes("?") ? path.indexOf("?") : path.length), "utf-8"); const result = compile(source, { - filename: path, + filetitle: path, generate: "client", dev: false, }); @@ -56,14 +52,13 @@ plugin({ }); }, }); - ``` --- Add this to `bunfig.toml` to tell Bun to preload the plugin, so it loads before your tests run. -```toml#bunfig.toml +```toml bunfig.toml icon="settings" [test] # Tell Bun to load this plugin before your tests run preload = ["./svelte-loader.js"] @@ -76,20 +71,20 @@ preload = ["./svelte-loader.js"] Add an example `.svelte` file in your project. -```html#Counter.svelte +```html Counter.svelte icon="file-code" - + ``` --- Now you can `import` or `require` `*.svelte` files in your tests, and it will load the Svelte component as a JavaScript module. -```ts#hello-svelte.test.ts +```ts hello-svelte.test.ts icon="/icons/typescript.svg" import { test, expect } from "bun:test"; import { render, fireEvent } from "@testing-library/svelte"; import Counter from "./Counter.svelte"; @@ -113,8 +108,6 @@ test("Counter increments when clicked", async () => { Use `bun test` to run your tests. -```bash -$ bun test +```bash terminal icon="terminal" +bun test ``` - ---- diff --git a/docs/guides/test/testing-library.md b/docs/guides/test/testing-library.mdx similarity index 73% rename from docs/guides/test/testing-library.md rename to docs/guides/test/testing-library.mdx index 3c3e4e9157..36e5b61540 100644 --- a/docs/guides/test/testing-library.md +++ b/docs/guides/test/testing-library.mdx @@ -1,5 +1,7 @@ --- -name: Using Testing Library with Bun +title: Using Testing Library with Bun +sidebarTitle: Testing Library +mode: center --- You can use [Testing Library](https://testing-library.com/) with Bun's test runner. @@ -8,7 +10,7 @@ You can use [Testing Library](https://testing-library.com/) with Bun's test runn As a prerequisite to using Testing Library you will need to install [Happy Dom](https://github.com/capricorn86/happy-dom). ([see Bun's Happy DOM guide for more information](https://bun.sh/guides/test/happy-dom)). -```sh +```sh terminal icon="terminal" bun add -D @happy-dom/global-registrator ``` @@ -16,7 +18,7 @@ bun add -D @happy-dom/global-registrator Next you should install the Testing Library packages you are planning on using. For example, if you are setting up testing for React your installs may look like this. You will also need to install `@testing-library/jest-dom` to get matchers working later. -```sh +```sh terminal icon="terminal" bun add -D @testing-library/react @testing-library/dom @testing-library/jest-dom ``` @@ -24,8 +26,8 @@ bun add -D @testing-library/react @testing-library/dom @testing-library/jest-dom Next you will need to create a preload script for Happy DOM and for Testing Library. For more details about the Happy DOM setup script see [Bun's Happy DOM guide](https://bun.sh/guides/test/happy-dom). -```ts#happydom.ts -import { GlobalRegistrator } from '@happy-dom/global-registrator'; +```ts happydom.ts icon="/icons/typescript.svg" +import { GlobalRegistrator } from "@happy-dom/global-registrator"; GlobalRegistrator.register(); ``` @@ -34,10 +36,10 @@ GlobalRegistrator.register(); For Testing Library, you will want to extend Bun's `expect` function with Testing Library's matchers. Optionally, to better match the behavior of test-runners like Jest, you may want to run cleanup after each test. -```ts#testing-library.ts -import { afterEach, expect } from 'bun:test'; -import { cleanup } from '@testing-library/react'; -import * as matchers from '@testing-library/jest-dom/matchers'; +```ts testing-library.ts icon="/icons/typescript.svg" +import { afterEach, expect } from "bun:test"; +import { cleanup } from "@testing-library/react"; +import * as matchers from "@testing-library/jest-dom/matchers"; expect.extend(matchers); @@ -51,7 +53,7 @@ afterEach(() => { Next, add these preload scripts to your `bunfig.toml` (you can also have everything in a single `preload.ts` script if you prefer). -```toml#bunfig.toml +```toml bunfig.toml icon="settings" [test] preload = ["./happydom.ts", "./testing-library.ts"] ``` @@ -60,13 +62,12 @@ preload = ["./happydom.ts", "./testing-library.ts"] If you are using TypeScript you will also need to make use of declaration merging in order to get the new matcher types to show up in your editor. To do this, create a type declaration file that extends `Matchers` like this. -```ts#matchers.d.ts -import { TestingLibraryMatchers } from '@testing-library/jest-dom/matchers'; -import { Matchers, AsymmetricMatchers } from 'bun:test'; +```ts matchers.d.ts icon="/icons/typescript.svg" +import { TestingLibraryMatchers } from "@testing-library/jest-dom/matchers"; +import { Matchers, AsymmetricMatchers } from "bun:test"; -declare module 'bun:test' { - interface Matchers - extends TestingLibraryMatchers {} +declare module "bun:test" { + interface Matchers extends TestingLibraryMatchers {} interface AsymmetricMatchers extends TestingLibraryMatchers {} } ``` @@ -75,7 +76,7 @@ declare module 'bun:test' { You should now be able to use Testing Library in your tests -```ts +```ts matchers.d.ts icon="/icons/typescript.svg" import { test, expect } from "bun:test"; import { screen, render } from "@testing-library/react"; import { MyComponent } from "./myComponent"; diff --git a/docs/guides/test/timeout.md b/docs/guides/test/timeout.mdx similarity index 64% rename from docs/guides/test/timeout.md rename to docs/guides/test/timeout.mdx index 7e8db4b071..182fbea339 100644 --- a/docs/guides/test/timeout.md +++ b/docs/guides/test/timeout.mdx @@ -1,13 +1,15 @@ --- -name: Set a per-test timeout with the Bun test runner +title: Set a per-test timeout with the Bun test runner +sidebarTitle: Test timeout +mode: center --- Use the `--timeout` flag to set a timeout for each test in milliseconds. If any test exceeds this timeout, it will be marked as failed. The default timeout is `5000` (5 seconds). -```sh -$ bun test --timeout 3000 # 3 seconds +```sh terminal icon="terminal" +bun test --timeout 3000 # 3 seconds ``` --- diff --git a/docs/guides/test/todo-tests.md b/docs/guides/test/todo-tests.mdx similarity index 84% rename from docs/guides/test/todo-tests.md rename to docs/guides/test/todo-tests.mdx index da9bd9e70c..a349b088a1 100644 --- a/docs/guides/test/todo-tests.md +++ b/docs/guides/test/todo-tests.mdx @@ -1,10 +1,12 @@ --- -name: Mark a test as a "todo" with the Bun test runner +title: Mark a test as a "todo" with the Bun test runner +sidebarTitle: Todo tests +mode: center --- To remind yourself to write a test later, use the `test.todo` function. There's no need to provide a test implementation. -```ts +```ts test.ts icon="/icons/typescript.svg" import { test, expect } from "bun:test"; // write this later @@ -15,9 +17,11 @@ test.todo("unimplemented feature"); The output of `bun test` indicates how many `todo` tests were encountered. -```sh -$ bun test +```sh terminal icon="terminal" +bun test +``` +```txt test.test.ts: ✓ add [0.03ms] ✓ multiply [0.02ms] @@ -46,8 +50,11 @@ test.todo("unimplemented feature", () => { If an implementation is provided, it will not be run unless the `--todo` flag is passed. If the `--todo` flag is passed, the test will be executed and _expected to fail_ by test runner! If a todo test passes, the `bun test` run will return a non-zero exit code to signal the failure. -```sh -$ bun test --todo +```sh terminal icon="terminal" +bun test --todo +``` + +```txt my.test.ts: ✗ unimplemented feature ^ this test is marked as todo but passes. Remove `.todo` or check that test is correct. diff --git a/docs/guides/test/update-snapshots.md b/docs/guides/test/update-snapshots.mdx similarity index 78% rename from docs/guides/test/update-snapshots.md rename to docs/guides/test/update-snapshots.mdx index 9d0fb7c967..4cdd5f3c2f 100644 --- a/docs/guides/test/update-snapshots.md +++ b/docs/guides/test/update-snapshots.mdx @@ -1,10 +1,12 @@ --- -name: Update snapshots in `bun test` +title: Update snapshots in `bun test` +sidebarTitle: Update snapshots +mode: center --- Bun's test runner supports Jest-style snapshot testing via `.toMatchSnapshot()`. -```ts#snap.test.ts +```ts snap.test.ts icon="/icons/typescript.svg" import { test, expect } from "bun:test"; test("snapshot", () => { @@ -16,7 +18,7 @@ test("snapshot", () => { The first time this test is executed, Bun will write a snapshot file to disk in a directory called `__snapshots__` that lives alongside the test file. -```txt +```txt File Tree icon="folder-tree" test ├── __snapshots__ │   └── snap.test.ts.snap @@ -27,10 +29,11 @@ test To regenerate snapshots, use the `--update-snapshots` flag. -```sh -$ bun test --update-snapshots -bun test v$BUN_LATEST_VERSION (9c68abdb) +```sh terminal icon="terminal" +bun test --update-snapshots +``` +```txt test/snap.test.ts: ✓ snapshot [0.86ms] diff --git a/docs/guides/test/watch-mode.md b/docs/guides/test/watch-mode.mdx similarity index 56% rename from docs/guides/test/watch-mode.md rename to docs/guides/test/watch-mode.mdx index b4b4c3f630..dd24911c7a 100644 --- a/docs/guides/test/watch-mode.md +++ b/docs/guides/test/watch-mode.mdx @@ -1,18 +1,23 @@ --- -name: Run tests in watch mode with Bun +title: Run tests in watch mode with Bun +sidebarTitle: Watch mode +mode: center --- Use the `--watch` flag to run your tests in watch mode. -```sh -$ bun test --watch +```sh terminal icon="terminal" +bun test --watch ``` --- This will restart the running Bun process whenever a file change is detected. It's fast. In this example, the editor is configured to save the file on every keystroke. -{% image src="https://github.com/oven-sh/bun/assets/3084745/dc49a36e-ba82-416f-b960-1c883a924248" caption="Running tests in watch mode in Bun" /%} + + ![Running tests in watch mode in + Bun](https://github.com/oven-sh/bun/assets/3084745/dc49a36e-ba82-416f-b960-1c883a924248) + --- diff --git a/docs/guides/util/base64.md b/docs/guides/util/base64.mdx similarity index 86% rename from docs/guides/util/base64.md rename to docs/guides/util/base64.mdx index 10685a7e88..11ae957d4d 100644 --- a/docs/guides/util/base64.md +++ b/docs/guides/util/base64.mdx @@ -1,5 +1,7 @@ --- -name: Encode and decode base64 strings +title: Encode and decode base64 strings +sidebarTitle: Base64 encoding +mode: center --- Bun implements the Web-standard [`atob`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/atob) and [`btoa`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/btoa) functions for encoding and decoding base64 strings. diff --git a/docs/guides/util/deep-equals.md b/docs/guides/util/deep-equals.mdx similarity index 85% rename from docs/guides/util/deep-equals.md rename to docs/guides/util/deep-equals.mdx index 4b25310795..f5f15fae28 100644 --- a/docs/guides/util/deep-equals.md +++ b/docs/guides/util/deep-equals.mdx @@ -1,10 +1,12 @@ --- -name: Check if two objects are deeply equal +title: Check if two objects are deeply equal +sidebarTitle: Deep equality +mode: center --- Check if two objects are deeply equal. This is used internally by `expect().toEqual()` in Bun's [test runner](https://bun.com/docs/test/writing). -```ts#index.ts +```ts index.ts icon="/icons/typescript.svg" const a = { a: 1, b: 2, c: { d: 3 } }; const b = { a: 1, b: 2, c: { d: 3 } }; @@ -17,7 +19,7 @@ Pass `true` as a third argument to enable strict mode. This is used internally b The following examples would return `true` in non-strict mode but `false` in strict mode. -```ts +```ts index.ts icon="/icons/typescript.svg" // undefined values Bun.deepEquals({}, { a: undefined }, true); // false diff --git a/docs/guides/util/deflate.md b/docs/guides/util/deflate.mdx similarity index 78% rename from docs/guides/util/deflate.md rename to docs/guides/util/deflate.mdx index 40b35e69d7..6c525db2ef 100644 --- a/docs/guides/util/deflate.md +++ b/docs/guides/util/deflate.mdx @@ -1,5 +1,7 @@ --- -name: Compress and decompress data with DEFLATE +title: Compress and decompress data with DEFLATE +sidebarTitle: DEFLATE compression +mode: center --- Use `Bun.deflateSync()` to compress a `Uint8Array` with DEFLATE. diff --git a/docs/guides/util/detect-bun.md b/docs/guides/util/detect-bun.mdx similarity index 88% rename from docs/guides/util/detect-bun.md rename to docs/guides/util/detect-bun.mdx index 03a40358c2..0f376749eb 100644 --- a/docs/guides/util/detect-bun.md +++ b/docs/guides/util/detect-bun.mdx @@ -1,5 +1,7 @@ --- -name: Detect when code is executed with Bun +title: Detect when code is executed with Bun +sidebarTitle: Detect Bun +mode: center --- The recommended way to conditionally detect when code is being executed with `bun` is to check for the existence of the `Bun` global. diff --git a/docs/guides/util/entrypoint.md b/docs/guides/util/entrypoint.mdx similarity index 77% rename from docs/guides/util/entrypoint.md rename to docs/guides/util/entrypoint.mdx index a86214e0f7..a7105a00d0 100644 --- a/docs/guides/util/entrypoint.md +++ b/docs/guides/util/entrypoint.mdx @@ -1,10 +1,12 @@ --- -name: Check if the current file is the entrypoint +title: Check if the current file is the entrypoint +sidebarTitle: Check entrypoint +mode: center --- Bun provides a handful of module-specific utilities on the [`import.meta`](https://bun.com/docs/api/import-meta) object. Use `import.meta.main` to check if the current file is the entrypoint of the current process. -```ts#index.ts +```ts index.ts icon="/icons/typescript.svg" if (import.meta.main) { // this file is directly executed with `bun run` } else { diff --git a/docs/guides/util/escape-html.md b/docs/guides/util/escape-html.mdx similarity index 89% rename from docs/guides/util/escape-html.md rename to docs/guides/util/escape-html.mdx index 1e03f71e9c..ca2fec3cfa 100644 --- a/docs/guides/util/escape-html.md +++ b/docs/guides/util/escape-html.mdx @@ -1,5 +1,7 @@ --- -name: Escape an HTML string +title: Escape an HTML string +sidebarTitle: Escape HTML +mode: center --- The `Bun.escapeHTML()` utility can be used to escape HTML characters in a string. The following replacements are made. diff --git a/docs/guides/util/file-url-to-path.md b/docs/guides/util/file-url-to-path.mdx similarity index 74% rename from docs/guides/util/file-url-to-path.md rename to docs/guides/util/file-url-to-path.mdx index f884e5855f..1ff86fc31b 100644 --- a/docs/guides/util/file-url-to-path.md +++ b/docs/guides/util/file-url-to-path.mdx @@ -1,5 +1,7 @@ --- -name: Convert a file URL to an absolute path +title: Convert a file URL to an absolute path +sidebarTitle: File URL to path +mode: center --- Use `Bun.fileURLToPath()` to convert a `file://` URL to an absolute path. diff --git a/docs/guides/util/gzip.md b/docs/guides/util/gzip.mdx similarity index 78% rename from docs/guides/util/gzip.md rename to docs/guides/util/gzip.mdx index afc67cf454..84b9d97067 100644 --- a/docs/guides/util/gzip.md +++ b/docs/guides/util/gzip.mdx @@ -1,5 +1,7 @@ --- -name: Compress and decompress data with gzip +title: Compress and decompress data with gzip +sidebarTitle: Gzip compression +mode: center --- Use `Bun.gzipSync()` to compress a `Uint8Array` with gzip. diff --git a/docs/guides/util/hash-a-password.md b/docs/guides/util/hash-a-password.mdx similarity index 95% rename from docs/guides/util/hash-a-password.md rename to docs/guides/util/hash-a-password.mdx index f74a7762ce..d45d6da6e2 100644 --- a/docs/guides/util/hash-a-password.md +++ b/docs/guides/util/hash-a-password.mdx @@ -1,5 +1,7 @@ --- -name: Hash a password +title: Hash a password +sidebarTitle: Hash password +mode: center --- The `Bun.password.hash()` function provides a fast, built-in mechanism for securely hashing passwords in Bun. No third-party dependencies are required. diff --git a/docs/guides/util/import-meta-dir.md b/docs/guides/util/import-meta-dir.mdx similarity index 66% rename from docs/guides/util/import-meta-dir.md rename to docs/guides/util/import-meta-dir.mdx index 69c37dc420..2890273034 100644 --- a/docs/guides/util/import-meta-dir.md +++ b/docs/guides/util/import-meta-dir.mdx @@ -1,10 +1,12 @@ --- -name: Get the directory of the current file +title: Get the directory of the current file +sidebarTitle: import.meta.dir +mode: center --- Bun provides a handful of module-specific utilities on the [`import.meta`](https://bun.com/docs/api/import-meta) object. -```ts#/a/b/c.ts +```ts /a/b/c.ts icon="/icons/typescript.svg" import.meta.dir; // => "/a/b" ``` diff --git a/docs/guides/util/import-meta-file.md b/docs/guides/util/import-meta-file.mdx similarity index 71% rename from docs/guides/util/import-meta-file.md rename to docs/guides/util/import-meta-file.mdx index 9464a31972..cf16226b35 100644 --- a/docs/guides/util/import-meta-file.md +++ b/docs/guides/util/import-meta-file.mdx @@ -1,10 +1,12 @@ --- -name: Get the file name of the current file +title: Get the file name of the current file +sidebarTitle: import.meta.file +mode: center --- Bun provides a handful of module-specific utilities on the [`import.meta`](https://bun.com/docs/api/import-meta) object. Use `import.meta.file` to retrieve the name of the current file. -```ts#/a/b/c.ts +```ts /a/b/c.ts icon="/icons/typescript.svg" import.meta.file; // => "c.ts" ``` diff --git a/docs/guides/util/import-meta-path.md b/docs/guides/util/import-meta-path.mdx similarity index 71% rename from docs/guides/util/import-meta-path.md rename to docs/guides/util/import-meta-path.mdx index 132f9efd72..3e1f20aec8 100644 --- a/docs/guides/util/import-meta-path.md +++ b/docs/guides/util/import-meta-path.mdx @@ -1,10 +1,12 @@ --- -name: Get the absolute path of the current file +title: Get the absolute path of the current file +sidebarTitle: import.meta.path +mode: center --- Bun provides a handful of module-specific utilities on the [`import.meta`](https://bun.com/docs/api/import-meta) object. Use `import.meta.path` to retrieve the absolute path of the current file. -```ts#/a/b/c.ts +```ts /a/b/c.ts icon="/icons/typescript.svg" import.meta.path; // => "/a/b/c.ts" ``` diff --git a/docs/guides/util/index.json b/docs/guides/util/index.json deleted file mode 100644 index 7e988ae252..0000000000 --- a/docs/guides/util/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Utilities", - "description": "A collection of guides relating to Bun's array of built-in utility functions" -} diff --git a/docs/guides/util/javascript-uuid.md b/docs/guides/util/javascript-uuid.mdx similarity index 89% rename from docs/guides/util/javascript-uuid.md rename to docs/guides/util/javascript-uuid.mdx index 4ec9129f0e..1d9a0ea0f6 100644 --- a/docs/guides/util/javascript-uuid.md +++ b/docs/guides/util/javascript-uuid.mdx @@ -1,5 +1,7 @@ --- -name: Generate a UUID +title: Generate a UUID +sidebarTitle: Generate UUID +mode: center --- Use `crypto.randomUUID()` to generate a UUID v4. This API works in Bun, Node.js, and browsers. It requires no dependencies. diff --git a/docs/guides/util/main.md b/docs/guides/util/main.mdx similarity index 51% rename from docs/guides/util/main.md rename to docs/guides/util/main.mdx index b423ae81d8..f308c64a7c 100644 --- a/docs/guides/util/main.md +++ b/docs/guides/util/main.mdx @@ -1,29 +1,40 @@ --- -name: Get the absolute path to the current entrypoint +title: Get the absolute path to the current entrypoint +sidebarTitle: Get entrypoint path +mode: center --- The `Bun.main` property contains the absolute path to the current entrypoint. -{% codetabs %} + -```ts#foo.ts +```ts foo.ts icon="/icons/typescript.svg" console.log(Bun.main); ``` -```ts#index.ts +```ts index.ts icon="/icons/typescript.svg" import "./foo.ts"; ``` -{% /codetabs %} + --- The printed path corresponds to the file that is executed with `bun run`. -```sh -$ bun run index.ts +```sh terminal icon="terminal" +bun run index.ts +``` + +```txt /path/to/index.ts -$ bun run foo.ts +``` + +```sh terminal icon="terminal" +bun run foo.ts +``` + +```txt /path/to/foo.ts ``` diff --git a/docs/guides/util/path-to-file-url.md b/docs/guides/util/path-to-file-url.mdx similarity index 74% rename from docs/guides/util/path-to-file-url.md rename to docs/guides/util/path-to-file-url.mdx index d413141717..aac29788df 100644 --- a/docs/guides/util/path-to-file-url.md +++ b/docs/guides/util/path-to-file-url.mdx @@ -1,5 +1,7 @@ --- -name: Convert an absolute path to a file URL +title: Convert an absolute path to a file URL +sidebarTitle: Path to file URL +mode: center --- Use `Bun.pathToFileURL()` to convert an absolute path to a `file://` URL. diff --git a/docs/guides/util/sleep.md b/docs/guides/util/sleep.mdx similarity index 86% rename from docs/guides/util/sleep.md rename to docs/guides/util/sleep.mdx index 4c81f7fbd8..30daedc805 100644 --- a/docs/guides/util/sleep.md +++ b/docs/guides/util/sleep.mdx @@ -1,5 +1,7 @@ --- -name: Sleep for a fixed number of milliseconds +title: Sleep for a fixed number of milliseconds +sidebarTitle: Sleep +mode: center --- The `Bun.sleep` method provides a convenient way to create a void `Promise` that resolves in a fixed number of milliseconds. diff --git a/docs/guides/util/version.md b/docs/guides/util/version.mdx similarity index 64% rename from docs/guides/util/version.md rename to docs/guides/util/version.mdx index 8861a13dd9..8120e210bd 100644 --- a/docs/guides/util/version.md +++ b/docs/guides/util/version.mdx @@ -1,18 +1,20 @@ --- -name: Get the current Bun version +title: Get the current Bun version +sidebarTitle: Get Bun version +mode: center --- Get the current version of Bun in a semver format. -```ts#index.ts -Bun.version; // => "$BUN_LATEST_VERSION" +```ts index.ts icon="/icons/typescript.svg" +Bun.version; // => "1.3.1" ``` --- Get the exact `git` commit of [`oven-sh/bun`](https://github.com/oven-sh/bun) that was compiled to produce this Bun binary. -```ts#index.ts +```ts index.ts icon="/icons/typescript.svg" Bun.revision; // => "49231b2cb9aa48497ab966fc0bb6b742dacc4994" ``` diff --git a/docs/guides/util/which-path-to-executable-bin.md b/docs/guides/util/which-path-to-executable-bin.mdx similarity index 73% rename from docs/guides/util/which-path-to-executable-bin.md rename to docs/guides/util/which-path-to-executable-bin.mdx index e6d159bd56..fe9cda53a6 100644 --- a/docs/guides/util/which-path-to-executable-bin.md +++ b/docs/guides/util/which-path-to-executable-bin.mdx @@ -1,10 +1,12 @@ --- -name: Get the path to an executable bin file +title: Get the path to an executable bin file +sidebarTitle: Find executable path +mode: center --- `Bun.which` is a utility function to find the absolute path of an executable file. It is similar to the `which` command in Unix-like systems. -```ts#foo.ts +```ts foo.ts icon="/icons/typescript.svg" Bun.which("sh"); // => "/bin/sh" Bun.which("notfound"); // => null Bun.which("bun"); // => "/home/user/.bun/bin/bun" diff --git a/docs/guides/websocket/compression.md b/docs/guides/websocket/compression.mdx similarity index 75% rename from docs/guides/websocket/compression.md rename to docs/guides/websocket/compression.mdx index dba4436441..ef1075b80e 100644 --- a/docs/guides/websocket/compression.md +++ b/docs/guides/websocket/compression.mdx @@ -1,10 +1,12 @@ --- -name: Enable compression for WebSocket messages +title: Enable compression for WebSocket messages +sidebarTitle: Enable compression +mode: center --- Per-message compression can be enabled with the `perMessageDeflate` parameter. When set, all messages will be compressed using the [permessage-deflate](https://tools.ietf.org/html/rfc7692) WebSocket extension. -```ts +```ts server.ts icon="/icons/typescript.svg" Bun.serve({ // ... websocket: { @@ -18,7 +20,7 @@ Bun.serve({ To enable compression for individual messages, pass `true` as the second parameter to `ws.send()`. -```ts +```ts server.ts icon="/icons/typescript.svg" Bun.serve({ // ... websocket: { diff --git a/docs/guides/websocket/context.md b/docs/guides/websocket/context.mdx similarity index 82% rename from docs/guides/websocket/context.md rename to docs/guides/websocket/context.mdx index 2dc35e6925..d313de2605 100644 --- a/docs/guides/websocket/context.md +++ b/docs/guides/websocket/context.mdx @@ -1,13 +1,15 @@ --- -name: Set per-socket contextual data on a WebSocket +title: Set per-socket contextual data on a WebSocket +sidebarTitle: Contextual data +mode: center --- When building a WebSocket server, it's typically necessary to store some identifying information or context associated with each connected client. -With [Bun.serve()](https://bun.com/docs/api/websockets#contextual-data), this "contextual data" is set when the connection is initially upgraded by passing a `data` parameter in the `server.upgrade()` call. +With [Bun.serve()](https://bun.com/docs/api/websockets contextual-data), this "contextual data" is set when the connection is initially upgraded by passing a `data` parameter in the `server.upgrade()` call. -```ts -Bun.serve({ +```ts server.ts icon="/icons/typescript.svg" +Bun.serve<{ socketId: number }>({ fetch(req, server) { const success = server.upgrade(req, { data: { @@ -20,9 +22,6 @@ Bun.serve({ // ... }, websocket: { - // TypeScript: specify the type of ws.data like this - data: {} as { socketId: number }, - // define websocket handlers async message(ws, message) { // the contextual data is available as the `data` property @@ -37,14 +36,15 @@ Bun.serve({ It's common to read cookies/headers from the incoming request to identify the connecting client. -```ts +```ts server.ts icon="/icons/typescript.svg" type WebSocketData = { createdAt: number; token: string; userId: string; }; -Bun.serve({ +// TypeScript: specify the type of `data` +Bun.serve({ async fetch(req, server) { // use a library to parse cookies const cookies = parseCookies(req.headers.get("Cookie")); @@ -62,9 +62,6 @@ Bun.serve({ if (upgraded) return undefined; }, websocket: { - // TypeScript: specify the type of ws.data like this - data: {} as WebSocketData, - async message(ws, message) { // save the message to a database await saveMessageToDatabase({ diff --git a/docs/guides/websocket/index.json b/docs/guides/websocket/index.json deleted file mode 100644 index cb4d71b1ad..0000000000 --- a/docs/guides/websocket/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "WebSocket", - "description": "A collection of guides relating to building WebSocket servers with Bun" -} diff --git a/docs/guides/websocket/pubsub.md b/docs/guides/websocket/pubsub.mdx similarity index 86% rename from docs/guides/websocket/pubsub.md rename to docs/guides/websocket/pubsub.mdx index b0adb26d65..b70c094046 100644 --- a/docs/guides/websocket/pubsub.md +++ b/docs/guides/websocket/pubsub.mdx @@ -1,13 +1,15 @@ --- -name: Build a publish-subscribe WebSocket server +title: Build a publish-subscribe WebSocket server +sidebarTitle: Pub-sub server +mode: center --- Bun's server-side `WebSocket` API provides a native pub-sub API. Sockets can be subscribed to a set of named channels using `socket.subscribe()`; messages can be published to a channel using `socket.publish(, )`. This code snippet implements a simple single-channel chat server. -```ts -const server = Bun.serve({ +```ts server.ts icon="/icons/typescript.svg" +const server = Bun.serve<{ username: string }>({ fetch(req, server) { const cookies = req.headers.get("cookie"); const username = getUsernameFromCookies(cookies); @@ -17,9 +19,6 @@ const server = Bun.serve({ return new Response("Hello world"); }, websocket: { - // TypeScript: specify the type of ws.data like this - data: {} as { username: string }, - open(ws) { const msg = `${ws.data.username} has entered the chat`; ws.subscribe("the-group-chat"); diff --git a/docs/guides/websocket/simple.md b/docs/guides/websocket/simple.mdx similarity index 81% rename from docs/guides/websocket/simple.md rename to docs/guides/websocket/simple.mdx index 81b2876ddc..1a2dd2dc15 100644 --- a/docs/guides/websocket/simple.md +++ b/docs/guides/websocket/simple.mdx @@ -1,13 +1,15 @@ --- -name: Build a simple WebSocket server +title: Build a simple WebSocket server +sidebarTitle: Simple server +mode: center --- Start a simple WebSocket server using [`Bun.serve`](https://bun.com/docs/api/http). Inside `fetch`, we attempt to upgrade incoming `ws:` or `wss:` requests to WebSocket connections. -```ts -const server = Bun.serve({ +```ts server.ts icon="/icons/typescript.svg" +const server = Bun.serve<{ authToken: string }>({ fetch(req, server) { const success = server.upgrade(req); if (success) { diff --git a/docs/guides/write-file/append.md b/docs/guides/write-file/append.mdx similarity index 93% rename from docs/guides/write-file/append.md rename to docs/guides/write-file/append.mdx index 40f534a625..44104e1076 100644 --- a/docs/guides/write-file/append.md +++ b/docs/guides/write-file/append.mdx @@ -1,5 +1,7 @@ --- -name: Append content to a file +title: Append content to a file +sidebarTitle: Append to file +mode: center --- Bun implements the `node:fs` module, which includes the `fs.appendFile` and `fs.appendFileSync` functions for appending content to files. diff --git a/docs/guides/write-file/basic.md b/docs/guides/write-file/basic.mdx similarity index 93% rename from docs/guides/write-file/basic.md rename to docs/guides/write-file/basic.mdx index 12396d2f69..4edd4207a2 100644 --- a/docs/guides/write-file/basic.md +++ b/docs/guides/write-file/basic.mdx @@ -1,5 +1,7 @@ --- -name: Write a string to a file +title: Write a string to a file +sidebarTitle: Write string to file +mode: center --- This code snippet writes a string to disk at a particular _absolute path_. diff --git a/docs/guides/write-file/blob.md b/docs/guides/write-file/blob.mdx similarity index 92% rename from docs/guides/write-file/blob.md rename to docs/guides/write-file/blob.mdx index 3a42ec134e..ccaaaefc78 100644 --- a/docs/guides/write-file/blob.md +++ b/docs/guides/write-file/blob.mdx @@ -1,5 +1,7 @@ --- -name: Write a Blob to a file +title: Write a Blob to a file +sidebarTitle: Write Blob +mode: center --- This code snippet writes a `Blob` to disk at a particular path. diff --git a/docs/guides/write-file/cat.md b/docs/guides/write-file/cat.mdx similarity index 80% rename from docs/guides/write-file/cat.md rename to docs/guides/write-file/cat.mdx index b608266112..44b5d91cf0 100644 --- a/docs/guides/write-file/cat.md +++ b/docs/guides/write-file/cat.mdx @@ -1,12 +1,14 @@ --- -name: Write a file to stdout +title: Write a file to stdout +sidebarTitle: Write file to stdout +mode: center --- Bun exposes `stdout` as a `BunFile` with the `Bun.stdout` property. This can be used as a destination for [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write). This code writes a file to `stdout` similar to the `cat` command in Unix. -```ts#cat.ts +```ts cat.ts icon="/icons/typescript.svg" const path = "/path/to/file.txt"; const file = Bun.file(path); await Bun.write(Bun.stdout, file); diff --git a/docs/guides/write-file/file-cp.md b/docs/guides/write-file/file-cp.mdx similarity index 88% rename from docs/guides/write-file/file-cp.md rename to docs/guides/write-file/file-cp.mdx index 1662cc5562..d43d5f7225 100644 --- a/docs/guides/write-file/file-cp.md +++ b/docs/guides/write-file/file-cp.mdx @@ -1,5 +1,7 @@ --- -name: Copy a file to another location +title: Copy a file to another location +sidebarTitle: Copy file +mode: center --- This code snippet copies a file to another location on disk. diff --git a/docs/guides/write-file/filesink.md b/docs/guides/write-file/filesink.mdx similarity index 93% rename from docs/guides/write-file/filesink.md rename to docs/guides/write-file/filesink.mdx index 89cf46ca93..5414ed933f 100644 --- a/docs/guides/write-file/filesink.md +++ b/docs/guides/write-file/filesink.mdx @@ -1,5 +1,7 @@ --- -name: Write a file incrementally +title: Write a file incrementally +sidebarTitle: Incremental write +mode: center --- Bun provides an API for incrementally writing to a file. This is useful for writing large files, or for writing to a file over a long period of time. diff --git a/docs/guides/write-file/index.json b/docs/guides/write-file/index.json deleted file mode 100644 index dea48e215f..0000000000 --- a/docs/guides/write-file/index.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Writing files", - "description": "A collection of guides for writing files with Bun" -} diff --git a/docs/guides/write-file/response.md b/docs/guides/write-file/response.mdx similarity index 89% rename from docs/guides/write-file/response.md rename to docs/guides/write-file/response.mdx index b4b31fbb53..2f0721623a 100644 --- a/docs/guides/write-file/response.md +++ b/docs/guides/write-file/response.mdx @@ -1,5 +1,7 @@ --- -name: Write a Response to a file +title: Write a Response to a file +sidebarTitle: Write Response +mode: center --- This code snippet writes a `Response` to disk at a particular path. Bun will consume the `Response` body according to its `Content-Type` header. diff --git a/docs/guides/write-file/stdout.md b/docs/guides/write-file/stdout.mdx similarity index 89% rename from docs/guides/write-file/stdout.md rename to docs/guides/write-file/stdout.mdx index c756a8a44e..1e980c0947 100644 --- a/docs/guides/write-file/stdout.md +++ b/docs/guides/write-file/stdout.mdx @@ -1,5 +1,7 @@ --- -name: Write to stdout +title: Write to stdout +sidebarTitle: Write to stdout +mode: center --- The `console.log` function writes to `stdout`. It will automatically append a line break at the end of the printed data. diff --git a/docs/guides/write-file/stream.md b/docs/guides/write-file/stream.mdx similarity index 86% rename from docs/guides/write-file/stream.md rename to docs/guides/write-file/stream.mdx index 6cc5b87eee..739c60c320 100644 --- a/docs/guides/write-file/stream.md +++ b/docs/guides/write-file/stream.mdx @@ -1,5 +1,7 @@ --- -name: Write a ReadableStream to a file +title: Write a ReadableStream to a file +sidebarTitle: Write stream +mode: center --- To write a `ReadableStream` to disk, first create a `Response` instance from the stream. This `Response` can then be written to disk using [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write). diff --git a/docs/guides/write-file/unlink.md b/docs/guides/write-file/unlink.mdx similarity index 85% rename from docs/guides/write-file/unlink.md rename to docs/guides/write-file/unlink.mdx index 85d87b3021..d20cc37287 100644 --- a/docs/guides/write-file/unlink.md +++ b/docs/guides/write-file/unlink.mdx @@ -1,5 +1,7 @@ --- -name: Delete a file +title: Delete a file +sidebarTitle: Delete file +mode: center --- The `Bun.file()` function accepts a path and returns a `BunFile` instance. Use the `.delete()` method to delete the file. diff --git a/docs/icons/apple.svg b/docs/icons/apple.svg new file mode 100644 index 0000000000..ea789e759f --- /dev/null +++ b/docs/icons/apple.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/docs/icons/bun.svg b/docs/icons/bun.svg new file mode 100644 index 0000000000..74127caf4b --- /dev/null +++ b/docs/icons/bun.svg @@ -0,0 +1 @@ +Bun Logo \ No newline at end of file diff --git a/docs/icons/ecosystem/prisma.svg b/docs/icons/ecosystem/prisma.svg new file mode 100644 index 0000000000..5aa0baf319 --- /dev/null +++ b/docs/icons/ecosystem/prisma.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/docs/icons/homebrew.svg b/docs/icons/homebrew.svg new file mode 100644 index 0000000000..eb47ab0977 --- /dev/null +++ b/docs/icons/homebrew.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/icons/javascript.svg b/docs/icons/javascript.svg new file mode 100644 index 0000000000..9650ca78ef --- /dev/null +++ b/docs/icons/javascript.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/docs/icons/linux.svg b/docs/icons/linux.svg new file mode 100644 index 0000000000..f7ba1c8053 --- /dev/null +++ b/docs/icons/linux.svg @@ -0,0 +1,362 @@ + + Tux + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/icons/npm.svg b/docs/icons/npm.svg new file mode 100644 index 0000000000..0bb562128d --- /dev/null +++ b/docs/icons/npm.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/docs/icons/rust.svg b/docs/icons/rust.svg new file mode 100644 index 0000000000..39ddf2a1f9 --- /dev/null +++ b/docs/icons/rust.svg @@ -0,0 +1,57 @@ + + + \ No newline at end of file diff --git a/docs/icons/typescript.svg b/docs/icons/typescript.svg new file mode 100644 index 0000000000..015ec83a47 --- /dev/null +++ b/docs/icons/typescript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/icons/windows.svg b/docs/icons/windows.svg new file mode 100644 index 0000000000..9840d84f0b --- /dev/null +++ b/docs/icons/windows.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/images/bun-create-shadcn.mp4 b/docs/images/bun-create-shadcn.mp4 new file mode 100644 index 0000000000..496d1b22b2 Binary files /dev/null and b/docs/images/bun-create-shadcn.mp4 differ diff --git a/docs/images/bun-s3-node.gif b/docs/images/bun-s3-node.gif new file mode 100644 index 0000000000..35fe990bd0 Binary files /dev/null and b/docs/images/bun-s3-node.gif differ diff --git a/docs/images/bundler-speed.png b/docs/images/bundler-speed.png new file mode 100644 index 0000000000..21a8942234 Binary files /dev/null and b/docs/images/bundler-speed.png differ diff --git a/docs/images/bundling.png b/docs/images/bundling.png new file mode 100644 index 0000000000..4fb75a5c8c Binary files /dev/null and b/docs/images/bundling.png differ diff --git a/docs/images/buntest.jpeg b/docs/images/buntest.jpeg new file mode 100644 index 0000000000..bcf3915db7 Binary files /dev/null and b/docs/images/buntest.jpeg differ diff --git a/docs/images/cat.jpg b/docs/images/cat.jpg new file mode 100644 index 0000000000..1dab72030b Binary files /dev/null and b/docs/images/cat.jpg differ diff --git a/docs/images/chrome-devtools-memory.png b/docs/images/chrome-devtools-memory.png new file mode 100644 index 0000000000..7081a3985e Binary files /dev/null and b/docs/images/chrome-devtools-memory.png differ diff --git a/docs/images/exception_page.png b/docs/images/exception_page.png new file mode 100644 index 0000000000..0541918215 Binary files /dev/null and b/docs/images/exception_page.png differ diff --git a/docs/images/ffi.png b/docs/images/ffi.png new file mode 100644 index 0000000000..7d0039a9fd Binary files /dev/null and b/docs/images/ffi.png differ diff --git a/docs/images/guides/astro.png b/docs/images/guides/astro.png new file mode 100644 index 0000000000..738871add8 Binary files /dev/null and b/docs/images/guides/astro.png differ diff --git a/docs/index.md b/docs/index.mdx similarity index 52% rename from docs/index.md rename to docs/index.mdx index c43862fa73..f45293b998 100644 --- a/docs/index.md +++ b/docs/index.mdx @@ -1,36 +1,108 @@ +--- +title: Welcome to Bun +description: Bun is an all-in-one toolkit for developing modern JavaScript/TypeScript applications. +mode: center +--- + + + + Get started with bun run + + } + > + A fast JavaScript runtime designed as a drop-in replacement for Node.js + + + Get started with bun install + + } + > + Install packages up to 30x faster than npm with a global cache and workspaces + + + Get started with bun test + + } + > + Jest-compatible, TypeScript-first tests with snapshots, DOM, and watch mode + + + Get started with bun build + + } + > + Bundle TypeScript, JSX, React & CSS for both browsers and servers + + + +--- + +## Get Started + +Bun ships as a single, dependency-free binary and includes a runtime, package manager, test runner, and bundler. New to Bun? + + + + Supported platforms and all install methods. + + + Hello world in minutes with Bun.serve. + + + +--- + +## What's Inside + +- Runtime: Execute JavaScript/TypeScript files and package scripts with near-zero overhead. +- Package Manager: Fast installs, workspaces, overrides, and audits with `bun install`. +- Test Runner: Jest-compatible, TypeScript-first tests with snapshots, DOM, and watch mode. +- Bundler: Native bundling for JS/TS/JSX with splitting, plugins, and HTML imports. + +Explore each area using the cards above. Each section is structured with an overview, quick examples, reference, and best practices for fast scanning and deep dives. + +--- + +## What is Bun? + Bun is an all-in-one toolkit for JavaScript and TypeScript apps. It ships as a single executable called `bun`. At its core is the _Bun runtime_, a fast JavaScript runtime designed as **a drop-in replacement for Node.js**. It's written in Zig and powered by JavaScriptCore under the hood, dramatically reducing startup times and memory usage. -```bash -$ bun run index.tsx # TS and JSX supported out of the box +```bash terminal icon="terminal" +bun run index.tsx # TS and JSX supported out of the box ``` The `bun` command-line tool also implements a test runner, script runner, and Node.js-compatible package manager, all significantly faster than existing tools and usable in existing Node.js projects with little to no changes necessary. -```bash -$ bun run start # run the `start` script -$ bun install # install a package -$ bun build ./index.tsx # bundle a project for browsers -$ bun test # run tests -$ bunx cowsay 'Hello, world!' # execute a package +```bash terminal icon="terminal" +bun run start # run the `start` script +bun install # install a package +bun build ./index.tsx # bundle a project for browsers +bun test # run tests +bunx cowsay 'Hello, world!' # execute a package ``` -Get started with one of the quick links below, or read on to learn more about Bun. - -{% block className="gap-2 grid grid-flow-row grid-cols-1 md:grid-cols-2" %} -{% arrowbutton href="/docs/installation" text="Install Bun" /%} -{% arrowbutton href="/docs/quickstart" text="Do the quickstart" /%} -{% arrowbutton href="/docs/cli/install" text="Install a package" /%} -{% arrowbutton href="/docs/cli/bun-create" text="Use a project template" /%} -{% arrowbutton href="/docs/bundler" text="Bundle code for production" /%} -{% arrowbutton href="/docs/api/http" text="Build an HTTP server" /%} -{% arrowbutton href="/docs/api/websockets" text="Build a Websocket server" /%} -{% arrowbutton href="/docs/api/file-io" text="Read and write files" /%} -{% arrowbutton href="/docs/api/sqlite" text="Run SQLite queries" /%} -{% arrowbutton href="/docs/cli/test" text="Write and run tests" /%} -{% /block %} - ## What is a runtime? JavaScript (or, more formally, ECMAScript) is just a _specification_ for a programming language. Anyone can write a JavaScript _engine_ that ingests a valid JavaScript program and executes it. The two most popular engines in use today are V8 (developed by Google) @@ -42,21 +114,12 @@ But most JavaScript programs don't run in a vacuum. They need a way to access th Notably, browsers ship with JavaScript runtimes that implement a set of Web-specific APIs that are exposed via the global `window` object. Any JavaScript code executed by the browser can use these APIs to implement interactive or dynamic behavior in the context of the current webpage. - - ### Node.js Similarly, Node.js is a JavaScript runtime that can be used in non-browser environments, like servers. JavaScript programs executed by Node.js have access to a set of Node.js-specific [globals](https://nodejs.org/api/globals.html) like `Buffer`, `process`, and `__dirname` in addition to built-in modules for performing OS-level tasks like reading/writing files (`node:fs`) and networking (`node:net`, `node:http`). Node.js also implements a CommonJS-based module system and resolution algorithm that pre-dates JavaScript's native module system. - - Bun is designed as a faster, leaner, more modern replacement for Node.js. - - ## Design goals Bun is designed from the ground-up with today's JavaScript ecosystem in mind. @@ -64,14 +127,7 @@ Bun is designed from the ground-up with today's JavaScript ecosystem in mind. - **Speed**. Bun processes start [4x faster than Node.js](https://twitter.com/jarredsumner/status/1499225725492076544) currently (try it yourself!) - **TypeScript & JSX support**. You can directly execute `.jsx`, `.ts`, and `.tsx` files; Bun's transpiler converts these to vanilla JavaScript before execution. - **ESM & CommonJS compatibility**. The world is moving towards ES modules (ESM), but millions of packages on npm still require CommonJS. Bun recommends ES modules, but supports CommonJS. -- **Web-standard APIs**. Bun implements standard Web APIs like `fetch`, `WebSocket`, and `ReadableStream`. Bun is powered by the JavaScriptCore engine, which is developed by Apple for Safari, so some APIs like [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) and [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) directly use [Safari's implementation](https://github.com/oven-sh/bun/blob/HEAD/src/bun.js/bindings/webcore/JSFetchHeaders.cpp). -- **Node.js compatibility**. In addition to supporting Node-style module resolution, Bun aims for full compatibility with built-in Node.js globals (`process`, `Buffer`) and modules (`path`, `fs`, `http`, etc.) _This is an ongoing effort that is not complete._ Refer to the [compatibility page](https://bun.com/docs/runtime/nodejs-apis) for the current status. +- **Web-standard APIs**. Bun implements standard Web APIs like `fetch`, `WebSocket`, and `ReadableStream`. Bun is powered by the JavaScriptCore engine, which is developed by Apple for Safari, so some APIs like [`Headers`](https://developer.mozilla.org/en-US/Web/API/Headers) and [`URL`](https://developer.mozilla.org/en-US/Web/API/URL) directly use [Safari's implementation](https://github.com/oven-sh/bun/blob/HEAD/src/bun.js/bindings/webcore/JSFetchHeaders.cpp). +- **Node.js compatibility**. In addition to supporting Node-style module resolution, Bun aims for full compatibility with built-in Node.js globals (`process`, `Buffer`) and modules (`path`, `fs`, `http`, etc.) _This is an ongoing effort that is not complete._ Refer to the [compatibility page](/runtime/nodejs-compat) for the current status. Bun is more than a runtime. The long-term goal is to be a cohesive, infrastructural toolkit for building apps with JavaScript/TypeScript, including a package manager, transpiler, bundler, script runner, test runner, and more. - - diff --git a/docs/install/index.md b/docs/install/index.md deleted file mode 100644 index 3ecd5c7838..0000000000 --- a/docs/install/index.md +++ /dev/null @@ -1,248 +0,0 @@ -The `bun` CLI contains an `npm`-compatible package manager designed to be a faster replacement for existing package management tools like `npm`, `yarn`, and `pnpm`. It's designed for Node.js compatibility; use it in any Bun or Node.js project. - -{% callout %} - -**⚡️ 80x faster** — Switch from `npm install` to `bun install` in any Node.js project to make your installations up to 80x faster. - -{% image src="https://user-images.githubusercontent.com/709451/147004342-571b6123-17a9-49a2-8bfd-dcfc5204047e.png" height="200" /%} - -{% /callout %} - -{% details summary="For Linux users" %} -The minimum Linux Kernel version is 5.1. If you're on Linux kernel 5.1 - 5.5, `bun install` should still work, but HTTP requests will be slow due to a lack of support for io_uring's `connect()` operation. - -If you're using Ubuntu 20.04, here's how to install a [newer kernel](https://wiki.ubuntu.com/Kernel/LTSEnablementStack): - -```bash -# If this returns a version >= 5.6, you don't need to do anything -uname -r - -# Install the official Ubuntu hardware enablement kernel -sudo apt install --install-recommends linux-generic-hwe-20.04 -``` - -{% /details %} - -## Manage dependencies - -### `bun install` - -To install all dependencies of a project: - -```bash -$ bun install -``` - -On Linux, `bun install` tends to install packages 20-100x faster than `npm install`. On macOS, it's more like 4-80x. - -![package install benchmark](https://user-images.githubusercontent.com/709451/147004342-571b6123-17a9-49a2-8bfd-dcfc5204047e.png) - -Running `bun install` will: - -- **Install** all `dependencies`, `devDependencies`, and `optionalDependencies`. Bun will install `peerDependencies` by default. -- **Run** your project's `{pre|post}install` scripts at the appropriate time. For security reasons Bun _does not execute_ lifecycle scripts of installed dependencies. -- **Write** a `bun.lock` lockfile to the project root. - -To install in production mode (i.e. without `devDependencies`): - -```bash -$ bun install --production -``` - -To install dependencies without allowing changes to lockfile (useful on CI): - -```bash -$ bun install --frozen-lockfile -``` - -To exclude dependency types from installing, use `--omit` with `dev`, `optional`, or `peer`: - -```bash -# Disable devDependencies and optionalDependencies -$ bun install --omit=dev --omit=optional -``` - -To perform a dry run (i.e. don't actually install anything or update the lockfile): - -```bash -$ bun install --dry-run -``` - -To generate a lockfile without install packages: - -```bash -$ bun install --lockfile-only -``` - -To modify logging verbosity: - -```bash -$ bun install --verbose # debug logging -$ bun install --silent # no logging -``` - -To use isolated installs instead of the default hoisted strategy: - -```bash -$ bun install --linker isolated -``` - -Isolated installs create strict dependency isolation similar to pnpm, preventing phantom dependencies and ensuring more deterministic builds. For complete documentation, see [Isolated installs](https://bun.com/docs/install/isolated). - -To protect against supply chain attacks, set a minimum age (in seconds) for package versions: - -```bash -$ bun install --minimum-release-age 259200 # 3 days -``` - -{% details summary="Configuring behavior" %} -The default behavior of `bun install` can be configured in `bunfig.toml`: - -```toml -[install] - -# whether to install optionalDependencies -optional = true - -# whether to install devDependencies -dev = true - -# whether to install peerDependencies -peer = true - -# equivalent to `--production` flag -production = false - -# equivalent to `--save-text-lockfile` flag -saveTextLockfile = false - -# equivalent to `--frozen-lockfile` flag -frozenLockfile = false - -# equivalent to `--dry-run` flag -dryRun = false - -# equivalent to `--concurrent-scripts` flag -concurrentScripts = 16 # (cpu count or GOMAXPROCS) x2 - -# installation strategy: "hoisted" or "isolated" -# default: "hoisted" -linker = "hoisted" - -# minimum package age in seconds (protects against supply chain attacks) -minimumReleaseAge = 259200 # 3 days - -# exclude packages from age requirement -minimumReleaseAgeExcludes = ["@types/node", "typescript"] -``` - -{% /details %} - -### `bun add` - -To add a particular package: - -```bash -$ bun add preact -``` - -To specify a version, version range, or tag: - -```bash -$ bun add zod@3.20.0 -$ bun add zod@^3.0.0 -$ bun add zod@latest -``` - -To add a package as a dev dependency (`"devDependencies"`): - -```bash -$ bun add --dev @types/react -$ bun add -d @types/react -``` - -To add a package as an optional dependency (`"optionalDependencies"`): - -```bash -$ bun add --optional lodash -``` - -To add a package as a peer dependency (`"peerDependencies"`): - -```bash -$ bun add --peer @types/bun -``` - -To install a package globally: - -```bash -$ bun add --global cowsay # or `bun add -g cowsay` -$ cowsay "Bun!" - ______ -< Bun! > - ------ - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || -``` - -{% details summary="Configuring global installation behavior" %} - -```toml -[install] -# where `bun install --global` installs packages -globalDir = "~/.bun/install/global" - -# where globally-installed package bins are linked -globalBinDir = "~/.bun/bin" -``` - -{% /details %} -To view a complete list of options for a given command: - -```bash -$ bun add --help -``` - -### `bun remove` - -To remove a dependency: - -```bash -$ bun remove preact -``` - -## Git dependencies - -To add a dependency from a git repository: - -```bash -$ bun install git@github.com:moment/moment.git -``` - -Bun supports a variety of protocols, including [`github`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#github-urls), [`git`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#git-urls-as-dependencies), `git+ssh`, `git+https`, and many more. - -```json -{ - "dependencies": { - "dayjs": "git+https://github.com/iamkun/dayjs.git", - "lodash": "git+ssh://github.com/lodash/lodash.git#4.17.21", - "moment": "git@github.com:moment/moment.git", - "zod": "github:colinhacks/zod" - } -} -``` - -## Tarball dependencies - -A package name can correspond to a publicly hosted `.tgz` file. During `bun install`, Bun will download and install the package from the specified tarball URL, rather than from the package registry. - -```json#package.json -{ - "dependencies": { - "zod": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz" - } -} -``` diff --git a/docs/installation.md b/docs/installation.md deleted file mode 100644 index ea5602c855..0000000000 --- a/docs/installation.md +++ /dev/null @@ -1,327 +0,0 @@ -Bun ships as a single executable with no dependencies that can be installed a few different ways. - -## Installing - -### macOS and Linux - -{% callout %} -**Linux users** — The `unzip` package is required to install Bun. Use `sudo apt install unzip` to install `unzip` package. -Kernel version 5.6 or higher is strongly recommended, but the minimum is 5.1. Use `uname -r` to check Kernel version. -{% /callout %} - -{% codetabs %} - -```bash#macOS/Linux_(curl) -$ curl -fsSL https://bun.com/install | bash # for macOS, Linux, and WSL -# to install a specific version -$ curl -fsSL https://bun.com/install | bash -s "bun-v$BUN_LATEST_VERSION" -``` - -```bash#npm -$ npm install -g bun # the last `npm` command you'll ever need -``` - -```bash#Homebrew -$ brew install oven-sh/bun/bun # for macOS and Linux -``` - -```bash#Docker -$ docker pull oven/bun -$ docker run --rm --init --ulimit memlock=-1:-1 oven/bun -``` - -{% /codetabs %} - -### Windows - -To install, paste this into a terminal: - -{% codetabs %} - -```powershell#PowerShell/cmd.exe -> powershell -c "irm bun.sh/install.ps1|iex" -``` - -```powershell#npm -> npm install -g bun # the last `npm` command you'll ever need -``` - -```powershell#Scoop -> scoop install bun -``` - -{% /codetabs %} - -{% callout %} -Bun requires a minimum of Windows 10 version 1809 -{% /callout %} - -For support and discussion, please join the [#windows channel on our Discord](http://bun.com/discord). - -## Docker - -Bun provides a [Docker image](https://hub.docker.com/r/oven/bun/tags) that supports both Linux x64 and arm64. - -```bash -$ docker pull oven/bun -$ docker run --rm --init --ulimit memlock=-1:-1 oven/bun -``` - -There are also image variants for different operating systems. - -```bash -$ docker pull oven/bun:debian -$ docker pull oven/bun:slim -$ docker pull oven/bun:distroless -$ docker pull oven/bun:alpine -``` - -## Checking installation - -To check that Bun was installed successfully, open a new terminal window and run `bun --version`. - -```sh -$ bun --version -1.x.y -``` - -To see the precise commit of [oven-sh/bun](https://github.com/oven-sh/bun) that you're using, run `bun --revision`. - -```sh -$ bun --revision -1.x.y+b7982ac13189 -``` - -If you've installed Bun but are seeing a `command not found` error, you may have to manually add the installation directory (`~/.bun/bin`) to your `PATH`. - -### How to add your `PATH` - -{% details summary="Linux / Mac" %} -First, determine what shell you're using: - -```sh -$ echo $SHELL -/bin/zsh # or /bin/bash or /bin/fish -``` - -Then add these lines below to bottom of your shell's configuration file. - -{% codetabs %} - -```bash#~/.zshrc -# add to ~/.zshrc -export BUN_INSTALL="$HOME/.bun" -export PATH="$BUN_INSTALL/bin:$PATH" -``` - -```bash#~/.bashrc -# add to ~/.bashrc -export BUN_INSTALL="$HOME/.bun" -export PATH="$BUN_INSTALL/bin:$PATH" -``` - -```sh#~/.config/fish/config.fish -# add to ~/.config/fish/config.fish -export BUN_INSTALL="$HOME/.bun" -export PATH="$BUN_INSTALL/bin:$PATH" -``` - -{% /codetabs %} -Save the file. You'll need to open a new shell/terminal window for the changes to take effect. - -{% /details %} - -{% details summary="Windows" %} -First, determine if the bun binary is properly installed on your system: - -```pwsh -& "$env:USERPROFILE\.bun\bin\bun" --version -``` - -If the command runs successfully but `bun --version` is not recognized, it means that bun is not in your system's `PATH`. To fix this, open a Powershell terminal and run the following command: - -```pwsh -[System.Environment]::SetEnvironmentVariable( - "Path", - [System.Environment]::GetEnvironmentVariable("Path", "User") + ";$env:USERPROFILE\.bun\bin", - [System.EnvironmentVariableTarget]::User -) -``` - -After running the command, restart your terminal and test with `bun --version` - -{% /details %} - -## Upgrading - -Once installed, the binary can upgrade itself. - -```sh -$ bun upgrade -``` - -{% callout %} -**Homebrew users** — To avoid conflicts with Homebrew, use `brew upgrade bun` instead. - -**Scoop users** — To avoid conflicts with Scoop, use `scoop update bun` instead. - -{% /callout %} - -## Canary builds - -Bun automatically releases an (untested) canary build on every commit to `main`. To upgrade to the latest canary build: - -```sh -$ bun upgrade --canary -``` - -The canary build is useful for testing new features and bug fixes before they're released in a stable build. To help the Bun team fix bugs faster, canary builds automatically upload crash reports to Bun's team. - -[View canary build](https://github.com/oven-sh/bun/releases/tag/canary) - -{% callout %} -**Note** — To switch back to a stable release from canary, run `bun upgrade --stable`. -{% /callout %} - -## Installing older versions of Bun - -Since Bun is a single binary, you can install older versions of Bun by re-running the installer script with a specific version. - -### Installing a specific version of Bun on Linux/Mac - -To install a specific version of Bun, you can pass the git tag of the version you want to install to the install script, such as `bun-v1.2.0` or `bun-v$BUN_LATEST_VERSION`. - -```sh -$ curl -fsSL https://bun.com/install | bash -s "bun-v$BUN_LATEST_VERSION" -``` - -### Installing a specific version of Bun on Windows - -On Windows, you can install a specific version of Bun by passing the version number to the Powershell install script. - -```sh -# PowerShell: -$ iex "& {$(irm https://bun.com/install.ps1)} -Version $BUN_LATEST_VERSION" -``` - -## Downloading Bun binaries directly - -To download Bun binaries directly, you can visit the [releases page](https://github.com/oven-sh/bun/releases) on GitHub. - -For convenience, here are download links for the latest version: - -- [`bun-linux-x64.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64.zip) -- [`bun-linux-x64-baseline.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64-baseline.zip) -- [`bun-linux-x64-musl.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64-musl.zip) -- [`bun-linux-x64-musl-baseline.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64-musl-baseline.zip) -- [`bun-windows-x64.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-windows-x64.zip) -- [`bun-windows-x64-baseline.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-windows-x64-baseline.zip) -- [`bun-darwin-aarch64.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-darwin-aarch64.zip) -- [`bun-linux-aarch64.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-aarch64.zip) -- [`bun-linux-aarch64-musl.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-aarch64-musl.zip) -- [`bun-darwin-x64.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-darwin-x64.zip) - -The `musl` binaries are built for distributions that do not ship with the glibc libraries by default, instead relying on musl. The two most popular distros are Void Linux and Alpine Linux, with the latter is used heavily in Docker containers. If you encounter an error like the following: `bun: /lib/x86_64-linux-gnu/libm.so.6: version GLIBC_2.29' not found (required by bun)`, try using the musl binary. Bun's install script automatically chooses the correct binary for your system. - -### CPU requirements and `baseline` builds - -Bun's `x64` binaries target the Haswell CPU architecture, which means they require AVX and AVX2 instructions. For Linux and Windows, the `x64-baseline` binaries are also available which target the Nehalem architecture. If you run into an "Illegal Instruction" error when running Bun, try using the `baseline` binaries instead. Bun's install script automatically chooses the correct binary for your system which helps avoid this issue. Baseline builds are slower than regular builds, so use them only if necessary. - -| Build | Intel requirement | AMD requirement | -| ------------ | ------------------------------------------------------------------ | ------------------ | -| x64 | Haswell (4th generation Core) or newer, except some low-end models | Excavator or newer | -| x64-baseline | Nehalem (1st generation Core) or newer | Bulldozer or newer | - -Bun does not currently support any CPUs older than the `baseline` target, which mandates the SSE4.2 extension. - -Bun also publishes `darwin-x64-baseline` binaries, but these are just a copy of the `darwin-x64` ones so they still have the same CPU requirement. We only maintain these since some tools expect them to exist. Bun requires macOS 13.0 or later, which does not support any CPUs that don't meet our requirement. - - - - - -## Uninstall - -If you need to remove Bun from your system, use the following commands. - -{% codetabs %} - -```bash#macOS/Linux_(curl) -$ rm -rf ~/.bun # for macOS, Linux, and WSL -``` - -```powershell#Windows -> powershell -c ~\.bun\uninstall.ps1 -``` - -```powershell#Scoop -> scoop uninstall bun -``` - -```bash#npm -$ npm uninstall -g bun -``` - -```bash#Homebrew -$ brew uninstall bun -``` - -{% /codetabs %} diff --git a/docs/installation.mdx b/docs/installation.mdx new file mode 100644 index 0000000000..9d5c971dae --- /dev/null +++ b/docs/installation.mdx @@ -0,0 +1,365 @@ +--- +title: Installation +description: Install Bun +--- + +## Overview + +Bun ships as a single, dependency-free executable. You can install it via script, package manager, or Docker across macOS, Linux, and Windows. + +After installation, verify with `bun --version` and `bun --revision`. + +## Installation + + + + + + ```bash curl icon="globe" + curl -fsSL https://bun.com/install | bash + ``` + + + + **Linux users**  The `unzip` package is required to install Bun. Use `sudo apt install unzip` to install the unzip package. Kernel version 5.6 or higher is strongly recommended, but the minimum is 5.1. Use `uname -r` to check Kernel version. + + + + + + + ```powershell PowerShell icon="terminal" + powershell -c "irm bun.sh/install.ps1|iex" + ``` + + + + Bun requires Windows 10 version 1809 or later. + + + + For support and discussion, please join the **#windows** channel on our [Discord](https://discord.gg/bun). + + + + + + + ```bash npm icon="npm" + npm install -g bun # the last `npm` command you'll ever need + ``` + + ```bash Homebrew icon="/icons/homebrew.svg" + brew install oven-sh/bun/bun + ``` + + ```bash Scoop icon="terminal" + scoop install bun + ``` + + + + + + + Bun provides a Docker image that supports both Linux x64 and arm64. + + ```bash Docker icon="docker" + docker pull oven/bun + docker run --rm --init --ulimit memlock=-1:-1 oven/bun + ``` + + ### Image Variants + + There are also image variants for different operating systems: + + ```bash Docker icon="docker" + docker pull oven/bun:debian + docker pull oven/bun:slim + docker pull oven/bun:distroless + docker pull oven/bun:alpine + ``` + + + + +To check that Bun was installed successfully, open a new terminal window and run: + +```bash terminal icon="terminal" +bun --version +# Output: 1.x.y + +# See the precise commit of `oven-sh/bun` that you're using +bun --revision +# Output: 1.x.y+b7982ac13189 +``` + + + If you've installed Bun but are seeing a `command not found` error, you may have to manually add the installation + directory (`~/.bun/bin`) to your `PATH`. + + + + + + + + ```bash terminal icon="terminal" + echo $SHELL + # /bin/zsh or /bin/bash or /bin/fish + ``` + + + - For bash: `~/.bashrc` + - For zsh: `~/.zshrc` + - For fish: `~/.config/fish/config.fish` + + + + Add this line to your configuration file: + ```bash terminal icon="terminal" + export BUN_INSTALL="$HOME/.bun" + export PATH="$BUN_INSTALL/bin:$PATH" + ``` + + + + ```bash terminal icon="terminal" + source ~/.bashrc # or ~/.zshrc + ``` + + + + + + + ```bash terminal icon="terminal" + & "$env:USERPROFILE\.bun\bin\bun" --version + ``` + + If the command runs successfully but `bun --version` is not recognized, it means that bun is not in your system's PATH. To fix this, open a Powershell terminal and run the following command: + + ```bash terminal icon="terminal" + [System.Environment]::SetEnvironmentVariable( + "Path", + [System.Environment]::GetEnvironmentVariable("Path", "User") + ";$env:USERPROFILE\.bun\bin", + [System.EnvironmentVariableTarget]::User + ) + ``` + + + + After running the command, restart your terminal and test with `bun --version` + + ```bash terminal icon="terminal" + bun --version + ``` + + + + + + + +--- + +## Upgrading + +Once installed, the binary can upgrade itself: + +```bash terminal icon="terminal" +bun upgrade +``` + + +**Homebrew users**
+To avoid conflicts with Homebrew, use `brew upgrade bun` instead. + +**Scoop users**
+To avoid conflicts with Scoop, use `scoop update bun` instead. + +
+ +--- + +## Canary Builds + +[-> View canary build](https://github.com/oven-sh/bun/releases/tag/canary) + +Bun automatically releases an (untested) canary build on every commit to main. To upgrade to the latest canary build: + +```bash terminal icon="terminal" +# Upgrade to latest canary +bun upgrade --canary + +# Switch back to stable +bun upgrade --stable +``` + +The canary build is useful for testing new features and bug fixes before they're released in a stable build. To help the Bun team fix bugs faster, canary builds automatically upload crash reports to Bun's team. + +--- + +## Installing Older Versions + +Since Bun is a single binary, you can install older versions by re-running the installer script with a specific version. + + + + To install a specific version, pass the git tag to the install script: + + ```bash terminal icon="terminal" + curl -fsSL https://bun.com/install | bash -s "bun-v1.3.1" + ``` + + + + On Windows, pass the version number to the PowerShell install script: + + ```powershell PowerShell icon="windows" + iex "& {$(irm https://bun.com/install.ps1)} -Version 1.3.1" + ``` + + + + +--- + +## Direct Downloads + +To download Bun binaries directly, visit the [releases page on GitHub](https://github.com/oven-sh/bun/releases). + +### Latest Version Downloads + + + + Standard Linux x64 binary + + + For older CPUs without AVX2 + + + Standard Windows binary + + + For older CPUs without AVX2 + + + Apple Silicon (M1/M2/M3) + + + Intel Macs + + + ARM64 Linux systems + + + +### Musl Binaries + +For distributions without `glibc` (Alpine Linux, Void Linux): + +- [Linux x64 musl](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64-musl.zip) +- [Linux x64 musl baseline](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64-musl-baseline.zip) +- [Linux ARM64 musl](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-aarch64-musl.zip) + + + If you encounter an error like `bun: /lib/x86_64-linux-gnu/libm.so.6: version GLIBC_2.29 not found`, try using the + musl binary. Bun's install script automatically chooses the correct binary for your system. + + +--- + +## CPU Requirements + +Bun has specific CPU requirements based on the binary you're using: + + + + **x64 binaries** target the Haswell CPU architecture (AVX and AVX2 instructions required) + | Platform | Intel Requirement | AMD Requirement | + |----------|-------------------|-----------------| + | x64 | Haswell (4th gen Core) or newer | Excavator or newer | + + + + **x64-baseline binaries** target the Nehalem architecture for older CPUs + | Platform | Intel Requirement | AMD Requirement | + |----------|-------------------|-----------------| + | x64-baseline | Nehalem (1st gen Core) or newer | Bulldozer or newer | + + + Baseline builds are slower than regular builds. Use them only if you encounter an "Illegal + Instruction" error. + + + + + + + Bun does not support CPUs older than the baseline target, which mandates the SSE4.2 extension. macOS requires version + 13.0 or later. + + +--- + +## Uninstall + +To remove Bun from your system: + + + + ```bash terminal icon="terminal" + rm -rf ~/.bun + ``` + + + + ```powershell PowerShell icon="windows" + powershell -c ~\.bun\uninstall.ps1 + ``` + + + + + ```bash npm icon="npm" + npm uninstall -g bun + ``` + ```bash Homebrew icon="/icons/homebrew.svg" + brew uninstall bun + ``` + ```bash Scoop icon="terminal" + scoop uninstall bun + ``` + + + + diff --git a/docs/logo/bun.png b/docs/logo/bun.png new file mode 100644 index 0000000000..3447dd52bc Binary files /dev/null and b/docs/logo/bun.png differ diff --git a/docs/logo/logo-with-wordmark-dark.svg b/docs/logo/logo-with-wordmark-dark.svg new file mode 100644 index 0000000000..485367b1b5 --- /dev/null +++ b/docs/logo/logo-with-wordmark-dark.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/logo/logo-with-wordmark-light.svg b/docs/logo/logo-with-wordmark-light.svg new file mode 100644 index 0000000000..33abf6e2f6 --- /dev/null +++ b/docs/logo/logo-with-wordmark-light.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/logo/logo.svg b/docs/logo/logo.svg new file mode 100644 index 0000000000..7ef15001d2 --- /dev/null +++ b/docs/logo/logo.svg @@ -0,0 +1 @@ +Bun Logo \ No newline at end of file diff --git a/docs/nav.ts b/docs/nav.ts deleted file mode 100644 index 3d776961a0..0000000000 --- a/docs/nav.ts +++ /dev/null @@ -1,477 +0,0 @@ -export type Nav = { - items: NavItem[]; -}; - -export type NavItem = NavPage | NavDivider; -export type NavPage = { - type: "page"; - slug: string; - title: string; - disabled?: boolean; - href?: string; -}; -type NavDivider = { - type: "divider"; - title: string; -}; - -function page(slug: string, title: string, props: { disabled?: boolean; href?: string; description: string }): NavPage { - return { type: "page", slug, title, ...props }; -} -function divider(title: string): NavDivider { - return { type: "divider", title }; -} - -export default { - items: [ - divider("Intro"), - page("index", "What is Bun?", { - description: - "Bun is an all-in-one runtime for JavaScript and TypeScript apps. Build, run, and test apps with one fast tool.", - }), - page("installation", "Installation", { - description: "Install Bun with npm, Homebrew, Docker, or the official install script.", - }), - page("quickstart", "Quickstart", { - description: "Get started with Bun by building and running a simple HTTP server in 6 lines of TypeScript.", - }), - page("typescript", "TypeScript", { - description: "Install and configure type declarations for Bun's APIs", - }), - - divider("Templating"), - page("cli/init", "`bun init`", { - description: "Scaffold an empty Bun project.", - }), - page("cli/bun-create", "`bun create`", { - description: "Scaffold a new Bun project from an official template or GitHub repo.", - }), - - // page("typescript", "TypeScript"), - - // divider("CLI"), - // page("cli/run", "`bun run`", { - // description: - // "Use `bun run` to execute JavaScript/TypeScript files, package.json scripts, and executable packages.", - // }), - // page("cli/install", "`bun install`", { - // description: "A 100x faster npm client with workspaces, git dependencies, and private registry support.", - // }), - // page("cli/test", "`bun test`", { - // description: "Bun's test runner uses Jest-compatible syntax but runs 100x faster.", - // }), - // page("cli/create", "`bun create`", { - // description: "Scaffold a new Bun project from an official template or GitHub repo.", - // }), - // page("cli/bunx", "`bunx`", { - // description: - // "Use `bunx` to auto-install and run executable packages from npm, or use locally installed command-line tools.", - // }), - // page("cli/deploy", "`bun deploy`", { - // disabled: true, - // description: "Deploy your Bun app to the cloud (eventually)", - // }), - - // page("bundler", "Bundler"), - // page("cli/bun-install", "`bun install`"), - // page("cli/bun-create", "`bun create`"), - // page("cli/bun-upgrade", "`bun upgrade`"), - // page("cli/bun-bun", "`bun bun`"), - // page("cli/bun-init", "`bun init`"), - // page("cli/bun-completions", "`bun completions`"), - // page("bundev", "Dev server"), - // page("benchmarks", "Benchmarks"), - - divider("Runtime"), - page("cli/run", "`bun run`", { - description: "Use `bun run` to execute JavaScript/TypeScript files and package.json scripts.", - }), - // page("runtime/index", "Overview", { - // description: `Bun is a new JavaScript runtime designed to be a faster, leaner, more modern replacement for Node.js.`, - // }), - // page("runtime/performance", "Performance", { - // description: `Bun is a new JavaScript runtime designed to be a faster, leaner, more modern replacement for Node.js.`, - // }), - page("runtime/loaders", "File types", { - description: `Bun's runtime supports JavaScript/TypeScript files, JSX syntax, Wasm, JSON/TOML imports, and more.`, - }), - page("runtime/typescript", "TypeScript", { - description: `Bun can directly execute TypeScript files without additional configuration.`, - }), - page("runtime/jsx", "JSX", { - description: `Bun can directly execute TypeScript files without additional configuration.`, - }), - // page("runtime/apis", "APIs", { - // description: `Bun is a new JavaScript runtime designed to be a faster, leaner, more modern replacement for Node.js.`, - // }), - page("runtime/env", "Environment variables", { - description: `How to read and set environment variables, plus how to use them to configure Bun`, - }), - page("runtime/bun-apis", "Bun APIs", { - description: `Bun provides a set of highly optimized native APIs for performing common tasks.`, - }), - page("runtime/web-apis", "Web APIs", { - description: `Bun implements an array of Web-standard APIs like fetch, URL, and WebSocket.`, - }), - page("runtime/nodejs-apis", "Node.js compatibility", { - description: `Bun aims for full Node.js compatibility. This page tracks the current compatibility status.`, - }), - page("bundler/executables", "Single-file executable", { - description: "Compile a TypeScript or JavaScript file to a standalone executable", - }), - page("runtime/plugins", "Plugins", { - description: `Implement custom loaders and module resolution logic with Bun's plugin system.`, - }), - - // page("runtime/nodejs", "Node.js compatibility", { - // description: `Track the status of Bun's API compatibility with Node.js.`, - // }), - // page("runtime/web-apis", "Web APIs"), - // page("runtime/loaders", "Loaders"), - - page("runtime/hot", "Watch mode", { - description: `Reload your application & tests automatically.`, - }), - page("runtime/modules", "Module resolution", { - description: `Bun uses ESM and implements an extended version of the Node.js module resolution algorithm.`, - }), - page("runtime/autoimport", "Auto-install", { - description: `Never use node_modules again. Bun can optionally auto-install your dependencies on the fly.`, - }), - page("runtime/bunfig", "bunfig.toml", { - description: `Bun's runtime is configurable with environment variables and the bunfig.toml config file.`, - }), - page("runtime/debugger", "Debugger", { - description: `Debug your code with Bun's web-based debugger or VS Code extension`, - }), - page("runtime/framework", "Framework API", { - disabled: true, - description: - "Coming soon. Use the Framework API to build a fast, cloud-ready framework on top of Bun's bundler and runtime.", - }), - - divider("Package manager"), - page("cli/install", "`bun install`", { - description: - "Install all dependencies with `bun install`, or manage dependencies with `bun add` and `bun remove`.", - }), - page("cli/add", "`bun add`", { - description: "Add dependencies to your project.", - }), - page("cli/remove", "`bun remove`", { - description: "Remove dependencies from your project.", - }), - page("cli/update", "`bun update`", { - description: "Update your project's dependencies.", - }), - page("cli/publish", "`bun publish`", { - description: "Publish your package to an npm registry.", - }), - page("cli/outdated", "`bun outdated`", { - description: "Check for outdated dependencies.", - }), - page("cli/link", "`bun link`", { - description: "Install local packages as dependencies in your project.", - }), - page("cli/pm", "`bun pm`", { - description: "Utilities relating to package management with Bun.", - }), - page("cli/why", "`bun why`", { - description: "Explains why a package is installed in your project.", - }), - page("install/cache", "Global cache", { - description: - "Bun's package manager installs all packages into a shared global cache to avoid redundant re-downloads.", - }), - page("install/isolated", "Isolated installs", { - description: "Create strict dependency isolation, preventing phantom dependencies.", - }), - page("install/workspaces", "Workspaces", { - description: "Bun's package manager supports workspaces and monorepo development workflows.", - }), - page("install/catalogs", "Catalogs", { - description: "Use catalogs to share dependency versions between packages in a monorepo.", - }), - page("install/lifecycle", "Lifecycle scripts", { - description: "How Bun handles package lifecycle scripts with trustedDependencies", - }), - page("cli/filter", "Filter", { - description: "Run scripts in multiple packages in parallel", - }), - page("install/lockfile", "Lockfile", { - description: - "Bun's lockfile `bun.lock` tracks your resolved dependency tree, making future installs fast and repeatable.", - }), - page("install/registries", "Scopes and registries", { - description: - "How to configure private scopes, custom package registries, authenticating with npm token, and more.", - }), - page("install/overrides", "Overrides and resolutions", { - description: "Specify version ranges for nested dependencies", - }), - page("install/patch", "Patch dependencies", { - description: - "Patch dependencies in your project to fix bugs or add features without vendoring the entire package.", - }), - page("install/audit", "Audit dependencies", { - description: "Check installed packages for vulnerabilities.", - }), - page("install/npmrc", ".npmrc support", { - description: "Bun supports loading some configuration options from .npmrc", - }), - page("install/security-scanner-api", "Security Scanner API", { - description: "Scan your project for vulnerabilities with Bun's security scanner API.", - }), - // page("install/utilities", "Utilities", { - // description: "Use `bun pm` to introspect your global module cache or project dependency tree.", - // }), - - divider("Bundler"), - page("bundler", "`Bun.build`", { - description: "Bundle code for consumption in the browser with Bun's native bundler.", - }), - page("bundler/html", "HTML & static sites", { - description: `Zero-config HTML bundler for single-page apps and multi-page apps. Automatic bundling, TailwindCSS plugins, TypeScript, JSX, React support, and incredibly fast builds`, - }), - page("bundler/css", "CSS", { - description: `Production ready CSS bundler with support for modern CSS features, CSS modules, and more.`, - }), - page("bundler/fullstack", "Fullstack Dev Server", { - description: "Serve your frontend and backend from the same app with Bun's dev server.", - }), - page("bundler/hmr", "Hot reloading", { - description: `Update modules in a running application without reloading the page using import.meta.hot`, - }), - - page("bundler/loaders", "Loaders", { - description: "Bun's built-in loaders for the bundler and runtime", - }), - page("bundler/plugins", "Plugins", { - description: `Implement custom loaders and module resolution logic with Bun's plugin system.`, - }), - page("bundler/macros", "Macros", { - description: `Run JavaScript functions at bundle-time and inline the results into your bundle`, - }), - - page("bundler/vs-esbuild", "vs esbuild", { - description: `Guides for migrating from other bundlers to Bun.`, - }), - - divider("Test runner"), - page("cli/test", "`bun test`", { - description: "Bun's test runner uses Jest-compatible syntax but runs 100x faster.", - }), - page("test/writing", "Writing tests", { - description: - "Write your tests using Jest-like expect matchers, plus setup/teardown hooks, snapshot testing, and more", - }), - page("test/hot", "Watch mode", { - description: "Reload your tests automatically on change.", - }), - page("test/lifecycle", "Lifecycle hooks", { - description: "Add lifecycle hooks to your tests that run before/after each test or test run", - }), - page("test/mocks", "Mocks", { - description: "Mocks functions and track method calls", - }), - page("test/snapshots", "Snapshots", { - description: "Add lifecycle hooks to your tests that run before/after each test or test run", - }), - page("test/time", "Dates and times", { - description: "Control the date & time in your tests for more reliable and deterministic tests", - }), - - page("test/coverage", "Code coverage", { - description: "Generate code coverage reports with `bun test --coverage`", - }), - page("test/reporters", "Test reporters", { - description: "Add a junit reporter to your test runs", - }), - page("test/configuration", "Test configuration", { - description: "Configure the test runner with bunfig.toml", - }), - page("test/runtime-behavior", "Runtime behavior", { - description: "Learn how the test runner affects Bun's runtime behavior", - }), - page("test/discovery", "Finding tests", { - description: "Learn how the test runner discovers tests", - }), - page("test/dom", "DOM testing", { - description: "Write headless tests for UI and React/Vue/Svelte/Lit components with happy-dom", - }), - - divider("Package runner"), - page("cli/bunx", "`bunx`", { - description: "Use `bunx` to auto-install and run executable packages from npm.", - }), - - // page("runtime/nodejs", "Node.js APIs"), - - // divider("Ecosystem"), - // page("ecosystem/react", "React", { - // description: `The Bun runtime supports JSX syntax out of the box and optimizes server-side rendering.`, - // }), - // page("ecosystem/express", "Express", { - // description: `Servers built with Express and other major Node.js HTTP libraries work out of the box.`, - // }), - // page("ecosystem/elysia", "Elysia", { - // description: `Get started with Elysia, a Bun-native framework designed for the edge.`, - // }), - // page("ecosystem/hono", "Hono", { - // description: `Hono is an ultra-fast, Bun-friendly web framework designed for edge environments.`, - // }), - // page("ecosystem/buchta", "Buchta", { - // description: `Buchta is a Bun-native fullstack framework for Svelte and Preact apps.`, - // }), - // page("ecosystem/stric", "Stric", { - // description: `Stric is a minimalist, fast web framework for Bun.`, - // }), - // page("ecosystem/awesome", "Awesome", { - // href: "https://github.com/apvarun/awesome-bun", - // description: ``, - // }), - - divider("API"), - page("api/http", "HTTP server", { - description: `Bun implements a fast HTTP server built on Request/Response objects, along with supporting node:http APIs.`, - }), // "`Bun.serve`"), - page("api/fetch", "HTTP client", { - description: `Bun implements Web-standard fetch with some Bun-native extensions.`, - }), // "fetch"), - page("api/websockets", "WebSockets", { - description: `Bun supports server-side WebSockets with on-the-fly compression, TLS support, and a Bun-native pubsub API.`, - }), // "`Bun.serve`"), - page("api/workers", "Workers", { - description: `Run code in a separate thread with Bun's native Worker API.`, - }), // "`Worker`"), - page("api/binary-data", "Binary data", { - description: `How to represent and manipulate binary data in Bun.`, - }), // "`Bun.serve`"), - page("api/streams", "Streams", { - description: `Reading, writing, and manipulating streams of data in Bun.`, - }), // "`Bun.serve`"), - page("api/sql", "SQL", { - description: `Bun provides fast, native bindings for interacting with PostgreSQL databases.`, - }), - page("api/s3", "S3 Object Storage", { - description: `Bun provides fast, native bindings for interacting with S3-compatible object storage services.`, - }), - page("api/file-io", "File I/O", { - description: `Read and write files fast with Bun's heavily optimized file system API.`, - }), // "`Bun.write`"), - page("api/redis", "Redis Client", { - description: `Bun provides a fast, native Redis client with automatic command pipelining for better performance.`, - }), - page("api/import-meta", "import.meta", { - description: `Module-scoped metadata and utilities`, - }), // "`bun:sqlite`"), - page("api/sqlite", "SQLite", { - description: `The fastest SQLite driver for JavaScript is baked directly into Bun.`, - }), // "`bun:sqlite`"), - page("api/file-system-router", "FileSystemRouter", { - description: `Resolve incoming HTTP requests against a local file system directory with Bun's fast, Next.js-compatible router.`, - }), // "`Bun.FileSystemRouter`"), - page("api/tcp", "TCP sockets", { - description: `Bun's native API implements Web-standard TCP Sockets, plus a Bun-native API for building fast TCP servers.`, - }), // "`Bun.{listen|connect}`") - page("api/udp", "UDP sockets", { - description: `Bun's native API implements fast and flexible UDP sockets.`, - }), // "`Bun.udpSocket`") - page("api/globals", "Globals", { - description: `Bun implements a range of Web APIs, Node.js APIs, and Bun-native APIs that are available in the global scope.`, - }), // "`Bun.write`"), - page("runtime/shell", "$ Shell", { - description: `Bun's cross-platform shell-scripting API makes shell scripting with JavaScript fun`, - }), // "`Bun.$`"), - page("api/spawn", "Child processes", { - description: `Spawn sync and async child processes with easily configurable input and output streams.`, - }), // "`Bun.spawn`"), - page("api/yaml", "YAML", { - description: `Bun.YAML.parse(string) lets you parse YAML files in JavaScript`, - }), // "`Bun.spawn`"), - page("api/html-rewriter", "HTMLRewriter", { - description: `Parse and transform HTML with Bun's native HTMLRewriter API, inspired by Cloudflare Workers.`, - }), // "`HTMLRewriter`"), - page("api/hashing", "Hashing", { - description: `Native support for a range of fast hashing algorithms.`, - }), // "`Bun.serve`"), - page("api/console", "Console", { - description: `Bun implements a Node.js-compatible \`console\` object with colorized output and deep pretty-printing.`, - }), // "`Node-API`"), - page("api/cookie", "Cookie", { - description: "Bun's native Cookie API simplifies working with HTTP cookies.", - }), // "`Node-API`"), - page("api/ffi", "FFI", { - description: `Call native code from JavaScript with Bun's foreign function interface (FFI) API.`, - }), // "`bun:ffi`"), - page("api/cc", "C Compiler", { - description: `Build & run native C from JavaScript with Bun's native C compiler API`, - }), // "`bun:ffi`"), - page("api/secrets", "Secrets", { - description: `Store and retrieve sensitive credentials securely using the operating system's native credential storage APIs.`, - }), // "`Bun.secrets`"), - page("cli/test", "Testing", { - description: `Bun's built-in test runner is fast and uses Jest-compatible syntax.`, - }), // "`bun:test`"), - page("api/utils", "Utils", { - description: `Bun implements a set of utilities that are commonly required by developers.`, - }), // "`Bun.peek`"), - page("api/node-api", "Node-API", { - description: `Bun implements the Node-API spec for building native addons.`, - }), // "`Node-API`"), - - page("api/glob", "Glob", { - description: `Bun includes a fast native Glob implementation for matching file paths.`, - }), // "`Glob`"), - - page("api/dns", "DNS", { - description: `Resolve domain names to IP addresses.`, - }), // "`bun:dns`"), - - page("api/semver", "Semver", { - description: `Bun's native Semver implementation is 20x faster than the popular \`node-semver\` package.`, - }), // "`Semver`"), - - page("api/color", "Color", { - description: `Bun's color function leverages Bun's CSS parser for parsing, normalizing, and converting colors from user input to a variety of output formats.`, - }), // "`Color`"), - page("api/transpiler", "Transpiler", { - description: `Bun exposes its internal transpiler as a pluggable API.`, - }), // "`Bun.Transpiler`"), - - // divider("Dev Server"), - // page("bun-dev", "Vanilla"), - // page("dev/css", "CSS"), - // page("dev/frameworks", "Frameworks"), - // page("dev/nextjs", "Next.js"), - // page("dev/cra", "Create React App"), - - divider("Project"), - page("project/roadmap", "Roadmap", { - description: `Track Bun's near-term and long-term goals.`, - }), - - page("project/benchmarking", "Benchmarking", { - description: `Bun is designed for performance. Learn how to benchmark Bun yourself.`, - }), - page("project/contributing", "Contributing", { - description: "Learn how to contribute to Bun and get your local development environment up and running.", - }), - page("project/building-windows", "Building Windows", { - description: "Learn how to setup a development environment for contributing to the Windows build of Bun.", - }), - page("project/bindgen", "Bindgen", { - description: "About the bindgen code generator", - }), - page("project/licensing", "License", { - description: `Bun is a MIT-licensed project with a large number of statically-linked dependencies with various licenses.`, - }), - - // misc - // page("roadmap", "Roadmap"), - // page("troubleshooting", "Troubleshooting"), - // page("bunfig", "bunfig.toml"), - // page("upgrading-webkit", "Upgrading WebKit"), - // page("bun-flavored-toml", "Bun-flavored TOML"), - ], -} satisfies Nav; diff --git a/docs/normalize-internal-links.js b/docs/normalize-internal-links.js new file mode 100644 index 0000000000..3fd5d44504 --- /dev/null +++ b/docs/normalize-internal-links.js @@ -0,0 +1,44 @@ +(function () { + function normalizeInternalLinks() { + const selectors = [ + 'a[href*="bun.com/docs/installation"]', + 'a[href="https://bun.com/reference"]', + 'a[href="https://bun.com/blog"]', + ]; + + selectors.forEach(selector => { + const elements = document.querySelectorAll(selector); + elements.forEach(element => { + if (element.hasAttribute("target")) { + element.removeAttribute("target"); + // Also remove rel="noreferrer" if present, typically paired with target="_blank" + if (element.getAttribute("rel") === "noreferrer") { + element.removeAttribute("rel"); + } + console.log(`Removed target="_blank" from: ${element.textContent || element.innerHTML.substring(0, 50)}`); + } + }); + }); + } + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", normalizeInternalLinks); + } else { + normalizeInternalLinks(); + } + + const observer = new MutationObserver(function (mutations) { + mutations.forEach(function (mutation) { + if (mutation.type === "childList" || mutation.type === "attributes") { + normalizeInternalLinks(); + } + }); + }); + + observer.observe(document.body, { + childList: true, + subtree: true, + attributes: true, + attributeFilter: ["target", "href"], + }); +})(); diff --git a/docs/cli/bunx.md b/docs/pm/bunx.mdx similarity index 64% rename from docs/cli/bunx.md rename to docs/pm/bunx.mdx index bf2debdd4b..d2299cf9df 100644 --- a/docs/cli/bunx.md +++ b/docs/pm/bunx.mdx @@ -1,20 +1,24 @@ -{% callout %} -**Note** — `bunx` is an alias for `bun x`. The `bunx` CLI will be auto-installed when you install `bun`. -{% /callout %} +--- +title: "bunx" +description: "Run packages from npm" +--- + +`bunx` is an alias for `bun x`. The `bunx` CLI will be auto-installed when you install `bun`. Use `bunx` to auto-install and run packages from `npm`. It's Bun's equivalent of `npx` or `yarn dlx`. -```bash -$ bunx cowsay "Hello world!" +```bash terminal icon="terminal" +bunx cowsay "Hello world!" ``` -{% callout %} -⚡️ **Speed** — With Bun's fast startup times, `bunx` is [roughly 100x faster](https://twitter.com/jarredsumner/status/1606163655527059458) than `npx` for locally installed packages. -{% /callout %} + + ⚡️ **Speed** — With Bun's fast startup times, `bunx` is [roughly 100x + faster](https://twitter.com/jarredsumner/status/1606163655527059458) than `npx` for locally installed packages. + Packages can declare executables in the `"bin"` field of their `package.json`. These are known as _package executables_ or _package binaries_. -```jsonc#package.json +```json package.json icon="file-json" { // ... other fields "name": "my-cli", @@ -26,7 +30,7 @@ Packages can declare executables in the `"bin"` field of their `package.json`. T These executables are commonly plain JavaScript files marked with a [shebang line]() to indicate which program should be used to execute them. The following file indicates that it should be executed with `node`. -```js#dist/index.js +```ts dist/index.js icon="/icons/javascript.svg" #!/usr/bin/env node console.log("Hello world!"); @@ -34,8 +38,8 @@ console.log("Hello world!"); These executables can be run with `bunx`, -```bash -$ bunx my-cli +```bash terminal icon="terminal" +bunx my-cli ``` As with `npx`, `bunx` will check for a locally installed package first, then fall back to auto-installing the package from `npm`. Installed packages will be stored in Bun's global cache for future use. @@ -44,46 +48,36 @@ As with `npx`, `bunx` will check for a locally installed package first, then fal To pass additional command-line flags and arguments through to the executable, place them after the executable name. -```bash -$ bunx my-cli --foo bar +```bash terminal icon="terminal" +bunx my-cli --foo bar ``` ## Shebangs By default, Bun respects shebangs. If an executable is marked with `#!/usr/bin/env node`, Bun will spin up a `node` process to execute the file. However, in some cases it may be desirable to run executables using Bun's runtime, even if the executable indicates otherwise. To do so, include the `--bun` flag. -```bash -$ bunx --bun my-cli +```bash terminal icon="terminal" +bunx --bun my-cli ``` The `--bun` flag must occur _before_ the executable name. Flags that appear _after_ the name are passed through to the executable. -```bash -$ bunx --bun my-cli # good -$ bunx my-cli --bun # bad +```bash terminal icon="terminal" +bunx --bun my-cli # good +bunx my-cli --bun # bad ``` ## Package flag **`--package ` or `-p `** - Run binary from specific package. Useful when binary name differs from package name: -```bash +```bash terminal icon="terminal" bunx -p renovate renovate-config-validator bunx --package @angular/cli ng ``` To force bun to always be used with a script, use a shebang. -``` +```ts dist/index.js icon="/icons/javascript.svg" #!/usr/bin/env bun ``` - - diff --git a/docs/install/catalogs.md b/docs/pm/catalogs.mdx similarity index 90% rename from docs/install/catalogs.md rename to docs/pm/catalogs.mdx index bbf0dbf72a..cf1237f4c8 100644 --- a/docs/install/catalogs.md +++ b/docs/pm/catalogs.mdx @@ -1,3 +1,8 @@ +--- +title: "Catalogs" +description: "Share common dependency versions across multiple packages in a monorepo" +--- + Catalogs in Bun provide a straightforward way to share common dependency versions across multiple packages in a monorepo. Rather than specifying the same versions repeatedly in each workspace package, you define them once in the root package.json and reference them consistently throughout your project. ## Overview @@ -33,7 +38,7 @@ my-monorepo/ In your root-level `package.json`, add a `catalog` or `catalogs` field within the `workspaces` object: -```json +```json package.json icon="file-json" { "name": "my-monorepo", "workspaces": { @@ -58,9 +63,7 @@ If you put `catalog` or `catalogs` at the top level of the `package.json` file, In your workspace packages, use the `catalog:` protocol to reference versions: -**packages/app/package.json** - -```json +```json packages/app/package.json icon="file-json" { "name": "app", "dependencies": { @@ -71,9 +74,7 @@ In your workspace packages, use the `catalog:` protocol to reference versions: } ``` -**packages/ui/package.json** - -```json +```json packages/ui/package.json icon="file-json" { "name": "ui", "dependencies": { @@ -97,7 +98,7 @@ Bun supports two ways to define catalogs: 1. **`catalog`** (singular): A single default catalog for commonly used dependencies - ```json + ```json package.json icon="file-json" "catalog": { "react": "^19.0.0", "react-dom": "^19.0.0" @@ -106,7 +107,7 @@ Bun supports two ways to define catalogs: Reference with simply `catalog:`: - ```json + ```json packages/app/package.json icon="file-json" "dependencies": { "react": "catalog:" } @@ -114,7 +115,7 @@ Bun supports two ways to define catalogs: 2. **`catalogs`** (plural): Multiple named catalogs for grouping dependencies - ```json + ```json package.json icon="file-json" "catalogs": { "testing": { "jest": "30.0.0" @@ -127,7 +128,7 @@ Bun supports two ways to define catalogs: Reference with `catalog:`: - ```json + ```json packages/app/package.json icon="file-json" "dependencies": { "jest": "catalog:testing", "tailwind": "catalog:ui" @@ -147,7 +148,7 @@ Here's a more comprehensive example for a React application: **Root package.json** -```json +```json package.json icon="file-json" { "name": "react-monorepo", "workspaces": { @@ -174,9 +175,7 @@ Here's a more comprehensive example for a React application: } ``` -**packages/app/package.json** - -```json +```json packages/app/package.json icon="file-json" { "name": "app", "dependencies": { @@ -195,9 +194,7 @@ Here's a more comprehensive example for a React application: } ``` -**packages/ui/package.json** - -```json +```json packages/ui/package.json icon="file-json" { "name": "@monorepo/ui", "dependencies": { @@ -211,9 +208,7 @@ Here's a more comprehensive example for a React application: } ``` -**packages/utils/package.json** - -```json +```json packages/utils/package.json icon="file-json" { "name": "@monorepo/utils", "dependencies": { @@ -229,7 +224,7 @@ Here's a more comprehensive example for a React application: To update versions across all packages, simply change the version in the root package.json: -```json +```json package.json icon="file-json" "catalog": { "react": "^19.1.0", // Updated from ^19.0.0 "react-dom": "^19.1.0" // Updated from ^19.0.0 @@ -245,8 +240,7 @@ Bun's lockfile tracks catalog versions, making it easy to ensure consistent inst - The catalog definitions from your package.json - The resolution of each cataloged dependency -``` -// bun.lock (excerpt) +```json bun.lock(excerpt) icon="file-json" { "lockfileVersion": 1, "workspaces": { diff --git a/docs/cli/add.md b/docs/pm/cli/add.mdx similarity index 61% rename from docs/cli/add.md rename to docs/pm/cli/add.mdx index edc2732c3f..f699951d37 100644 --- a/docs/cli/add.md +++ b/docs/pm/cli/add.mdx @@ -1,91 +1,97 @@ +--- +title: "bun add" +description: "Add packages to your project with Bun's fast package manager" +--- + +import Add from "/snippets/cli/add.mdx"; + To add a particular package: -```bash -$ bun add preact +```bash terminal icon="terminal" +bun add preact ``` To specify a version, version range, or tag: -```bash -$ bun add zod@3.20.0 -$ bun add zod@^3.0.0 -$ bun add zod@latest +```bash terminal icon="terminal" +bun add zod@3.20.0 +bun add zod@^3.0.0 +bun add zod@latest ``` ## `--dev` -{% callout %} -**Alias** — `--development`, `-d`, `-D` -{% /callout %} +**Alias** — `--development`, `-d`, `-D` To add a package as a dev dependency (`"devDependencies"`): -```bash -$ bun add --dev @types/react -$ bun add -d @types/react +```bash terminal icon="terminal" +bun add --dev @types/react +bun add -d @types/react ``` ## `--optional` To add a package as an optional dependency (`"optionalDependencies"`): -```bash -$ bun add --optional lodash +```bash terminal icon="terminal" +bun add --optional lodash ``` ## `--peer` To add a package as a peer dependency (`"peerDependencies"`): -```bash -$ bun add --peer @types/bun +```bash terminal icon="terminal" +bun add --peer @types/bun ``` ## `--exact` -{% callout %} -**Alias** — `-E` -{% /callout %} +**Alias** — `-E` To add a package and pin to the resolved version, use `--exact`. This will resolve the version of the package and add it to your `package.json` with an exact version number instead of a version range. -```bash -$ bun add react --exact -$ bun add react -E +```bash terminal icon="terminal" +bun add react --exact +bun add react -E ``` This will add the following to your `package.json`: -```jsonc +```json package.json icon="file-json" { "dependencies": { // without --exact "react": "^18.2.0", // this matches >= 18.2.0 < 19.0.0 // with --exact - "react": "18.2.0", // this matches only 18.2.0 exactly - }, + "react": "18.2.0" // this matches only 18.2.0 exactly + } } ``` To view a complete list of options for this command: -```bash -$ bun add --help +```bash terminal icon="terminal" +bun add --help ``` ## `--global` -{% callout %} -**Note** — This would not modify package.json of your current project folder. -**Alias** - `bun add --global`, `bun add -g`, `bun install --global` and `bun install -g` -{% /callout %} + + **Note** — This would not modify package.json of your current project folder. **Alias** - `bun add --global`, `bun add + -g`, `bun install --global` and `bun install -g` + To install a package globally, use the `-g`/`--global` flag. This will not modify the `package.json` of your current project. Typically this is used for installing command-line tools. -```bash -$ bun add --global cowsay # or `bun add -g cowsay` -$ cowsay "Bun!" +```bash terminal icon="terminal" +bun add --global cowsay # or `bun add -g cowsay` +cowsay "Bun!" +``` + +```txt ______ < Bun! > ------ @@ -96,9 +102,9 @@ $ cowsay "Bun!" || || ``` -{% details summary="Configuring global installation behavior" %} + -```toml +```toml bunfig.toml icon="settings" [install] # where `bun add --global` installs packages globalDir = "~/.bun/install/global" @@ -107,7 +113,7 @@ globalDir = "~/.bun/install/global" globalBinDir = "~/.bun/bin" ``` -{% /details %} + ## Trusted dependencies @@ -115,33 +121,31 @@ Unlike other npm clients, Bun does not execute arbitrary lifecycle scripts for i To tell Bun to allow lifecycle scripts for a particular package, add the package to `trustedDependencies` in your package.json. -```json-diff - { - "name": "my-app", - "version": "1.0.0", -+ "trustedDependencies": ["my-trusted-package"] - } +```json package.json icon="file-json" +{ + "name": "my-app", + "version": "1.0.0", + "trustedDependencies": ["my-trusted-package"] // [!code ++] +} ``` Bun reads this field and will run lifecycle scripts for `my-trusted-package`. - - ## Git dependencies To add a dependency from a public or private git repository: -```bash -$ bun add git@github.com:moment/moment.git +```bash terminal icon="terminal" +bun add git@github.com:moment/moment.git ``` -{% callout %} -**Note** — To install private repositories, your system needs the appropriate SSH credentials to access the repository. -{% /callout %} + + To install private repositories, your system needs the appropriate SSH credentials to access the repository. + Bun supports a variety of protocols, including [`github`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#github-urls), [`git`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#git-urls-as-dependencies), `git+ssh`, `git+https`, and many more. -```json +```json package.json icon="file-json" { "dependencies": { "dayjs": "git+https://github.com/iamkun/dayjs.git", @@ -156,13 +160,13 @@ Bun supports a variety of protocols, including [`github`](https://docs.npmjs.com A package name can correspond to a publicly hosted `.tgz` file. During installation, Bun will download and install the package from the specified tarball URL, rather than from the package registry. -```sh -$ bun add zod@https://registry.npmjs.org/zod/-/zod-3.21.4.tgz +```sh terminal icon="terminal" +bun add zod@https://registry.npmjs.org/zod/-/zod-3.21.4.tgz ``` This will add the following line to your `package.json`: -```json#package.json +```json package.json icon="file-json" { "dependencies": { "zod": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz" @@ -170,4 +174,6 @@ This will add the following line to your `package.json`: } ``` -{% bunCLIUsage command="add" /%} +--- + + diff --git a/docs/install/audit.md b/docs/pm/cli/audit.mdx similarity index 83% rename from docs/install/audit.md rename to docs/pm/cli/audit.mdx index 794f55ecd7..ce387d94e6 100644 --- a/docs/install/audit.md +++ b/docs/pm/cli/audit.mdx @@ -1,9 +1,12 @@ -`bun audit` checks your installed packages for known security vulnerabilities. +--- +title: "bun audit" +description: "Check your installed packages for known security vulnerabilities" +--- Run the command in a project with a `bun.lock` file: -```bash -$ bun audit +```bash terminal icon="terminal" +bun audit ``` Bun sends the list of installed packages and versions to NPM, and prints a report of any vulnerabilities that were found. Packages installed from registries other than the default registry are skipped. @@ -28,19 +31,19 @@ To update all dependencies to the latest versions (including breaking changes): **`--audit-level=`** - Only show vulnerabilities at this severity level or higher: -```bash +```bash terminal icon="terminal" bun audit --audit-level=high ``` **`--prod`** - Audit only production dependencies (excludes devDependencies): -```bash +```bash terminal icon="terminal" bun audit --prod ``` **`--ignore `** - Ignore specific CVEs (can be used multiple times): -```bash +```bash terminal icon="terminal" bun audit --ignore CVE-2022-25883 --ignore CVE-2023-26136 ``` @@ -48,8 +51,8 @@ bun audit --ignore CVE-2022-25883 --ignore CVE-2023-26136 Use the `--json` flag to print the raw JSON response from the registry instead of the formatted report: -```bash -$ bun audit --json +```bash terminal icon="terminal" +bun audit --json ``` ### Exit code diff --git a/docs/cli/install.md b/docs/pm/cli/install.mdx similarity index 62% rename from docs/cli/install.md rename to docs/pm/cli/install.mdx index 68578a1c22..7affb62646 100644 --- a/docs/cli/install.md +++ b/docs/pm/cli/install.mdx @@ -1,40 +1,50 @@ +--- +title: "bun install" +description: "Install packages with Bun's fast package manager" +--- + +import Install from "/snippets/cli/install.mdx"; + +## Basic Usage + +```bash terminal icon="terminal" +bun install react +bun install react@19.1.1 # specific version +bun install react@latest # specific tag +``` + The `bun` CLI contains a Node.js-compatible package manager designed to be a dramatically faster replacement for `npm`, `yarn`, and `pnpm`. It's a standalone tool that will work in pre-existing Node.js projects; if your project has a `package.json`, `bun install` can help you speed up your workflow. -{% callout %} + **⚡️ 25x faster** — Switch from `npm install` to `bun install` in any Node.js project to make your installations up to 25x faster. -{% image src="https://user-images.githubusercontent.com/709451/147004342-571b6123-17a9-49a2-8bfd-dcfc5204047e.png" height="200" /%} + + ![Bun installation speed + comparison](https://user-images.githubusercontent.com/709451/147004342-571b6123-17a9-49a2-8bfd-dcfc5204047e.png) + -{% /callout %} + -{% callout %} - -**💾 Disk efficient** — Bun install stores all packages in a global cache (`~/.bun/install/cache/`) and creates hardlinks (Linux) or copy-on-write clones (macOS) to `node_modules`. This means duplicate packages across projects point to the same underlying data, taking up virtually no extra disk space. - -For more details, see [Package manager > Global cache](https://bun.com/docs/install/cache). - -{% /callout %} - -{% details summary="For Linux users" %} + The recommended minimum Linux Kernel version is 5.6. If you're on Linux kernel 5.1 - 5.5, `bun install` will work, but HTTP requests will be slow due to a lack of support for io_uring's `connect()` operation. If you're using Ubuntu 20.04, here's how to install a [newer kernel](https://wiki.ubuntu.com/Kernel/LTSEnablementStack): -```bash +```bash terminal icon="terminal" # If this returns a version >= 5.6, you don't need to do anything -$ uname -r +uname -r # Install the official Ubuntu hardware enablement kernel -$ sudo apt install --install-recommends linux-generic-hwe-20.04 +sudo apt install --install-recommends linux-generic-hwe-20.04 ``` -{% /details %} + To install all dependencies of a project: -```bash -$ bun install +```bash terminal icon="terminal" +bun install ``` Running `bun install` will: @@ -43,89 +53,105 @@ Running `bun install` will: - **Run** your project's `{pre|post}install` and `{pre|post}prepare` scripts at the appropriate time. For security reasons Bun _does not execute_ lifecycle scripts of installed dependencies. - **Write** a `bun.lock` lockfile to the project root. +--- + ## Logging To modify logging verbosity: -```bash -$ bun install --verbose # debug logging -$ bun install --silent # no logging +```bash terminal icon="terminal" +bun install --verbose # debug logging +bun install --silent # no logging ``` +--- + ## Lifecycle scripts Unlike other npm clients, Bun does not execute arbitrary lifecycle scripts like `postinstall` for installed dependencies. Executing arbitrary scripts represents a potential security risk. To tell Bun to allow lifecycle scripts for a particular package, add the package to `trustedDependencies` in your package.json. -```json-diff - { - "name": "my-app", - "version": "1.0.0", -+ "trustedDependencies": ["my-trusted-package"] - } +```json package.json icon="file-json" +{ + "name": "my-app", + "version": "1.0.0", + "trustedDependencies": ["my-trusted-package"] // [!code ++] +} ``` Then re-install the package. Bun will read this field and run lifecycle scripts for `my-trusted-package`. Lifecycle scripts will run in parallel during installation. To adjust the maximum number of concurrent scripts, use the `--concurrent-scripts` flag. The default is two times the reported cpu count or GOMAXPROCS. -```bash -$ bun install --concurrent-scripts 5 +```bash terminal icon="terminal" +bun install --concurrent-scripts 5 ``` +--- + ## Workspaces -Bun supports `"workspaces"` in package.json. For complete documentation refer to [Package manager > Workspaces](https://bun.com/docs/install/workspaces). +Bun supports `"workspaces"` in package.json. For complete documentation refer to [Package manager > Workspaces](/pm/workspaces). -```json#package.json +```json package.json icon="file-json" { "name": "my-app", "version": "1.0.0", - "workspaces": ["packages/*"], + "workspaces": ["packages/*"], // [!code ++] "dependencies": { "preact": "^10.5.13" } } ``` +--- + ## Installing dependencies for specific packages In a monorepo, you can install the dependencies for a subset of packages using the `--filter` flag. -```bash +```bash terminal icon="terminal" # Install dependencies for all workspaces except `pkg-c` -$ bun install --filter '!pkg-c' +bun install --filter '!pkg-c' # Install dependencies for only `pkg-a` in `./packages/pkg-a` -$ bun install --filter './packages/pkg-a' +bun install --filter './packages/pkg-a' ``` -For more information on filtering with `bun install`, refer to [Package Manager > Filtering](https://bun.com/docs/cli/filter#bun-install-and-bun-outdated) +For more information on filtering with `bun install`, refer to [Package Manager > Filtering](/pm/filter#bun-install-and-bun-outdated) + +--- ## Overrides and resolutions -Bun supports npm's `"overrides"` and Yarn's `"resolutions"` in `package.json`. These are mechanisms for specifying a version range for _metadependencies_—the dependencies of your dependencies. Refer to [Package manager > Overrides and resolutions](https://bun.com/docs/install/overrides) for complete documentation. +Bun supports npm's `"overrides"` and Yarn's `"resolutions"` in `package.json`. These are mechanisms for specifying a version range for _metadependencies_—the dependencies of your dependencies. Refer to [Package manager > Overrides and resolutions](/pm/overrides) for complete documentation. -```json-diff#package.json - { - "name": "my-app", - "dependencies": { - "foo": "^2.0.0" - }, -+ "overrides": { -+ "bar": "~4.4.0" -+ } - } +```json package.json file="file-json" +{ + "name": "my-app", + "dependencies": { + "foo": "^2.0.0" + }, + "overrides": { + // [!code ++] + "bar": "~4.4.0" // [!code ++] + } // [!code ++] +} ``` +--- + ## Global packages To install a package globally, use the `-g`/`--global` flag. Typically this is used for installing command-line tools. -```bash -$ bun install --global cowsay # or `bun install -g cowsay` -$ cowsay "Bun!" +```bash terminal icon="terminal" +bun install --global cowsay # or `bun install -g cowsay` +cowsay "Bun!" +``` + +```txt ______ < Bun! > ------ @@ -136,49 +162,57 @@ $ cowsay "Bun!" || || ``` +--- + ## Production mode To install in production mode (i.e. without `devDependencies` or `optionalDependencies`): -```bash -$ bun install --production +```bash terminal icon="terminal" +bun install --production ``` For reproducible installs, use `--frozen-lockfile`. This will install the exact versions of each package specified in the lockfile. If your `package.json` disagrees with `bun.lock`, Bun will exit with an error. The lockfile will not be updated. -```bash -$ bun install --frozen-lockfile +```bash terminal icon="terminal" +bun install --frozen-lockfile ``` -For more information on Bun's lockfile `bun.lock`, refer to [Package manager > Lockfile](https://bun.com/docs/install/lockfile). +For more information on Bun's lockfile `bun.lock`, refer to [Package manager > Lockfile](/pm/lockfile). + +--- ## Omitting dependencies To omit dev, peer, or optional dependencies use the `--omit` flag. -```bash +```bash terminal icon="terminal" # Exclude "devDependencies" from the installation. This will apply to the # root package and workspaces if they exist. Transitive dependencies will # not have "devDependencies". -$ bun install --omit dev +bun install --omit dev # Install only dependencies from "dependencies" -$ bun install --omit=dev --omit=peer --omit=optional +bun install --omit=dev --omit=peer --omit=optional ``` +--- + ## Dry run To perform a dry run (i.e. don't actually install anything): -```bash -$ bun install --dry-run +```bash terminal icon="terminal" +bun install --dry-run ``` +--- + ## Non-npm dependencies -Bun supports installing dependencies from Git, GitHub, and local or remotely-hosted tarballs. For complete documentation refer to [Package manager > Git, GitHub, and tarball dependencies](https://bun.com/docs/cli/add). +Bun supports installing dependencies from Git, GitHub, and local or remotely-hosted tarballs. For complete documentation refer to [Package manager > Git, GitHub, and tarball dependencies](/pm/cli/add). -```json#package.json +```json package.json icon="file-json" { "dependencies": { "dayjs": "git+https://github.com/iamkun/dayjs.git", @@ -191,6 +225,8 @@ Bun supports installing dependencies from Git, GitHub, and local or remotely-hos } ``` +--- + ## Installation strategies Bun supports two package installation strategies that determine how dependencies are organized in `node_modules`: @@ -199,40 +235,36 @@ Bun supports two package installation strategies that determine how dependencies The traditional npm/Yarn approach that flattens dependencies into a shared `node_modules` directory: -```bash -$ bun install --linker hoisted +```bash terminal icon="terminal" +bun install --linker hoisted ``` ### Isolated installs A pnpm-like approach that creates strict dependency isolation to prevent phantom dependencies: -```bash -$ bun install --linker isolated +```bash terminal icon="terminal" +bun install --linker isolated ``` Isolated installs create a central package store in `node_modules/.bun/` with symlinks in the top-level `node_modules`. This ensures packages can only access their declared dependencies. -For complete documentation on isolated installs, refer to [Package manager > Isolated installs](https://bun.com/docs/install/isolated). +For complete documentation on isolated installs, refer to [Package manager > Isolated installs](/pm/isolated-installs). -## Disk efficiency - -Bun uses a global cache at `~/.bun/install/cache/` to minimize disk usage. Packages are stored once and linked to `node_modules` using hardlinks (Linux/Windows) or copy-on-write (macOS), so duplicate packages across projects don't consume additional disk space. - -For complete documentation refer to [Package manager > Global cache](https://bun.com/docs/install/cache). +--- ## Minimum release age To protect against supply chain attacks where malicious packages are quickly published, you can configure a minimum age requirement for npm packages. Package versions published more recently than the specified threshold (in seconds) will be filtered out during installation. -```bash +```bash terminal icon="terminal" # Only install package versions published at least 3 days ago -$ bun add @types/bun --minimum-release-age 259200 # seconds +bun add @types/bun --minimum-release-age 259200 # seconds ``` You can also configure this in `bunfig.toml`: -```toml +```toml bunfig.toml icon="settings" [install] # Only install package versions published at least 3 days ago minimumReleaseAge = 259200 # seconds @@ -251,13 +283,15 @@ When the minimum age filter is active: - Exact version requests (like `package@1.1.1`) still respect the age gate but bypass the stability check - Versions without a `time` field are treated as passing the age check (npm registry should always provide timestamps) -For more advanced security scanning, including integration with services & custom filtering, see [Package manager > Security Scanner API](https://bun.com/docs/install/security-scanner-api). +For more advanced security scanning, including integration with services & custom filtering, see [Package manager > Security Scanner API](/pm/security-scanner-api). + +--- ## Configuration The default behavior of `bun install` can be configured in `bunfig.toml`. The default values are shown below. -```toml +```toml bunfig.toml icon="settings" [install] # whether to install optionalDependencies @@ -285,19 +319,23 @@ dryRun = false concurrentScripts = 16 # (cpu count or GOMAXPROCS) x2 # installation strategy: "hoisted" or "isolated" -# default: "hoisted" +# default: "hoisted" (for single-project projects) +# default: "isolated" (for monorepo projects) linker = "hoisted" + # minimum age config minimumReleaseAge = 259200 # seconds minimumReleaseAgeExcludes = ["@types/node", "typescript"] ``` +--- + ## CI/CD Use the official [`oven-sh/setup-bun`](https://github.com/oven-sh/setup-bun) action to install `bun` in a GitHub Actions pipeline: -```yaml#.github/workflows/release.yml +```yaml .github/workflows/release.yml icon="file-code" name: bun-types jobs: build: @@ -316,15 +354,15 @@ jobs: For CI/CD environments that want to enforce reproducible builds, use `bun ci` to fail the build if the package.json is out of sync with the lockfile: -```bash -$ bun ci +```bash terminal icon="terminal" +bun ci ``` This is equivalent to `bun install --frozen-lockfile`. It installs exact versions from `bun.lock` and fails if `package.json` doesn't match the lockfile. To use `bun ci` or `bun install --frozen-lockfile`, you must commit `bun.lock` to version control. And instead of running `bun install`, run `bun ci`. -```yaml#.github/workflows/release.yml +```yaml .github/workflows/release.yml icon="file-code" name: bun-types jobs: build: @@ -341,4 +379,93 @@ jobs: run: bun run build ``` -{% bunCLIUsage command="install" /%} +## pnpm migration + +Bun automatically migrates projects from pnpm to bun. When a `pnpm-lock.yaml` file is detected and no `bun.lock` file exists, Bun will automatically migrate the lockfile to `bun.lock` during installation. The original `pnpm-lock.yaml` file remains unmodified. + +```bash terminal icon="terminal" +bun install +``` + +**Note**: Migration only runs when `bun.lock` is absent. There is currently no opt-out flag for pnpm migration. + +The migration process handles: + +### Lockfile Migration + +- Converts `pnpm-lock.yaml` to `bun.lock` format +- Preserves package versions and resolution information +- Maintains dependency relationships and peer dependencies +- Handles patched dependencies with integrity hashes + +### Workspace Configuration + +When a `pnpm-workspace.yaml` file exists, Bun migrates workspace settings to your root `package.json`: + +```yaml pnpm-workspace.yaml icon="file-code" +packages: + - "apps/*" + - "packages/*" + +catalog: + react: ^18.0.0 + typescript: ^5.0.0 + +catalogs: + build: + webpack: ^5.0.0 + babel: ^7.0.0 +``` + +The workspace packages list and catalogs are moved to the `workspaces` field in `package.json`: + +```json package.json icon="file-json" +{ + "workspaces": { + "packages": ["apps/*", "packages/*"], + "catalog": { + "react": "^18.0.0", + "typescript": "^5.0.0" + }, + "catalogs": { + "build": { + "webpack": "^5.0.0", + "babel": "^7.0.0" + } + } + } +} +``` + +### Catalog Dependencies + +Dependencies using pnpm's `catalog:` protocol are preserved: + +```json package.json icon="file-json" +{ + "dependencies": { + "react": "catalog:", + "webpack": "catalog:build" + } +} +``` + +### Configuration Migration + +The following pnpm configuration is migrated from both `pnpm-lock.yaml` and `pnpm-workspace.yaml`: + +- **Overrides**: Moved from `pnpm.overrides` to root-level `overrides` in `package.json` +- **Patched Dependencies**: Moved from `pnpm.patchedDependencies` to root-level `patchedDependencies` in `package.json` +- **Workspace Overrides**: Applied from `pnpm-workspace.yaml` to root `package.json` + +### Requirements + +- Requires pnpm lockfile version 7 or higher +- Workspace packages must have a `name` field in their `package.json` +- All catalog entries referenced by dependencies must exist in the catalogs definition + +After migration, you can safely remove `pnpm-lock.yaml` and `pnpm-workspace.yaml` files. + +--- + + diff --git a/docs/cli/link.md b/docs/pm/cli/link.mdx similarity index 62% rename from docs/cli/link.md rename to docs/pm/cli/link.mdx index cc11038c6f..6515ae30eb 100644 --- a/docs/cli/link.md +++ b/docs/pm/cli/link.mdx @@ -1,13 +1,19 @@ +--- +title: "bun link" +description: "Link local packages for development" +--- + +import Link from "/snippets/cli/link.mdx"; + Use `bun link` in a local directory to register the current package as a "linkable" package. -```bash -$ cd /path/to/cool-pkg -$ cat package.json -{ - "name": "cool-pkg", - "version": "1.0.0" -} -$ bun link +```bash terminal icon="terminal" +cd /path/to/cool-pkg +cat package.json +bun link +``` + +```txt bun link v1.x (7416672e) Success! Registered "cool-pkg" @@ -20,21 +26,23 @@ Or add it in dependencies in your package.json file: This package can now be "linked" into other projects using `bun link cool-pkg`. This will create a symlink in the `node_modules` directory of the target project, pointing to the local directory. -```bash -$ cd /path/to/my-app -$ bun link cool-pkg +```bash terminal icon="terminal" +cd /path/to/my-app +bun link cool-pkg ``` In addition, the `--save` flag can be used to add `cool-pkg` to the `dependencies` field of your app's package.json with a special version specifier that tells Bun to load from the registered local directory instead of installing from `npm`: -```json-diff - { - "name": "my-app", - "version": "1.0.0", - "dependencies": { -+ "cool-pkg": "link:cool-pkg" - } +```json package.json icon="file-json" +{ + "name": "my-app", + "version": "1.0.0", + "dependencies": { + "cool-pkg": "link:cool-pkg" // [!code ++] } +} ``` -{% bunCLIUsage command="link" /%} +--- + + diff --git a/docs/cli/outdated.md b/docs/pm/cli/outdated.mdx similarity index 62% rename from docs/cli/outdated.md rename to docs/pm/cli/outdated.mdx index 2b1d15aee9..da72e0c490 100644 --- a/docs/cli/outdated.md +++ b/docs/pm/cli/outdated.mdx @@ -1,6 +1,30 @@ +--- +title: "bun outdated" +description: "Check for outdated dependencies" +--- + +import Outdated from "/snippets/cli/outdated.mdx"; + Use `bun outdated` to check for outdated dependencies in your project. This command displays a table of dependencies that have newer versions available. -{% bunOutdatedTerminal displayGlob="" filter="" glob="" /%} +```sh terminal icon="terminal" +bun outdated +``` + +```txt +| Package | Current | Update | Latest | +| ------------------------------ | ------- | --------- | ---------- | +| @sinclair/typebox | 0.34.15 | 0.34.16 | 0.34.16 | +| @types/bun (dev) | 1.2.0 | 1.2.23 | 1.2.23 | +| eslint (dev) | 8.57.1 | 8.57.1 | 9.20.0 | +| eslint-plugin-security (dev) | 2.1.1 | 2.1.1 | 3.0.1 | +| eslint-plugin-sonarjs (dev) | 0.23.0 | 0.23.0 | 3.0.1 | +| expect-type (dev) | 0.16.0 | 0.16.0 | 1.1.0 | +| prettier (dev) | 3.4.2 | 3.5.0 | 3.5.0 | +| tsup (dev) | 8.3.5 | 8.3.6 | 8.3.6 | +| typescript (dev) | 5.7.2 | 5.7.3 | 5.7.3 | + +``` ## Version Information @@ -16,40 +40,121 @@ The output table shows three version columns: To check if specific dependencies are outdated, pass the package names as positional arguments: -{% bunOutdatedTerminal displayGlob="eslint-plugin-security eslint-plugin-sonarjs" glob="eslint-plugin-*" /%} +```sh terminal icon="terminal" +bun outdated eslint-plugin-security eslint-plugin-sonarjs +``` + +```txt +| Package | Current | Update | Latest | +| ------------------------------ | ------- | ------ | --------- | +| eslint-plugin-security (dev) | 2.1.1 | 2.1.1 | 3.0.1 | +| eslint-plugin-sonarjs (dev) | 0.23.0 | 0.23.0 | 3.0.1 | + +``` You can also pass glob patterns to check for outdated packages: -{% bunOutdatedTerminal displayGlob="'eslint*'" glob="eslint*" /%} +```sh terminal icon="terminal" +bun outdated eslint* +``` + +```txt +| Package | Current | Update | Latest | +| ------------------------------ | ------- | ------ | ---------- | +| eslint (dev) | 8.57.1 | 8.57.1 | 9.20.0 | +| eslint-plugin-security (dev) | 2.1.1 | 2.1.1 | 3.0.1 | +| eslint-plugin-sonarjs (dev) | 0.23.0 | 0.23.0 | 3.0.1 | +``` For example, to check for outdated `@types/*` packages: -{% bunOutdatedTerminal displayGlob="'@types/*'" glob="@types/*" /%} +```sh terminal icon="terminal" +bun outdated '@types/*' +``` + +```txt +| Package | Current | Update | Latest | +| ------------------ | ------- | ------ | ------ | +| @types/bun (dev) | 1.2.0 | 1.2.23 | 1.2.23 | +``` Or to exclude all `@types/*` packages: -{% bunOutdatedTerminal displayGlob="'!@types/*'" glob="!@types/*" /%} +```sh terminal icon="terminal" +bun outdated '!@types/*' +``` + +```txt +| Package | Current | Update | Latest | +| ------------------------------ | ------- | --------- | ---------- | +| @sinclair/typebox | 0.34.15 | 0.34.16 | 0.34.16 | +| eslint (dev) | 8.57.1 | 8.57.1 | 9.20.0 | +| eslint-plugin-security (dev) | 2.1.1 | 2.1.1 | 3.0.1 | +| eslint-plugin-sonarjs (dev) | 0.23.0 | 0.23.0 | 3.0.1 | +| expect-type (dev) | 0.16.0 | 0.16.0 | 1.1.0 | +| prettier (dev) | 3.4.2 | 3.5.0 | 3.5.0 | +| tsup (dev) | 8.3.5 | 8.3.6 | 8.3.6 | +| typescript (dev) | 5.7.2 | 5.7.3 | 5.7.3 | +``` ### Workspace Filters Use the `--filter` flag to check for outdated dependencies in a different workspace package: -{% bunOutdatedTerminal glob="t*" filter="@monorepo/types" /%} +```sh terminal icon="terminal" +bun outdated --filter='@monorepo/types' +``` + +```txt +| Package | Current | Update | Latest | +| ------------------ | ------- | ------ | ------ | +| tsup (dev) | 8.3.5 | 8.3.6 | 8.3.6 | +| typescript (dev) | 5.7.2 | 5.7.3 | 5.7.3 | +``` You can pass multiple `--filter` flags to check multiple workspaces: -{% bunOutdatedTerminal glob="{e,t}*" displayGlob="--filter @monorepo/types --filter @monorepo/cli" /%} +```sh terminal icon="terminal" +bun outdated --filter @monorepo/types --filter @monorepo/cli +``` + +```txt +| Package | Current | Update | Latest | +| ------------------------------ | ------- | ------ | ---------- | +| eslint (dev) | 8.57.1 | 8.57.1 | 9.20.0 | +| eslint-plugin-security (dev) | 2.1.1 | 2.1.1 | 3.0.1 | +| eslint-plugin-sonarjs (dev) | 0.23.0 | 0.23.0 | 3.0.1 | +| expect-type (dev) | 0.16.0 | 0.16.0 | 1.1.0 | +| tsup (dev) | 8.3.5 | 8.3.6 | 8.3.6 | +| typescript (dev) | 5.7.2 | 5.7.3 | 5.7.3 | +``` You can also pass glob patterns to filter by workspace names: -{% bunOutdatedTerminal glob="{e,t}*" displayGlob="--filter='@monorepo/{types,cli}'" /%} +```sh terminal icon="terminal" +bun outdated --filter='@monorepo/{types,cli}' +``` + +```txt +| Package | Current | Update | Latest | +| ------------------------------ | ------- | ------ | ---------- | +| eslint (dev) | 8.57.1 | 8.57.1 | 9.20.0 | +| eslint-plugin-security (dev) | 2.1.1 | 2.1.1 | 3.0.1 | +| eslint-plugin-sonarjs (dev) | 0.23.0 | 0.23.0 | 3.0.1 | +| expect-type (dev) | 0.16.0 | 0.16.0 | 1.1.0 | +| tsup (dev) | 8.3.5 | 8.3.6 | 8.3.6 | +| typescript (dev) | 5.7.2 | 5.7.3 | 5.7.3 | +``` ### Catalog Dependencies -`bun outdated` supports checking catalog dependencies defined in `package.json`: +`bun outdated` supports checking catalog dependencies defined in`package.json`: -```sh -$ bun outdated -r +```sh terminal icon="terminal" +bun outdated -r +``` + +```txt ┌────────────────────┬─────────┬─────────┬─────────┬────────────────────────────────┐ │ Package │ Current │ Update │ Latest │ Workspace │ ├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤ @@ -87,4 +192,6 @@ $ bun outdated -r └────────────────────┴─────────┴─────────┴─────────┴────────────────────────────────┘ ``` -{% bunCLIUsage command="outdated" /%} +--- + + diff --git a/docs/install/patch.md b/docs/pm/cli/patch.mdx similarity index 68% rename from docs/install/patch.md rename to docs/pm/cli/patch.mdx index 3d1a09815e..56e48c202b 100644 --- a/docs/install/patch.md +++ b/docs/pm/cli/patch.mdx @@ -1,3 +1,10 @@ +--- +title: "bun patch" +description: "Persistently patch node_modules packages in a git-friendly way" +--- + +import Patch from "/snippets/cli/patch.mdx"; + `bun patch` lets you persistently patch node_modules in a maintainable, git-friendly way. Sometimes, you need to make a small change to a package in `node_modules/` to fix a bug or add a feature. `bun patch` makes it easy to do this without vendoring the entire package and reuse the patch across multiple installs, multiple projects, and multiple machines. @@ -7,7 +14,7 @@ Features: - Generates `.patch` files applied to dependencies in `node_modules` on install - `.patch` files can be committed to your repository, reused across multiple installs, projects, and machines - `"patchedDependencies"` in `package.json` keeps track of patched packages -- `bun patch` lets you patch packages in `node_modules/` while preserving the integrity of Bun's [Global Cache](https://bun.com/docs/install/cache) +- `bun patch` lets you patch packages in `node_modules/` while preserving the integrity of Bun's [Global Cache](/pm/global-cache) - Test your changes locally before committing them with `bun patch --commit ` - To preserve disk space and keep `bun install` fast, patched packages are committed to the Global Cache and shared across projects where possible @@ -15,26 +22,27 @@ Features: To get started, use `bun patch ` to prepare the package for patching: -```bash +```bash terminal icon="terminal" # you can supply the package name -$ bun patch react +bun patch react # ...and a precise version in case multiple versions are installed -$ bun patch react@17.0.2 +bun patch react@17.0.2 # or the path to the package -$ bun patch node_modules/react +bun patch node_modules/react ``` -{% callout %} -**Note** — Don't forget to call `bun patch `! This ensures the package folder in `node_modules/` contains a fresh copy of the package with no symlinks/hardlinks to Bun's cache. + +Don't forget to call `bun patch `! This ensures the package folder in `node_modules/` contains a fresh copy of the package with no symlinks/hardlinks to Bun's cache. If you forget to do this, you might end up editing the package globally in the cache! -{% /callout %} + + #### Step 2. Test your changes locally -`bun patch ` makes it safe to edit the `` in `node_modules/` directly, while preserving the integrity of Bun's [Global Cache](https://bun.com/docs/install/cache). This works by re-creating an unlinked clone of the package in `node_modules/` and diffing it against the original package in the Global Cache. +`bun patch ` makes it safe to edit the `` in `node_modules/` directly, while preserving the integrity of Bun's [Global Cache](/pm/global-cache). This works by re-creating an unlinked clone of the package in `node_modules/` and diffing it against the original package in the Global Cache. #### Step 3. Commit your changes @@ -42,18 +50,20 @@ Once you're happy with your changes, run `bun patch --commit `. Bun will generate a patch file in `patches/`, update your `package.json` and lockfile, and Bun will start using the patched package: -```bash +```bash terminal icon="terminal" # you can supply the path to the patched package -$ bun patch --commit node_modules/react +bun patch --commit node_modules/react # ... or the package name and optionally the version -$ bun patch --commit react@17.0.2 +bun patch --commit react@17.0.2 # choose the directory to store the patch files -$ bun patch --commit react --patches-dir=mypatches +bun patch --commit react --patches-dir=mypatches # `patch-commit` is available for compatibility with pnpm -$ bun patch-commit react +bun patch-commit react ``` -{% bunCLIUsage command="patch" /%} +--- + + diff --git a/docs/cli/pm.md b/docs/pm/cli/pm.mdx similarity index 68% rename from docs/cli/pm.md rename to docs/pm/cli/pm.mdx index 9536841594..fc297753d3 100644 --- a/docs/cli/pm.md +++ b/docs/pm/cli/pm.mdx @@ -1,11 +1,16 @@ +--- +title: "bun pm" +description: "Package manager utilities" +--- + The `bun pm` command group provides a set of utilities for working with Bun's package manager. ## pack To create a tarball of the current workspace: -```bash -$ bun pm pack +```bash terminal icon="terminal" +bun pm pack ``` This command creates a `.tgz` file containing all files that would be published to npm, following the same rules as `npm pack`. @@ -14,23 +19,26 @@ This command creates a `.tgz` file containing all files that would be published Basic usage: -```bash -$ bun pm pack +```bash terminal icon="terminal" +bun pm pack # Creates my-package-1.0.0.tgz in current directory ``` Quiet mode for scripting: -```bash -$ TARBALL=$(bun pm pack --quiet) -$ echo "Created: $TARBALL" -# Output: Created: my-package-1.0.0.tgz +```bash terminal icon="terminal" +TARBALL=$(bun pm pack --quiet) +echo "Created: $TARBALL" +``` + +```txt +Created: my-package-1.0.0.tgz ``` Custom destination: -```bash -$ bun pm pack --destination ./dist +```bash terminal icon="terminal" +bun pm pack --destination ./dist # Saves tarball in ./dist/ directory ``` @@ -49,8 +57,11 @@ $ bun pm pack --destination ./dist **Default output:** -```bash -$ bun pm pack +```bash terminal icon="terminal" +bun pm pack +``` + +```txt bun pack v1.2.19 packed 131B package.json @@ -66,8 +77,11 @@ Packed size: 249B **Quiet output:** -```bash -$ bun pm pack --quiet +```bash terminal icon="terminal" +bun pm pack --quiet +``` + +```txt my-package-1.0.0.tgz ``` @@ -77,15 +91,21 @@ The `--quiet` flag is particularly useful for automation workflows where you nee To print the path to the `bin` directory for the local project: -```bash -$ bun pm bin +```bash terminal icon="terminal" +bun pm bin +``` + +```txt /path/to/current/project/node_modules/.bin ``` To print the path to the global `bin` directory: -```bash -$ bun pm bin -g +```bash terminal icon="terminal" +bun pm bin -g +``` + +```txt <$HOME>/.bun/bin ``` @@ -93,8 +113,11 @@ $ bun pm bin -g To print a list of installed dependencies in the current project and their resolved versions, excluding their dependencies. -```bash -$ bun pm ls +```bash terminal icon="terminal" +bun pm ls +``` + +```txt /path/to/project node_modules (135) ├── eslint@8.38.0 ├── react@18.2.0 @@ -105,8 +128,11 @@ $ bun pm ls To print all installed dependencies, including nth-order dependencies. -```bash -$ bun pm ls --all +```bash terminal icon="terminal" +bun pm ls --all +``` + +```txt /path/to/project node_modules (135) ├── @eslint-community/eslint-utils@4.4.0 ├── @eslint-community/regexpp@4.5.0 @@ -126,59 +152,61 @@ $ bun pm ls --all Print your npm username. Requires you to be logged in (`bunx npm login`) with credentials in either `bunfig.toml` or `.npmrc`: -```bash -$ bun pm whoami +```bash terminal icon="terminal" +bun pm whoami ``` ## hash To generate and print the hash of the current lockfile: -```bash -$ bun pm hash +```bash terminal icon="terminal" +bun pm hash ``` To print the string used to hash the lockfile: -```bash -$ bun pm hash-string +```bash terminal icon="terminal" +bun pm hash-string ``` To print the hash stored in the current lockfile: -```bash -$ bun pm hash-print +```bash terminal icon="terminal" +bun pm hash-print ``` ## cache To print the path to Bun's global module cache: -```bash -$ bun pm cache +```bash terminal icon="terminal" +bun pm cache ``` To clear Bun's global module cache: -```bash -$ bun pm cache rm +```bash terminal icon="terminal" +bun pm cache rm ``` ## migrate To migrate another package manager's lockfile without installing anything: -```bash -$ bun pm migrate +```bash terminal icon="terminal" +bun pm migrate ``` ## untrusted To print current untrusted dependencies with scripts: -```bash -$ bun pm untrusted +```bash terminal icon="terminal" +bun pm untrusted +``` +```txt ./node_modules/@biomejs/biome @1.8.3 » [postinstall]: node scripts/postinstall.js @@ -189,8 +217,8 @@ These dependencies had their lifecycle scripts blocked during install. To run scripts for untrusted dependencies and add to `trustedDependencies`: -```bash -$ bun pm trust +```bash terminal icon="terminal" +bun pm trust ``` Options for the `trust` command: @@ -201,8 +229,8 @@ Options for the `trust` command: To print the default trusted dependencies list: -```bash -$ bun pm default-trusted +```bash terminal icon="terminal" +bun pm default-trusted ``` see the current list on GitHub [here](https://github.com/oven-sh/bun/blob/main/src/install/default-trusted-dependencies.txt) @@ -211,9 +239,12 @@ see the current list on GitHub [here](https://github.com/oven-sh/bun/blob/main/s To display current package version and help: -```bash -$ bun pm version -bun pm version v$BUN_LATEST_VERSION (ca7428e9) +```bash terminal icon="terminal" +bun pm version +``` + +```txt +bun pm version v1.3.1 (ca7428e9) Current package version: v1.0.0 Increment: @@ -235,15 +266,18 @@ Options: --force, -f Bypass dirty git history check Examples: - $ bun pm version patch - $ bun pm version 1.2.3 --no-git-tag-version - $ bun pm version prerelease --preid beta --message "Release beta: %s" + bun pm version patch + bun pm version 1.2.3 --no-git-tag-version + bun pm version prerelease --preid beta --message "Release beta: %s" ``` To bump the version in `package.json`: -```bash -$ bun pm version patch +```bash terminal icon="terminal" +bun pm version patch +``` + +```txt v1.0.1 ``` @@ -255,7 +289,7 @@ Manage `package.json` data with get, set, delete, and fix operations. All commands support dot and bracket notation: -```bash +```bash terminal icon="terminal" scripts.build # dot notation contributors[0] # array access workspaces.0 # dot with numeric index @@ -264,22 +298,22 @@ scripts[test:watch] # bracket for special chars Examples: -```bash +```bash terminal icon="terminal" # set -$ bun pm pkg get name # single property -$ bun pm pkg get name version # multiple properties -$ bun pm pkg get # entire package.json -$ bun pm pkg get scripts.build # nested property +bun pm pkg get name # single property +bun pm pkg get name version # multiple properties +bun pm pkg get # entire package.json +bun pm pkg get scripts.build # nested property # set -$ bun pm pkg set name="my-package" # simple property -$ bun pm pkg set scripts.test="jest" version=2.0.0 # multiple properties -$ bun pm pkg set {"private":"true"} --json # JSON values with --json flag +bun pm pkg set name="my-package" # simple property +bun pm pkg set scripts.test="jest" version=2.0.0 # multiple properties +bun pm pkg set {"private":"true"} --json # JSON values with --json flag # delete -$ bun pm pkg delete description # single property -$ bun pm pkg delete scripts.test contributors[0] # multiple/nested +bun pm pkg delete description # single property +bun pm pkg delete scripts.test contributors[0] # multiple/nested # fix -$ bun pm pkg fix # auto-fix common issues +bun pm pkg fix # auto-fix common issues ``` diff --git a/docs/cli/publish.md b/docs/pm/cli/publish.mdx similarity index 71% rename from docs/cli/publish.md rename to docs/pm/cli/publish.mdx index 8b930f290f..0966cf20f4 100644 --- a/docs/cli/publish.md +++ b/docs/pm/cli/publish.mdx @@ -1,13 +1,19 @@ -Use `bun publish` to publish a package to the npm registry. +--- +title: "bun publish" +description: Use `bun publish` to publish a package to the npm registry +--- + +import Publish from "/snippets/cli/publish.mdx"; `bun publish` will automatically pack your package into a tarball, strip catalog and workspace protocols from the `package.json` (resolving versions if necessary), and publish to the registry specified in your configuration files. Both `bunfig.toml` and `.npmrc` files are supported. -```sh +```sh terminal icon="terminal" ## Publishing the package from the current working directory -$ bun publish +bun publish +``` -## Output -bun publish v$BUN_LATEST_VERSION (ca7428e9) +```txt +bun publish v1.3.1 (ca7428e9) packed 203B package.json packed 224B README.md @@ -28,27 +34,28 @@ Registry: http://localhost:4873/ Alternatively, you can pack and publish your package separately by using `bun pm pack` followed by `bun publish` with the path to the output tarball. -```sh -$ bun pm pack +```sh terminal icon="terminal" +bun pm pack ... -$ bun publish ./package.tgz +bun publish ./package.tgz ``` -{% callout %} -**Note** - `bun publish` will not run lifecycle scripts (`prepublishOnly/prepack/prepare/postpack/publish/postpublish`) if a tarball path is provided. Scripts will only be run if the package is packed by `bun publish`. -{% /callout %} + + `bun publish` will not run lifecycle scripts (`prepublishOnly/prepack/prepare/postpack/publish/postpublish`) if a + tarball path is provided. Scripts will only be run if the package is packed by `bun publish`. + ### `--access` The `--access` flag can be used to set the access level of the package being published. The access level can be one of `public` or `restricted`. Unscoped packages are always public, and attempting to publish an unscoped package with `--access restricted` will result in an error. -```sh -$ bun publish --access public +```sh terminal icon="terminal" +bun publish --access public ``` `--access` can also be set in the `publishConfig` field of your `package.json`. -```json +```json package.json icon="file-json" { "publishConfig": { "access": "restricted" @@ -60,13 +67,13 @@ $ bun publish --access public Set the tag of the package version being published. By default, the tag is `latest`. The initial version of a package is always given the `latest` tag in addition to the specified tag. -```sh -$ bun publish --tag alpha +```sh terminal icon="terminal" +bun publish --tag alpha ``` `--tag` can also be set in the `publishConfig` field of your `package.json`. -```json +```json package.json icon="file-json" { "publishConfig": { "tag": "next" @@ -78,29 +85,20 @@ $ bun publish --tag alpha The `--dry-run` flag can be used to simulate the publish process without actually publishing the package. This is useful for verifying the contents of the published package without actually publishing the package. -```sh -$ bun publish --dry-run -``` - -### `--tolerate-republish` - -Exit with code 0 instead of 1 if the package version already exists. Useful in CI/CD where jobs may be re-run. - -```sh -$ bun publish --tolerate-republish +```sh terminal icon="terminal" +bun publish --dry-run ``` ### `--gzip-level` Specify the level of gzip compression to use when packing the package. Only applies to `bun publish` without a tarball path argument. Values range from `0` to `9` (default is `9`). -{% bunCLIUsage command="publish" /%} ### `--auth-type` If you have 2FA enabled for your npm account, `bun publish` will prompt you for a one-time password. This can be done through a browser or the CLI. The `--auth-type` flag can be used to tell the npm registry which method you prefer. The possible values are `web` and `legacy`, with `web` being the default. -```sh -$ bun publish --auth-type legacy +```sh terminal icon="terminal" +bun publish --auth-type legacy ... This operation requires a one-time password. Enter OTP: 123456 @@ -111,10 +109,15 @@ Enter OTP: 123456 Provide a one-time password directly to the CLI. If the password is valid, this will skip the extra prompt for a one-time password before publishing. Example usage: -```sh -$ bun publish --otp 123456 +```sh terminal icon="terminal" +bun publish --otp 123456 ``` -{% callout %} -**Note** - `bun publish` respects the `NPM_CONFIG_TOKEN` environment variable which can be used when publishing in github actions or automated workflows. -{% /callout %} + + `bun publish` respects the `NPM_CONFIG_TOKEN` environment variable which can be used when publishing in github actions + or automated workflows. + + +--- + + diff --git a/docs/pm/cli/remove.mdx b/docs/pm/cli/remove.mdx new file mode 100644 index 0000000000..9df4d760cd --- /dev/null +++ b/docs/pm/cli/remove.mdx @@ -0,0 +1,16 @@ +--- +title: "bun remove" +description: "Remove dependencies from your project" +--- + +import Remove from "/snippets/cli/remove.mdx"; + +## Basic Usage + +```bash terminal icon="terminal" +bun remove ts-node +``` + +--- + + diff --git a/docs/cli/update.md b/docs/pm/cli/update.mdx similarity index 85% rename from docs/cli/update.md rename to docs/pm/cli/update.mdx index c73952ddf9..edc81203f8 100644 --- a/docs/cli/update.md +++ b/docs/pm/cli/update.mdx @@ -1,22 +1,31 @@ +--- +title: "bun update" +description: "Update dependencies to latest versions" +--- + +import Update from "/snippets/cli/update.mdx"; + +To upgrade your Bun CLI version, see [`bun upgrade`](/installation#upgrading). + To update all dependencies to the latest version: -```sh -$ bun update +```sh terminal icon="terminal" +bun update ``` To update a specific dependency to the latest version: -```sh -$ bun update [package] +```sh terminal icon="terminal" +bun update [package] ``` ## `--interactive` For a more controlled update experience, use the `--interactive` flag to select which packages to update: -```sh -$ bun update --interactive -$ bun update -i +```sh terminal icon="terminal" +bun update --interactive +bun update -i ``` This launches an interactive terminal interface that shows all outdated packages with their current and target versions. You can then select which packages to update. @@ -25,7 +34,7 @@ This launches an interactive terminal interface that shows all outdated packages The interface displays packages grouped by dependency type: -``` +```txt ? Select packages to update - Space to toggle, Enter to confirm, a to select all, n to select none, i to invert, l to toggle latest dependencies Current Target Latest @@ -94,9 +103,9 @@ Within each section, individual packages may have additional suffixes (` dev`, ` Use the `--recursive` flag with `--interactive` to update dependencies across all workspaces in a monorepo: -```sh -$ bun update --interactive --recursive -$ bun update -i -r +```sh terminal icon="terminal" +bun update --interactive --recursive +bun update -i -r ``` This displays an additional "Workspace" column showing which workspace each dependency belongs to. @@ -107,15 +116,15 @@ By default, `bun update` will update to the latest version of a dependency that To update to the latest version, regardless of if it's compatible with the current version range, use the `--latest` flag: -```sh -$ bun update --latest +```sh terminal icon="terminal" +bun update --latest ``` In interactive mode, you can toggle individual packages between their target version (respecting semver) and latest version using the **l** key. For example, with the following `package.json`: -```json +```json package.json icon="file-json" { "dependencies": { "react": "^17.0.2" @@ -126,4 +135,6 @@ For example, with the following `package.json`: - `bun update` would update to a version that matches `17.x`. - `bun update --latest` would update to a version that matches `18.x` or later. -{% bunCLIUsage command="update" /%} +--- + + diff --git a/docs/cli/why.md b/docs/pm/cli/why.mdx similarity index 79% rename from docs/cli/why.md rename to docs/pm/cli/why.mdx index c9a2364259..5c835a0cbe 100644 --- a/docs/cli/why.md +++ b/docs/pm/cli/why.mdx @@ -1,9 +1,14 @@ +--- +title: "bun why" +description: "Explain why a package is installed" +--- + The `bun why` command explains why a package is installed in your project by showing the dependency chain that led to its installation. ## Usage -```bash -$ bun why +```bash terminal icon="terminal" +bun why ``` ## Arguments @@ -19,16 +24,22 @@ $ bun why Check why a specific package is installed: -```bash -$ bun why react +```bash terminal icon="terminal" +bun why react +``` + +```txt react@18.2.0 └─ my-app@1.0.0 (requires ^18.0.0) ``` Check why all packages with a specific pattern are installed: -```bash -$ bun why "@types/*" +```bash terminal icon="terminal" +bun why "@types/*" +``` + +```txt @types/react@18.2.15 └─ dev my-app@1.0.0 (requires ^18.0.0) @@ -38,16 +49,22 @@ $ bun why "@types/*" Show only top-level dependencies: -```bash -$ bun why express --top +```bash terminal icon="terminal" +bun why express --top +``` + +```txt express@4.18.2 └─ my-app@1.0.0 (requires ^4.18.2) ``` Limit the dependency tree depth: -```bash -$ bun why express --depth 2 +```bash terminal icon="terminal" +bun why express --depth 2 +``` + +```txt express@4.18.2 └─ express-pollyfill@1.20.1 (requires ^4.18.2) └─ body-parser@1.20.1 (requires ^1.20.1) diff --git a/docs/cli/filter.md b/docs/pm/filter.mdx similarity index 79% rename from docs/cli/filter.md rename to docs/pm/filter.mdx index bd5e41601f..f64a756f54 100644 --- a/docs/cli/filter.md +++ b/docs/pm/filter.mdx @@ -1,7 +1,14 @@ +--- +title: "bun --filter" +description: "Select packages by pattern in a monorepo using the --filter flag" +--- + The `--filter` (or `-F`) flag is used for selecting packages by pattern in a monorepo. Patterns can be used to match package names or package paths, with full glob syntax support. Currently `--filter` is supported by `bun install` and `bun outdated`, and can also be used to run scripts for multiple packages at once. +--- + ## Matching ### Package Name `--filter ` @@ -12,6 +19,8 @@ Name patterns select packages based on the package name, as specified in `packag Path patterns are specified by starting the pattern with `./`, and will select all packages in directories that match the pattern. For example, to match all packages in subdirectories of `packages`, you can use `--filter './packages/**'`. To match a package located in `packages/foo`, use `--filter ./packages/foo`. +--- + ## `bun install` and `bun outdated` Both `bun install` and `bun outdated` support the `--filter` flag. @@ -20,40 +29,42 @@ Both `bun install` and `bun outdated` support the `--filter` flag. Given a monorepo with workspaces `pkg-a`, `pkg-b`, and `pkg-c` under `./packages`: -```bash +```bash terminal icon="terminal" # Install dependencies for all workspaces except `pkg-c` -$ bun install --filter '!pkg-c' +bun install --filter '!pkg-c' # Install dependencies for packages in `./packages` (`pkg-a`, `pkg-b`, `pkg-c`) -$ bun install --filter './packages/*' +bun install --filter './packages/*' # Save as above, but exclude the root package.json -$ bun install --filter '!./' --filter './packages/*' +bun install --filter '!./' --filter './packages/*' ``` Similarly, `bun outdated` will display outdated dependencies for all packages in the monorepo, and `--filter` can be used to restrict the command to a subset of the packages: -```bash +```bash terminal icon="terminal" # Display outdated dependencies for workspaces starting with `pkg-` -$ bun outdated --filter 'pkg-*' +bun outdated --filter 'pkg-*' # Display outdated dependencies for only the root package.json -$ bun outdated --filter './' +bun outdated --filter './' ``` -For more information on both these commands, see [`bun install`](https://bun.com/docs/cli/install) and [`bun outdated`](https://bun.com/docs/cli/outdated). +For more information on both these commands, see [`bun install`](/pm/cli/install) and [`bun outdated`](/pm/cli/outdated). + +--- ## Running scripts with `--filter` Use the `--filter` flag to execute scripts in multiple packages at once: -```bash +```bash terminal icon="terminal" bun --filter ``` -{% /codetabs %} + It will output a new HTML file with the bundled assets: -{% codetabs %} + -```html#dist/output.html +```html dist/output.html - Local image - External image + Local image + External image ``` -{% /codetabs %} + Under the hood, it uses [`lol-html`](https://github.com/cloudflare/lol-html) to extract script and link tags as entrypoints, and other assets as external. @@ -311,7 +269,7 @@ Currently, the list of selectors is: - `video[poster]` - `video[src]` -{% callout %} + **HTML Loader Behavior in Different Contexts** @@ -323,16 +281,16 @@ The `html` loader behaves differently depending on how it's used: 3. **Full-stack Build:** When you run `bun build --target=bun server.ts` (where `server.ts` imports an HTML file), the import resolves to a manifest object that `Bun.serve` uses to efficiently serve pre-bundled assets in production. -{% /callout %} + ### `sh` loader **Bun Shell loader**. Default for `.sh` files -This loader is used to parse [Bun Shell](https://bun.com/docs/runtime/shell) scripts. It's only supported when starting Bun itself, so it's not available in the bundler or in the runtime. +This loader is used to parse [Bun Shell](/runtime/shell) scripts. It's only supported when starting Bun itself, so it's not available in the bundler or in the runtime. ```sh -$ bun run ./script.sh +bun run ./script.sh ``` ### `file` @@ -341,7 +299,7 @@ $ bun run ./script.sh The file loader resolves the import as a _path/URL_ to the imported file. It's commonly used for referencing media or font assets. -```ts#logo.ts +```ts logo.ts import logo from "./logo.svg"; console.log(logo); ``` @@ -349,47 +307,32 @@ console.log(logo); _In the runtime_, Bun checks that the `logo.svg` file exists and converts it to an absolute path to the location of `logo.svg` on disk. ```bash -$ bun run logo.ts +bun run logo.ts /path/to/project/logo.svg ``` _In the bundler_, things are slightly different. The file is copied into `outdir` as-is, and the import is resolved as a relative path pointing to the copied file. -```ts#Output +```ts Output var logo = "./logo.svg"; console.log(logo); ``` If a value is specified for `publicPath`, the import will use value as a prefix to construct an absolute path/URL. -{% table %} +| Public path | Resolved import | +| ---------------------------- | ---------------------------------- | +| `""` (default) | `/logo.svg` | +| `"/assets"` | `/assets/logo.svg` | +| `"https://cdn.example.com/"` | `https://cdn.example.com/logo.svg` | -- Public path -- Resolved import + + The location and file name of the copied file is determined by the value of [`naming.asset`](/bundler#naming). + +This loader is copied into the `outdir` as-is. The name of the copied file is determined using the value of +`naming.asset`. ---- - -- `""` (default) -- `/logo.svg` - ---- - -- `"/assets"` -- `/assets/logo.svg` - ---- - -- `"https://cdn.example.com/"` -- `https://cdn.example.com/logo.svg` - -{% /table %} - -{% callout %} -The location and file name of the copied file is determined by the value of [`naming.asset`](https://bun.com/docs/bundler#naming). -{% /callout %} -This loader is copied into the `outdir` as-is. The name of the copied file is determined using the value of `naming.asset`. - -{% details summary="Fixing TypeScript import errors" %} + If you're using TypeScript, you may get an error like this: ```ts @@ -407,4 +350,5 @@ declare module "*.svg" { ``` This tells TypeScript that any default imports from `.svg` should be treated as a string. -{% /details %} + + diff --git a/docs/api/glob.md b/docs/runtime/glob.mdx similarity index 97% rename from docs/api/glob.md rename to docs/runtime/glob.mdx index 0da7e72251..bb04ab1aa0 100644 --- a/docs/api/glob.md +++ b/docs/runtime/glob.mdx @@ -1,4 +1,7 @@ -Bun includes a fast native implementation of file globbing. +--- +title: Glob +description: Use Bun's fast native implementation of file globbing +--- ## Quickstart diff --git a/docs/runtime/globals.mdx b/docs/runtime/globals.mdx new file mode 100644 index 0000000000..2059b038ab --- /dev/null +++ b/docs/runtime/globals.mdx @@ -0,0 +1,72 @@ +--- +title: Globals +description: Use Bun's global objects +--- + +Bun implements the following globals. + +| Global | Source | Notes | +| ----------------------------------------------------------------------------------------------------------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) | Web | | +| [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) | Web | | +| [`alert`](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert) | Web | Intended for command-line tools | +| [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) | Web | | +| [`Buffer`](https://nodejs.org/api/buffer.html#class-buffer) | Node.js | See [Node.js > `Buffer`](/runtime/nodejs-compat#node-buffer) | +| `Bun` | Bun | Subject to change as additional APIs are added | +| [`ByteLengthQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/ByteLengthQueuingStrategy) | Web | | +| [`confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm) | Web | Intended for command-line tools | +| [`__dirname`](https://nodejs.org/api/globals.html#__dirname) | Node.js | | +| [`__filename`](https://nodejs.org/api/globals.html#__filename) | Node.js | | +| [`atob()`](https://developer.mozilla.org/en-US/docs/Web/API/atob) | Web | | +| [`btoa()`](https://developer.mozilla.org/en-US/docs/Web/API/btoa) | Web | | +| `BuildMessage` | Bun | | +| [`clearImmediate()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearImmediate) | Web | | +| [`clearInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearInterval) | Web | | +| [`clearTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearTimeout) | Web | | +| [`console`](https://developer.mozilla.org/en-US/docs/Web/API/console) | Web | | +| [`CountQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/CountQueuingStrategy) | Web | | +| [`Crypto`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto) | Web | | +| [`crypto`](https://developer.mozilla.org/en-US/docs/Web/API/crypto) | Web | | +| [`CryptoKey`](https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey) | Web | | +| [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) | Web | | +| [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) | Web | Also [`ErrorEvent`](https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent) [`CloseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) [`MessageEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent). | +| [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) | Web | | +| [`exports`](https://nodejs.org/api/globals.html#exports) | Node.js | | +| [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch) | Web | | +| [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) | Web | | +| [`global`](https://nodejs.org/api/globals.html#global) | Node.js | See [Node.js > `global`](/runtime/nodejs-compat#global). | +| [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis) | Cross-platform | Aliases to `global` | +| [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) | Web | | +| [`HTMLRewriter`](/runtime/html-rewriter) | Cloudflare | | +| [`JSON`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON) | Web | | +| [`MessageEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent) | Web | | +| [`module`](https://nodejs.org/api/globals.html#module) | Node.js | | +| [`performance`](https://developer.mozilla.org/en-US/docs/Web/API/performance) | Web | | +| [`process`](https://nodejs.org/api/process.html) | Node.js | See [Node.js > `process`](/runtime/nodejs-compat#node-process) | +| [`prompt`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt) | Web | Intended for command-line tools | +| [`queueMicrotask()`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask) | Web | | +| [`ReadableByteStreamController`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableByteStreamController) | Web | | +| [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) | Web | | +| [`ReadableStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultController) | Web | | +| [`ReadableStreamDefaultReader`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader) | Web | | +| [`reportError`](https://developer.mozilla.org/en-US/docs/Web/API/reportError) | Web | | +| [`require()`](https://nodejs.org/api/globals.html#require) | Node.js | | +| `ResolveMessage` | Bun | | +| [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) | Web | | +| [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) | Web | | +| [`setImmediate()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate) | Web | | +| [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval) | Web | | +| [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout) | Web | | +| [`ShadowRealm`](https://github.com/tc39/proposal-shadowrealm) | Web | Stage 3 proposal | +| [`SubtleCrypto`](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto) | Web | | +| [`DOMException`](https://developer.mozilla.org/en-US/docs/Web/API/DOMException) | Web | | +| [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder) | Web | | +| [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder) | Web | | +| [`TransformStream`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream) | Web | | +| [`TransformStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStreamDefaultController) | Web | | +| [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) | Web | | +| [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) | Web | | +| [`WebAssembly`](https://nodejs.org/api/globals.html#webassembly) | Web | | +| [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream) | Web | | +| [`WritableStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultController) | Web | | +| [`WritableStreamDefaultWriter`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter) | Web | | diff --git a/docs/api/hashing.md b/docs/runtime/hashing.mdx similarity index 92% rename from docs/api/hashing.md rename to docs/runtime/hashing.mdx index 384a20243c..4b9f1199d5 100644 --- a/docs/api/hashing.md +++ b/docs/runtime/hashing.mdx @@ -1,8 +1,14 @@ -{% callout %} +--- +title: Hashing +description: Bun provides a set of utility functions for hashing and verifying passwords with various cryptographically secure algorithms +--- -Bun implements the `createHash` and `createHmac` functions from [`node:crypto`](https://nodejs.org/api/crypto.html) in addition to the Bun-native APIs documented below. + + Bun implements the `createHash` and `createHmac` functions from [`node:crypto`](https://nodejs.org/api/crypto.html) in + addition to the Bun-native APIs documented below. + -{% /callout %} +--- ## `Bun.password` @@ -132,6 +138,8 @@ The format is composed of: - `salt`: `$xXnlSvPh4ym5KYmxKAuuHVlDvy2QGHBNuI6bJJrRDOs` - `hash`: `$2YY6M48XmHn+s5NoBaL+ficzXajq2Yj8wut3r0vnrwI` +--- + ## `Bun.hash` `Bun.hash` is a collection of utilities for _non-cryptographic_ hashing. Non-cryptographic hashing algorithms are optimized for speed of computation over collision-resistance or security. @@ -178,13 +186,14 @@ Bun.hash.murmur64v2("data", 1234); Bun.hash.rapidhash("data", 1234); ``` +--- + ## `Bun.CryptoHasher` `Bun.CryptoHasher` is a general-purpose utility class that lets you incrementally compute a hash of string or binary data using a range of cryptographic hash algorithms. The following algorithms are supported: - `"blake2b256"` - `"blake2b512"` -- `"blake2s256"` - `"md4"` - `"md5"` - `"ripemd160"` @@ -221,24 +230,11 @@ hasher.update(new ArrayBuffer(10)); If a `string` is passed, an optional second parameter can be used to specify the encoding (default `'utf-8'`). The following encodings are supported: -{% table %} - ---- - -- Binary encodings -- `"base64"` `"base64url"` `"hex"` `"binary"` - ---- - -- Character encodings -- `"utf8"` `"utf-8"` `"utf16le"` `"latin1"` - ---- - -- Legacy character encodings -- `"ascii"` `"binary"` `"ucs2"` `"ucs-2"` - -{% /table %} +| Category | Encodings | +| -------------------------- | ------------------------------------------- | +| Binary encodings | `"base64"` `"base64url"` `"hex"` `"binary"` | +| Character encodings | `"utf8"` `"utf-8"` `"utf16le"` `"latin1"` | +| Legacy character encodings | `"ascii"` `"binary"` `"ucs2"` `"ucs-2"` | ```ts hasher.update("hello world"); // defaults to utf8 diff --git a/docs/api/html-rewriter.md b/docs/runtime/html-rewriter.mdx similarity index 86% rename from docs/api/html-rewriter.md rename to docs/runtime/html-rewriter.mdx index 0f0f8c2802..cdddae9e1a 100644 --- a/docs/api/html-rewriter.md +++ b/docs/runtime/html-rewriter.mdx @@ -1,5 +1,12 @@ +--- +title: HTMLRewriter +description: Use Bun's HTMLRewriter to transform HTML documents with CSS selectors +--- + HTMLRewriter lets you use CSS selectors to transform HTML documents. It works with `Request`, `Response`, as well as `string`. Bun's implementation is based on Cloudflare's [lol-html](https://github.com/cloudflare/lol-html). +--- + ## Usage A common usecase is rewriting URLs in HTML content. Here's an example that rewrites image sources and link URLs to use a CDN domain: @@ -9,16 +16,12 @@ A common usecase is rewriting URLs in HTML content. Here's an example that rewri const rewriter = new HTMLRewriter().on("img", { element(img) { // Famous rickroll video thumbnail - img.setAttribute( - "src", - "https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg", - ); + img.setAttribute("src", "https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg"); // Wrap the image in a link to the video - img.before( - '', - { html: true }, - ); + img.before('', { + html: true, + }); img.after("", { html: true }); // Add some fun alt text @@ -43,21 +46,29 @@ console.log(result); This replaces all images with a thumbnail of Rick Astley and wraps each `` in a link, producing a diff like this: -```html-diff +```html -- -- -- -+ -+ Definitely not a rickroll -+ -+ -+ Definitely not a rickroll -+ -+ -+ Definitely not a rickroll -+ + // [!code --] // [!code --] + // [!code --] + + // [!code ++] + Definitely not a rickroll + // [!code ++] + + // [!code ++] + + // [!code ++] + Definitely not a rickroll + // [!code ++] + + // [!code ++] + + // [!code ++] + Definitely not a rickroll + // [!code ++] + + // [!code ++] ``` @@ -180,10 +191,7 @@ rewriter.on("div", { el.setInnerContent(""); // Clear content // Position manipulation - el.before("Content before") - .after("Content after") - .prepend("First child") - .append("Last child"); + el.before("Content before").after("Content after").prepend("First child").append("Last child"); // HTML content insertion el.before("before", { html: true }) @@ -255,11 +263,7 @@ rewriter.on("*", { console.log(comment.removed); // Whether comment was removed // Manipulation - comment - .before("Before comment") - .after("After comment") - .replace("New comment") - .remove(); + comment.before("Before comment").after("After comment").replace("New comment").remove(); // HTML content insertion comment @@ -329,6 +333,8 @@ try { } ``` +--- + ## See also You can also read the [Cloudflare documentation](https://developers.cloudflare.com/workers/runtime-apis/html-rewriter/), which this API is intended to be compatible with. diff --git a/docs/runtime/http/cookies.mdx b/docs/runtime/http/cookies.mdx new file mode 100644 index 0000000000..7eb3a323a0 --- /dev/null +++ b/docs/runtime/http/cookies.mdx @@ -0,0 +1,79 @@ +--- +title: Cookies +description: Work with cookies in HTTP requests and responses using Bun's built-in Cookie API. +--- + +Bun provides a built-in API for working with cookies in HTTP requests and responses. The `BunRequest` object includes a `cookies` property that provides a `CookieMap` for easily accessing and manipulating cookies. When using `routes`, `Bun.serve()` automatically tracks `request.cookies.set` and applies them to the response. + +## Reading cookies + +Read cookies from incoming requests using the `cookies` property on the `BunRequest` object: + +```ts +Bun.serve({ + routes: { + "/profile": req => { + // Access cookies from the request + const userId = req.cookies.get("user_id"); + const theme = req.cookies.get("theme") || "light"; + + return Response.json({ + userId, + theme, + message: "Profile page", + }); + }, + }, +}); +``` + +## Setting cookies + +To set cookies, use the `set` method on the `CookieMap` from the `BunRequest` object. + +```ts +Bun.serve({ + routes: { + "/login": req => { + const cookies = req.cookies; + + // Set a cookie with various options + cookies.set("user_id", "12345", { + maxAge: 60 * 60 * 24 * 7, // 1 week + httpOnly: true, + secure: true, + path: "/", + }); + + // Add a theme preference cookie + cookies.set("theme", "dark"); + + // Modified cookies from the request are automatically applied to the response + return new Response("Login successful"); + }, + }, +}); +``` + +`Bun.serve()` automatically tracks modified cookies from the request and applies them to the response. + +## Deleting cookies + +To delete a cookie, use the `delete` method on the `request.cookies` (`CookieMap`) object: + +```ts +Bun.serve({ + routes: { + "/logout": req => { + // Delete the user_id cookie + req.cookies.delete("user_id", { + path: "/", + }); + + return new Response("Logged out successfully"); + }, + }, +}); +``` + +Deleted cookies become a `Set-Cookie` header on the response with the `maxAge` set to `0` and an empty `value`. diff --git a/docs/runtime/http/error-handling.mdx b/docs/runtime/http/error-handling.mdx new file mode 100644 index 0000000000..898b40a0b7 --- /dev/null +++ b/docs/runtime/http/error-handling.mdx @@ -0,0 +1,40 @@ +--- +title: Error Handling +description: Learn how to handle errors in Bun's development server +--- + +To activate development mode, set `development: true`. + +```ts title="server.ts" icon="/icons/typescript.svg" +Bun.serve({ + development: true, // [!code ++] + fetch(req) { + throw new Error("woops!"); + }, +}); +``` + +In development mode, Bun will surface errors in-browser with a built-in error page. + +![Bun's built-in 500 page](/images/exception_page.png) + +### `error` callback + +To handle server-side errors, implement an `error` handler. This function should return a `Response` to serve to the client when an error occurs. This response will supersede Bun's default error page in `development` mode. + +```ts +Bun.serve({ + fetch(req) { + throw new Error("woops!"); + }, + error(error) { + return new Response(`
${error}\n${error.stack}
`, { + headers: { + "Content-Type": "text/html", + }, + }); + }, +}); +``` + +[Learn more about debugging in Bun](/runtime/debugger) diff --git a/docs/runtime/http/metrics.mdx b/docs/runtime/http/metrics.mdx new file mode 100644 index 0000000000..a43a8f9818 --- /dev/null +++ b/docs/runtime/http/metrics.mdx @@ -0,0 +1,36 @@ +--- +title: Metrics +description: Monitor server activity with built-in metrics +--- + +### `server.pendingRequests` and `server.pendingWebSockets` + +Monitor server activity with built-in counters: + +```ts +const server = Bun.serve({ + fetch(req, server) { + return new Response( + `Active requests: ${server.pendingRequests}\n` + `Active WebSockets: ${server.pendingWebSockets}`, + ); + }, +}); +``` + +### `server.subscriberCount(topic)` + +Get count of subscribers for a WebSocket topic: + +```ts +const server = Bun.serve({ + fetch(req, server) { + const chatUsers = server.subscriberCount("chat"); + return new Response(`${chatUsers} users in chat`); + }, + websocket: { + message(ws) { + ws.subscribe("chat"); + }, + }, +}); +``` diff --git a/docs/runtime/http/routing.mdx b/docs/runtime/http/routing.mdx new file mode 100644 index 0000000000..06af0a2cbb --- /dev/null +++ b/docs/runtime/http/routing.mdx @@ -0,0 +1,289 @@ +--- +title: Routing +description: Define routes in `Bun.serve` using static paths, parameters, and wildcards +--- + +You can add routes to `Bun.serve()` by using the `routes` property (for static paths, parameters, and wildcards) or by handling unmatched requests with the [`fetch`](#fetch) method. + +`Bun.serve()`'s router builds on top uWebSocket's [tree-based approach](https://github.com/oven-sh/bun/blob/0d1a00fa0f7830f8ecd99c027fce8096c9d459b6/packages/bun-uws/src/HttpRouter.h#L57-L64) to add [SIMD-accelerated route parameter decoding](https://github.com/oven-sh/bun/blob/main/src/bun.js/bindings/decodeURIComponentSIMD.cpp#L21-L271) and [JavaScriptCore structure caching](https://github.com/oven-sh/bun/blob/main/src/bun.js/bindings/ServerRouteList.cpp#L100-L101) to push the performance limits of what modern hardware allows. + +## Basic Setup + +```ts title="server.ts" icon="/icons/typescript.svg" +Bun.serve({ + routes: { + "/": () => new Response("Home"), + "/api": () => Response.json({ success: true }), + "/users": async () => Response.json({ users: [] }), + }, + fetch() { + return new Response("Unmatched route"); + }, +}); +``` + +Routes in `Bun.serve()` receive a `BunRequest` (which extends [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)) and return a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) or `Promise`. This makes it easier to use the same code for both sending & receiving HTTP requests. + +```ts +// Simplified for brevity +interface BunRequest extends Request { + params: Record; + readonly cookies: CookieMap; +} +``` + +## Asynchronous Routes + +### Async/await + +You can use async/await in route handlers to return a `Promise`. + +```ts +import { sql, serve } from "bun"; + +serve({ + port: 3001, + routes: { + "/api/version": async () => { + const [version] = await sql`SELECT version()`; + return Response.json(version); + }, + }, +}); +``` + +### Promise + +You can also return a `Promise` from a route handler. + +```ts +import { sql, serve } from "bun"; + +serve({ + routes: { + "/api/version": () => { + return new Promise(resolve => { + setTimeout(async () => { + const [version] = await sql`SELECT version()`; + resolve(Response.json(version)); + }, 100); + }); + }, + }, +}); +``` + +--- + +## Route precedence + +Routes are matched in order of specificity: + +1. Exact routes (`/users/all`) +2. Parameter routes (`/users/:id`) +3. Wildcard routes (`/users/*`) +4. Global catch-all (`/*`) + +```ts +Bun.serve({ + routes: { + // Most specific first + "/api/users/me": () => new Response("Current user"), + "/api/users/:id": req => new Response(`User ${req.params.id}`), + "/api/*": () => new Response("API catch-all"), + "/*": () => new Response("Global catch-all"), + }, +}); +``` + +--- + +## Type-safe route parameters + +TypeScript parses route parameters when passed as a string literal, so that your editor will show autocomplete when accessing `request.params`. + +```ts title="index.ts" +import type { BunRequest } from "bun"; + +Bun.serve({ + routes: { + // TypeScript knows the shape of params when passed as a string literal + "/orgs/:orgId/repos/:repoId": req => { + const { orgId, repoId } = req.params; + return Response.json({ orgId, repoId }); + }, + + "/orgs/:orgId/repos/:repoId/settings": ( + // optional: you can explicitly pass a type to BunRequest: + req: BunRequest<"/orgs/:orgId/repos/:repoId/settings">, + ) => { + const { orgId, repoId } = req.params; + return Response.json({ orgId, repoId }); + }, + }, +}); +``` + +Percent-encoded route parameter values are automatically decoded. Unicode characters are supported. Invalid unicode is replaced with the unicode replacement character `&0xFFFD;`. + +### Static responses + +Routes can also be `Response` objects (without the handler function). Bun.serve() optimizes it for zero-allocation dispatch - perfect for health checks, redirects, and fixed content: + +```ts +Bun.serve({ + routes: { + // Health checks + "/health": new Response("OK"), + "/ready": new Response("Ready", { + headers: { + // Pass custom headers + "X-Ready": "1", + }, + }), + + // Redirects + "/blog": Response.redirect("https://bun.com/blog"), + + // API responses + "/api/config": Response.json({ + version: "1.0.0", + env: "production", + }), + }, +}); +``` + +Static responses do not allocate additional memory after initialization. You can generally expect at least a 15% performance improvement over manually returning a `Response` object. + +Static route responses are cached for the lifetime of the server object. To reload static routes, call `server.reload(options)`. + +### File Responses vs Static Responses + +When serving files in routes, there are two distinct behaviors depending on whether you buffer the file content or serve it directly: + +```ts +Bun.serve({ + routes: { + // Static route - content is buffered in memory at startup + "/logo.png": new Response(await Bun.file("./logo.png").bytes()), + + // File route - content is read from filesystem on each request + "/download.zip": new Response(Bun.file("./download.zip")), + }, +}); +``` + +**Static routes** (`new Response(await file.bytes())`) buffer content in memory at startup: + +- **Zero filesystem I/O** during requests - content served entirely from memory +- **ETag support** - Automatically generates and validates ETags for caching +- **If-None-Match** - Returns `304 Not Modified` when client ETag matches +- **No 404 handling** - Missing files cause startup errors, not runtime 404s +- **Memory usage** - Full file content stored in RAM +- **Best for**: Small static assets, API responses, frequently accessed files + +**File routes** (`new Response(Bun.file(path))`) read from filesystem per request: + +- **Filesystem reads** on each request - checks file existence and reads content +- **Built-in 404 handling** - Returns `404 Not Found` if file doesn't exist or becomes inaccessible +- **Last-Modified support** - Uses file modification time for `If-Modified-Since` headers +- **If-Modified-Since** - Returns `304 Not Modified` when file hasn't changed since client's cached version +- **Range request support** - Automatically handles partial content requests with `Content-Range` headers +- **Streaming transfers** - Uses buffered reader with backpressure handling for efficient memory usage +- **Memory efficient** - Only buffers small chunks during transfer, not entire file +- **Best for**: Large files, dynamic content, user uploads, files that change frequently + +--- + +## Streaming files + +To stream a file, return a `Response` object with a `BunFile` object as the body. + +```ts +Bun.serve({ + fetch(req) { + return new Response(Bun.file("./hello.txt")); + }, +}); +``` + + + ⚡️ **Speed** — Bun automatically uses the [`sendfile(2)`](https://man7.org/linux/man-pages/man2/sendfile.2.html) + system call when possible, enabling zero-copy file transfers in the kernel—the fastest way to send files. + + +You can send part of a file using the [`slice(start, end)`](https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice) method on the `Bun.file` object. This automatically sets the `Content-Range` and `Content-Length` headers on the `Response` object. + +```ts +Bun.serve({ + fetch(req) { + // parse `Range` header + const [start = 0, end = Infinity] = req.headers + .get("Range") // Range: bytes=0-100 + .split("=") // ["Range: bytes", "0-100"] + .at(-1) // "0-100" + .split("-") // ["0", "100"] + .map(Number); // [0, 100] + + // return a slice of the file + const bigFile = Bun.file("./big-video.mp4"); + return new Response(bigFile.slice(start, end)); + }, +}); +``` + +--- + +## `fetch` request handler + +The `fetch` handler handles incoming requests that weren't matched by any route. It receives a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object and returns a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) or [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). + +```ts +Bun.serve({ + fetch(req) { + const url = new URL(req.url); + if (url.pathname === "/") return new Response("Home page!"); + if (url.pathname === "/blog") return new Response("Blog!"); + return new Response("404!"); + }, +}); +``` + +The `fetch` handler supports async/await: + +```ts +import { sleep, serve } from "bun"; + +serve({ + async fetch(req) { + const start = performance.now(); + await sleep(10); + const end = performance.now(); + return new Response(`Slept for ${end - start}ms`); + }, +}); +``` + +Promise-based responses are also supported: + +```ts +Bun.serve({ + fetch(req) { + // Forward the request to another server. + return fetch("https://example.com"); + }, +}); +``` + +You can also access the `Server` object from the `fetch` handler. It's the second argument passed to the `fetch` function. + +```ts +// `server` is passed in as the second argument to `fetch`. +const server = Bun.serve({ + fetch(req, server) { + const ip = server.requestIP(req); + return new Response(`Your IP is ${ip}`); + }, +}); +``` diff --git a/docs/runtime/http/server.mdx b/docs/runtime/http/server.mdx new file mode 100644 index 0000000000..f230f1e7ea --- /dev/null +++ b/docs/runtime/http/server.mdx @@ -0,0 +1,647 @@ +--- +title: Server +description: Use `Bun.serve` to start a high-performance HTTP server in Bun +--- + +## Basic Setup + +```ts title="index.ts" icon="/icons/typescript.svg" +const server = Bun.serve({ + // `routes` requires Bun v1.2.3+ + routes: { + // Static routes + "/api/status": new Response("OK"), + + // Dynamic routes + "/users/:id": req => { + return new Response(`Hello User ${req.params.id}!`); + }, + + // Per-HTTP method handlers + "/api/posts": { + GET: () => new Response("List posts"), + POST: async req => { + const body = await req.json(); + return Response.json({ created: true, ...body }); + }, + }, + + // Wildcard route for all routes that start with "/api/" and aren't otherwise matched + "/api/*": Response.json({ message: "Not found" }, { status: 404 }), + + // Redirect from /blog/hello to /blog/hello/world + "/blog/hello": Response.redirect("/blog/hello/world"), + + // Serve a file by buffering it in memory + "/favicon.ico": new Response(await Bun.file("./favicon.ico").bytes(), { + headers: { + "Content-Type": "image/x-icon", + }, + }), + }, + + // (optional) fallback for unmatched routes: + // Required if Bun's version < 1.2.3 + fetch(req) { + return new Response("Not Found", { status: 404 }); + }, +}); + +console.log(`Server running at ${server.url}`); +``` + +--- + +## HTML imports + +Bun supports importing HTML files directly into your server code, enabling full-stack applications with both server-side and client-side code. HTML imports work in two modes: + +**Development (`bun --hot`):** Assets are bundled on-demand at runtime, enabling hot module replacement (HMR) for a fast, iterative development experience. When you change your frontend code, the browser automatically updates without a full page reload. + +**Production (`bun build`):** When building with `bun build --target=bun`, the `import index from "./index.html"` statement resolves to a pre-built manifest object containing all bundled client assets. `Bun.serve` consumes this manifest to serve optimized assets with zero runtime bundling overhead. This is ideal for deploying to production. + +```ts +import myReactSinglePageApp from "./index.html"; + +Bun.serve({ + routes: { + "/": myReactSinglePageApp, + }, +}); +``` + +HTML imports don't just serve HTML — it's a full-featured frontend bundler, transpiler, and toolkit built using Bun's [bundler](/bundler), JavaScript transpiler and CSS parser. You can use this to build full-featured frontends with React, TypeScript, Tailwind CSS, and more. + +For a complete guide on building full-stack applications with HTML imports, including detailed examples and best practices, see [/docs/bundler/fullstack](/bundler/fullstack). + +--- + +## Configuration + +### Changing the `port` and `hostname` + +To configure which port and hostname the server will listen on, set `port` and `hostname` in the options object. + +```ts +Bun.serve({ + port: 8080, // defaults to $BUN_PORT, $PORT, $NODE_PORT otherwise 3000 // [!code ++] + hostname: "mydomain.com", // defaults to "0.0.0.0" // [!code ++] + fetch(req) { + return new Response("404!"); + }, +}); +``` + +To randomly select an available port, set `port` to `0`. + +```ts +const server = Bun.serve({ + port: 0, // random port // [!code ++] + fetch(req) { + return new Response("404!"); + }, +}); + +// server.port is the randomly selected port +console.log(server.port); +``` + +You can view the chosen port by accessing the `port` property on the server object, or by accessing the `url` property. + +```ts +console.log(server.port); // 3000 +console.log(server.url); // http://localhost:3000 +``` + +### Configuring a default port + +Bun supports several options and environment variables to configure the default port. The default port is used when the `port` option is not set. + +- `--port` CLI flag + +```sh +bun --port=4002 server.ts +``` + +- `BUN_PORT` environment variable + +```sh +bun_PORT=4002 bun server.ts +``` + +- `PORT` environment variable + +```sh terminal icon="terminal" +PORT=4002 bun server.ts +``` + +- `NODE_PORT` environment variable + +```sh terminal icon="terminal" +NODE_PORT=4002 bun server.ts +``` + +--- + +## Unix domain sockets + +To listen on a [unix domain socket](https://en.wikipedia.org/wiki/Unix_domain_socket), pass the `unix` option with the path to the socket. + +```ts +Bun.serve({ + unix: "/tmp/my-socket.sock", // path to socket + fetch(req) { + return new Response(`404!`); + }, +}); +``` + +### Abstract namespace sockets + +Bun supports Linux abstract namespace sockets. To use an abstract namespace socket, prefix the `unix` path with a null byte. + +```ts +Bun.serve({ + unix: "\0my-abstract-socket", // abstract namespace socket + fetch(req) { + return new Response(`404!`); + }, +}); +``` + +Unlike unix domain sockets, abstract namespace sockets are not bound to the filesystem and are automatically removed when the last reference to the socket is closed. + +--- + +## idleTimeout + +To configure the idle timeout, set the `idleTimeout` field in Bun.serve. + +```ts +Bun.serve({ + // 10 seconds: + idleTimeout: 10, + + fetch(req) { + return new Response("Bun!"); + }, +}); +``` + +This is the maximum amount of time a connection is allowed to be idle before the server closes it. A connection is idling if there is no data sent or received. + +--- + +## export default syntax + +Thus far, the examples on this page have used the explicit `Bun.serve` API. Bun also supports an alternate syntax. + +```ts server.ts +import { type Serve } from "bun"; + +export default { + fetch(req) { + return new Response("Bun!"); + }, +} satisfies Serve; +``` + +Instead of passing the server options into `Bun.serve`, `export default` it. This file can be executed as-is; when Bun sees a file with a `default` export containing a `fetch` handler, it passes it into `Bun.serve` under the hood. + +--- + +## Hot Route Reloading + +Update routes without server restarts using `server.reload()`: + +```ts +const server = Bun.serve({ + routes: { + "/api/version": () => Response.json({ version: "1.0.0" }), + }, +}); + +// Deploy new routes without downtime +server.reload({ + routes: { + "/api/version": () => Response.json({ version: "2.0.0" }), + }, +}); +``` + +--- + +## Server Lifecycle Methods + +### `server.stop()` + +To stop the server from accepting new connections: + +```ts +const server = Bun.serve({ + fetch(req) { + return new Response("Hello!"); + }, +}); + +// Gracefully stop the server (waits for in-flight requests) +await server.stop(); + +// Force stop and close all active connections +await server.stop(true); +``` + +By default, `stop()` allows in-flight requests and WebSocket connections to complete. Pass `true` to immediately terminate all connections. + +### `server.ref()` and `server.unref()` + +Control whether the server keeps the Bun process alive: + +```ts +// Don't keep process alive if server is the only thing running +server.unref(); + +// Restore default behavior - keep process alive +server.ref(); +``` + +### `server.reload()` + +Update the server's handlers without restarting: + +```ts +const server = Bun.serve({ + routes: { + "/api/version": Response.json({ version: "v1" }), + }, + fetch(req) { + return new Response("v1"); + }, +}); + +// Update to new handler +server.reload({ + routes: { + "/api/version": Response.json({ version: "v2" }), + }, + fetch(req) { + return new Response("v2"); + }, +}); +``` + +This is useful for development and hot reloading. Only `fetch`, `error`, and `routes` can be updated. + +--- + +## Per-Request Controls + +### `server.timeout(Request, seconds)` + +Set a custom idle timeout for individual requests: + +```ts +const server = Bun.serve({ + async fetch(req, server) { + // Set 60 second timeout for this request + server.timeout(req, 60); + + // If they take longer than 60 seconds to send the body, the request will be aborted + await req.text(); + + return new Response("Done!"); + }, +}); +``` + +Pass `0` to disable the timeout for a request. + +### `server.requestIP(Request)` + +Get client IP and port information: + +```ts +const server = Bun.serve({ + fetch(req, server) { + const address = server.requestIP(req); + if (address) { + return new Response(`Client IP: ${address.address}, Port: ${address.port}`); + } + return new Response("Unknown client"); + }, +}); +``` + +Returns `null` for closed requests or Unix domain sockets. + +--- + +## Server Metrics + +### `server.pendingRequests` and `server.pendingWebSockets` + +Monitor server activity with built-in counters: + +```ts +const server = Bun.serve({ + fetch(req, server) { + return new Response( + `Active requests: ${server.pendingRequests}\n` + `Active WebSockets: ${server.pendingWebSockets}`, + ); + }, +}); +``` + +### `server.subscriberCount(topic)` + +Get count of subscribers for a WebSocket topic: + +```ts +const server = Bun.serve({ + fetch(req, server) { + const chatUsers = server.subscriberCount("chat"); + return new Response(`${chatUsers} users in chat`); + }, + websocket: { + message(ws) { + ws.subscribe("chat"); + }, + }, +}); +``` + +--- + +## Benchmarks + +Below are Bun and Node.js implementations of a simple HTTP server that responds `Bun!` to each incoming `Request`. + +```ts Bun +Bun.serve({ + fetch(req: Request) { + return new Response("Bun!"); + }, + port: 3000, +}); +``` + +```ts +require("http") + .createServer((req, res) => res.end("Bun!")) + .listen(8080); +``` + +The `Bun.serve` server can handle roughly 2.5x more requests per second than Node.js on Linux. + +| Runtime | Requests per second | +| ------- | ------------------- | +| Node 16 | ~64,000 | +| Bun | ~160,000 | + + + ![image](https://user-images.githubusercontent.com/709451/162389032-fc302444-9d03-46be-ba87-c12bd8ce89a0.png) + + +--- + +## Practical example: REST API + +Here's a basic database-backed REST API using Bun's router with zero dependencies: + + + +```ts server.ts expandable icon="file-code" +import type { Post } from "./types.ts"; +import { Database } from "bun:sqlite"; + +const db = new Database("posts.db"); +db.exec(` + CREATE TABLE IF NOT EXISTS posts ( + id TEXT PRIMARY KEY, + title TEXT NOT NULL, + content TEXT NOT NULL, + created_at TEXT NOT NULL + ) +`); + +Bun.serve({ + routes: { + // List posts + "/api/posts": { + GET: () => { + const posts = db.query("SELECT * FROM posts").all(); + return Response.json(posts); + }, + + // Create post + POST: async req => { + const post: Omit = await req.json(); + const id = crypto.randomUUID(); + + db.query( + `INSERT INTO posts (id, title, content, created_at) + VALUES (?, ?, ?, ?)`, + ).run(id, post.title, post.content, new Date().toISOString()); + + return Response.json({ id, ...post }, { status: 201 }); + }, + }, + + // Get post by ID + "/api/posts/:id": req => { + const post = db.query("SELECT * FROM posts WHERE id = ?").get(req.params.id); + + if (!post) { + return new Response("Not Found", { status: 404 }); + } + + return Response.json(post); + }, + }, + + error(error) { + console.error(error); + return new Response("Internal Server Error", { status: 500 }); + }, +}); +``` + +```ts types.ts icon="/icons/typescript.svg" +export interface Post { + id: string; + title: string; + content: string; + created_at: string; +} +``` + + + +--- + +## Reference + +```ts expandable See TypeScript Definitions +interface Server extends Disposable { + /** + * Stop the server from accepting new connections. + * @param closeActiveConnections If true, immediately terminates all connections + * @returns Promise that resolves when the server has stopped + */ + stop(closeActiveConnections?: boolean): Promise; + + /** + * Update handlers without restarting the server. + * Only fetch and error handlers can be updated. + */ + reload(options: Serve): void; + + /** + * Make a request to the running server. + * Useful for testing or internal routing. + */ + fetch(request: Request | string): Response | Promise; + + /** + * Upgrade an HTTP request to a WebSocket connection. + * @returns true if upgrade successful, false if failed + */ + upgrade( + request: Request, + options?: { + headers?: Bun.HeadersInit; + data?: T; + }, + ): boolean; + + /** + * Publish a message to all WebSocket clients subscribed to a topic. + * @returns Bytes sent, 0 if dropped, -1 if backpressure applied + */ + publish( + topic: string, + data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer, + compress?: boolean, + ): ServerWebSocketSendStatus; + + /** + * Get count of WebSocket clients subscribed to a topic. + */ + subscriberCount(topic: string): number; + + /** + * Get client IP address and port. + * @returns null for closed requests or Unix sockets + */ + requestIP(request: Request): SocketAddress | null; + + /** + * Set custom idle timeout for a request. + * @param seconds Timeout in seconds, 0 to disable + */ + timeout(request: Request, seconds: number): void; + + /** + * Keep process alive while server is running. + */ + ref(): void; + + /** + * Allow process to exit if server is only thing running. + */ + unref(): void; + + /** Number of in-flight HTTP requests */ + readonly pendingRequests: number; + + /** Number of active WebSocket connections */ + readonly pendingWebSockets: number; + + /** Server URL including protocol, hostname and port */ + readonly url: URL; + + /** Port server is listening on */ + readonly port: number; + + /** Hostname server is bound to */ + readonly hostname: string; + + /** Whether server is in development mode */ + readonly development: boolean; + + /** Server instance identifier */ + readonly id: string; +} + +interface WebSocketHandler { + /** Maximum WebSocket message size in bytes */ + maxPayloadLength?: number; + + /** Bytes of queued messages before applying backpressure */ + backpressureLimit?: number; + + /** Whether to close connection when backpressure limit hit */ + closeOnBackpressureLimit?: boolean; + + /** Called when backpressure is relieved */ + drain?(ws: ServerWebSocket): void | Promise; + + /** Seconds before idle timeout */ + idleTimeout?: number; + + /** Enable per-message deflate compression */ + perMessageDeflate?: + | boolean + | { + compress?: WebSocketCompressor | boolean; + decompress?: WebSocketCompressor | boolean; + }; + + /** Send ping frames to keep connection alive */ + sendPings?: boolean; + + /** Whether server receives its own published messages */ + publishToSelf?: boolean; + + /** Called when connection opened */ + open?(ws: ServerWebSocket): void | Promise; + + /** Called when message received */ + message(ws: ServerWebSocket, message: string | Buffer): void | Promise; + + /** Called when connection closed */ + close?(ws: ServerWebSocket, code: number, reason: string): void | Promise; + + /** Called when ping frame received */ + ping?(ws: ServerWebSocket, data: Buffer): void | Promise; + + /** Called when pong frame received */ + pong?(ws: ServerWebSocket, data: Buffer): void | Promise; +} + +interface TLSOptions { + /** Certificate authority chain */ + ca?: string | Buffer | BunFile | Array; + + /** Server certificate */ + cert?: string | Buffer | BunFile | Array; + + /** Path to DH parameters file */ + dhParamsFile?: string; + + /** Private key */ + key?: string | Buffer | BunFile | Array; + + /** Reduce TLS memory usage */ + lowMemoryMode?: boolean; + + /** Private key passphrase */ + passphrase?: string; + + /** OpenSSL options flags */ + secureOptions?: number; + + /** Server name for SNI */ + serverName?: string; +} +``` diff --git a/docs/runtime/http/tls.mdx b/docs/runtime/http/tls.mdx new file mode 100644 index 0000000000..55872d5b94 --- /dev/null +++ b/docs/runtime/http/tls.mdx @@ -0,0 +1,101 @@ +--- +title: TLS +description: Enable TLS in Bun.serve +--- + +Bun supports TLS out of the box, powered by [BoringSSL](https://boringssl.googlesource.com/boringssl). Enable TLS by passing in a value for `key` and `cert`; both are required to enable TLS. + +```ts +Bun.serve({ + tls: { + key: Bun.file("./key.pem"), // [!code ++] + cert: Bun.file("./cert.pem"), // [!code ++] + }, +}); +``` + +The `key` and `cert` fields expect the _contents_ of your TLS key and certificate, _not a path to it_. This can be a string, `BunFile`, `TypedArray`, or `Buffer`. + +```ts +Bun.serve({ + tls: { + key: Bun.file("./key.pem"), // BunFile + key: fs.readFileSync("./key.pem"), // Buffer + key: fs.readFileSync("./key.pem", "utf8"), // string + key: [Bun.file("./key1.pem"), Bun.file("./key2.pem")], // array of above + }, +}); +``` + +### Passphrase + +If your private key is encrypted with a passphrase, provide a value for `passphrase` to decrypt it. + +```ts +Bun.serve({ + tls: { + key: Bun.file("./key.pem"), + cert: Bun.file("./cert.pem"), + passphrase: "my-secret-passphrase", // [!code ++] + }, +}); +``` + +### CA Certificates + +Optionally, you can override the trusted CA certificates by passing a value for `ca`. By default, the server will trust the list of well-known CAs curated by Mozilla. When `ca` is specified, the Mozilla list is overwritten. + +```ts +Bun.serve({ + tls: { + key: Bun.file("./key.pem"), // path to TLS key + cert: Bun.file("./cert.pem"), // path to TLS cert + ca: Bun.file("./ca.pem"), // path to root CA certificate // [!code ++] + }, +}); +``` + +### Diffie-Hellman + +To override Diffie-Hellman parameters: + +```ts +Bun.serve({ + tls: { + dhParamsFile: "/path/to/dhparams.pem", // path to Diffie Hellman parameters // [!code ++] + }, +}); +``` + +--- + +## Server name indication (SNI) + +To configure the server name indication (SNI) for the server, set the `serverName` field in the `tls` object. + +```ts +Bun.serve({ + tls: { + serverName: "my-server.com", // SNI // [!code ++] + }, +}); +``` + +To allow multiple server names, pass an array of objects to `tls`, each with a `serverName` field. + +```ts +Bun.serve({ + tls: [ + { + key: Bun.file("./key1.pem"), + cert: Bun.file("./cert1.pem"), + serverName: "my-server1.com", // [!code ++] + }, + { + key: Bun.file("./key2.pem"), + cert: Bun.file("./cert2.pem"), + serverName: "my-server2.com", // [!code ++] + }, + ], +}); +``` diff --git a/docs/api/websockets.md b/docs/runtime/http/websockets.mdx similarity index 61% rename from docs/api/websockets.md rename to docs/runtime/http/websockets.mdx index d20cbeea84..b33f37c29f 100644 --- a/docs/api/websockets.md +++ b/docs/runtime/http/websockets.mdx @@ -1,22 +1,32 @@ +--- +title: WebSockets +description: Server-side WebSockets in Bun +--- + `Bun.serve()` supports server-side WebSockets, with on-the-fly compression, TLS support, and a Bun-native publish-subscribe API. -{% callout %} + -**⚡️ 7x more throughput** — Bun's WebSockets are fast. For a [simple chatroom](https://github.com/oven-sh/bun/tree/main/bench/websocket-server/README.md) on Linux x64, Bun can handle 7x more requests per second than Node.js + [`"ws"`](https://github.com/websockets/ws). +**⚡️ 7x more throughput** -| Messages sent per second | Runtime | Clients | -| ------------------------ | ------------------------------ | ------- | -| ~700,000 | (`Bun.serve`) Bun v0.2.1 (x64) | 16 | -| ~100,000 | (`ws`) Node v18.10.0 (x64) | 16 | +Bun's WebSockets are fast. For a [simple chatroom](https://github.com/oven-sh/bun/tree/main/bench/websocket-server/README.md) on Linux x64, Bun can handle 7x more requests per second than Node.js + [`"ws"`](https://github.com/websockets/ws). + +| **Messages sent per second** | **Runtime** | **Clients** | +| ---------------------------- | ------------------------------ | ----------- | +| ~700,000 | (`Bun.serve`) Bun v0.2.1 (x64) | 16 | +| ~100,000 | (`ws`) Node v18.10.0 (x64) | 16 | Internally Bun's WebSocket implementation is built on [uWebSockets](https://github.com/uNetworking/uWebSockets). -{% /callout %} + + + +--- ## Start a WebSocket server Below is a simple WebSocket server built with `Bun.serve`, in which all incoming requests are [upgraded](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism) to WebSocket connections in the `fetch` handler. The socket handlers are declared in the `websocket` parameter. -```ts +```ts server.ts icon="/icons/typescript.svg" Bun.serve({ fetch(req, server) { // upgrade the request to a WebSocket @@ -31,7 +41,7 @@ Bun.serve({ The following WebSocket event handlers are supported: -```ts +```ts server.ts icon="/icons/typescript.svg" Bun.serve({ fetch(req, server) {}, // upgrade logic websocket: { @@ -43,7 +53,7 @@ Bun.serve({ }); ``` -{% details summary="An API designed for speed" %} + In Bun, handlers are declared once per server, instead of per socket. @@ -60,11 +70,12 @@ But servers tend to have **many** socket connections open, which means: So, instead of using an event-based API, `ServerWebSocket` expects you to pass a single object with methods for each event in `Bun.serve()` and it is reused for each connection. This leads to less memory usage and less time spent adding/removing event listeners. -{% /details %} + + The first argument to each handler is the instance of `ServerWebSocket` handling the event. The `ServerWebSocket` class is a fast, Bun-native implementation of [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) with some additional features. -```ts +```ts server.ts icon="/icons/typescript.svg" Bun.serve({ fetch(req, server) {}, // upgrade logic websocket: { @@ -79,24 +90,32 @@ Bun.serve({ Each `ServerWebSocket` instance has a `.send()` method for sending messages to the client. It supports a range of input types. -```ts -ws.send("Hello world"); // string -ws.send(response.arrayBuffer()); // ArrayBuffer -ws.send(new Uint8Array([1, 2, 3])); // TypedArray | DataView +```ts server.ts icon="/icons/typescript.svg" focus={4-6} +Bun.serve({ + fetch(req, server) {}, // upgrade logic + websocket: { + message(ws, message) { + ws.send("Hello world"); // string + ws.send(response.arrayBuffer()); // ArrayBuffer + ws.send(new Uint8Array([1, 2, 3])); // TypedArray | DataView + }, + }, +}); ``` ### Headers Once the upgrade succeeds, Bun will send a `101 Switching Protocols` response per the [spec](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism). Additional `headers` can be attached to this `Response` in the call to `server.upgrade()`. -```ts +```ts server.ts icon="/icons/typescript.svg" Bun.serve({ fetch(req, server) { const sessionId = await generateSessionId(); server.upgrade(req, { headers: { - "Set-Cookie": `SessionId=${sessionId}`, - }, + // [!code ++] + "Set-Cookie": `SessionId=${sessionId}`, // [!code ++] + }, // [!code ++] }); }, websocket: {}, // handlers @@ -107,9 +126,7 @@ Bun.serve({ Contextual `data` can be attached to a new WebSocket in the `.upgrade()` call. This data is made available on the `ws.data` property inside the WebSocket handlers. -To strongly type `ws.data`, add a `data` property to the `websocket` handler object. This types `ws.data` across all lifecycle hooks. - -```ts +```ts server.ts icon="/icons/typescript.svg" type WebSocketData = { createdAt: number; channelId: string; @@ -134,7 +151,6 @@ Bun.serve({ websocket: { // TypeScript: specify the type of ws.data like this data: {} as WebSocketData, - // handler called when a message is received async message(ws, message) { // ws.data is now properly typed as WebSocketData @@ -150,29 +166,28 @@ Bun.serve({ }); ``` -{% callout %} -**Note:** Previously, you could specify the type of `ws.data` using a type parameter on `Bun.serve`, like `Bun.serve({...})`. This pattern was removed due to [a limitation in TypeScript](https://github.com/microsoft/TypeScript/issues/26242) in favor of the `data` property shown above. -{% /callout %} - To connect to this server from the browser, create a new `WebSocket`. -```ts#browser.js +```ts browser.js icon="file-code" const socket = new WebSocket("ws://localhost:3000/chat"); socket.addEventListener("message", event => { console.log(event.data); -}) +}); ``` -{% callout %} -**Identifying users** — The cookies that are currently set on the page will be sent with the WebSocket upgrade request and available on `req.headers` in the `fetch` handler. Parse these cookies to determine the identity of the connecting user and set the value of `data` accordingly. -{% /callout %} + +**Identifying users** + +The cookies that are currently set on the page will be sent with the WebSocket upgrade request and available on `req.headers` in the `fetch` handler. Parse these cookies to determine the identity of the connecting user and set the value of `data` accordingly. + + ### Pub/Sub Bun's `ServerWebSocket` implementation implements a native publish-subscribe API for topic-based broadcasting. Individual sockets can `.subscribe()` to a topic (specified with a string identifier) and `.publish()` messages to all other subscribers to that topic (excluding itself). This topic-based broadcast API is similar to [MQTT](https://en.wikipedia.org/wiki/MQTT) and [Redis Pub/Sub](https://redis.io/topics/pubsub). -```ts +```ts server.ts icon="/icons/typescript.svg" const server = Bun.serve({ fetch(req, server) { const url = new URL(req.url); @@ -180,9 +195,7 @@ const server = Bun.serve({ console.log(`upgrade!`); const username = getUsernameFromReq(req); const success = server.upgrade(req, { data: { username } }); - return success - ? undefined - : new Response("WebSocket upgrade error", { status: 400 }); + return success ? undefined : new Response("WebSocket upgrade error", { status: 400 }); } return new Response("Hello world"); @@ -190,7 +203,6 @@ const server = Bun.serve({ websocket: { // TypeScript: specify the type of ws.data like this data: {} as { username: string }, - open(ws) { const msg = `${ws.data.username} has entered the chat`; ws.subscribe("the-group-chat"); @@ -229,12 +241,10 @@ server.publish("the-group-chat", "Hello world"); Per-message [compression](https://websockets.readthedocs.io/en/stable/topics/compression.html) can be enabled with the `perMessageDeflate` parameter. -```ts +```ts server.ts icon="/icons/typescript.svg" Bun.serve({ - fetch(req, server) {}, // upgrade logic websocket: { - // enable compression and decompression - perMessageDeflate: true, + perMessageDeflate: true, // [!code ++] }, }); ``` @@ -265,9 +275,7 @@ By default, Bun will close a WebSocket connection if it is idle for 120 seconds. Bun.serve({ fetch(req, server) {}, // upgrade logic websocket: { - idleTimeout: 60, // 60 seconds - - // ... + idleTimeout: 60, // 60 seconds // [!code ++] }, }); ``` @@ -278,13 +286,13 @@ Bun will also close a WebSocket connection if it receives a message that is larg Bun.serve({ fetch(req, server) {}, // upgrade logic websocket: { - maxPayloadLength: 1024 * 1024, // 1 MB - - // ... + maxPayloadLength: 1024 * 1024, // 1 MB // [!code ++] }, }); ``` +--- + ## Connect to a `Websocket` server Bun implements the `WebSocket` class. To create a WebSocket client that connects to a `ws://` or `wss://` server, create an instance of `WebSocket`, as you would in the browser. @@ -303,19 +311,8 @@ For convenience, Bun lets you setting custom headers directly in the constructor ```ts const socket = new WebSocket("ws://localhost:3000", { headers: { - // custom headers - }, -}); -``` - -### Client compression - -WebSocket clients support permessage-deflate compression. The `extensions` property shows negotiated compression: - -```ts -const socket = new WebSocket("wss://echo.websocket.org"); -socket.addEventListener("open", () => { - console.log(socket.extensions); // => "permessage-deflate" + /* custom headers */ + }, // [!code ++] }); ``` @@ -335,17 +332,16 @@ socket.addEventListener("close", event => {}); socket.addEventListener("error", event => {}); ``` +--- + ## Reference -```ts +```ts See Typescript Definitions expandable namespace Bun { export function serve(params: { fetch: (req: Request, server: Server) => Response | Promise; websocket?: { - message: ( - ws: ServerWebSocket, - message: string | ArrayBuffer | Uint8Array, - ) => void; + message: (ws: ServerWebSocket, message: string | ArrayBuffer | Uint8Array) => void; open?: (ws: ServerWebSocket) => void; close?: (ws: ServerWebSocket, code: number, reason: string) => void; error?: (ws: ServerWebSocket, error: Error) => void; @@ -383,11 +379,7 @@ type Compressor = interface Server { pendingWebSockets: number; - publish( - topic: string, - data: string | ArrayBufferView | ArrayBuffer, - compress?: boolean, - ): number; + publish(topic: string, data: string | ArrayBufferView | ArrayBuffer, compress?: boolean): number; upgrade( req: Request, options?: { @@ -410,164 +402,3 @@ interface ServerWebSocket { cork(cb: (ws: ServerWebSocket) => void): void; } ``` - - diff --git a/docs/runtime/index.md b/docs/runtime/index.md deleted file mode 100644 index c55e11323f..0000000000 --- a/docs/runtime/index.md +++ /dev/null @@ -1,312 +0,0 @@ -Bun is a new JavaScript & TypeScript runtime designed to be a faster, leaner, and more modern drop-in replacement for Node.js. - -## Speed - -Bun is designed to start fast and run fast. Its transpiler and runtime are written in Zig, a modern, high-performance language. On Linux, this translates into startup times [4x faster](https://twitter.com/jarredsumner/status/1499225725492076544) than Node.js. - -{% image src="/images/bun-run-speed.jpeg" caption="Bun vs Node.js vs Deno running Hello World" /%} - - - -Performance sensitive APIs like `Buffer`, `fetch`, and `Response` are heavily profiled and optimized. Under the hood Bun uses the [JavaScriptCore engine](https://developer.apple.com/documentation/javascriptcore), which is developed by Apple for Safari. It starts and runs faster than V8, the engine used by Node.js and Chromium-based browsers. - -## TypeScript - -Bun natively supports TypeScript out of the box. All files are transpiled on the fly by Bun's fast native transpiler before being executed. Similar to other build tools, Bun does not perform typechecking; it simply removes type annotations from the file. - -```bash -$ bun index.js -$ bun index.jsx -$ bun index.ts -$ bun index.tsx -``` - -Some aspects of Bun's runtime behavior are affected by the contents of your `tsconfig.json` file. Refer to [Runtime > TypeScript](https://bun.com/docs/runtime/typescript) page for details. - - - - - - - - - - - -## JSX - -## JSON, TOML, and YAML - -Source files can import `*.json`, `*.toml`, or `*.yaml` files to load their contents as plain JavaScript objects. - -```ts -import pkg from "./package.json"; -import bunfig from "./bunfig.toml"; -import config from "./config.yaml"; -``` - -See the [YAML API documentation](/docs/api/yaml) for more details on YAML support. - -## WASI - -{% callout %} -🚧 **Experimental** -{% /callout %} - -Bun has experimental support for WASI, the [WebAssembly System Interface](https://github.com/WebAssembly/WASI). To run a `.wasm` binary with Bun: - -```bash -$ bun ./my-wasm-app.wasm -# if the filename doesn't end with ".wasm" -$ bun run ./my-wasm-app.whatever -``` - -{% callout %} - -**Note** — WASI support is based on [wasi-js](https://github.com/sagemathinc/cowasm/tree/main/core/wasi-js). Currently, it only supports WASI binaries that use the `wasi_snapshot_preview1` or `wasi_unstable` APIs. Bun's implementation is not fully optimized for performance; this will become more of a priority as WASM grows in popularity. -{% /callout %} - -## Node.js compatibility - -Long-term, Bun aims for complete Node.js compatibility. Most Node.js packages already work with Bun out of the box, but certain low-level APIs like `dgram` are still unimplemented. Track the current compatibility status at [Ecosystem > Node.js](https://bun.com/docs/runtime/nodejs-apis). - -Bun implements the Node.js module resolution algorithm, so dependencies can still be managed with `package.json`, `node_modules`, and CommonJS-style imports. - -{% callout %} -**Note** — We recommend using Bun's [built-in package manager](https://bun.com/docs/cli/install) for a performance boost over other npm clients. -{% /callout %} - -## Web APIs - - - -Some Web APIs aren't relevant in the context of a server-first runtime like Bun, such as the [DOM API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API#html_dom_api_interfaces) or [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API). Many others, though, are broadly useful outside of the browser context; when possible, Bun implements these Web-standard APIs instead of introducing new APIs. - -The following Web APIs are partially or completely supported. - -{% table %} - ---- - -- HTTP -- [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch) [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) - ---- - -- URLs -- [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) - ---- - -- Streams -- [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream) [`TransformStream`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream) [`ByteLengthQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/ByteLengthQueuingStrategy) [`CountQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/CountQueuingStrategy) and associated classes - ---- - -- Blob -- [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) - ---- - -- WebSockets -- [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) - ---- - -- Encoding and decoding -- [`atob`](https://developer.mozilla.org/en-US/docs/Web/API/atob) [`btoa`](https://developer.mozilla.org/en-US/docs/Web/API/btoa) [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder) [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder) - ---- - -- Timeouts -- [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout) [`clearTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/clearTimeout) - ---- - -- Intervals -- [`setInterval`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval)[`clearInterval`](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval) - ---- - -- Crypto -- [`crypto`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto) [`SubtleCrypto`](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto) - [`CryptoKey`](https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey) - ---- - -- Debugging - -- [`console`](https://developer.mozilla.org/en-US/docs/Web/API/console) [`performance`](https://developer.mozilla.org/en-US/docs/Web/API/Performance) - ---- - -- Microtasks -- [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask) - ---- - -- Errors -- [`reportError`](https://developer.mozilla.org/en-US/docs/Web/API/reportError) - ---- - -- User interaction -- [`alert`](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert) [`confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm) [`prompt`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt) (intended for interactive CLIs) - - - - ---- - -- Realms -- [`ShadowRealm`](https://github.com/tc39/proposal-shadowrealm) - ---- - -- Events -- [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) - [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) [`ErrorEvent`](https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent) [`CloseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) [`MessageEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent) - ---- - -{% /table %} - -## Bun APIs - -Bun exposes a set of Bun-specific APIs on the `Bun` global object and through a number of built-in modules. These APIs represent the canonical "Bun-native" way to perform some common development tasks. They are all heavily optimized for performance. Click the link in the left column to view the associated documentation. - -{% table %} - -- Topic -- APIs - ---- - -- [HTTP](https://bun.com/docs/api/http) -- `Bun.serve` - ---- - -- [File I/O](https://bun.com/docs/api/file-io) -- `Bun.file` `Bun.write` - ---- - -- [Processes](https://bun.com/docs/api/spawn) -- `Bun.spawn` `Bun.spawnSync` - ---- - -- [TCP](https://bun.com/docs/api/tcp) -- `Bun.listen` `Bun.connect` - ---- - -- [Transpiler](https://bun.com/docs/api/transpiler) -- `Bun.Transpiler` - ---- - -- [Routing](https://bun.com/docs/api/file-system-router) -- `Bun.FileSystemRouter` - ---- - -- [HTMLRewriter](https://bun.com/docs/api/html-rewriter) -- `HTMLRewriter` - ---- - -- [Utils](https://bun.com/docs/api/utils) -- `Bun.peek` `Bun.which` - ---- - -- [SQLite](https://bun.com/docs/api/sqlite) -- `bun:sqlite` - ---- - -- [FFI](https://bun.com/docs/api/ffi) -- `bun:ffi` - ---- - -- [DNS](https://bun.com/docs/api/dns) -- `bun:dns` - ---- - -- [Testing](https://bun.com/docs/api/test) -- `bun:test` - ---- - -- [Node-API](https://bun.com/docs/api/node-api) -- `Node-API` - ---- - -{% /table %} - -## Plugins - -Support for additional file types can be implemented with plugins. Refer to [Runtime > Plugins](https://bun.com/docs/bundler/plugins) for full documentation. diff --git a/docs/cli/run.md b/docs/runtime/index.mdx similarity index 60% rename from docs/cli/run.md rename to docs/runtime/index.mdx index 5330aba3d9..7ffa1d0a9b 100644 --- a/docs/cli/run.md +++ b/docs/runtime/index.mdx @@ -1,93 +1,78 @@ -The `bun` CLI can be used to execute JavaScript/TypeScript files, `package.json` scripts, and [executable packages](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#bin). - -## Performance - -Bun is designed to start fast and run fast. - -Under the hood Bun uses the [JavaScriptCore engine](https://developer.apple.com/documentation/javascriptcore), which is developed by Apple for Safari. In most cases, the startup and running performance is faster than V8, the engine used by Node.js and Chromium-based browsers. Its transpiler and runtime are written in Zig, a modern, high-performance language. On Linux, this translates into startup times [4x faster](https://twitter.com/jarredsumner/status/1499225725492076544) than Node.js. - -{% table %} - +--- +title: Bun Runtime +description: Execute JavaScript/TypeScript files, package.json scripts, and executable packages with Bun's fast runtime. --- -- `bun hello.js` -- `5.2ms` +import Run from "/snippets/cli/run.mdx"; ---- +The Bun Runtime is designed to start fast and run fast. -- `node hello.js` -- `25.1ms` +Under the hood, Bun uses the [JavaScriptCore engine](https://developer.apple.com/documentation/javascriptcore), which is developed by Apple for Safari. In most cases, the startup and running performance is faster than V8, the engine used by Node.js and Chromium-based browsers. Its transpiler and runtime are written in Zig, a modern, high-performance language. On Linux, this translates into startup times [4x faster](https://twitter.com/jarredsumner/status/1499225725492076544) than Node.js. -{% /table %} -{% caption content="Running a simple Hello World script on Linux" /%} +| Command | Time | +| --------------- | -------- | +| `bun hello.js` | `5.2ms` | +| `node hello.js` | `25.1ms` | - - - - - +This benchmark is based on running a simple Hello World script on Linux ## Run a file -{% callout %} -Compare to `node ` -{% /callout %} - Use `bun run` to execute a source file. -```bash -$ bun run index.js +```bash terminal icon="terminal" +bun run index.js ``` Bun supports TypeScript and JSX out of the box. Every file is transpiled on the fly by Bun's fast native transpiler before being executed. -```bash -$ bun run index.js -$ bun run index.jsx -$ bun run index.ts -$ bun run index.tsx +```bash terminal icon="terminal" +bun run index.js +bun run index.jsx +bun run index.ts +bun run index.tsx ``` Alternatively, you can omit the `run` keyword and use the "naked" command; it behaves identically. -```bash -$ bun index.tsx -$ bun index.js +```bash terminal icon="terminal" +bun index.tsx +bun index.js ``` ### `--watch` To run a file in watch mode, use the `--watch` flag. -```bash -$ bun --watch run index.tsx +```bash terminal icon="terminal" +bun --watch run index.tsx ``` -{% callout %} -**Note** — When using `bun run`, put Bun flags like `--watch` immediately after `bun`. + +When using `bun run`, put Bun flags like `--watch` immediately after `bun`. ```bash -$ bun --watch run dev # ✔️ do this -$ bun run dev --watch # ❌ don't do this +bun --watch run dev # ✔️ do this +bun run dev --watch # ❌ don't do this ``` Flags that occur at the end of the command will be ignored and passed through to the `"dev"` script itself. -{% /callout %} + + ## Run a `package.json` script -{% note %} + Compare to `npm run - -{% /raw %} diff --git a/docs/cli/test.md b/docs/test/index.mdx similarity index 73% rename from docs/cli/test.md rename to docs/test/index.mdx index 476b7c5a39..c731dce49d 100644 --- a/docs/cli/test.md +++ b/docs/test/index.mdx @@ -1,3 +1,10 @@ +--- +title: "Test runner" +description: "Bun's fast, built-in, Jest-compatible test runner with TypeScript support, lifecycle hooks, mocking, and watch mode" +--- + +import Test from "/snippets/cli/test.mdx"; + Bun ships with a fast, built-in, Jest-compatible test runner. Tests are executed with the Bun runtime, and support the following features. - TypeScript and JSX @@ -7,19 +14,20 @@ Bun ships with a fast, built-in, Jest-compatible test runner. Tests are executed - Watch mode with `--watch` - Script pre-loading with `--preload` -{% callout %} -Bun aims for compatibility with Jest, but not everything is implemented. To track compatibility, see [this tracking issue](https://github.com/oven-sh/bun/issues/1825). -{% /callout %} + + Bun aims for compatibility with Jest, but not everything is implemented. To track compatibility, see [this tracking + issue](https://github.com/oven-sh/bun/issues/1825). + ## Run tests -```bash -$ bun test +```bash terminal icon="terminal" +bun test ``` -Tests are written in JavaScript or TypeScript with a Jest-like API. Refer to [Writing tests](https://bun.com/docs/test/writing) for full documentation. +Tests are written in JavaScript or TypeScript with a Jest-like API. Refer to [Writing tests](/test/writing-tests) for full documentation. -```ts#math.test.ts +```ts math.test.ts icon="/icons/typescript.svg" import { expect, test } from "bun:test"; test("2 + 2", () => { @@ -36,26 +44,24 @@ The runner recursively searches the working directory for files that match the f You can filter the set of _test files_ to run by passing additional positional arguments to `bun test`. Any test file with a path that matches one of the filters will run. Commonly, these filters will be file or directory names; glob patterns are not yet supported. -```bash -$ bun test ... +```bash terminal icon="terminal" +bun test ... ``` To filter by _test name_, use the `-t`/`--test-name-pattern` flag. -```sh +```sh terminal icon="terminal" # run all tests or test suites with "addition" in the name -$ bun test --test-name-pattern addition +bun test --test-name-pattern addition ``` -When no tests match the filter, `bun test` exits with code 1. - To run a specific file in the test runner, make sure the path starts with `./` or `/` to distinguish it from a filter name. -```bash -$ bun test ./test/specific-file.test.ts +```bash terminal icon="terminal" +bun test ./test/specific-file.test.ts ``` -The test runner runs all tests in a single process. It loads all `--preload` scripts (see [Lifecycle](https://bun.com/docs/test/lifecycle) for details), then runs all tests. If a test fails, the test runner will exit with a non-zero exit code. +The test runner runs all tests in a single process. It loads all `--preload` scripts (see [Lifecycle](/test/lifecycle) for details), then runs all tests. If a test fails, the test runner will exit with a non-zero exit code. ## CI/CD integration @@ -71,7 +77,7 @@ No configuration is needed, other than installing `bun` in the workflow and runn To use `bun test` in a GitHub Actions workflow, add the following step: -```yaml +```yaml title=".github/workflows/test.yml" icon="file-code" jobs: build: name: build-app @@ -93,8 +99,8 @@ From there, you'll get GitHub Actions annotations. To use `bun test` with a JUnit XML reporter, you can use the `--reporter=junit` in combination with `--reporter-outfile`. -```sh -$ bun test --reporter=junit --reporter-outfile=./bun.xml +```sh terminal icon="terminal" +bun test --reporter=junit --reporter-outfile=./bun.xml ``` This will continue to output to stdout/stderr as usual, and also write a JUnit @@ -106,9 +112,9 @@ JUnit XML is a popular format for reporting test results in CI/CD pipelines. Use the `--timeout` flag to specify a _per-test_ timeout in milliseconds. If a test times out, it will be marked as failed. The default value is `5000`. -```bash +```bash terminal icon="terminal" # default value is 5000 -$ bun test --timeout 20 +bun test --timeout 20 ``` ## Concurrent test execution @@ -119,8 +125,8 @@ By default, Bun runs all tests sequentially within each test file. You can enabl Use the `--concurrent` flag to run all tests concurrently within their respective files: -```sh -$ bun test --concurrent +```sh terminal icon="terminal" +bun test --concurrent ``` When this flag is enabled, all tests will run in parallel unless explicitly marked with `test.serial`. @@ -129,12 +135,12 @@ When this flag is enabled, all tests will run in parallel unless explicitly mark Control the maximum number of tests running simultaneously with the `--max-concurrency` flag: -```sh +```sh terminal icon="terminal" # Limit to 4 concurrent tests -$ bun test --concurrent --max-concurrency 4 +bun test --concurrent --max-concurrency 4 # Default: 20 -$ bun test --concurrent +bun test --concurrent ``` This helps prevent resource exhaustion when running many concurrent tests. The default value is 20. @@ -143,7 +149,7 @@ This helps prevent resource exhaustion when running many concurrent tests. The d Mark individual tests to run concurrently, even when the `--concurrent` flag is not used: -```ts +```ts title="math.test.ts" icon="/icons/typescript.svg" import { test, expect } from "bun:test"; // These tests run in parallel with each other @@ -167,7 +173,7 @@ test("sequential test", () => { Force tests to run sequentially, even when the `--concurrent` flag is enabled: -```ts +```ts title="math.test.ts" icon="/icons/typescript.svg" import { test, expect } from "bun:test"; let sharedState = 0; @@ -199,22 +205,25 @@ test.failing.each([1, 2, 3])("chained qualifiers %d", input => { Use the `--rerun-each` flag to run each test multiple times. This is useful for detecting flaky or non-deterministic test failures. -```sh -$ bun test --rerun-each 100 +```sh terminal icon="terminal" +bun test --rerun-each 100 ``` ## Randomize test execution order Use the `--randomize` flag to run tests in a random order. This helps detect tests that depend on shared state or execution order. -```sh -$ bun test --randomize +```sh terminal icon="terminal" +bun test --randomize ``` When using `--randomize`, the seed used for randomization will be displayed in the test summary: -```sh -$ bun test --randomize +```sh terminal icon="terminal" +bun test --randomize +``` + +```txt # ... test output ... --seed=12345 2 pass @@ -226,9 +235,9 @@ Ran 10 tests across 2 files. [50.00ms] Use the `--seed` flag to specify a seed for the randomization. This allows you to reproduce the same test order when debugging order-dependent failures. -```sh +```sh terminal icon="terminal" # Reproduce a previous randomized run -$ bun test --seed 123456 +bun test --seed 123456 ``` The `--seed` flag implies `--randomize`, so you don't need to specify both. Using the same seed value will always produce the same test execution order, making it easier to debug intermittent failures caused by test interdependencies. @@ -237,47 +246,46 @@ The `--seed` flag implies `--randomize`, so you don't need to specify both. Usin Use the `--bail` flag to abort the test run early after a pre-determined number of test failures. By default Bun will run all tests and report all failures, but sometimes in CI environments it's preferable to terminate earlier to reduce CPU usage. -```sh +```sh terminal icon="terminal" # bail after 1 failure -$ bun test --bail +bun test --bail # bail after 10 failure -$ bun test --bail=10 +bun test --bail=10 ``` ## Watch mode Similar to `bun run`, you can pass the `--watch` flag to `bun test` to watch for changes and re-run tests. -```bash -$ bun test --watch +```bash terminal icon="terminal" +bun test --watch ``` ## Lifecycle hooks Bun supports the following lifecycle hooks: -| Hook | Description | -| ---------------- | -------------------------------------------------------- | -| `beforeAll` | Runs once before all tests. | -| `beforeEach` | Runs before each test. | -| `afterEach` | Runs after each test. | -| `afterAll` | Runs once after all tests. | -| `onTestFinished` | Runs after a test finishes, including after `afterEach`. | +| Hook | Description | +| ------------ | --------------------------- | +| `beforeAll` | Runs once before all tests. | +| `beforeEach` | Runs before each test. | +| `afterEach` | Runs after each test. | +| `afterAll` | Runs once after all tests. | These hooks can be defined inside test files, or in a separate file that is preloaded with the `--preload` flag. -```ts -$ bun test --preload ./setup.ts +```ts terminal icon="terminal" +bun test --preload ./setup.ts ``` -See [Test > Lifecycle](https://bun.com/docs/test/lifecycle) for complete documentation. +See [Test > Lifecycle](/test/lifecycle) for complete documentation. ## Mocks Create mock functions with the `mock` function. -```ts +```ts title="math.test.ts" icon="/icons/typescript.svg" import { test, expect, mock } from "bun:test"; const random = mock(() => Math.random()); @@ -291,21 +299,21 @@ test("random", () => { Alternatively, you can use `jest.fn()`, it behaves identically. -```ts-diff -- import { test, expect, mock } from "bun:test"; -+ import { test, expect, jest } from "bun:test"; +```ts title="math.test.ts" icon="/icons/typescript.svg" +import { test, expect, mock } from "bun:test"; // [!code --] +import { test, expect, jest } from "bun:test"; // [!code ++] -- const random = mock(() => Math.random()); -+ const random = jest.fn(() => Math.random()); +const random = mock(() => Math.random()); // [!code --] +const random = jest.fn(() => Math.random()); // [!code ++] ``` -See [Test > Mocks](https://bun.com/docs/test/mocks) for complete documentation. +See [Test > Mocks](/test/mocks) for complete documentation. ## Snapshot testing Snapshots are supported by `bun test`. -```ts +```ts title="math.test.ts" icon="/icons/typescript.svg" // example usage of toMatchSnapshot import { test, expect } from "bun:test"; @@ -316,11 +324,11 @@ test("snapshot", () => { To update snapshots, use the `--update-snapshots` flag. -```sh -$ bun test --update-snapshots +```sh terminal icon="terminal" +bun test --update-snapshots ``` -See [Test > Snapshots](https://bun.com/docs/test/snapshots) for complete documentation. +See [Test > Snapshots](/test/snapshots) for complete documentation. ## UI & DOM testing @@ -330,40 +338,13 @@ Bun is compatible with popular UI testing libraries: - [DOM Testing Library](https://testing-library.com/docs/dom-testing-library/intro/) - [React Testing Library](https://testing-library.com/docs/react-testing-library/intro) -See [Test > DOM Testing](https://bun.com/docs/test/dom) for complete documentation. +See [Test > DOM Testing](/test/dom) for complete documentation. ## Performance Bun's test runner is fast. -{% image src="/images/buntest.jpeg" caption="Running 266 React SSR tests faster than Jest can print its version number." /%} - - +![Running 266 React SSR tests faster than Jest can print its version number.](/images/buntest.jpeg) ## AI Agent Integration @@ -385,13 +366,15 @@ When an AI agent environment is detected: - Passing, skipped, and todo test indicators are hidden - Summary statistics remain intact -```bash +```bash terminal icon="terminal" # Example: Enable quiet output for Claude Code -$ CLAUDECODE=1 bun test +CLAUDECODE=1 bun test # Still shows failures and summary, but hides verbose passing test output ``` This feature is particularly useful in AI-assisted development workflows where reduced output verbosity improves context efficiency while maintaining visibility into test failures. -{% bunCLIUsage command="test" /%} +--- + + diff --git a/docs/test/lifecycle.md b/docs/test/lifecycle.md deleted file mode 100644 index 3fa69b1e3f..0000000000 --- a/docs/test/lifecycle.md +++ /dev/null @@ -1,81 +0,0 @@ -The test runner supports the following lifecycle hooks. This is useful for loading test fixtures, mocking data, and configuring the test environment. - -| Hook | Description | -| ------------ | --------------------------- | -| `beforeAll` | Runs once before all tests. | -| `beforeEach` | Runs before each test. | -| `afterEach` | Runs after each test. | -| `afterAll` | Runs once after all tests. | - -Perform per-test setup and teardown logic with `beforeEach` and `afterEach`. - -```ts -import { beforeEach, afterEach } from "bun:test"; - -beforeEach(() => { - console.log("running test."); -}); - -afterEach(() => { - console.log("done with test."); -}); - -// tests... -``` - -Perform per-scope setup and teardown logic with `beforeAll` and `afterAll`. The _scope_ is determined by where the hook is defined. - -To scope the hooks to a particular `describe` block: - -```ts -import { describe, beforeAll } from "bun:test"; - -describe("test group", () => { - beforeAll(() => { - // setup - }); - - // tests... -}); -``` - -To scope the hooks to a test file: - -```ts -import { describe, beforeAll } from "bun:test"; - -beforeAll(() => { - // setup -}); - -describe("test group", () => { - // tests... -}); -``` - -To scope the hooks to an entire multi-file test run, define the hooks in a separate file. - -```ts#setup.ts -import { beforeAll, afterAll } from "bun:test"; - -beforeAll(() => { - // global setup -}); - -afterAll(() => { - // global teardown -}); -``` - -Then use `--preload` to run the setup script before any test files. - -```ts -$ bun test --preload ./setup.ts -``` - -To avoid typing `--preload` every time you run tests, it can be added to your `bunfig.toml`: - -```toml -[test] -preload = ["./setup.ts"] -``` diff --git a/docs/test/lifecycle.mdx b/docs/test/lifecycle.mdx new file mode 100644 index 0000000000..6427175df6 --- /dev/null +++ b/docs/test/lifecycle.mdx @@ -0,0 +1,348 @@ +--- +title: "Lifecycle hooks" +description: "Learn how to use beforeAll, beforeEach, afterEach, and afterAll lifecycle hooks in Bun tests" +--- + +The test runner supports the following lifecycle hooks. This is useful for loading test fixtures, mocking data, and configuring the test environment. + +| Hook | Description | +| ------------ | --------------------------- | +| `beforeAll` | Runs once before all tests. | +| `beforeEach` | Runs before each test. | +| `afterEach` | Runs after each test. | +| `afterAll` | Runs once after all tests. | + +## Per-Test Setup and Teardown + +Perform per-test setup and teardown logic with `beforeEach` and `afterEach`. + +```ts title="test.ts" icon="/icons/typescript.svg" +import { beforeEach, afterEach, test } from "bun:test"; + +beforeEach(() => { + console.log("running test."); +}); + +afterEach(() => { + console.log("done with test."); +}); + +// tests... +test("example test", () => { + // This test will have beforeEach run before it + // and afterEach run after it +}); +``` + +## Per-Scope Setup and Teardown + +Perform per-scope setup and teardown logic with `beforeAll` and `afterAll`. The scope is determined by where the hook is defined. + +### Scoped to a Describe Block + +To scope the hooks to a particular describe block: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { describe, beforeAll, afterAll, test } from "bun:test"; + +describe("test group", () => { + beforeAll(() => { + // setup for this describe block + console.log("Setting up test group"); + }); + + afterAll(() => { + // teardown for this describe block + console.log("Tearing down test group"); + }); + + test("test 1", () => { + // test implementation + }); + + test("test 2", () => { + // test implementation + }); +}); +``` + +### Scoped to a Test File + +To scope the hooks to an entire test file: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { describe, beforeAll, afterAll, test } from "bun:test"; + +beforeAll(() => { + // setup for entire file + console.log("Setting up test file"); +}); + +afterAll(() => { + // teardown for entire file + console.log("Tearing down test file"); +}); + +describe("test group", () => { + test("test 1", () => { + // test implementation + }); +}); +``` + +## Global Setup and Teardown + +To scope the hooks to an entire multi-file test run, define the hooks in a separate file. + +```ts title="setup.ts" icon="/icons/typescript.svg" +import { beforeAll, afterAll } from "bun:test"; + +beforeAll(() => { + // global setup + console.log("Global test setup"); + // Initialize database connections, start servers, etc. +}); + +afterAll(() => { + // global teardown + console.log("Global test teardown"); + // Close database connections, stop servers, etc. +}); +``` + +Then use `--preload` to run the setup script before any test files. + +```bash terminal icon="terminal" +bun test --preload ./setup.ts +``` + +To avoid typing `--preload` every time you run tests, it can be added to your `bunfig.toml`: + +```toml title="bunfig.toml" icon="settings" +[test] +preload = ["./setup.ts"] +``` + +## Practical Examples + +### Database Setup + +```ts title="database-setup.ts" icon="/icons/typescript.svg" +import { beforeAll, afterAll, beforeEach, afterEach } from "bun:test"; +import { createConnection, closeConnection, clearDatabase } from "./db"; + +let connection; + +beforeAll(async () => { + // Connect to test database + connection = await createConnection({ + host: "localhost", + database: "test_db", + }); +}); + +afterAll(async () => { + // Close database connection + await closeConnection(connection); +}); + +beforeEach(async () => { + // Start with clean database for each test + await clearDatabase(connection); +}); +``` + +### API Server Setup + +```ts title="server-setup.ts" icon="/icons/typescript.svg" +import { beforeAll, afterAll } from "bun:test"; +import { startServer, stopServer } from "./server"; + +let server; + +beforeAll(async () => { + // Start test server + server = await startServer({ + port: 3001, + env: "test", + }); +}); + +afterAll(async () => { + // Stop test server + await stopServer(server); +}); +``` + +### Mock Setup + +```ts title="mock-setup.ts" icon="/icons/typescript.svg" +import { beforeEach, afterEach } from "bun:test"; +import { mock } from "bun:test"; + +beforeEach(() => { + // Set up common mocks + mock.module("./api-client", () => ({ + fetchUser: mock(() => Promise.resolve({ id: 1, name: "Test User" })), + createUser: mock(() => Promise.resolve({ id: 2 })), + })); +}); + +afterEach(() => { + // Clear all mocks after each test + mock.restore(); +}); +``` + +## Async Lifecycle Hooks + +All lifecycle hooks support async functions: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { beforeAll, afterAll, test } from "bun:test"; + +beforeAll(async () => { + // Async setup + await new Promise(resolve => setTimeout(resolve, 100)); + console.log("Async setup complete"); +}); + +afterAll(async () => { + // Async teardown + await new Promise(resolve => setTimeout(resolve, 100)); + console.log("Async teardown complete"); +}); + +test("async test", async () => { + // Test will wait for beforeAll to complete + await expect(Promise.resolve("test")).resolves.toBe("test"); +}); +``` + +## Nested Hooks + +Hooks can be nested and will run in the appropriate order: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { describe, beforeAll, beforeEach, afterEach, afterAll, test } from "bun:test"; + +beforeAll(() => console.log("File beforeAll")); +afterAll(() => console.log("File afterAll")); + +describe("outer describe", () => { + beforeAll(() => console.log("Outer beforeAll")); + beforeEach(() => console.log("Outer beforeEach")); + afterEach(() => console.log("Outer afterEach")); + afterAll(() => console.log("Outer afterAll")); + + describe("inner describe", () => { + beforeAll(() => console.log("Inner beforeAll")); + beforeEach(() => console.log("Inner beforeEach")); + afterEach(() => console.log("Inner afterEach")); + afterAll(() => console.log("Inner afterAll")); + + test("nested test", () => { + console.log("Test running"); + }); + }); +}); +``` + +```txt +// Output order: +// File beforeAll +// Outer beforeAll +// Inner beforeAll +// Outer beforeEach +// Inner beforeEach +// Test running +// Inner afterEach +// Outer afterEach +// Inner afterAll +// Outer afterAll +// File afterAll +``` + +## Error Handling + +If a lifecycle hook throws an error, it will affect test execution: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { beforeAll, test } from "bun:test"; + +beforeAll(() => { + // If this throws, all tests in this scope will be skipped + throw new Error("Setup failed"); +}); + +test("this test will be skipped", () => { + // This won't run because beforeAll failed +}); +``` + +For better error handling: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { beforeAll, test, expect } from "bun:test"; + +beforeAll(async () => { + try { + await setupDatabase(); + } catch (error) { + console.error("Database setup failed:", error); + throw error; // Re-throw to fail the test suite + } +}); +``` + +## Best Practices + +### Keep Hooks Simple + +```ts title="test.ts" icon="/icons/typescript.svg" +// Good: Simple, focused setup +beforeEach(() => { + clearLocalStorage(); + resetMocks(); +}); + +// Avoid: Complex logic in hooks +beforeEach(async () => { + // Too much complex logic makes tests hard to debug + const data = await fetchComplexData(); + await processData(data); + await setupMultipleServices(data); +}); +``` + +### Use Appropriate Scope + +```ts title="test.ts" icon="/icons/typescript.svg" +// Good: File-level setup for shared resources +beforeAll(async () => { + await startTestServer(); +}); + +// Good: Test-level setup for test-specific state +beforeEach(() => { + user = createTestUser(); +}); +``` + +### Clean Up Resources + +```ts title="test.ts" icon="/icons/typescript.svg" +import { afterAll, afterEach } from "bun:test"; + +afterEach(() => { + // Clean up after each test + document.body.innerHTML = ""; + localStorage.clear(); +}); + +afterAll(async () => { + // Clean up expensive resources + await closeDatabase(); + await stopServer(); +}); +``` diff --git a/docs/test/mocks.md b/docs/test/mocks.md deleted file mode 100644 index 5fd1369bef..0000000000 --- a/docs/test/mocks.md +++ /dev/null @@ -1,313 +0,0 @@ -Create mocks with the `mock` function. - -```ts -import { test, expect, mock } from "bun:test"; -const random = mock(() => Math.random()); - -test("random", async () => { - const val = random(); - expect(val).toBeGreaterThan(0); - expect(random).toHaveBeenCalled(); - expect(random).toHaveBeenCalledTimes(1); -}); -``` - -{% callout %} -Alternatively, you can use the `jest.fn()` function, as in Jest. It behaves identically. - -```ts -import { test, expect, jest } from "bun:test"; -const random = jest.fn(() => Math.random()); - -test("random", async () => { - const val = random(); - expect(val).toBeGreaterThan(0); - expect(random).toHaveBeenCalled(); - expect(random).toHaveBeenCalledTimes(1); -}); -``` - -{% /callout %} - -The result of `mock()` is a new function that's been decorated with some additional properties. - -```ts -import { mock } from "bun:test"; -const random = mock((multiplier: number) => multiplier * Math.random()); - -random(2); -random(10); - -random.mock.calls; -// [[ 2 ], [ 10 ]] - -random.mock.results; -// [ -// { type: "return", value: 0.6533907460954099 }, -// { type: "return", value: 0.6452713933037312 } -// ] -``` - -The following properties and methods are implemented on mock functions. - -- [x] [mockFn.getMockName()](https://jestjs.io/docs/mock-function-api#mockfngetmockname) -- [x] [mockFn.mock.calls](https://jestjs.io/docs/mock-function-api#mockfnmockcalls) -- [x] [mockFn.mock.results](https://jestjs.io/docs/mock-function-api#mockfnmockresults) -- [x] [mockFn.mock.instances](https://jestjs.io/docs/mock-function-api#mockfnmockinstances) -- [x] [mockFn.mock.contexts](https://jestjs.io/docs/mock-function-api#mockfnmockcontexts) -- [x] [mockFn.mock.lastCall](https://jestjs.io/docs/mock-function-api#mockfnmocklastcall) -- [x] [mockFn.mockClear()](https://jestjs.io/docs/mock-function-api#mockfnmockclear) - Clears call history -- [x] [mockFn.mockReset()](https://jestjs.io/docs/mock-function-api#mockfnmockreset) - Clears call history and removes implementation -- [x] [mockFn.mockRestore()](https://jestjs.io/docs/mock-function-api#mockfnmockrestore) - Restores original implementation -- [x] [mockFn.mockImplementation(fn)](https://jestjs.io/docs/mock-function-api#mockfnmockimplementationfn) -- [x] [mockFn.mockImplementationOnce(fn)](https://jestjs.io/docs/mock-function-api#mockfnmockimplementationoncefn) -- [x] [mockFn.mockName(name)](https://jestjs.io/docs/mock-function-api#mockfnmocknamename) -- [x] [mockFn.mockReturnThis()](https://jestjs.io/docs/mock-function-api#mockfnmockreturnthis) -- [x] [mockFn.mockReturnValue(value)](https://jestjs.io/docs/mock-function-api#mockfnmockreturnvaluevalue) -- [x] [mockFn.mockReturnValueOnce(value)](https://jestjs.io/docs/mock-function-api#mockfnmockreturnvalueoncevalue) -- [x] [mockFn.mockResolvedValue(value)](https://jestjs.io/docs/mock-function-api#mockfnmockresolvedvaluevalue) -- [x] [mockFn.mockResolvedValueOnce(value)](https://jestjs.io/docs/mock-function-api#mockfnmockresolvedvalueoncevalue) -- [x] [mockFn.mockRejectedValue(value)](https://jestjs.io/docs/mock-function-api#mockfnmockrejectedvaluevalue) -- [x] [mockFn.mockRejectedValueOnce(value)](https://jestjs.io/docs/mock-function-api#mockfnmockrejectedvalueoncevalue) -- [x] [mockFn.withImplementation(fn, callback)](https://jestjs.io/docs/mock-function-api#mockfnwithimplementationfn-callback) - -## `.spyOn()` - -It's possible to track calls to a function without replacing it with a mock. Use `spyOn()` to create a spy; these spies can be passed to `.toHaveBeenCalled()` and `.toHaveBeenCalledTimes()`. - -```ts -import { test, expect, spyOn } from "bun:test"; - -const ringo = { - name: "Ringo", - sayHi() { - console.log(`Hello I'm ${this.name}`); - }, -}; - -const spy = spyOn(ringo, "sayHi"); - -test("spyon", () => { - expect(spy).toHaveBeenCalledTimes(0); - ringo.sayHi(); - expect(spy).toHaveBeenCalledTimes(1); -}); -``` - -## Module mocks with `mock.module()` - -Module mocking lets you override the behavior of a module. Use `mock.module(path: string, callback: () => Object)` to mock a module. - -```ts -import { test, expect, mock } from "bun:test"; - -mock.module("./module", () => { - return { - foo: "bar", - }; -}); - -test("mock.module", async () => { - const esm = await import("./module"); - expect(esm.foo).toBe("bar"); - - const cjs = require("./module"); - expect(cjs.foo).toBe("bar"); -}); -``` - -Like the rest of Bun, module mocks support both `import` and `require`. - -### Overriding already imported modules - -If you need to override a module that's already been imported, there's nothing special you need to do. Just call `mock.module()` and the module will be overridden. - -```ts -import { test, expect, mock } from "bun:test"; - -// The module we're going to mock is here: -import { foo } from "./module"; - -test("mock.module", async () => { - const cjs = require("./module"); - expect(foo).toBe("bar"); - expect(cjs.foo).toBe("bar"); - - // We update it here: - mock.module("./module", () => { - return { - foo: "baz", - }; - }); - - // And the live bindings are updated. - expect(foo).toBe("baz"); - - // The module is also updated for CJS. - expect(cjs.foo).toBe("baz"); -}); -``` - -### Hoisting & preloading - -If you need to ensure a module is mocked before it's imported, you should use `--preload` to load your mocks before your tests run. - -```ts -// my-preload.ts -import { mock } from "bun:test"; - -mock.module("./module", () => { - return { - foo: "bar", - }; -}); -``` - -```sh -bun test --preload ./my-preload -``` - -To make your life easier, you can put `preload` in your `bunfig.toml`: - -```toml - -[test] -# Load these modules before running tests. -preload = ["./my-preload"] - -``` - -#### What happens if I mock a module that's already been imported? - -If you mock a module that's already been imported, the module will be updated in the module cache. This means that any modules that import the module will get the mocked version, BUT the original module will still have been evaluated. That means that any side effects from the original module will still have happened. - -If you want to prevent the original module from being evaluated, you should use `--preload` to load your mocks before your tests run. - -### `__mocks__` directory and auto-mocking - -Auto-mocking is not supported yet. If this is blocking you from switching to Bun, please file an issue. - -### Implementation details - -Module mocks have different implementations for ESM and CommonJS modules. For ES Modules, we've added patches to JavaScriptCore that allow Bun to override export values at runtime and update live bindings recursively. - -As of Bun v1.0.19, Bun automatically resolves the `specifier` argument to `mock.module()` as though you did an `import`. If it successfully resolves, then the resolved specifier string is used as the key in the module cache. This means that you can use relative paths, absolute paths, and even module names. If the `specifier` doesn't resolve, then the original `specifier` is used as the key in the module cache. - -After resolution, the mocked module is stored in the ES Module registry **and** the CommonJS require cache. This means that you can use `import` and `require` interchangeably for mocked modules. - -The callback function is called lazily, only if the module is imported or required. This means that you can use `mock.module()` to mock modules that don't exist yet, and it means that you can use `mock.module()` to mock modules that are imported by other modules. - -### Module Mock Implementation Details - -Understanding how `mock.module()` works helps you use it more effectively: - -1. **Cache Interaction**: Module mocks interacts with both ESM and CommonJS module caches. - -2. **Lazy Evaluation**: The mock factory callback is only evaluated when the module is actually imported or required. - -3. **Path Resolution**: Bun automatically resolves the module specifier as though you were doing an import, supporting: - - Relative paths (`'./module'`) - - Absolute paths (`'/path/to/module'`) - - Package names (`'lodash'`) - -4. **Import Timing Effects**: - - When mocking before first import: No side effects from the original module occur - - When mocking after import: The original module's side effects have already happened - - For this reason, using `--preload` is recommended for mocks that need to prevent side effects - -5. **Live Bindings**: Mocked ESM modules maintain live bindings, so changing the mock will update all existing imports - -## Global Mock Functions - -### Clear all mocks with `mock.clearAllMocks()` - -Reset all mock function state (calls, results, etc.) without restoring their original implementation: - -```ts -import { expect, mock, test } from "bun:test"; - -const random1 = mock(() => Math.random()); -const random2 = mock(() => Math.random()); - -test("clearing all mocks", () => { - random1(); - random2(); - - expect(random1).toHaveBeenCalledTimes(1); - expect(random2).toHaveBeenCalledTimes(1); - - mock.clearAllMocks(); - - expect(random1).toHaveBeenCalledTimes(0); - expect(random2).toHaveBeenCalledTimes(0); - - // Note: implementations are preserved - expect(typeof random1()).toBe("number"); - expect(typeof random2()).toBe("number"); -}); -``` - -This resets the `.mock.calls`, `.mock.instances`, `.mock.contexts`, and `.mock.results` properties of all mocks, but unlike `mock.restore()`, it does not restore the original implementation. - -### Restore all function mocks with `mock.restore()` - -Instead of manually restoring each mock individually with `mockFn.mockRestore()`, restore all mocks with one command by calling `mock.restore()`. Doing so does not reset the value of modules overridden with `mock.module()`. - -Using `mock.restore()` can reduce the amount of code in your tests by adding it to `afterEach` blocks in each test file or even in your [test preload code](https://bun.sh/docs/runtime/bunfig#test-preload). - -```ts -import { expect, mock, spyOn, test } from "bun:test"; - -import * as fooModule from "./foo.ts"; -import * as barModule from "./bar.ts"; -import * as bazModule from "./baz.ts"; - -test("foo, bar, baz", () => { - const fooSpy = spyOn(fooModule, "foo"); - const barSpy = spyOn(barModule, "bar"); - const bazSpy = spyOn(bazModule, "baz"); - - expect(fooSpy).toBe("foo"); - expect(barSpy).toBe("bar"); - expect(bazSpy).toBe("baz"); - - fooSpy.mockImplementation(() => 42); - barSpy.mockImplementation(() => 43); - bazSpy.mockImplementation(() => 44); - - expect(fooSpy).toBe(42); - expect(barSpy).toBe(43); - expect(bazSpy).toBe(44); - - mock.restore(); - - expect(fooSpy).toBe("foo"); - expect(barSpy).toBe("bar"); - expect(bazSpy).toBe("baz"); -}); -``` - -## Vitest Compatibility - -For added compatibility with tests written for [Vitest](https://vitest.dev/), Bun provides the `vi` global object as an alias for parts of the Jest mocking API: - -```ts -import { test, expect } from "bun:test"; - -// Using the 'vi' alias similar to Vitest -test("vitest compatibility", () => { - const mockFn = vi.fn(() => 42); - - mockFn(); - expect(mockFn).toHaveBeenCalled(); - - // The following functions are available on the vi object: - // vi.fn - // vi.spyOn - // vi.mock - // vi.restoreAllMocks - // vi.clearAllMocks -}); -``` - -This makes it easier to port tests from Vitest to Bun without having to rewrite all your mocks. diff --git a/docs/test/mocks.mdx b/docs/test/mocks.mdx new file mode 100644 index 0000000000..02c048a380 --- /dev/null +++ b/docs/test/mocks.mdx @@ -0,0 +1,637 @@ +--- +title: "Mocks" +description: "Learn how to create and use mock functions, spies, and module mocks in Bun tests" +--- + +Mocking is essential for testing by allowing you to replace dependencies with controlled implementations. Bun provides comprehensive mocking capabilities including function mocks, spies, and module mocks. + +## Basic Function Mocks + +Create mocks with the `mock` function. + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect, mock } from "bun:test"; + +const random = mock(() => Math.random()); + +test("random", () => { + const val = random(); + expect(val).toBeGreaterThan(0); + expect(random).toHaveBeenCalled(); + expect(random).toHaveBeenCalledTimes(1); +}); +``` + +### Jest Compatibility + +Alternatively, you can use the `jest.fn()` function, as in Jest. It behaves identically. + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect, jest } from "bun:test"; + +const random = jest.fn(() => Math.random()); + +test("random", () => { + const val = random(); + expect(val).toBeGreaterThan(0); + expect(random).toHaveBeenCalled(); + expect(random).toHaveBeenCalledTimes(1); +}); +``` + +## Mock Function Properties + +The result of `mock()` is a new function that's been decorated with some additional properties. + +```ts title="test.ts" icon="/icons/typescript.svg" +import { mock } from "bun:test"; + +const random = mock((multiplier: number) => multiplier * Math.random()); + +random(2); +random(10); + +random.mock.calls; +// [[ 2 ], [ 10 ]] + +random.mock.results; +// [ +// { type: "return", value: 0.6533907460954099 }, +// { type: "return", value: 0.6452713933037312 } +// ] +``` + +### Available Properties and Methods + +The following properties and methods are implemented on mock functions: + +| Property/Method | Description | +| ----------------------------------------- | ---------------------------------------------- | +| `mockFn.getMockName()` | Returns the mock name | +| `mockFn.mock.calls` | Array of call arguments for each invocation | +| `mockFn.mock.results` | Array of return values for each invocation | +| `mockFn.mock.instances` | Array of `this` contexts for each invocation | +| `mockFn.mock.contexts` | Array of `this` contexts for each invocation | +| `mockFn.mock.lastCall` | Arguments of the most recent call | +| `mockFn.mockClear()` | Clears call history | +| `mockFn.mockReset()` | Clears call history and removes implementation | +| `mockFn.mockRestore()` | Restores original implementation | +| `mockFn.mockImplementation(fn)` | Sets a new implementation | +| `mockFn.mockImplementationOnce(fn)` | Sets implementation for next call only | +| `mockFn.mockName(name)` | Sets the mock name | +| `mockFn.mockReturnThis()` | Sets the return value to `this` | +| `mockFn.mockReturnValue(value)` | Sets a return value | +| `mockFn.mockReturnValueOnce(value)` | Sets return value for next call only | +| `mockFn.mockResolvedValue(value)` | Sets a resolved Promise value | +| `mockFn.mockResolvedValueOnce(value)` | Sets resolved Promise for next call only | +| `mockFn.mockRejectedValue(value)` | Sets a rejected Promise value | +| `mockFn.mockRejectedValueOnce(value)` | Sets rejected Promise for next call only | +| `mockFn.withImplementation(fn, callback)` | Temporarily changes implementation | + +### Practical Examples + +#### Basic Mock Usage + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect, mock } from "bun:test"; + +test("mock function behavior", () => { + const mockFn = mock((x: number) => x * 2); + + // Call the mock + const result1 = mockFn(5); + const result2 = mockFn(10); + + // Verify calls + expect(mockFn).toHaveBeenCalledTimes(2); + expect(mockFn).toHaveBeenCalledWith(5); + expect(mockFn).toHaveBeenLastCalledWith(10); + + // Check results + expect(result1).toBe(10); + expect(result2).toBe(20); + + // Inspect call history + expect(mockFn.mock.calls).toEqual([[5], [10]]); + expect(mockFn.mock.results).toEqual([ + { type: "return", value: 10 }, + { type: "return", value: 20 }, + ]); +}); +``` + +#### Dynamic Mock Implementations + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect, mock } from "bun:test"; + +test("dynamic mock implementations", () => { + const mockFn = mock(); + + // Set different implementations + mockFn.mockImplementationOnce(() => "first"); + mockFn.mockImplementationOnce(() => "second"); + mockFn.mockImplementation(() => "default"); + + expect(mockFn()).toBe("first"); + expect(mockFn()).toBe("second"); + expect(mockFn()).toBe("default"); + expect(mockFn()).toBe("default"); // Uses default implementation +}); +``` + +#### Async Mocks + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect, mock } from "bun:test"; + +test("async mock functions", async () => { + const asyncMock = mock(); + + // Mock resolved values + asyncMock.mockResolvedValueOnce("first result"); + asyncMock.mockResolvedValue("default result"); + + expect(await asyncMock()).toBe("first result"); + expect(await asyncMock()).toBe("default result"); + + // Mock rejected values + const rejectMock = mock(); + rejectMock.mockRejectedValue(new Error("Mock error")); + + await expect(rejectMock()).rejects.toThrow("Mock error"); +}); +``` + +## Spies with spyOn() + +It's possible to track calls to a function without replacing it with a mock. Use `spyOn()` to create a spy; these spies can be passed to `.toHaveBeenCalled()` and `.toHaveBeenCalledTimes()`. + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect, spyOn } from "bun:test"; + +const ringo = { + name: "Ringo", + sayHi() { + console.log(`Hello I'm ${this.name}`); + }, +}; + +const spy = spyOn(ringo, "sayHi"); + +test("spyon", () => { + expect(spy).toHaveBeenCalledTimes(0); + ringo.sayHi(); + expect(spy).toHaveBeenCalledTimes(1); +}); +``` + +### Advanced Spy Usage + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect, spyOn, afterEach } from "bun:test"; + +class UserService { + async getUser(id: string) { + // Original implementation + return { id, name: `User ${id}` }; + } + + async saveUser(user: any) { + // Original implementation + return { ...user, saved: true }; + } +} + +const userService = new UserService(); + +afterEach(() => { + // Restore all spies after each test + jest.restoreAllMocks(); +}); + +test("spy on service methods", async () => { + // Spy without changing implementation + const getUserSpy = spyOn(userService, "getUser"); + const saveUserSpy = spyOn(userService, "saveUser"); + + // Use the service normally + const user = await userService.getUser("123"); + await userService.saveUser(user); + + // Verify calls + expect(getUserSpy).toHaveBeenCalledWith("123"); + expect(saveUserSpy).toHaveBeenCalledWith(user); +}); + +test("spy with mock implementation", async () => { + // Spy and override implementation + const getUserSpy = spyOn(userService, "getUser").mockResolvedValue({ + id: "123", + name: "Mocked User", + }); + + const result = await userService.getUser("123"); + + expect(result.name).toBe("Mocked User"); + expect(getUserSpy).toHaveBeenCalledWith("123"); +}); +``` + +## Module Mocks with mock.module() + +Module mocking lets you override the behavior of a module. Use `mock.module(path: string, callback: () => Object)` to mock a module. + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect, mock } from "bun:test"; + +mock.module("./module", () => { + return { + foo: "bar", + }; +}); + +test("mock.module", async () => { + const esm = await import("./module"); + expect(esm.foo).toBe("bar"); + + const cjs = require("./module"); + expect(cjs.foo).toBe("bar"); +}); +``` + +Like the rest of Bun, module mocks support both `import` and `require`. + +### Overriding Already Imported Modules + +If you need to override a module that's already been imported, there's nothing special you need to do. Just call `mock.module()` and the module will be overridden. + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect, mock } from "bun:test"; + +// The module we're going to mock is here: +import { foo } from "./module"; + +test("mock.module", async () => { + const cjs = require("./module"); + expect(foo).toBe("bar"); + expect(cjs.foo).toBe("bar"); + + // We update it here: + mock.module("./module", () => { + return { + foo: "baz", + }; + }); + + // And the live bindings are updated. + expect(foo).toBe("baz"); + + // The module is also updated for CJS. + expect(cjs.foo).toBe("baz"); +}); +``` + +### Hoisting & Preloading + +If you need to ensure a module is mocked before it's imported, you should use `--preload` to load your mocks before your tests run. + +```ts title="my-preload.ts" icon="/icons/typescript.svg" +import { mock } from "bun:test"; + +mock.module("./module", () => { + return { + foo: "bar", + }; +}); +``` + +```bash terminal icon="terminal" +bun test --preload ./my-preload +``` + +To make your life easier, you can put preload in your `bunfig.toml`: + +```toml title="bunfig.toml" icon="settings" +[test] +# Load these modules before running tests. +preload = ["./my-preload"] +``` + +### Module Mock Best Practices + +#### When to Use Preload + +**What happens if I mock a module that's already been imported?** + +If you mock a module that's already been imported, the module will be updated in the module cache. This means that any modules that import the module will get the mocked version, BUT the original module will still have been evaluated. That means that any side effects from the original module will still have happened. + +If you want to prevent the original module from being evaluated, you should use `--preload` to load your mocks before your tests run. + +#### Practical Module Mock Examples + +```ts title="api-client.test.ts" icon="/icons/typescript.svg" +import { test, expect, mock, beforeEach } from "bun:test"; + +// Mock the API client module +mock.module("./api-client", () => ({ + fetchUser: mock(async (id: string) => ({ id, name: `User ${id}` })), + createUser: mock(async (user: any) => ({ ...user, id: "new-id" })), + updateUser: mock(async (id: string, user: any) => ({ ...user, id })), +})); + +test("user service with mocked API", async () => { + const { fetchUser } = await import("./api-client"); + const { UserService } = await import("./user-service"); + + const userService = new UserService(); + const user = await userService.getUser("123"); + + expect(fetchUser).toHaveBeenCalledWith("123"); + expect(user.name).toBe("User 123"); +}); +``` + +#### Mocking External Dependencies + +```ts title="database.test.ts" icon="/icons/typescript.svg" +import { test, expect, mock } from "bun:test"; + +// Mock external database library +mock.module("pg", () => ({ + Client: mock(function () { + return { + connect: mock(async () => {}), + query: mock(async (sql: string) => ({ + rows: [{ id: 1, name: "Test User" }], + })), + end: mock(async () => {}), + }; + }), +})); + +test("database operations", async () => { + const { Database } = await import("./database"); + const db = new Database(); + + const users = await db.getUsers(); + expect(users).toHaveLength(1); + expect(users[0].name).toBe("Test User"); +}); +``` + +## Global Mock Functions + +### Clear All Mocks + +Reset all mock function state (calls, results, etc.) without restoring their original implementation: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { expect, mock, test } from "bun:test"; + +const random1 = mock(() => Math.random()); +const random2 = mock(() => Math.random()); + +test("clearing all mocks", () => { + random1(); + random2(); + + expect(random1).toHaveBeenCalledTimes(1); + expect(random2).toHaveBeenCalledTimes(1); + + mock.clearAllMocks(); + + expect(random1).toHaveBeenCalledTimes(0); + expect(random2).toHaveBeenCalledTimes(0); + + // Note: implementations are preserved + expect(typeof random1()).toBe("number"); + expect(typeof random2()).toBe("number"); +}); +``` + +This resets the `.mock.calls`, `.mock.instances`, `.mock.contexts`, and `.mock.results` properties of all mocks, but unlike `mock.restore()`, it does not restore the original implementation. + +### Restore All Mocks + +Instead of manually restoring each mock individually with `mockFn.mockRestore()`, restore all mocks with one command by calling `mock.restore()`. Doing so does not reset the value of modules overridden with `mock.module()`. + +```ts title="test.ts" icon="/icons/typescript.svg" +import { expect, mock, spyOn, test } from "bun:test"; + +import * as fooModule from "./foo.ts"; +import * as barModule from "./bar.ts"; +import * as bazModule from "./baz.ts"; + +test("foo, bar, baz", () => { + const fooSpy = spyOn(fooModule, "foo"); + const barSpy = spyOn(barModule, "bar"); + const bazSpy = spyOn(bazModule, "baz"); + + // Original values + expect(fooSpy).toBe("foo"); + expect(barSpy).toBe("bar"); + expect(bazSpy).toBe("baz"); + + // Mock implementations + fooSpy.mockImplementation(() => 42); + barSpy.mockImplementation(() => 43); + bazSpy.mockImplementation(() => 44); + + expect(fooSpy()).toBe(42); + expect(barSpy()).toBe(43); + expect(bazSpy()).toBe(44); + + // Restore all + mock.restore(); + + expect(fooSpy()).toBe("foo"); + expect(barSpy()).toBe("bar"); + expect(bazSpy()).toBe("baz"); +}); +``` + +Using `mock.restore()` can reduce the amount of code in your tests by adding it to `afterEach` blocks in each test file or even in your test preload code. + +## Vitest Compatibility + +For added compatibility with tests written for Vitest, Bun provides the `vi` global object as an alias for parts of the Jest mocking API: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect } from "bun:test"; + +// Using the 'vi' alias similar to Vitest +test("vitest compatibility", () => { + const mockFn = vi.fn(() => 42); + + mockFn(); + expect(mockFn).toHaveBeenCalled(); + + // The following functions are available on the vi object: + // vi.fn + // vi.spyOn + // vi.mock + // vi.restoreAllMocks + // vi.clearAllMocks +}); +``` + +This makes it easier to port tests from Vitest to Bun without having to rewrite all your mocks. + +## Implementation Details + +Understanding how `mock.module()` works helps you use it more effectively: + +### Cache Interaction + +Module mocks interact with both ESM and CommonJS module caches. + +### Lazy Evaluation + +The mock factory callback is only evaluated when the module is actually imported or required. + +### Path Resolution + +Bun automatically resolves the module specifier as though you were doing an import, supporting: + +- Relative paths (`'./module'`) +- Absolute paths (`'/path/to/module'`) +- Package names (`'lodash'`) + +### Import Timing Effects + +- **When mocking before first import**: No side effects from the original module occur +- **When mocking after import**: The original module's side effects have already happened + +For this reason, using `--preload` is recommended for mocks that need to prevent side effects. + +### Live Bindings + +Mocked ESM modules maintain live bindings, so changing the mock will update all existing imports. + +## Advanced Patterns + +### Factory Functions + +```ts title="test.ts" icon="/icons/typescript.svg" +import { mock } from "bun:test"; + +function createMockUser(overrides = {}) { + return { + id: "mock-id", + name: "Mock User", + email: "mock@example.com", + ...overrides, + }; +} + +const mockUserService = { + getUser: mock(async (id: string) => createMockUser({ id })), + createUser: mock(async (data: any) => createMockUser(data)), + updateUser: mock(async (id: string, data: any) => createMockUser({ id, ...data })), +}; +``` + +### Conditional Mocking + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect, mock } from "bun:test"; + +const shouldUseMockApi = process.env.NODE_ENV === "test"; + +if (shouldUseMockApi) { + mock.module("./api", () => ({ + fetchData: mock(async () => ({ data: "mocked" })), + })); +} + +test("conditional API usage", async () => { + const { fetchData } = await import("./api"); + const result = await fetchData(); + + if (shouldUseMockApi) { + expect(result.data).toBe("mocked"); + } +}); +``` + +### Mock Cleanup Patterns + +```ts title="test.ts" icon="/icons/typescript.svg" +import { afterEach, beforeEach } from "bun:test"; + +beforeEach(() => { + // Set up common mocks + mock.module("./logger", () => ({ + log: mock(() => {}), + error: mock(() => {}), + warn: mock(() => {}), + })); +}); + +afterEach(() => { + // Clean up all mocks + mock.restore(); + mock.clearAllMocks(); +}); +``` + +## Best Practices + +### Keep Mocks Simple + +```ts title="test.ts" icon="/icons/typescript.svg" +// Good: Simple, focused mock +const mockUserApi = { + getUser: mock(async id => ({ id, name: "Test User" })), +}; + +// Avoid: Overly complex mock behavior +const complexMock = mock(input => { + if (input.type === "A") { + return processTypeA(input); + } else if (input.type === "B") { + return processTypeB(input); + } + // ... lots of complex logic +}); +``` + +### Use Type-Safe Mocks + +```ts +interface UserService { + getUser(id: string): Promise; + createUser(data: CreateUserData): Promise; +} + +const mockUserService: UserService = { + getUser: mock(async (id: string) => ({ id, name: "Test User" })), + createUser: mock(async data => ({ id: "new-id", ...data })), +}; +``` + +### Test Mock Behavior + +```ts title="test.ts" icon="/icons/typescript.svg" +test("service calls API correctly", async () => { + const mockApi = { fetchUser: mock(async () => ({ id: "1" })) }; + + const service = new UserService(mockApi); + await service.getUser("123"); + + // Verify the mock was called correctly + expect(mockApi.fetchUser).toHaveBeenCalledWith("123"); + expect(mockApi.fetchUser).toHaveBeenCalledTimes(1); +}); +``` + +## Notes + +### Auto-mocking + +`__mocks__` directory and auto-mocking are not supported yet. If this is blocking you from switching to Bun, please [file an issue](https://github.com/oven-sh/bun/issues). + +### ESM vs CommonJS + +Module mocks have different implementations for ESM and CommonJS modules. For ES Modules, Bun has added patches to JavaScriptCore that allow Bun to override export values at runtime and update live bindings recursively. diff --git a/docs/test/reporters.md b/docs/test/reporters.mdx similarity index 92% rename from docs/test/reporters.md rename to docs/test/reporters.mdx index 6a3faf6cf1..7568296506 100644 --- a/docs/test/reporters.md +++ b/docs/test/reporters.mdx @@ -1,12 +1,19 @@ +--- +title: Test Reporters +description: +--- + bun test supports different output formats through reporters. This document covers both built-in reporters and how to implement your own custom reporters. +--- + ## Built-in Reporters ### Default Console Reporter By default, bun test outputs results to the console in a human-readable format: -```sh +```sh terminal icon="terminal" test/package-json-lint.test.ts: ✓ test/package.json [0.88ms] ✓ test/js/third_party/grpc-js/package.json [0.18ms] @@ -21,7 +28,7 @@ Ran 4 tests in 1.44ms When a terminal doesn't support colors, the output avoids non-ascii characters: -```sh +```sh terminal icon="terminal" test/package-json-lint.test.ts: (pass) test/package.json [0.48ms] (pass) test/js/third_party/grpc-js/package.json [0.10ms] @@ -34,15 +41,6 @@ test/package-json-lint.test.ts: Ran 4 tests across 1 files. [0.66ms] ``` -### Dots Reporter - -The dots reporter shows `.` for passing tests and `F` for failures—useful for large test suites. - -```sh -$ bun test --dots -$ bun test --reporter=dots -``` - ### JUnit XML Reporter For CI/CD environments, Bun supports generating JUnit XML reports. JUnit XML is a widely-adopted format for test results that can be parsed by many CI/CD systems, including GitLab, Jenkins, and others. @@ -51,8 +49,8 @@ For CI/CD environments, Bun supports generating JUnit XML reports. JUnit XML is To generate a JUnit XML report, use the `--reporter=junit` flag along with `--reporter-outfile` to specify the output file: -```sh -$ bun test --reporter=junit --reporter-outfile=./junit.xml +```sh terminal icon="terminal" +bun test --reporter=junit --reporter-outfile=./junit.xml ``` This continues to output to the console as usual while also writing the JUnit XML report to the specified path at the end of the test run. @@ -61,7 +59,7 @@ This continues to output to the console as usual while also writing the JUnit XM You can also configure the JUnit reporter in your `bunfig.toml` file: -```toml +```toml title="bunfig.toml" icon="settings" [test.reporter] junit = "path/to/junit.xml" # Output path for JUnit XML report ``` @@ -91,7 +89,9 @@ The JUnit reporter currently has a few limitations that will be addressed in fut Bun test automatically detects when it's running inside GitHub Actions and emits GitHub Actions annotations to the console directly. No special configuration is needed beyond installing Bun and running `bun test`. -For a GitHub Actions workflow configuration example, see the [CI/CD integration](../cli/test.md#cicd-integration) section of the CLI documentation. +For a GitHub Actions workflow configuration example, see the [CI/CD integration](/pm/cli/install#ci%2Fcd) section of the CLI documentation. + +--- ## Custom Reporters diff --git a/docs/test/runtime-behavior.md b/docs/test/runtime-behavior.md deleted file mode 100644 index 5c51e06063..0000000000 --- a/docs/test/runtime-behavior.md +++ /dev/null @@ -1,95 +0,0 @@ -`bun test` is deeply integrated with Bun's runtime. This is part of what makes `bun test` fast and simple to use. - -#### `$NODE_ENV` environment variable - -`bun test` automatically sets `$NODE_ENV` to `"test"` unless it's already set in the environment or via .env files. This is standard behavior for most test runners and helps ensure consistent test behavior. - -```ts -import { test, expect } from "bun:test"; - -test("NODE_ENV is set to test", () => { - expect(process.env.NODE_ENV).toBe("test"); -}); -``` - -When `NODE_ENV` is set to `"test"`, Bun will not load `.env.local` files. This ensures consistent test environments across different executions by preventing local overrides during testing. Instead, use `.env.test` for test-specific environment variables, which should be committed to your repository for consistency across all developers and CI environments. - -#### `$TZ` environment variable - -By default, all `bun test` runs use UTC (`Etc/UTC`) as the time zone unless overridden by the `TZ` environment variable. This ensures consistent date and time behavior across different development environments. - -#### Test Timeouts - -Each test has a default timeout of 5000ms (5 seconds) if not explicitly overridden. Tests that exceed this timeout will fail. This can be changed globally with the `--timeout` flag or per-test as the third parameter to the test function. - -## Error Handling - -### Unhandled Errors - -`bun test` tracks unhandled promise rejections and errors that occur between tests. If such errors occur, the final exit code will be non-zero (specifically, the count of such errors), even if all tests pass. - -This helps catch errors in asynchronous code that might otherwise go unnoticed: - -```ts -import { test } from "bun:test"; - -test("test 1", () => { - // This test passes -}); - -// This error happens outside any test -setTimeout(() => { - throw new Error("Unhandled error"); -}, 0); - -test("test 2", () => { - // This test also passes -}); - -// The test run will still fail with a non-zero exit code -// because of the unhandled error -``` - -Internally, this occurs with a higher precedence than `process.on("unhandledRejection")` or `process.on("uncaughtException")`, which makes it simpler to integrate with existing code. - -## Using General CLI Flags with Tests - -Several Bun CLI flags can be used with `bun test` to modify its behavior: - -### Memory Usage - -- `--smol`: Reduces memory usage for the test runner VM - -### Debugging - -- `--inspect`, `--inspect-brk`: Attaches the debugger to the test runner process - -### Module Loading - -- `--preload`: Runs scripts before test files (useful for global setup/mocks) -- `--define`: Sets compile-time constants -- `--loader`: Configures custom loaders -- `--tsconfig-override`: Uses a different tsconfig -- `--conditions`: Sets package.json conditions for module resolution -- `--env-file`: Loads environment variables for tests - -### Installation-related Flags - -- `--prefer-offline`, `--frozen-lockfile`, etc.: Affect any network requests or auto-installs during test execution - -## Watch and Hot Reloading - -When running `bun test` with the `--watch` flag, the test runner will watch for file changes and re-run affected tests. - -The `--hot` flag provides similar functionality but is more aggressive about trying to preserve state between runs. For most test scenarios, `--watch` is the recommended option. - -## Global Variables - -The following globals are automatically available in test files without importing (though they can be imported from `bun:test` if preferred): - -- `test`, `it`: Define tests -- `describe`: Group tests -- `expect`: Make assertions -- `beforeAll`, `beforeEach`, `afterAll`, `afterEach`: Lifecycle hooks -- `jest`: Jest global object -- `vi`: Vitest compatibility alias for common jest methods diff --git a/docs/test/runtime-behavior.mdx b/docs/test/runtime-behavior.mdx new file mode 100644 index 0000000000..d6897c19b1 --- /dev/null +++ b/docs/test/runtime-behavior.mdx @@ -0,0 +1,342 @@ +--- +title: "Runtime behavior" +description: "Learn about Bun test's runtime integration, environment variables, timeouts, and error handling" +--- + +`bun test` is deeply integrated with Bun's runtime. This is part of what makes `bun test` fast and simple to use. + +## Environment Variables + +### NODE_ENV + +`bun test` automatically sets `$NODE_ENV` to `"test"` unless it's already set in the environment or via `.env` files. This is standard behavior for most test runners and helps ensure consistent test behavior. + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect } from "bun:test"; + +test("NODE_ENV is set to test", () => { + expect(process.env.NODE_ENV).toBe("test"); +}); +``` + +You can override this by setting `NODE_ENV` explicitly: + +```bash terminal icon="terminal" +NODE_ENV=development bun test +``` + +### TZ (Timezone) + +By default, all `bun test` runs use UTC (`Etc/UTC`) as the time zone unless overridden by the `TZ` environment variable. This ensures consistent date and time behavior across different development environments. + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect } from "bun:test"; + +test("timezone is UTC by default", () => { + const date = new Date(); + expect(date.getTimezoneOffset()).toBe(0); +}); +``` + +To test with a specific timezone: + +```bash terminal icon="terminal" +TZ=America/New_York bun test +``` + +## Test Timeouts + +Each test has a default timeout of 5000ms (5 seconds) if not explicitly overridden. Tests that exceed this timeout will fail. + +### Global Timeout + +Change the timeout globally with the `--timeout` flag: + +```bash terminal icon="terminal" +bun test --timeout 10000 # 10 seconds +``` + +### Per-Test Timeout + +Set timeout per test as the third parameter to the test function: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect } from "bun:test"; + +test("fast test", () => { + expect(1 + 1).toBe(2); +}, 1000); // 1 second timeout + +test("slow test", async () => { + await new Promise(resolve => setTimeout(resolve, 8000)); +}, 10000); // 10 second timeout +``` + +### Infinite Timeout + +Use `0` or `Infinity` to disable timeout: + +```ts title="test.ts" icon="/icons/typescript.svg" +test("test without timeout", async () => { + // This test can run indefinitely + await someVeryLongOperation(); +}, 0); +``` + +## Error Handling + +### Unhandled Errors + +`bun test` tracks unhandled promise rejections and errors that occur between tests. If such errors occur, the final exit code will be non-zero (specifically, the count of such errors), even if all tests pass. + +This helps catch errors in asynchronous code that might otherwise go unnoticed: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test } from "bun:test"; + +test("test 1", () => { + // This test passes + expect(true).toBe(true); +}); + +// This error happens outside any test +setTimeout(() => { + throw new Error("Unhandled error"); +}, 0); + +test("test 2", () => { + // This test also passes + expect(true).toBe(true); +}); + +// The test run will still fail with a non-zero exit code +// because of the unhandled error +``` + +### Promise Rejections + +Unhandled promise rejections are also caught: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test } from "bun:test"; + +test("passing test", () => { + expect(1).toBe(1); +}); + +// This will cause the test run to fail +Promise.reject(new Error("Unhandled rejection")); +``` + +### Custom Error Handling + +You can set up custom error handlers in your test setup: + +```ts title="test-setup.ts" icon="/icons/typescript.svg" +process.on("uncaughtException", error => { + console.error("Uncaught Exception:", error); + process.exit(1); +}); + +process.on("unhandledRejection", (reason, promise) => { + console.error("Unhandled Rejection at:", promise, "reason:", reason); + process.exit(1); +}); +``` + +## CLI Flags Integration + +Several Bun CLI flags can be used with `bun test` to modify its behavior: + +### Memory Usage + +```bash terminal icon="terminal" +# Reduces memory usage for the test runner VM +bun test --smol +``` + +### Debugging + +```bash terminal icon="terminal" +# Attaches the debugger to the test runner process +bun test --inspect +bun test --inspect-brk +``` + +### Module Loading + +```bash terminal icon="terminal" +# Runs scripts before test files (useful for global setup/mocks) +bun test --preload ./setup.ts + +# Sets compile-time constants +bun test --define "process.env.API_URL='http://localhost:3000'" + +# Configures custom loaders +bun test --loader .special:special-loader + +# Uses a different tsconfig +bun test --tsconfig-override ./test-tsconfig.json + +# Sets package.json conditions for module resolution +bun test --conditions development + +# Loads environment variables for tests +bun test --env-file .env.test +``` + +### Installation-related Flags + +```bash +# Affect any network requests or auto-installs during test execution +bun test --prefer-offline +bun test --frozen-lockfile +``` + +## Watch and Hot Reloading + +### Watch Mode + +When running `bun test` with the `--watch` flag, the test runner will watch for file changes and re-run affected tests. + +```bash terminal icon="terminal" +bun test --watch +``` + +The test runner is smart about which tests to re-run: + +```ts title="math.test.ts" icon="/icons/typescript.svg" +import { add } from "./math.js"; +import { test, expect } from "bun:test"; + +test("addition", () => { + expect(add(2, 3)).toBe(5); +}); +``` + +If you modify `math.js`, only `math.test.ts` will re-run, not all tests. + +### Hot Reloading + +The `--hot` flag provides similar functionality but is more aggressive about trying to preserve state between runs: + +```bash terminal icon="terminal" +bun test --hot +``` + +For most test scenarios, `--watch` is the recommended option as it provides better isolation between test runs. + +## Global Variables + +The following globals are automatically available in test files without importing (though they can be imported from `bun:test` if preferred): + +```ts title="test.ts" icon="/icons/typescript.svg" +// All of these are available globally +test("global test function", () => { + expect(true).toBe(true); +}); + +describe("global describe", () => { + beforeAll(() => { + // global beforeAll + }); + + it("global it function", () => { + // it is an alias for test + }); +}); + +// Jest compatibility +jest.fn(); + +// Vitest compatibility +vi.fn(); +``` + +You can also import them explicitly if you prefer: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, it, describe, expect, beforeAll, beforeEach, afterAll, afterEach, jest, vi } from "bun:test"; +``` + +## Process Integration + +### Exit Codes + +`bun test` uses standard exit codes: + +- `0`: All tests passed, no unhandled errors +- `1`: Test failures occurred +- `>1`: Number of unhandled errors (even if tests passed) + +### Signal Handling + +The test runner properly handles common signals: + +```bash terminal icon="terminal" +# Gracefully stops test execution +kill -SIGTERM + +# Immediately stops test execution +kill -SIGKILL +``` + +### Environment Detection + +Bun automatically detects certain environments and adjusts behavior: + +```ts title="test.ts" icon="/icons/typescript.svg" +// GitHub Actions detection +if (process.env.GITHUB_ACTIONS) { + // Bun automatically emits GitHub Actions annotations +} + +// CI detection +if (process.env.CI) { + // Certain behaviors may be adjusted for CI environments +} +``` + +## Performance Considerations + +### Single Process + +The test runner runs all tests in a single process by default. This provides: + +- **Faster startup** - No need to spawn multiple processes +- **Shared memory** - Efficient resource usage +- **Simple debugging** - All tests in one process + +However, this means: + +- Tests share global state (use lifecycle hooks to clean up) +- One test crash can affect others +- No true parallelization of individual tests + +### Memory Management + +```bash terminal icon="terminal" +# Monitor memory usage +bun test --smol # Reduces memory footprint + +# For large test suites, consider splitting files +bun test src/unit/ +bun test src/integration/ +``` + +### Test Isolation + +Since tests run in the same process, ensure proper cleanup: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { afterEach } from "bun:test"; + +afterEach(() => { + // Clean up global state + global.myGlobalVar = undefined; + delete process.env.TEST_VAR; + + // Reset modules if needed + jest.resetModules(); +}); +``` diff --git a/docs/test/snapshots.md b/docs/test/snapshots.md deleted file mode 100644 index 0661cf2b4b..0000000000 --- a/docs/test/snapshots.md +++ /dev/null @@ -1,68 +0,0 @@ -Snapshot testing saves the output of a value and compares it against future test runs. This is particularly useful for UI components, complex objects, or any output that needs to remain consistent. - -## Basic snapshots - -Snapshot tests are written using the `.toMatchSnapshot()` matcher: - -```ts -import { test, expect } from "bun:test"; - -test("snap", () => { - expect("foo").toMatchSnapshot(); -}); -``` - -The first time this test is run, the argument to `expect` will be serialized and written to a special snapshot file in a `__snapshots__` directory alongside the test file. On future runs, the argument is compared against the snapshot on disk. Snapshots can be re-generated with the following command: - -```bash -$ bun test --update-snapshots -``` - -## Inline snapshots - -For smaller values, you can use inline snapshots with `.toMatchInlineSnapshot()`. These snapshots are stored directly in your test file: - -```ts -import { test, expect } from "bun:test"; - -test("inline snapshot", () => { - // First run: snapshot will be inserted automatically - expect({ hello: "world" }).toMatchInlineSnapshot(); - - // After first run, the test file will be updated to: - // expect({ hello: "world" }).toMatchInlineSnapshot(` - // { - // "hello": "world", - // } - // `); -}); -``` - -When you run the test, Bun automatically updates the test file itself with the generated snapshot string. This makes the tests more portable and easier to understand, since the expected output is right next to the test. - -### Using inline snapshots - -1. Write your test with `.toMatchInlineSnapshot()` -2. Run the test once -3. Bun automatically updates your test file with the snapshot -4. On subsequent runs, the value will be compared against the inline snapshot - -Inline snapshots are particularly useful for small, simple values where it's helpful to see the expected output right in the test file. - -## Error snapshots - -You can also snapshot error messages using `.toThrowErrorMatchingSnapshot()` and `.toThrowErrorMatchingInlineSnapshot()`: - -```ts -import { test, expect } from "bun:test"; - -test("error snapshot", () => { - expect(() => { - throw new Error("Something went wrong"); - }).toThrowErrorMatchingSnapshot(); - - expect(() => { - throw new Error("Another error"); - }).toThrowErrorMatchingInlineSnapshot(); -}); -``` diff --git a/docs/test/snapshots.mdx b/docs/test/snapshots.mdx new file mode 100644 index 0000000000..aa62e8261f --- /dev/null +++ b/docs/test/snapshots.mdx @@ -0,0 +1,434 @@ +--- +title: "Snapshots" +description: "Learn how to use snapshot testing in Bun to save and compare output between test runs" +--- + +Snapshot testing saves the output of a value and compares it against future test runs. This is particularly useful for UI components, complex objects, or any output that needs to remain consistent. + +## Basic Snapshots + +Snapshot tests are written using the `.toMatchSnapshot()` matcher: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect } from "bun:test"; + +test("snap", () => { + expect("foo").toMatchSnapshot(); +}); +``` + +The first time this test is run, the argument to `expect` will be serialized and written to a special snapshot file in a `__snapshots__` directory alongside the test file. + +### Snapshot Files + +After running the test above, Bun will create: + +```text title="directory structure" icon="file-directory" +your-project/ +├── snap.test.ts +└── __snapshots__/ + └── snap.test.ts.snap +``` + +The snapshot file contains: + +```txt title="snapshot file" icon="file-code" +// Bun Snapshot v1, https://goo.gl/fbAQLP + +exports[`snap 1`] = `"foo"`; +``` + +On future runs, the argument is compared against the snapshot on disk. + +## Updating Snapshots + +Snapshots can be re-generated with the following command: + +```bash terminal icon="terminal" +bun test --update-snapshots +``` + +This is useful when: + +- You've intentionally changed the output +- You're adding new snapshot tests +- The expected output has legitimately changed + +## Inline Snapshots + +For smaller values, you can use inline snapshots with `.toMatchInlineSnapshot()`. These snapshots are stored directly in your test file: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect } from "bun:test"; + +test("inline snapshot", () => { + // First run: snapshot will be inserted automatically + expect({ hello: "world" }).toMatchInlineSnapshot(); +}); +``` + +After the first run, Bun automatically updates your test file: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect } from "bun:test"; + +test("inline snapshot", () => { + expect({ hello: "world" }).toMatchInlineSnapshot(` +{ + "hello": "world", +} +`); +}); +``` + +### Using Inline Snapshots + +1. Write your test with `.toMatchInlineSnapshot()` +2. Run the test once +3. Bun automatically updates your test file with the snapshot +4. On subsequent runs, the value will be compared against the inline snapshot + +Inline snapshots are particularly useful for small, simple values where it's helpful to see the expected output right in the test file. + +## Error Snapshots + +You can also snapshot error messages using `.toThrowErrorMatchingSnapshot()` and `.toThrowErrorMatchingInlineSnapshot()`: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect } from "bun:test"; + +test("error snapshot", () => { + expect(() => { + throw new Error("Something went wrong"); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + throw new Error("Another error"); + }).toThrowErrorMatchingInlineSnapshot(); +}); +``` + +After running, the inline version becomes: + +```ts title="test.ts" icon="/icons/typescript.svg" +test("error snapshot", () => { + expect(() => { + throw new Error("Something went wrong"); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + throw new Error("Another error"); + }).toThrowErrorMatchingInlineSnapshot(`"Another error"`); +}); +``` + +## Advanced Snapshot Usage + +### Complex Objects + +Snapshots work well with complex nested objects: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect } from "bun:test"; + +test("complex object snapshot", () => { + const user = { + id: 1, + name: "John Doe", + email: "john@example.com", + profile: { + age: 30, + preferences: { + theme: "dark", + notifications: true, + }, + }, + tags: ["developer", "javascript", "bun"], + }; + + expect(user).toMatchSnapshot(); +}); +``` + +### Array Snapshots + +Arrays are also well-suited for snapshot testing: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect } from "bun:test"; + +test("array snapshot", () => { + const numbers = [1, 2, 3, 4, 5].map(n => n * 2); + expect(numbers).toMatchSnapshot(); +}); +``` + +### Function Output Snapshots + +Snapshot the output of functions: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect } from "bun:test"; + +function generateReport(data: any[]) { + return { + total: data.length, + summary: data.map(item => ({ id: item.id, name: item.name })), + timestamp: "2024-01-01", // Fixed for testing + }; +} + +test("report generation", () => { + const data = [ + { id: 1, name: "Alice", age: 30 }, + { id: 2, name: "Bob", age: 25 }, + ]; + + expect(generateReport(data)).toMatchSnapshot(); +}); +``` + +## React Component Snapshots + +Snapshots are particularly useful for React components: + +```tsx title="test.ts" icon="/icons/typescript.svg" +import { test, expect } from "bun:test"; +import { render } from "@testing-library/react"; + +function Button({ children, variant = "primary" }) { + return ; +} + +test("Button component snapshots", () => { + const { container: primary } = render(); + const { container: secondary } = render(); + + expect(primary.innerHTML).toMatchSnapshot(); + expect(secondary.innerHTML).toMatchSnapshot(); +}); +``` + +## Property Matchers + +For values that change between test runs (like timestamps or IDs), use property matchers: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect } from "bun:test"; + +test("snapshot with dynamic values", () => { + const user = { + id: Math.random(), // This changes every run + name: "John", + createdAt: new Date().toISOString(), // This also changes + }; + + expect(user).toMatchSnapshot({ + id: expect.any(Number), + createdAt: expect.any(String), + }); +}); +``` + +The snapshot will store: + +```txt title="snapshot file" icon="file-code" +exports[`snapshot with dynamic values 1`] = ` +{ + "createdAt": Any, + "id": Any, + "name": "John", +} +`; +``` + +## Custom Serializers + +You can customize how objects are serialized in snapshots: + +```ts title="test.ts" icon="/icons/typescript.svg" +import { test, expect } from "bun:test"; + +// Custom serializer for Date objects +expect.addSnapshotSerializer({ + test: val => val instanceof Date, + serialize: val => `"${val.toISOString()}"`, +}); + +test("custom serializer", () => { + const event = { + name: "Meeting", + date: new Date("2024-01-01T10:00:00Z"), + }; + + expect(event).toMatchSnapshot(); +}); +``` + +## Best Practices + +### Keep Snapshots Small + +```ts title="test.ts" icon="/icons/typescript.svg" +// Good: Focused snapshots +test("user name formatting", () => { + const formatted = formatUserName("john", "doe"); + expect(formatted).toMatchInlineSnapshot(`"John Doe"`); +}); + +// Avoid: Huge snapshots that are hard to review +test("entire page render", () => { + const page = renderEntirePage(); + expect(page).toMatchSnapshot(); // This could be thousands of lines +}); +``` + +### Use Descriptive Test Names + +```ts title="test.ts" icon="/icons/typescript.svg" +// Good: Clear what the snapshot represents +test("formats currency with USD symbol", () => { + expect(formatCurrency(99.99)).toMatchInlineSnapshot(`"$99.99"`); +}); + +// Avoid: Unclear what's being tested +test("format test", () => { + expect(format(99.99)).toMatchInlineSnapshot(`"$99.99"`); +}); +``` + +### Group Related Snapshots + +```ts title="test.ts" icon="/icons/typescript.svg" +import { describe, test, expect } from "bun:test"; + +describe("Button component", () => { + test("primary variant", () => { + expect(render()) + .toMatchSnapshot(); + }); + + test("secondary variant", () => { + expect(render()) + .toMatchSnapshot(); + }); + + test("disabled state", () => { + expect(render()) + .toMatchSnapshot(); + }); +}); +``` + +### Handle Dynamic Data + +```ts title="test.ts" icon="/icons/typescript.svg" +// Good: Normalize dynamic data +test("API response format", () => { + const response = { + data: { id: 1, name: "Test" }, + timestamp: Date.now(), + requestId: generateId(), + }; + + expect({ + ...response, + timestamp: "TIMESTAMP", + requestId: "REQUEST_ID", + }).toMatchSnapshot(); +}); + +// Or use property matchers +test("API response with matchers", () => { + const response = getApiResponse(); + + expect(response).toMatchSnapshot({ + timestamp: expect.any(Number), + requestId: expect.any(String), + }); +}); +``` + +## Managing Snapshots + +### Reviewing Snapshot Changes + +When snapshots change, carefully review them: + +```bash terminal icon="terminal" +# See what changed +git diff __snapshots__/ + +# Update if changes are intentional +bun test --update-snapshots + +# Commit the updated snapshots +git add __snapshots__/ +git commit -m "Update snapshots after UI changes" +``` + +### Cleaning Up Unused Snapshots + +Bun will warn about unused snapshots: + +```txt title="warning" icon="warning" +Warning: 1 unused snapshot found: + my-test.test.ts.snap: "old test that no longer exists 1" +``` + +Remove unused snapshots by deleting them from the snapshot files or by running tests with cleanup flags if available. + +### Organizing Large Snapshot Files + +For large projects, consider organizing tests to keep snapshot files manageable: + +```text title="directory structure" icon="file-directory" +tests/ +├── components/ +│ ├── Button.test.tsx +│ └── __snapshots__/ +│ └── Button.test.tsx.snap +├── utils/ +│ ├── formatters.test.ts +│ └── __snapshots__/ +│ └── formatters.test.ts.snap +``` + +## Troubleshooting + +### Snapshot Failures + +When snapshots fail, you'll see a diff: + +```text title="diff" icon="file-code" +- Expected ++ Received + + Object { +- "name": "John", ++ "name": "Jane", + } +``` + +Common causes: + +- Intentional changes (update with `--update-snapshots`) +- Unintentional changes (fix the code) +- Dynamic data (use property matchers) +- Environment differences (normalize the data) + +### Platform Differences + +Be aware of platform-specific differences: + +```ts title="test.ts" icon="/icons/typescript.svg" +// Paths might differ between Windows/Unix +test("file operations", () => { + const result = processFile("./test.txt"); + + expect({ + ...result, + path: result.path.replace(/\\/g, "/"), // Normalize paths + }).toMatchSnapshot(); +}); +``` diff --git a/docs/test/writing-tests.mdx b/docs/test/writing-tests.mdx new file mode 100644 index 0000000000..a2de3839a5 --- /dev/null +++ b/docs/test/writing-tests.mdx @@ -0,0 +1,635 @@ +--- +title: "Writing tests" +description: "Learn how to write tests using Bun's Jest-compatible API with support for async tests, timeouts, and various test modifiers" +--- + +Define tests with a Jest-like API imported from the built-in `bun:test` module. Long term, Bun aims for complete Jest compatibility; at the moment, a limited set of expect matchers are supported. + +## Basic Usage + +To define a simple test: + +```ts title="math.test.ts" icon="/icons/typescript.svg" +import { expect, test } from "bun:test"; + +test("2 + 2", () => { + expect(2 + 2).toBe(4); +}); +``` + +### Grouping Tests + +Tests can be grouped into suites with `describe`. + +```ts title="math.test.ts" icon="/icons/typescript.svg" +import { expect, test, describe } from "bun:test"; + +describe("arithmetic", () => { + test("2 + 2", () => { + expect(2 + 2).toBe(4); + }); + + test("2 * 2", () => { + expect(2 * 2).toBe(4); + }); +}); +``` + +### Async Tests + +Tests can be async. + +```ts title="math.test.ts" icon="/icons/typescript.svg" +import { expect, test } from "bun:test"; + +test("2 * 2", async () => { + const result = await Promise.resolve(2 * 2); + expect(result).toEqual(4); +}); +``` + +Alternatively, use the `done` callback to signal completion. If you include the `done` callback as a parameter in your test definition, you must call it or the test will hang. + +```ts title="math.test.ts" icon="/icons/typescript.svg" +import { expect, test } from "bun:test"; + +test("2 * 2", done => { + Promise.resolve(2 * 2).then(result => { + expect(result).toEqual(4); + done(); + }); +}); +``` + +## Timeouts + +Optionally specify a per-test timeout in milliseconds by passing a number as the third argument to `test`. + +```ts title="math.test.ts" icon="/icons/typescript.svg" +import { test } from "bun:test"; + +test("wat", async () => { + const data = await slowOperation(); + expect(data).toBe(42); +}, 500); // test must run in <500ms +``` + +In `bun:test`, test timeouts throw an uncatchable exception to force the test to stop running and fail. We also kill any child processes that were spawned in the test to avoid leaving behind zombie processes lurking in the background. + +The default timeout for each test is 5000ms (5 seconds) if not overridden by this timeout option or `jest.setDefaultTimeout()`. + +### 🧟 Zombie Process Killer + +When a test times out and processes spawned in the test via `Bun.spawn`, `Bun.spawnSync`, or `node:child_process` are not killed, they will be automatically killed and a message will be logged to the console. This prevents zombie processes from lingering in the background after timed-out tests. + +## Test Modifiers + +### test.skip + +Skip individual tests with `test.skip`. These tests will not be run. + +```ts title="math.test.ts" icon="/icons/typescript.svg" +import { expect, test } from "bun:test"; + +test.skip("wat", () => { + // TODO: fix this + expect(0.1 + 0.2).toEqual(0.3); +}); +``` + +### test.todo + +Mark a test as a todo with `test.todo`. These tests will not be run. + +```ts title="math.test.ts" icon="/icons/typescript.svg" +import { expect, test } from "bun:test"; + +test.todo("fix this", () => { + myTestFunction(); +}); +``` + +To run todo tests and find any which are passing, use `bun test --todo`. + +```bash terminal icon="terminal" +bun test --todo +``` + +``` +my.test.ts: +✗ unimplemented feature + ^ this test is marked as todo but passes. Remove `.todo` or check that test is correct. + + 0 pass + 1 fail + 1 expect() calls +``` + +With this flag, failing todo tests will not cause an error, but todo tests which pass will be marked as failing so you can remove the todo mark or fix the test. + +### test.only + +To run a particular test or suite of tests use `test.only()` or `describe.only()`. + +```ts title="example.test.ts" icon="/icons/typescript.svg" +import { test, describe } from "bun:test"; + +test("test #1", () => { + // does not run +}); + +test.only("test #2", () => { + // runs +}); + +describe.only("only", () => { + test("test #3", () => { + // runs + }); +}); +``` + +The following command will only execute tests #2 and #3. + +```bash terminal icon="terminal" +bun test --only +``` + +The following command will only execute tests #1, #2 and #3. + +```bash terminal icon="terminal" +bun test +``` + +### test.if + +To run a test conditionally, use `test.if()`. The test will run if the condition is truthy. This is particularly useful for tests that should only run on specific architectures or operating systems. + +```ts title="example.test.ts" icon="/icons/typescript.svg" +test.if(Math.random() > 0.5)("runs half the time", () => { + // ... +}); + +const macOS = process.platform === "darwin"; +test.if(macOS)("runs on macOS", () => { + // runs if macOS +}); +``` + +### test.skipIf + +To instead skip a test based on some condition, use `test.skipIf()` or `describe.skipIf()`. + +```ts title="example.test.ts" icon="/icons/typescript.svg" +const macOS = process.platform === "darwin"; + +test.skipIf(macOS)("runs on non-macOS", () => { + // runs if *not* macOS +}); +``` + +### test.todoIf + +If instead you want to mark the test as TODO, use `test.todoIf()` or `describe.todoIf()`. Carefully choosing `skipIf` or `todoIf` can show a difference between, for example, intent of "invalid for this target" and "planned but not implemented yet." + +```ts title="example.test.ts" icon="/icons/typescript.svg" +const macOS = process.platform === "darwin"; + +// TODO: we've only implemented this for Linux so far. +test.todoIf(macOS)("runs on posix", () => { + // runs if *not* macOS +}); +``` + +### test.failing + +Use `test.failing()` when you know a test is currently failing but you want to track it and be notified when it starts passing. This inverts the test result: + +- A failing test marked with `.failing()` will pass +- A passing test marked with `.failing()` will fail (with a message indicating it's now passing and should be fixed) + +```ts math.test.ts icon="/icons/typescript.svg" +// This will pass because the test is failing as expected +test.failing("math is broken", () => { + expect(0.1 + 0.2).toBe(0.3); // fails due to floating point precision +}); + +// This will fail with a message that the test is now passing +test.failing("fixed bug", () => { + expect(1 + 1).toBe(2); // passes, but we expected it to fail +}); +``` + +This is useful for tracking known bugs that you plan to fix later, or for implementing test-driven development. + +## Conditional Tests for Describe Blocks + +The conditional modifiers `.if()`, `.skipIf()`, and `.todoIf()` can also be applied to describe blocks, affecting all tests within the suite: + +```ts title="example.test.ts" icon="/icons/typescript.svg" +const isMacOS = process.platform === "darwin"; + +// Only runs the entire suite on macOS +describe.if(isMacOS)("macOS-specific features", () => { + test("feature A", () => { + // only runs on macOS + }); + + test("feature B", () => { + // only runs on macOS + }); +}); + +// Skips the entire suite on Windows +describe.skipIf(process.platform === "win32")("Unix features", () => { + test("feature C", () => { + // skipped on Windows + }); +}); + +// Marks the entire suite as TODO on Linux +describe.todoIf(process.platform === "linux")("Upcoming Linux support", () => { + test("feature D", () => { + // marked as TODO on Linux + }); +}); +``` + +## Parametrized Tests + +### `test.each` and `describe.each` + +To run the same test with multiple sets of data, use `test.each`. This creates a parametrized test that runs once for each test case provided. + +```ts title="math.test.ts" icon="/icons/typescript.svg" +const cases = [ + [1, 2, 3], + [3, 4, 7], +]; + +test.each(cases)("%p + %p should be %p", (a, b, expected) => { + expect(a + b).toBe(expected); +}); +``` + +You can also use `describe.each` to create a parametrized suite that runs once for each test case: + +```ts title="sum.test.ts" icon="/icons/typescript.svg" +describe.each([ + [1, 2, 3], + [3, 4, 7], +])("add(%i, %i)", (a, b, expected) => { + test(`returns ${expected}`, () => { + expect(a + b).toBe(expected); + }); + + test(`sum is greater than each value`, () => { + expect(a + b).toBeGreaterThan(a); + expect(a + b).toBeGreaterThan(b); + }); +}); +``` + +### Argument Passing + +How arguments are passed to your test function depends on the structure of your test cases: + +- If a table row is an array (like `[1, 2, 3]`), each element is passed as an individual argument +- If a row is not an array (like an object), it's passed as a single argument + +```ts title="example.test.ts" icon="/icons/typescript.svg" +// Array items passed as individual arguments +test.each([ + [1, 2, 3], + [4, 5, 9], +])("add(%i, %i) = %i", (a, b, expected) => { + expect(a + b).toBe(expected); +}); + +// Object items passed as a single argument +test.each([ + { a: 1, b: 2, expected: 3 }, + { a: 4, b: 5, expected: 9 }, +])("add($a, $b) = $expected", data => { + expect(data.a + data.b).toBe(data.expected); +}); +``` + +### Format Specifiers + +There are a number of options available for formatting the test title: + +| Specifier | Description | +| --------- | ----------------------- | +| `%p` | pretty-format | +| `%s` | String | +| `%d` | Number | +| `%i` | Integer | +| `%f` | Floating point | +| `%j` | JSON | +| `%o` | Object | +| `%#` | Index of the test case | +| `%%` | Single percent sign (%) | + +#### Examples + +```ts title="example.test.ts" icon="/icons/typescript.svg" +// Basic specifiers +test.each([ + ["hello", 123], + ["world", 456], +])("string: %s, number: %i", (str, num) => { + // "string: hello, number: 123" + // "string: world, number: 456" +}); + +// %p for pretty-format output +test.each([ + [{ name: "Alice" }, { a: 1, b: 2 }], + [{ name: "Bob" }, { x: 5, y: 10 }], +])("user %p with data %p", (user, data) => { + // "user { name: 'Alice' } with data { a: 1, b: 2 }" + // "user { name: 'Bob' } with data { x: 5, y: 10 }" +}); + +// %# for index +test.each(["apple", "banana"])("fruit #%# is %s", fruit => { + // "fruit #0 is apple" + // "fruit #1 is banana" +}); +``` + +## Assertion Counting + +Bun supports verifying that a specific number of assertions were called during a test: + +### expect.hasAssertions() + +Use `expect.hasAssertions()` to verify that at least one assertion is called during a test: + +```ts title="example.test.ts" icon="/icons/typescript.svg" +test("async work calls assertions", async () => { + expect.hasAssertions(); // Will fail if no assertions are called + + const data = await fetchData(); + expect(data).toBeDefined(); +}); +``` + +This is especially useful for async tests to ensure your assertions actually run. + +### expect.assertions(count) + +Use `expect.assertions(count)` to verify that a specific number of assertions are called during a test: + +```ts title="example.test.ts" icon="/icons/typescript.svg" +test("exactly two assertions", () => { + expect.assertions(2); // Will fail if not exactly 2 assertions are called + + expect(1 + 1).toBe(2); + expect("hello").toContain("ell"); +}); +``` + +This helps ensure all your assertions run, especially in complex async code with multiple code paths. + +## Type Testing + +Bun includes `expectTypeOf` for testing TypeScript types, compatible with Vitest. + +### expectTypeOf + + + These functions are no-ops at runtime - you need to run TypeScript separately to verify the type checks. + + +The `expectTypeOf` function provides type-level assertions that are checked by TypeScript's type checker. To test your types: + +1. Write your type assertions using `expectTypeOf` +2. Run `bunx tsc --noEmit` to check that your types are correct + +```ts title="example.test.ts" icon="/icons/typescript.svg" +import { expectTypeOf } from "bun:test"; + +// Basic type assertions +expectTypeOf().toEqualTypeOf(); +expectTypeOf(123).toBeNumber(); +expectTypeOf("hello").toBeString(); + +// Object type matching +expectTypeOf({ a: 1, b: "hello" }).toMatchObjectType<{ a: number }>(); + +// Function types +function greet(name: string): string { + return `Hello ${name}`; +} + +expectTypeOf(greet).toBeFunction(); +expectTypeOf(greet).parameters.toEqualTypeOf<[string]>(); +expectTypeOf(greet).returns.toEqualTypeOf(); + +// Array types +expectTypeOf([1, 2, 3]).items.toBeNumber(); + +// Promise types +expectTypeOf(Promise.resolve(42)).resolves.toBeNumber(); +``` + +For full documentation on expectTypeOf matchers, see the [API Reference](https://bun.com/reference/bun/test/expectTypeOf). + +## Matchers + +Bun implements the following matchers. Full Jest compatibility is on the roadmap; [track progress here](https://github.com/oven-sh/bun/issues/1825). + +### Basic Matchers + +| Status | Matcher | +| ------ | ------------------ | +| ✅ | `.not` | +| ✅ | `.toBe()` | +| ✅ | `.toEqual()` | +| ✅ | `.toBeNull()` | +| ✅ | `.toBeUndefined()` | +| ✅ | `.toBeNaN()` | +| ✅ | `.toBeDefined()` | +| ✅ | `.toBeFalsy()` | +| ✅ | `.toBeTruthy()` | +| ✅ | `.toStrictEqual()` | + +### String and Array Matchers + +| Status | Matcher | +| ------ | --------------------- | +| ✅ | `.toContain()` | +| ✅ | `.toHaveLength()` | +| ✅ | `.toMatch()` | +| ✅ | `.toContainEqual()` | +| ✅ | `.stringContaining()` | +| ✅ | `.stringMatching()` | +| ✅ | `.arrayContaining()` | + +### Object Matchers + +| Status | Matcher | +| ------ | ----------------------- | +| ✅ | `.toHaveProperty()` | +| ✅ | `.toMatchObject()` | +| ✅ | `.toContainAllKeys()` | +| ✅ | `.toContainValue()` | +| ✅ | `.toContainValues()` | +| ✅ | `.toContainAllValues()` | +| ✅ | `.toContainAnyValues()` | +| ✅ | `.objectContaining()` | + +### Number Matchers + +| Status | Matcher | +| ------ | --------------------------- | +| ✅ | `.toBeCloseTo()` | +| ✅ | `.closeTo()` | +| ✅ | `.toBeGreaterThan()` | +| ✅ | `.toBeGreaterThanOrEqual()` | +| ✅ | `.toBeLessThan()` | +| ✅ | `.toBeLessThanOrEqual()` | + +### Function and Class Matchers + +| Status | Matcher | +| ------ | ------------------- | +| ✅ | `.toThrow()` | +| ✅ | `.toBeInstanceOf()` | + +### Promise Matchers + +| Status | Matcher | +| ------ | ------------- | +| ✅ | `.resolves()` | +| ✅ | `.rejects()` | + +### Mock Function Matchers + +| Status | Matcher | +| ------ | ----------------------------- | +| ✅ | `.toHaveBeenCalled()` | +| ✅ | `.toHaveBeenCalledTimes()` | +| ✅ | `.toHaveBeenCalledWith()` | +| ✅ | `.toHaveBeenLastCalledWith()` | +| ✅ | `.toHaveBeenNthCalledWith()` | +| ✅ | `.toHaveReturned()` | +| ✅ | `.toHaveReturnedTimes()` | +| ✅ | `.toHaveReturnedWith()` | +| ✅ | `.toHaveLastReturnedWith()` | +| ✅ | `.toHaveNthReturnedWith()` | + +### Snapshot Matchers + +| Status | Matcher | +| ------ | --------------------------------------- | +| ✅ | `.toMatchSnapshot()` | +| ✅ | `.toMatchInlineSnapshot()` | +| ✅ | `.toThrowErrorMatchingSnapshot()` | +| ✅ | `.toThrowErrorMatchingInlineSnapshot()` | + +### Utility Matchers + +| Status | Matcher | +| ------ | ------------------ | +| ✅ | `.extend` | +| ✅ | `.anything()` | +| ✅ | `.any()` | +| ✅ | `.assertions()` | +| ✅ | `.hasAssertions()` | + +### Not Yet Implemented + +| Status | Matcher | +| ------ | -------------------------- | +| ❌ | `.addSnapshotSerializer()` | + +## Best Practices + +### Use Descriptive Test Names + +```ts title="example.test.ts" icon="/icons/typescript.svg" +// Good +test("should calculate total price including tax for multiple items", () => { + // test implementation +}); + +// Avoid +test("price calculation", () => { + // test implementation +}); +``` + +### Group Related Tests + +```ts title="auth.test.ts" icon="/icons/typescript.svg" +describe("User authentication", () => { + describe("with valid credentials", () => { + test("should return user data", () => { + // test implementation + }); + + test("should set authentication token", () => { + // test implementation + }); + }); + + describe("with invalid credentials", () => { + test("should throw authentication error", () => { + // test implementation + }); + }); +}); +``` + +### Use Appropriate Matchers + +```ts title="auth.test.ts" icon="/icons/typescript.svg" +// Good: Use specific matchers +expect(users).toHaveLength(3); +expect(user.email).toContain("@"); +expect(response.status).toBeGreaterThanOrEqual(200); + +// Avoid: Using toBe for everything +expect(users.length === 3).toBe(true); +expect(user.email.includes("@")).toBe(true); +expect(response.status >= 200).toBe(true); +``` + +### Test Error Conditions + +```ts title="example.test.ts" icon="/icons/typescript.svg" +test("should throw error for invalid input", () => { + expect(() => { + validateEmail("not-an-email"); + }).toThrow("Invalid email format"); +}); + +test("should handle async errors", async () => { + await expect(async () => { + await fetchUser("invalid-id"); + }).rejects.toThrow("User not found"); +}); +``` + +### Use Setup and Teardown + +```ts title="example.test.ts" icon="/icons/typescript.svg" +import { beforeEach, afterEach, test } from "bun:test"; + +let testUser; + +beforeEach(() => { + testUser = createTestUser(); +}); + +afterEach(() => { + cleanupTestUser(testUser); +}); + +test("should update user profile", () => { + // Use testUser in test +}); +``` diff --git a/docs/test/writing.md b/docs/test/writing.md deleted file mode 100644 index 9ada86ffb6..0000000000 --- a/docs/test/writing.md +++ /dev/null @@ -1,825 +0,0 @@ -Define tests with a Jest-like API imported from the built-in `bun:test` module. Long term, Bun aims for complete Jest compatibility; at the moment, a [limited set](#matchers) of `expect` matchers are supported. - -## Basic usage - -To define a simple test: - -```ts#math.test.ts -import { expect, test } from "bun:test"; - -test("2 + 2", () => { - expect(2 + 2).toBe(4); -}); -``` - -{% details summary="Jest-style globals" %} -As in Jest, you can use `describe`, `test`, `expect`, and other functions without importing them. Unlike Jest, they are not injected into the global scope. Instead, the Bun transpiler will automatically inject an import from `bun:test` internally. - -```ts -typeof globalThis.describe; // "undefined" -typeof describe; // "function" -``` - -This transpiler integration only occurs during `bun test`, and only for test files & preloaded scripts. In practice there's no significant difference to the end user. -{% /details %} - -Tests can be grouped into suites with `describe`. - -```ts#math.test.ts -import { expect, test, describe } from "bun:test"; - -describe("arithmetic", () => { - test("2 + 2", () => { - expect(2 + 2).toBe(4); - }); - - test("2 * 2", () => { - expect(2 * 2).toBe(4); - }); -}); -``` - -Tests can be `async`. - -```ts -import { expect, test } from "bun:test"; - -test("2 * 2", async () => { - const result = await Promise.resolve(2 * 2); - expect(result).toEqual(4); -}); -``` - -Alternatively, use the `done` callback to signal completion. If you include the `done` callback as a parameter in your test definition, you _must_ call it or the test will hang. - -```ts -import { expect, test } from "bun:test"; - -test("2 * 2", done => { - Promise.resolve(2 * 2).then(result => { - expect(result).toEqual(4); - done(); - }); -}); -``` - -## Timeouts - -Optionally specify a per-test timeout in milliseconds by passing a number as the third argument to `test`. - -```ts -import { test } from "bun:test"; - -test("wat", async () => { - const data = await slowOperation(); - expect(data).toBe(42); -}, 500); // test must run in <500ms -``` - -In `bun:test`, test timeouts throw an uncatchable exception to force the test to stop running and fail. We also kill any child processes that were spawned in the test to avoid leaving behind zombie processes lurking in the background. - -The default timeout for each test is 5000ms (5 seconds) if not overridden by this timeout option or `jest.setDefaultTimeout()`. - -### 🧟 Zombie process killer - -When a test times out and processes spawned in the test via `Bun.spawn`, `Bun.spawnSync`, or `node:child_process` are not killed, they will be automatically killed and a message will be logged to the console. This prevents zombie processes from lingering in the background after timed-out tests. - -## `test.skip` - -Skip individual tests with `test.skip`. These tests will not be run. - -```ts -import { expect, test } from "bun:test"; - -test.skip("wat", () => { - // TODO: fix this - expect(0.1 + 0.2).toEqual(0.3); -}); -``` - -## `test.todo` - -Mark a test as a todo with `test.todo`. These tests will not be run. - -```ts -import { expect, test } from "bun:test"; - -test.todo("fix this", () => { - myTestFunction(); -}); -``` - -To run todo tests and find any which are passing, use `bun test --todo`. - -```sh -$ bun test --todo -my.test.ts: -✗ unimplemented feature - ^ this test is marked as todo but passes. Remove `.todo` or check that test is correct. - - 0 pass - 1 fail - 1 expect() calls -``` - -With this flag, failing todo tests will not cause an error, but todo tests which pass will be marked as failing so you can remove the todo mark or -fix the test. - -## `test.only` - -To run a particular test or suite of tests use `test.only()` or `describe.only()`. - -```ts -import { test, describe } from "bun:test"; - -test("test #1", () => { - // does not run -}); - -test.only("test #2", () => { - // runs -}); - -describe.only("only", () => { - test("test #3", () => { - // runs - }); -}); -``` - -The following command will only execute tests #2 and #3. - -```sh -$ bun test -``` - -## `test.if` - -To run a test conditionally, use `test.if()`. The test will run if the condition is truthy. This is particularly useful for tests that should only run on specific architectures or operating systems. - -```ts -test.if(Math.random() > 0.5)("runs half the time", () => { - // ... -}); - -const macOS = process.arch === "darwin"; -test.if(macOS)("runs on macOS", () => { - // runs if macOS -}); -``` - -## `test.skipIf` - -To instead skip a test based on some condition, use `test.skipIf()` or `describe.skipIf()`. - -```ts -const macOS = process.arch === "darwin"; - -test.skipIf(macOS)("runs on non-macOS", () => { - // runs if *not* macOS -}); -``` - -## `test.todoIf` - -If instead you want to mark the test as TODO, use `test.todoIf()` or `describe.todoIf()`. Carefully choosing `skipIf` or `todoIf` can show a difference between, for example, intent of "invalid for this target" and "planned but not implemented yet." - -```ts -const macOS = process.arch === "darwin"; - -// TODO: we've only implemented this for Linux so far. -test.todoIf(macOS)("runs on posix", () => { - // runs if *not* macOS -}); -``` - -## `test.failing` - -Use `test.failing()` when you know a test is currently failing but you want to track it and be notified when it starts passing. This inverts the test result: - -- A failing test marked with `.failing()` will pass -- A passing test marked with `.failing()` will fail (with a message indicating it's now passing and should be fixed) - -```ts -// This will pass because the test is failing as expected -test.failing("math is broken", () => { - expect(0.1 + 0.2).toBe(0.3); // fails due to floating point precision -}); - -// This will fail with a message that the test is now passing -test.failing("fixed bug", () => { - expect(1 + 1).toBe(2); // passes, but we expected it to fail -}); -``` - -This is useful for tracking known bugs that you plan to fix later, or for implementing test-driven development. - -## Conditional Tests for Describe Blocks - -The conditional modifiers `.if()`, `.skipIf()`, and `.todoIf()` can also be applied to `describe` blocks, affecting all tests within the suite: - -```ts -const isMacOS = process.platform === "darwin"; - -// Only runs the entire suite on macOS -describe.if(isMacOS)("macOS-specific features", () => { - test("feature A", () => { - // only runs on macOS - }); - - test("feature B", () => { - // only runs on macOS - }); -}); - -// Skips the entire suite on Windows -describe.skipIf(process.platform === "win32")("Unix features", () => { - test("feature C", () => { - // skipped on Windows - }); -}); - -// Marks the entire suite as TODO on Linux -describe.todoIf(process.platform === "linux")("Upcoming Linux support", () => { - test("feature D", () => { - // marked as TODO on Linux - }); -}); -``` - -## `test.each` and `describe.each` - -To run the same test with multiple sets of data, use `test.each`. This creates a parametrized test that runs once for each test case provided. - -```ts -const cases = [ - [1, 2, 3], - [3, 4, 7], -]; - -test.each(cases)("%p + %p should be %p", (a, b, expected) => { - expect(a + b).toBe(expected); -}); -``` - -You can also use `describe.each` to create a parametrized suite that runs once for each test case: - -```ts -describe.each([ - [1, 2, 3], - [3, 4, 7], -])("add(%i, %i)", (a, b, expected) => { - test(`returns ${expected}`, () => { - expect(a + b).toBe(expected); - }); - - test(`sum is greater than each value`, () => { - expect(a + b).toBeGreaterThan(a); - expect(a + b).toBeGreaterThan(b); - }); -}); -``` - -### Argument Passing - -How arguments are passed to your test function depends on the structure of your test cases: - -- If a table row is an array (like `[1, 2, 3]`), each element is passed as an individual argument -- If a row is not an array (like an object), it's passed as a single argument - -```ts -// Array items passed as individual arguments -test.each([ - [1, 2, 3], - [4, 5, 9], -])("add(%i, %i) = %i", (a, b, expected) => { - expect(a + b).toBe(expected); -}); - -// Object items passed as a single argument -test.each([ - { a: 1, b: 2, expected: 3 }, - { a: 4, b: 5, expected: 9 }, -])("add($a, $b) = $expected", data => { - expect(data.a + data.b).toBe(data.expected); -}); -``` - -### Format Specifiers - -There are a number of options available for formatting the test title: - -{% table %} - ---- - -- `%p` -- [`pretty-format`](https://www.npmjs.com/package/pretty-format) - ---- - -- `%s` -- String - ---- - -- `%d` -- Number - ---- - -- `%i` -- Integer - ---- - -- `%f` -- Floating point - ---- - -- `%j` -- JSON - ---- - -- `%o` -- Object - ---- - -- `%#` -- Index of the test case - ---- - -- `%%` -- Single percent sign (`%`) - -{% /table %} - -#### Examples - -```ts -// Basic specifiers -test.each([ - ["hello", 123], - ["world", 456], -])("string: %s, number: %i", (str, num) => { - // "string: hello, number: 123" - // "string: world, number: 456" -}); - -// %p for pretty-format output -test.each([ - [{ name: "Alice" }, { a: 1, b: 2 }], - [{ name: "Bob" }, { x: 5, y: 10 }], -])("user %p with data %p", (user, data) => { - // "user { name: 'Alice' } with data { a: 1, b: 2 }" - // "user { name: 'Bob' } with data { x: 5, y: 10 }" -}); - -// %# for index -test.each(["apple", "banana"])("fruit #%# is %s", fruit => { - // "fruit #0 is apple" - // "fruit #1 is banana" -}); -``` - -## Assertion Counting - -Bun supports verifying that a specific number of assertions were called during a test: - -### expect.hasAssertions() - -Use `expect.hasAssertions()` to verify that at least one assertion is called during a test: - -```ts -test("async work calls assertions", async () => { - expect.hasAssertions(); // Will fail if no assertions are called - - const data = await fetchData(); - expect(data).toBeDefined(); -}); -``` - -This is especially useful for async tests to ensure your assertions actually run. - -### expect.assertions(count) - -Use `expect.assertions(count)` to verify that a specific number of assertions are called during a test: - -```ts -test("exactly two assertions", () => { - expect.assertions(2); // Will fail if not exactly 2 assertions are called - - expect(1 + 1).toBe(2); - expect("hello").toContain("ell"); -}); -``` - -This helps ensure all your assertions run, especially in complex async code with multiple code paths. - -## Type Testing - -Bun includes `expectTypeOf` for testing typescript types, compatible with Vitest. - -### expectTypeOf - -{% callout %} - -**Note** — These functions are no-ops at runtime - you need to run TypeScript separately to verify the type checks. - -{% endcallout %} - -The `expectTypeOf` function provides type-level assertions that are checked by TypeScript's type checker. **Important**: - -To test your types: - -1. Write your type assertions using `expectTypeOf` -2. Run `bunx tsc --noEmit` to check that your types are correct - -```ts -import { expectTypeOf } from "bun:test"; - -// Basic type assertions -expectTypeOf().toEqualTypeOf(); -expectTypeOf(123).toBeNumber(); -expectTypeOf("hello").toBeString(); - -// Object type matching -expectTypeOf({ a: 1, b: "hello" }).toMatchObjectType<{ a: number }>(); - -// Function types -function greet(name: string): string { - return `Hello ${name}`; -} - -expectTypeOf(greet).toBeFunction(); -expectTypeOf(greet).parameters.toEqualTypeOf<[string]>(); -expectTypeOf(greet).returns.toEqualTypeOf(); - -// Array types -expectTypeOf([1, 2, 3]).items.toBeNumber(); - -// Promise types -expectTypeOf(Promise.resolve(42)).resolves.toBeNumber(); -``` - -For full documentation on expectTypeOf matchers, see the [API Reference](/reference/bun/test/expectTypeOf) - -## Matchers - -Bun implements the following matchers. Full Jest compatibility is on the roadmap; track progress [here](https://github.com/oven-sh/bun/issues/1825). - -{% table %} - ---- - -- ✅ -- [`.not`](https://jestjs.io/docs/expect#not) - ---- - -- ✅ -- [`.toBe()`](https://jestjs.io/docs/expect#tobevalue) - ---- - -- ✅ -- [`.toEqual()`](https://jestjs.io/docs/expect#toequalvalue) - ---- - -- ✅ -- [`.toBeNull()`](https://jestjs.io/docs/expect#tobenull) - ---- - -- ✅ -- [`.toBeUndefined()`](https://jestjs.io/docs/expect#tobeundefined) - ---- - -- ✅ -- [`.toBeNaN()`](https://jestjs.io/docs/expect#tobenan) - ---- - -- ✅ -- [`.toBeDefined()`](https://jestjs.io/docs/expect#tobedefined) - ---- - -- ✅ -- [`.toBeFalsy()`](https://jestjs.io/docs/expect#tobefalsy) - ---- - -- ✅ -- [`.toBeTruthy()`](https://jestjs.io/docs/expect#tobetruthy) - ---- - -- ✅ -- [`.toContain()`](https://jestjs.io/docs/expect#tocontainitem) - ---- - -- ✅ -- [`.toContainAllKeys()`](https://jest-extended.jestcommunity.dev/docs/matchers/Object#tocontainallkeyskeys) - ---- - -- ✅ -- [`.toContainValue()`](https://jest-extended.jestcommunity.dev/docs/matchers/Object#tocontainvaluevalue) - ---- - -- ✅ -- [`.toContainValues()`](https://jest-extended.jestcommunity.dev/docs/matchers/Object#tocontainvaluesvalues) - ---- - -- ✅ -- [`.toContainAllValues()`](https://jest-extended.jestcommunity.dev/docs/matchers/Object#tocontainallvaluesvalues) - ---- - -- ✅ -- [`.toContainAnyValues()`](https://jest-extended.jestcommunity.dev/docs/matchers/Object#tocontainanyvaluesvalues) - ---- - -- ✅ -- [`.toStrictEqual()`](https://jestjs.io/docs/expect#tostrictequalvalue) - ---- - -- ✅ -- [`.toThrow()`](https://jestjs.io/docs/expect#tothrowerror) - ---- - -- ✅ -- [`.toHaveLength()`](https://jestjs.io/docs/expect#tohavelengthnumber) - ---- - -- ✅ -- [`.toHaveProperty()`](https://jestjs.io/docs/expect#tohavepropertykeypath-value) - ---- - -- ✅ -- [`.extend`](https://jestjs.io/docs/expect#expectextendmatchers) - ---- - -- ✅ -- [`.anything()`](https://jestjs.io/docs/expect#expectanything) - ---- - -- ✅ -- [`.any()`](https://jestjs.io/docs/expect#expectanyconstructor) - ---- - -- ✅ -- [`.arrayContaining()`](https://jestjs.io/docs/expect#expectarraycontainingarray) - ---- - -- ✅ -- [`.assertions()`](https://jestjs.io/docs/expect#expectassertionsnumber) - ---- - -- ✅ -- [`.closeTo()`](https://jestjs.io/docs/expect#expectclosetonumber-numdigits) - ---- - -- ✅ -- [`.hasAssertions()`](https://jestjs.io/docs/expect#expecthasassertions) - ---- - -- ✅ -- [`.objectContaining()`](https://jestjs.io/docs/expect#expectobjectcontainingobject) - ---- - -- ✅ -- [`.stringContaining()`](https://jestjs.io/docs/expect#expectstringcontainingstring) - ---- - -- ✅ -- [`.stringMatching()`](https://jestjs.io/docs/expect#expectstringmatchingstring--regexp) - ---- - -- ❌ -- [`.addSnapshotSerializer()`](https://jestjs.io/docs/expect#expectaddsnapshotserializerserializer) - ---- - -- ✅ -- [`.resolves()`](https://jestjs.io/docs/expect#resolves) - ---- - -- ✅ -- [`.rejects()`](https://jestjs.io/docs/expect#rejects) - ---- - -- ✅ -- [`.toHaveBeenCalled()`](https://jestjs.io/docs/expect#tohavebeencalled) - ---- - -- ✅ -- [`.toHaveBeenCalledTimes()`](https://jestjs.io/docs/expect#tohavebeencalledtimesnumber) - ---- - -- ✅ -- [`.toHaveBeenCalledWith()`](https://jestjs.io/docs/expect#tohavebeencalledwitharg1-arg2-) - ---- - -- ✅ -- [`.toHaveBeenLastCalledWith()`](https://jestjs.io/docs/expect#tohavebeenlastcalledwitharg1-arg2-) - ---- - -- ✅ -- [`.toHaveBeenNthCalledWith()`](https://jestjs.io/docs/expect#tohavebeennthcalledwithnthcall-arg1-arg2-) - ---- - -- ✅ -- [`.toHaveReturned()`](https://jestjs.io/docs/expect#tohavereturned) - ---- - -- ✅ -- [`.toHaveReturnedTimes()`](https://jestjs.io/docs/expect#tohavereturnedtimesnumber) - ---- - -- ✅ -- [`.toHaveReturnedWith()`](https://jestjs.io/docs/expect#tohavereturnedwithvalue) - ---- - -- ✅ -- [`.toHaveLastReturnedWith()`](https://jestjs.io/docs/expect#tohavelastreturnedwithvalue) - ---- - -- ✅ -- [`.toHaveNthReturnedWith()`](https://jestjs.io/docs/expect#tohaventhreturnedwithnthcall-value) - ---- - -- ✅ -- [`.toBeCloseTo()`](https://jestjs.io/docs/expect#tobeclosetonumber-numdigits) - ---- - -- ✅ -- [`.toBeGreaterThan()`](https://jestjs.io/docs/expect#tobegreaterthannumber--bigint) - ---- - -- ✅ -- [`.toBeGreaterThanOrEqual()`](https://jestjs.io/docs/expect#tobegreaterthanorequalnumber--bigint) - ---- - -- ✅ -- [`.toBeLessThan()`](https://jestjs.io/docs/expect#tobelessthannumber--bigint) - ---- - -- ✅ -- [`.toBeLessThanOrEqual()`](https://jestjs.io/docs/expect#tobelessthanorequalnumber--bigint) - ---- - -- ✅ -- [`.toBeInstanceOf()`](https://jestjs.io/docs/expect#tobeinstanceofclass) - ---- - -- ✅ -- [`.toContainEqual()`](https://jestjs.io/docs/expect#tocontainequalitem) - ---- - -- ✅ -- [`.toMatch()`](https://jestjs.io/docs/expect#tomatchregexp--string) - ---- - -- ✅ -- [`.toMatchObject()`](https://jestjs.io/docs/expect#tomatchobjectobject) - ---- - -- ✅ -- [`.toMatchSnapshot()`](https://jestjs.io/docs/expect#tomatchsnapshotpropertymatchers-hint) - ---- - -- ✅ -- [`.toMatchInlineSnapshot()`](https://jestjs.io/docs/expect#tomatchinlinesnapshotpropertymatchers-inlinesnapshot) - ---- - -- ✅ -- [`.toThrowErrorMatchingSnapshot()`](https://jestjs.io/docs/expect#tothrowerrormatchingsnapshothint) - ---- - -- ✅ -- [`.toThrowErrorMatchingInlineSnapshot()`](https://jestjs.io/docs/expect#tothrowerrormatchinginlinesnapshotinlinesnapshot) - -{% /table %} - -## TypeScript Type Safety - -Bun's test runner provides enhanced TypeScript support with intelligent type checking for your test assertions. The type system helps catch potential bugs at compile time while still allowing flexibility when needed. - -### Strict Type Checking by Default - -By default, Bun's test matchers enforce strict type checking between the actual value and expected value: - -```ts -import { expect, test } from "bun:test"; - -test("strict typing", () => { - const str = "hello"; - const num = 42; - - expect(str).toBe("hello"); // ✅ OK: string to string - expect(num).toBe(42); // ✅ OK: number to number - expect(str).toBe(42); // ❌ TypeScript error: string vs number -}); -``` - -This helps catch common mistakes where you might accidentally compare values of different types. - -### Relaxed Type Checking with Type Parameters - -Sometimes you need more flexibility in your tests, especially when working with: - -- Dynamic data from APIs -- Polymorphic functions that can return multiple types -- Generic utility functions -- Migration of existing test suites - -For these cases, you can "opt out" of strict type checking by providing an explicit type parameter to matcher methods: - -```ts -import { expect, test } from "bun:test"; - -test("relaxed typing with type parameters", () => { - const value: unknown = getSomeValue(); - - // These would normally cause TypeScript errors, but type parameters allow them: - expect(value).toBe(42); // No TS error, runtime check still works - expect(value).toEqual("hello"); // No TS error, runtime check still works - expect(value).toStrictEqual(true); // No TS error, runtime check still works -}); - -test("useful for dynamic data", () => { - const apiResponse: any = { status: "success" }; - - // Without type parameter: TypeScript error (any vs string) - // expect(apiResponse.status).toBe("success"); - - // With type parameter: No TypeScript error, runtime assertion still enforced - expect(apiResponse.status).toBe("success"); // ✅ OK -}); -``` - -### Migration from Looser Type Systems - -If migrating from a test framework with looser TypeScript integration, you can use type parameters as a stepping stone: - -```ts -// Old Jest test that worked but wasn't type-safe -expect(response.data).toBe(200); // No type error in some setups - -// Bun equivalent with explicit typing during migration -expect(response.data).toBe(200); // Explicit about expected type - -// Ideal Bun test after refactoring -const statusCode: number = response.data; -expect(statusCode).toBe(200); // Type-safe without explicit parameter -``` diff --git a/docs/typescript.mdx b/docs/typescript.mdx new file mode 100644 index 0000000000..18eaddb13b --- /dev/null +++ b/docs/typescript.mdx @@ -0,0 +1,54 @@ +--- +title: TypeScript +description: Using TypeScript with Bun, including type definitions and compiler options +--- + +To install the TypeScript definitions for Bun's built-in APIs, install `@types/bun`. + +```zsh terminal icon="terminal" +bun add -d @types/bun # dev dependency +``` + +At this point, you should be able to reference the `Bun` global in your TypeScript files without seeing errors in your editor. + +## Suggested `compilerOptions` + +Bun supports things like top-level await, JSX, and extensioned `.ts` imports, which TypeScript doesn't allow by default. Below is a set of recommended `compilerOptions` for a Bun project, so you can use these features without seeing compiler warnings from TypeScript. + +```json tsconfig.json icon="file-code" +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} +``` + +If you run `bun init` in a new directory, this `tsconfig.json` will be generated for you. (The stricter flags are disabled by default.) + +```sh terminal icon="terminal" +bun init +```