Bun
Bun 是采用 Zig 语言编写的高性能 “全家桶” JavaScript 运行时,官方称其为 "all-in-one JavaScript runtime"
README
bun
bun is a new:
- JavaScript runtime with Web APIs like [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch), [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket), and several more built-in. bun embeds JavaScriptCore, which tends to be faster and more memory efficient than more popular engines like V8 (though harder to embed)
- JavaScript/TypeScript/JSX transpiler
- JavaScript & CSS bundler
- Task runner for package.json scripts
- npm-compatible package manager
All in one fast & easy-to-use tool. Instead of 1,000 node_modules for development, you only need bun.
bun is experimental software. Join bun’s Discord for help and have a look at things that don’t work yet.
Today, bun's primary focus is bun.js: bun's JavaScript runtime.
Install
Native: (macOS x64 & Silicon, Linux x64, Windows Subsystem for Linux)
- ```sh
- curl -fsSL https://bun.sh/install | bash
- ```
Homebrew: (MacOS and Linux)
- ```sh
- brew tap oven-sh/bun
- brew install bun
- ```
Docker: (Linux x64)
- ```sh
- docker pull jarredsumner/bun:edge
- docker run --rm --init --ulimit memlock=-1:-1 jarredsumner/bun:edge
- ```
If using Linux, kernel version 5.6 or higher is strongly recommended, but the minimum is 5.1.
Upgrade
To upgrade to the latest version of Bun, run:
- ```sh
- bun upgrade
- ```
Bun automatically releases a canary build on every commit to main. To upgrade to the latest canary build, run:
- ```sh
- bun upgrade --canary
- ```
Table of Contents
- Install
- Loaders
- [When platform is browser](#when-platform-is-browser)
- [When platform is bun](#when-platform-is-bun)
- OpenSUSE
- [bun install](#bun-install)
- [Configuring bun install with bunfig.toml](#configuring-bun-install-with-bunfigtoml)
- Lockfile
- Cache
- [bun run](#bun-run)
- [bun create](#bun-create)
- Usage
- Flags
- Config
- [How bun create works](#how-bun-create-works)
- [bun init](#bun-init)
- [bun bun](#bun-bun)
- [What is .bun?](#what-is-bun)
- Advanced
- [bun upgrade](#bun-upgrade)
- [bun completions](#bun-completions)
- [Bun.serve - fast HTTP server](#bunserve---fast-http-server)
- Usage
- HTTPS
- [Bun.write – optimizing I/O](#bunwrite--optimizing-io)
- [Bun.spawn - spawn processes](#bunspawn--spawn-a-process)
- [Bun.which - find the path to a bin](#bunwhich--find-the-path-to-a-binary)
- [Database](#database)
- [bun:ffi (Foreign Functions Interface)](#bunffi-foreign-functions-interface)
- Usage
- [Supported FFI types (FFIType)](#supported-ffi-types-ffitype)
- [Strings (CString)](#strings-cstring)
- [Function pointers (CFunction)](#function-pointers-CFunction)
- Pointers
- [Bun.Transpiler](#buntranspiler)
- [Bun.Transpiler.transformSync](#buntranspilertransformsync)
- [Bun.Transpiler.transform](#buntranspilertransform)
- [Bun.Transpiler.scan](#buntranspilerscan)
- [Bun.Transpiler.scanImports](#buntranspilerscanimports)
- [Bun.peek - read a promise same-tick](#bunpeek---read-a-promise-without-resolving-it)
- Credits
- License
- MacOS
Using bun.js - a new JavaScript runtime environment
bun.js focuses on performance, developer experience and compatibility with the JavaScript ecosystem.
- ```ts
- // http.ts
- export default {
- port: 3000,
- fetch(request: Request) {
- return new Response("Hello World");
- },
- };
- // bun ./http.ts
- ```
Requests | OS | CPU | bun |
---|---|---|---|
---------------------------------------------------------------------- | ----- | ------------------------------ | ----------- |
[260,000](https://twitter.com/jarredsumner/status/1512040623200616449) | macOS | Apple | 0.0.76 |
[160,000](https://twitter.com/jarredsumner/status/1511988933587976192) | Linux | AMD | 0.0.76 |
bun.js prefers Web API compatibility instead of designing new APIs when possible. bun.js also implements some Node.js APIs.
- TypeScript & JSX support is built-in, powered by Bun's JavaScript transpiler
- ESM & CommonJS modules are supported (internally, bun.js uses ESM)
- Many npm packages "just work" with bun.js (when they use few/no node APIs)
- tsconfig.json "paths" is natively supported, along with "exports" in package.json
- fs, path, and process from Node are partially implemented
- Web APIs like [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch), [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response), [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL) and more are built-in
- [HTMLRewriter](https://developers.cloudflare.com/workers/runtime-apis/html-rewriter/) makes it easy to transform HTML in bun.js
- Starts 4x faster than Node (try it yourself)
- .env files automatically load into process.env and Bun.env
- top level await
The runtime uses JavaScriptCore, the JavaScript engine powering WebKit and Safari. Some web 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.
cat clone that runs 2x faster than GNU cat 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),
- );
- // bun ./cat.js ./path-to-file
- ```
Server-side render React:
- ``` js
- // requires Bun v0.1.0 or later
- // react-ssr.tsx
- import { renderToReadableStream } from "react-dom/server";
- const dt = new Intl.DateTimeFormat();
- export default {
- port: 3000,
- async fetch(request: Request) {
- return new Response(
- await renderToReadableStream(
- <html>
- <head>
- <title>Hello World</title>
- </head>
- <body>
- <h1>Hello from React!</h1>
- <p>The date is {dt.format(new Date())}</p>
- </body>
- </html>,
- ),
- );
- },
- };
- // bun react-ssr.tsx
- ```
There are some more examples in the examples folder.
PRs adding more examples are very welcome!
Types for bun.js (editor autocomplete)
The best docs right now are the TypeScript types in the [bun-types](https://github.com/oven-sh/bun/tree/main/packages/bun-types) npm package. A docs site is coming soon.
To get autocomplete for bun.js types in your editor,
1. Install the bun-types npm package:
- ``` sh
- # yarn/npm/pnpm work too, "bun-types" is an ordinary npm package
- bun add bun-types
- ```
2. Add this to your tsconfig.json or jsconfig.json:
- ``` jsonc
- {
- "compilerOptions": {
- "lib": ["ESNext"],
- "module": "esnext",
- "target": "esnext",
- "moduleResolution": "node",
- // "bun-types" is the important part
- "types": ["bun-types"]
- }
- }
- ```
You can also view the types here.
To contribute to the types, head over to oven-sh/bun-types.
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"));
- ```
Using bun as a package manager
On Linux, bun install tends to install packages 20x - 100x faster than npm install. On macOS, it’s more like 4x - 80x.
To install packages from package.json:
- ``` sh
- bun install
- ```
To add or remove packages from package.json:
- ``` sh
- bun remove react
- bun add preact
- ```
For Linux users: bun install
needs Linux Kernel 5.6 or higher to work well
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:
- ``` sh
- # 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
- ```
Using bun as a task runner
Instead of waiting 170ms for your npm client to start for each task, you wait 6ms for bun.
To use bun as a task runner, run bun run instead of npm run.
- ``` sh
- # Instead of "npm run clean"
- bun run clean
- # This also works
- bun clean
- ```
Assuming a package.json with a "clean" command in "scripts":
- ``` json
- {
- "name": "myapp",
- "scripts": {
- "clean": "rm -rf dist out node_modules"
- }
- }
- ```
Creating a Discord bot with Bun
Application Commands
Application commands are native ways to interact with apps in the Discord client. There are 3 types of commands accessible in different interfaces: the chat input, a message's context menu (top-right menu or right-clicking in a message), and a user's context menu (right-clicking on a user).
To get started you can use the interactions template:
- ``` sh
- bun create discord-interactions my-interactions-bot
- cd my-interactions-bot
- ```
If you don't have a Discord bot/application yet, you can create one here (https://discord.com/developers/applications/me).
Afterwards you will need to get your bot's token, public key, and application id from the application page and put them into .env.example file
Then you can run the http server that will handle your interactions:
- ``` sh
- bun install
- mv .env.example .env
- bun run.js # listening on port 1337
- ```
Discord does not accept an insecure HTTP server, so you will need to provide an SSL certificate or put the interactions server behind a secure reverse proxy. For development, you can use ngrok/cloudflare tunnel to expose local ports as secure URL.
Using bun with Next.js
To create a new Next.js app with bun:
- ``` sh
- bun create next ./app
- cd app
- bun dev # start dev server
- ```
To use an existing Next.js app with bun:
- ``` sh
- bun add bun-framework-next
- echo "framework = 'next'" > bunfig.toml
- bun bun # bundle dependencies
- bun dev # start dev server
- ```
Many of Next.js’ features are supported, but not all.
Here’s what doesn’t work yet:
- getStaticPaths
- same-origin fetch inside of getStaticProps or getServerSideProps
- locales, zones, assetPrefix (workaround: change --origin \"http://localhost:3000/assetPrefixInhere\")
- `next/image` is polyfilled to a regular `` tag.- proxy and anything else in next.config.js
- API routes, middleware (middleware is easier to support, though! Similar SSR API)
- styled-jsx (technically not Next.js, but often used with it)
- React Server Components
When using Next.js, bun automatically reads configuration from .env.local, .env.development and .env (in that order). process.env.NEXT_PUBLIC_ and process.env.NEXT_ automatically are replaced via --define.
Currently, any time you import new dependencies from node_modules, you will need to re-run bun bun --use next. This will eventually be automatic.
Using bun with single-page apps
In your project folder root (where package.json is):
- ``` sh
- bun bun ./entry-point-1.js ./entry-point-2.jsx
- bun
- ```
By default, bun will look for any HTML files in the public directory and serve that. For browsers navigating to the page, the .html file extension is optional in the URL, and index.html will automatically rewrite for the directory.
Here are examples of routing from public/ and how they’re matched:
Dev | File |
---|---|
|----------------|-----------| | |
/dir | public/dir/index.html |
/ | public/index.html |
/index | public/index.html |
/hi | public/hi.html |
/file | public/file.html |
/font/Inter.woff2 | public/font/Inter.woff2 |
/hello | public/index.html |
If public/index.html exists, it becomes the default page instead of a 404 page, unless that pathname has a file extension.
Using bun with Create React App
To create a new React app:
- ``` sh
- bun create react ./app
- cd app
- bun dev # start dev server
- ```
To use an existing React app:
- ``` sh
- # To enable React Fast Refresh, ensure it is installed
- bun add -d react-refresh
- # Generate a bundle for your entry point(s)
- bun bun ./src/index.js # jsx, tsx, ts also work. can be multiple files
- # Start the dev server
- bun dev
- ```
From there, bun relies on the filesystem for mapping dev server paths to source files. All URL paths are relative to the project root (where package.json is located).
Here are examples of routing source code file paths:
Dev | File |
---|---|
-------------------------- | --------------------------- |
/src/components/Button.tsx | src/components/Button.tsx |
/src/index.tsx | src/index.tsx |
/pages/index.js | pages/index.js |
You do not need to include file extensions in import paths. CommonJS-style import paths without the file extension work.
You can override the public directory by passing --public-dir="path-to-folder".
If no directory is specified and ./public/ doesn’t exist, bun will try ./static/. If ./static/ does not exist, but won’t serve from a public directory. If you pass --public-dir=./ bun will serve from the current directory, but it will check the current directory last instead of first.
Using bun with TypeScript
Transpiling TypeScript with Bun
TypeScript just works. There’s nothing to configure and nothing extra to install. If you import a .ts or .tsx file, bun will transpile it into JavaScript. bun also transpiles node_modules containing .ts or .tsx files. This is powered by bun’s TypeScript transpiler, so it’s fast.
bun also reads tsconfig.json, including baseUrl and paths.
Adding Type Definitions
To get TypeScript working with the global API, add bun-types to your project:
- ```sh
- bun add -d bun-types
- ```
And to the types field in your tsconfig.json:
- ``` json
- {
- "compilerOptions": {
- "types": ["bun-types"]
- }
- }
- ```
Not implemented yet
bun is a project with an incredibly large scope and is still in its early days.
You can see Bun's Roadmap, but here are some additional things that are planned:
Feature | In |
---|---|
------------------------------------------------------------------------------------- | -------------- |
Web | bun.js |
Web | bun.js |
Package | bun |
Source | JS |
Source | CSS |
JavaScript | JS |
CSS | CSS |
CSS | CSS |
Tree-shaking | JavaScript |
Tree-shaking | CSS |
[TypeScript | TS |
`@jsxPragma` | JS |
Sharing | bun |
Dates | TOML |
[Hash | JSX |
JS Transpiler == JavaScript Transpiler
TS Transpiler == TypeScript Transpiler
Package manager ==
bun install
bun.js == bun’s JavaScriptCore integration that executes JavaScript. Similar to how Node.js & Deno embed V8.
Limitations & intended usage
Today, bun is mostly focused on bun.js: the JavaScript runtime.
While you could use bun's bundler & transpiler separately to build for browsers or node, bun doesn't have a minifier or support tree-shaking yet. For production browser builds, you probably should use a tool like esbuild or swc.
Longer-term, bun intends to replace Node.js, Webpack, Babel, yarn, and PostCSS (in production).
Upcoming breaking changes
- Bun's CLI flags will change to better support bun as a JavaScript runtime. They were chosen when bun was just a frontend development tool.
- Bun's bundling format will change to accommodate production browser bundles and on-demand production bundling
Configuration
bunfig.toml
bunfig.toml is bun's configuration file.
It lets you load configuration from a file instead of passing flags to the CLI each time. The config file is loaded before CLI arguments are parsed, which means CLI arguments can override them.
Here is an example:
- ```toml
- # Set a default framework to use
- # By default, bun will look for an npm package like `bun-framework-${framework}`, followed by `${framework}`
- framework = "next"
- logLevel = "debug"
- # publicDir = "public"
- # external = ["jquery"]
- [macros]
- # Remap any import like this:
- # import {graphql} from 'react-relay';
- # To:
- # import {graphql} from 'macro:bun-macro-relay';
- react-relay = { "graphql" = "bun-macro-relay" }
- [bundle]
- saveTo = "node_modules.bun"
- # Don't need this if `framework` is set, but showing it here as an example anyway
- entryPoints = ["./app/index.ts"]
- [bundle.packages]
- # If you're bundling packages that do not actually live in a `node_modules` folder or do not have the full package name in the file path, you can pass this to bundle them anyway
- "@bigapp/design-system" = true
- [dev]
- # Change the default port from 3000 to 5000
- # Also inherited by Bun.serve
- port = 5000
- [define]
- # Replace any usage of "process.env.bagel" with the string `lox`.
- # The values are parsed as JSON, except single-quoted strings are supported and `'undefined'` becomes `undefined` in JS.
- # This will probably change in a future release to be just regular TOML instead. It is a holdover from the CLI argument parsing.
- "process.env.bagel" = "'lox'"
- [loaders]
- # When loading a .bagel file, run the JS parser
- ".bagel" = "js"
- [debug]
- # When navigating to a blob: or src: link, open the file in your editor
- # If not, it tries $EDITOR or $VISUAL
- # If that still fails, it will try Visual Studio Code, then Sublime Text, then a few others
- # This is used by Bun.openInEditor()
- editor = "code"
- # List of editors:
- # - "subl", "sublime"
- # - "vscode", "code"
- # - "textmate", "mate"
- # - "idea"
- # - "webstorm"
- # - "nvim", "neovim"
- # - "vim","vi"
- # - "emacs"
- # - "atom"
- # If you pass it a file path, it will open with the file path instead
- # It will recognize non-GUI editors, but I don't think it will work yet
- ```
TODO: list each property name
Loaders
A loader determines how to map imports & file extensions to transforms and output.
Currently, bun implements the following loaders:
Input | Loader | Output |
---|---|---|
----- | ----------------------------- | ------ |
.js | JSX | .js |
.jsx | JSX | .js |
.ts | TypeScript | .js |
.tsx | TypeScript | .js |
.mjs | JavaScript | .js |
.cjs | JavaScript | .js |
.mts | TypeScript | .js |
.cts | TypeScript | .js |
.toml | TOML | .js |
.css | CSS | .css |
.env | Env | N/A |
.\* | file | string |
Everything else is treated as file. file replaces the import with a URL (or a path).
You can configure which loaders map to which extensions by passing --loaders to bun. For example:
- ```sh
- bun --loader=.js:js
- ```
This will disable JSX transforms for .js files.
CSS in JS (bun dev only)
When importing CSS in JavaScript-like loaders, CSS is treated special.
By default, bun will transform a statement like this:
- ``` js
- import "../styles/global.css";
- ```
When platform is browser
- ``` js
- globalThis.document?.dispatchEvent(
- new CustomEvent("onimportcss", {
- detail: "http://localhost:3000/styles/globals.css",
- }),
- );
- ```
When platform is bun
- ``` js
- //@import url("http://localhost:3000/styles/globals.css");
- ```
Additionally, bun exposes an API for SSR/SSG that returns a flat list of URLs to css files imported. That function is Bun.getImportedStyles().
- ```ts
- // This specifically is for "framework" in package.json when loaded via `bun dev`
- // This API needs to be changed somewhat to work more generally with Bun.js
- // Initially, you could only use bun.js through `bun dev`
- // and this API was created at that time
- addEventListener("fetch", async (event: FetchEvent) => {
- let route = Bun.match(event);
- const App = await import("pages/_app");
- // This returns all .css files that were imported in the line above.
- // It’s recursive, so any file that imports a CSS file will be included.
- const appStylesheets = bun.getImportedStyles();
- // ...rest of code
- });
- ```
This is useful for preventing flash of unstyled content.
CSS Loader
bun bundles .css files imported via @import into a single file. It doesn’t autoprefix or minify CSS today. Multiple .css files imported in one JavaScript file will _not_ be bundled into one file. You’ll have to import those from a .css file.
This input:
- ```css
- @import url("./hi.css");
- @import url("./hello.css");
- @import url("./yo.css");
- ```
Becomes:
- ```css
- /* hi.css */
- /* ...contents of hi.css */
- /* hello.css */
- /* ...contents of hello.css */
- /* yo.css */
- /* ...contents of yo.css */
- ```
CSS runtime
To support hot CSS reloading, bun inserts @supports annotations into CSS that tag which files a stylesheet is composed of. Browsers ignore this, so it doesn’t impact styles.
Frameworks
Warning
This will soon have breaking changes. It was designed when Bun was mostly a dev server and not a JavaScript runtime.
Frameworks preconfigure bun to enable developers to use bun with their existing tooling.
Frameworks are configured via the framework object in the package.json of the framework (not in the application’s package.json):
Here is an example:
- ``` json
- {
- "name": "bun-framework-next",
- "version": "0.0.0-18",
- "description": ,
- "framework": {
- "displayName": "Next.js",
- "static": "public",
- "assetPrefix": "_next/",
- "router": {
- "dir": ["pages", "src/pages"],
- "extensions": [".js", ".ts", ".tsx", ".jsx"]
- },
- "css": "onimportcss",
- "development": {
- "client": "client.development.tsx",
- "fallback": "fallback.development.tsx",
- "server": "server.development.tsx",
- "css": "onimportcss",
- "define": {
- "client": {
- ".env": "NEXT_PUBLIC_",
- "defaults": {
- "process.env.__NEXT_TRAILING_SLASH": "false",
- "process.env.NODE_ENV": "\"development\",
- "process.env.__NEXT_ROUTER_BASEPATH": "''",
- "process.env.__NEXT_SCROLL_RESTORATION": "false",
- "process.env.__NEXT_I18N_SUPPORT": "false",
- "process.env.__NEXT_HAS_REWRITES": "false",
- "process.env.__NEXT_ANALYTICS_ID": "null",
- "process.env.__NEXT_OPTIMIZE_CSS": "false",
- "process.env.__NEXT_CROSS_ORIGIN": "''",
- "process.env.__NEXT_STRICT_MODE": "false",
- "process.env.__NEXT_IMAGE_OPTS": "null"
- }
- },
- "server": {
- ".env": "NEXT_",
- "defaults": {
- "process.env.__NEXT_TRAILING_SLASH": "false",
- "process.env.__NEXT_OPTIMIZE_FONTS": "false",
- "process.env.NODE_ENV": "\"development\",
- "process.env.__NEXT_OPTIMIZE_IMAGES": "false",
- "process.env.__NEXT_OPTIMIZE_CSS": "false",
- "process.env.__NEXT_ROUTER_BASEPATH": "''",
- "process.env.__NEXT_SCROLL_RESTORATION": "false",
- "process.env.__NEXT_I18N_SUPPORT": "false",
- "process.env.__NEXT_HAS_REWRITES": "false",
- "process.env.__NEXT_ANALYTICS_ID": "null",
- "process.env.__NEXT_CROSS_ORIGIN": "''",
- "process.env.__NEXT_STRICT_MODE": "false",
- "process.env.__NEXT_IMAGE_OPTS": "null",
- "global": "globalThis",
- "window": "undefined"
- }
- }
- }
- }
- }
- }
- ```
Here are type definitions:
- ```ts
- type Framework = Environment & {
- // This changes what’s printed in the console on load
- displayName?: string;
- // This allows a prefix to be added (and ignored) to requests.
- // Useful for integrating an existing framework that expects internal routes to have a prefix
- // e.g. "_next"
- assetPrefix?: string;
- development?: Environment;
- production?: Environment;
- // The directory used for serving unmodified assets like fonts and images
- // Defaults to "public" if exists, else "static", else disabled.
- static?: string;
- // "onimportcss" disables the automatic "onimportcss" feature
- // If the framework does routing, you may want to handle CSS manually
- // "facade" removes CSS imports from JavaScript files,
- // and replaces an imported object with a proxy that mimics CSS module support without doing any class renaming.
- css?: "onimportcss" | "facade";
- // bun’s filesystem router
- router?: Router;
- };
- type Define = {
- // By passing ".env", bun will automatically load .env.local, .env.development, and .env if exists in the project root
- // (in addition to the processes’ environment variables)
- // When "*", all environment variables will be automatically injected into the JavaScript loader
- // When a string like "NEXT_PUBLIC_", only environment variables starting with that prefix will be injected
- ".env": string | "*";
- // These environment variables will be injected into the JavaScript loader
- // These are the equivalent of Webpack’s resolve.alias and esbuild’s --define.
- // Values are parsed as JSON, so they must be valid JSON. The only exception is '' is a valid string, to simplify writing stringified JSON in JSON.
- // If not set, `process.env.NODE_ENV` will be transformed into "development".
- defaults: Record<string, string>;
- };
- type Environment = {
- // This is a wrapper for the client-side entry point for a route.
- // This allows frameworks to run initialization code on pages.
- client: string;
- // This is a wrapper for the server-side entry point for a route.
- // This allows frameworks to run initialization code on pages.
- server: string;
- // This runs when "server" code fails to load due to an exception.
- fallback: string;
- // This is how environment variables and .env is configured.
- define?: Define;
- };
- // bun’s filesystem router
- // Currently, bun supports pages by either an absolute match or a parameter match.
- // pages/index.tsx will be executed on navigation to "/" and "/index"
- // pages/posts/[id].tsx will be executed on navigation to "/posts/123"
- // Routes & parameters are automatically passed to `fallback` and `server`.
- type Router = {
- // This determines the folder to look for pages
- dir: string[];
- // These are the allowed file extensions for pages.
- extensions?: string[];
- };
- ```
To use a framework, you pass bun bun --use package-name.
Your framework’s package.json name should start with bun-framework-. This is so that people can type something like bun bun --use next and it will check bun-framework-next first. This is similar to how Babel plugins tend to start with babel-plugin-.
For developing frameworks, you can also do bun bun --use ./relative-path-to-framework.
If you’re interested in adding a framework integration, please reach out. There’s a lot here, and it’s not entirely documented yet.
Troubleshooting
bun not running on an M1 (or Apple Silicon)
If you see a message like this
[1] 28447 killed bun create next ./test
It most likely means you’re running bun’s x64 version on Apple Silicon. This happens if bun is running via Rosetta. Rosetta is unable to emulate AVX2 instructions, which bun indirectly uses.
The fix is to ensure you installed a version of bun built for Apple Silicon.
error: Unexpected
If you see an error like this:
It usually means the max number of open file descriptors is being explicitly set to a low number. By default, bun requests the max number of file descriptors available (which on macOS, is something like 32,000). But, if you previously ran into ulimit issues with, e.g., Chokidar, someone on The Internet may have advised you to run ulimit -n 8096.
That advice unfortunately lowers the hard limit to 8096. This can be a problem in large repositories or projects with lots of dependencies. Chokidar (and other watchers) don’t seem to call setrlimit, which means they’re reliant on the (much lower) soft limit.
To fix this issue:
1. Remove any scripts that call ulimit -n and restart your shell.
2. Try again, and if the error still occurs, try setting ulimit -n to an absurdly high number, such as ulimit -n 2147483646
3. Try again, and if that still doesn’t fix it, open an issue
Unzip is required
Unzip is required to install bun on Linux. You can use one of the following commands to install unzip:
Debian / Ubuntu / Mint
- ```sh
- sudo apt install unzip
- ```
RedHat / CentOS / Fedora
- ```sh
- sudo dnf install unzip
- ```
Arch / Manjaro
- ```sh
- sudo pacman -S unzip
- ```
OpenSUSE
- ```sh
- sudo zypper install unzip
- ```
bun install is stuck
Please run bun install --verbose 2> logs.txt and send them to me in bun's discord. If you're on Linux, it would also be helpful if you run sudo perf trace bun install --silent and attach the logs.
Reference
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
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 fallsback 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
- # Don't actually install
- dryRun = true
- # Install optionalDependencies (default: true)
- optional = true
- # Install local devDependencies (default: true)
- dev = true
- # Install peerDependencies (default: false)
- peer = false
- # 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.lockb into a yarn.lock
- print = "yarn"
- # Path to read bun.lockb from
- path = "bun.lockb"
- # Path to save bun.lockb to
- savePath = "bun.lockb"
- # 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;
- 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<string, Registry>;
- export interface Cache {
- dir: string;
- disable: boolean;
- disableManifest: boolean;
- }
- export interface Lockfile {
- print?: "yarn";
- path: string;
- savePath: string;
- save: boolean;
- }
- ```
Configuring with environment variables
Environment variables have a higher priority than bunfig.toml.
Name | Description |
---|---|
-------------------------------- | ------------------------------------------------------------- |
BUN_CONFIG_REGISTRY | Set |
BUN_CONFIG_TOKEN | Set |
BUN_CONFIG_LOCKFILE_SAVE_PATH | File |
BUN_CONFIG_YARN_LOCKFILE | Save |
BUN_CONFIG_LINK_NATIVE_BINS | Point |
BUN_CONFIG_SKIP_SAVE_LOCKFILE | Don’t |
BUN_CONFIG_SKIP_LOAD_LOCKFILE | Don’t |
BUN_CONFIG_SKIP_INSTALL_PACKAGES | Don’t |
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.lockb doesn’t exist or package.json has changed dependencies, tarballs are downloaded & extracted eagerly while resolving.
When a bun.lockb 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.
Peer dependencies?
Peer dependencies are handled similarly to yarn. bun install does not automatically install peer dependencies and will try to choose an existing dependency.
Lockfile
bun.lockb is bun’s binary lockfile format.
Why is it binary?
In a word: Performance. bun’s lockfile saves & loads incredibly quickly, and saves a lot more data than what is typically inside lockfiles.
How do I inspect it?
For now, the easiest thing is to run bun install -y. That prints a Yarn v1-style yarn.lock file.
What does the lockfile store?
Packages, metadata for those packages, the hoisted install order, dependencies for each package, what packages those dependencies resolved to, an integrity hash (if available), what each package was resolved to and which version (or equivalent).
Why is it fast?
It uses linear arrays for all data. Packages are referenced by an auto-incrementing integer ID or a hash of the package name. Strings longer than 8 characters are de-duplicated. Prior to saving on disk, the lockfile is garbage-collected & made deterministic by walking the package tree and cloning the packages in dependency order.
Cache
To delete the cache:
- ``` sh
- 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.
- ``` sh
- 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.
- ``` sh
- 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.
- ``` sh
- 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().
- ``` sh
- 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 it's own node_modules folder or you pass --preserve-symlinks to node. See [Node.js documentation on --preserve-symlinks](https://nodejs.org/api/cli.html#--preserve-symlinks).
- ``` sh
- rm -rf node_modules
- bun install --backend symlink
- # https://nodejs.org/api/cli.html#--preserve-symlinks
- node --preserve-symlinks ./my-file.js
- ```
bun's runtime does not currently expose an equivalent of --preserve-symlinks, though the code for it does exist.
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.
bun run
bun run is a fast package.json script runner. Instead of waiting 170ms for your npm client to start every time, you wait 6ms for bun.
By default, bun run prints the script that will be invoked:
- ``` sh
- bun run clean
- $ rm -rf node_modules/.cache dist
- ```
You can disable that with --silent
- ``` sh
- bun run --silent clean
- ```
bun run ${script-name} runs the equivalent of npm run script-name. For example, bun run dev runs the dev script in package.json, which may sometimes spin up non-bun processes.
bun run ${javascript-file.js} will run it with bun, as long as the file doesn't have a node shebang.
To print a list of scripts, bun run without additional args:
- ``` sh
- # This command
- bun run
- # Prints this
- hello-create-react-app scripts:
- bun run start
- react-scripts start
- bun run build
- react-scripts build
- bun run test
- react-scripts test
- bun run eject
- react-scripts eject
- 4 scripts
- ```
bun run automatically loads environment variables from .env into the shell/task. .env files are loaded with the same priority as the rest of bun, so that means:
1. .env.local is first
2. if ($NODE_ENV === "production") .env.production else .env.development
3. .env
If something is unexpected there, you can run bun run env to get a list of environment variables.
The default shell it uses is bash, but if that’s not found, it tries sh and if still not found, it tries zsh. This is not configurable right now, but if you care, file an issue.
bun run automatically adds any parent node_modules/.bin to $PATH and if no scripts match, it will load that binary instead. That means you can run executables from packages, too.
- ``` sh
- # If you use Relay
- bun run relay-compiler
- # You can also do this, but:
- # - It will only lookup packages in `node_modules/.bin` instead of `$PATH`
- # - It will start bun’s dev server if the script name doesn’t exist (`bun` starts the dev server by default)
- bun relay-compiler
- ```
To pass additional flags through to the task or executable, there are two ways:
- ``` sh
- # Explicit: include "--" and anything after will be added. This is the recommended way because it is more reliable.
- bun run relay-compiler -- -–help
- # Implicit: if you do not include "--", anything *after* the script name will be passed through
- # bun flags are parsed first, which means e.g. `bun run relay-compiler --help` will print bun’s help instead of relay-compiler’s help.
- bun run relay-compiler --schema foo.graphql
- ```
bun run supports lifecycle hooks like post${task} and pre{task}. If they exist, they will run, matching the behavior of npm clients. If the pre${task} fails, the next task will not be run. There is currently no flag to skip these lifecycle tasks if they exist, if you want that file an issue.
bun --hot
bun --hot enables hot reloading of code in Bun's JavaScript runtime. This is a very experimental feature available in Bun v0.2.0.
Unlike file watchers like nodemon, bun --hot can keep stateful objects like the HTTP server running.
Bun v0.2.0 | Nodemon |
---|
To use it with Bun's HTTP server (automatic):
server.ts:
- ```ts
- // The global object is preserved across code reloads
- // You can use it to store state, for now until Bun implements import.meta.hot.
- const reloadCount = globalThis.reloadCount || 0;
- globalThis.reloadCount = reloadCount + 1;
- export default {
- fetch(req: Request) {
- return new Response(`Code reloaded ${reloadCount} times`, {
- headers: { "content-type": "text/plain" },
- });
- },
- };
- ```
Then, run:
- ``` sh
- bun --hot server.ts
- ```
You can also use bun run:
- ``` sh
- bun run --hot server.ts
- ```
To use it manually:
- ```ts
- // The global object is preserved across code reloads
- // You can use it to store state, for now until Bun implements import.meta.hot.
- const reloadCount = globalThis.reloadCount || 0;
- globalThis.reloadCount = reloadCount + 1;
- const reloadServer = (globalThis.reloadServer ||= (() => {
- let server;
- return (handler) => {
- if (server) {
- // call `server.reload` to reload the server
- server.reload(handler);
- } else {
- server = Bun.serve(handler);
- }
- return server;
- };
- })());
- const handler = {
- fetch(req: Request) {
- return new Response(`Code reloaded ${reloadCount} times`, {
- headers: { "content-type": "text/plain" },
- });
- },
- };
- reloadServer(handler);
- ```
In a future version of Bun, support for Vite's import.meta.hot is planned to enable better lifecycle management for hot reloading and to align with the ecosystem.
How bun --hot works
bun --hot monitors imported files for changes and reloads them. It does not monitor files that are not imported and it does not monitor node_modules.
On reload, it resets the internal require cache and ES module registry (Loader.registry).
Then:
- It runs the garbage collector synchronously (to minimize memory leaks, at the cost of runtime performance)
- Bun re-transpiles all of your code from scratch (including sourcemaps)
- JavaScriptCore (the engine) re-evaluates the code.
Traditional file watchers restart the entire process which means that HTTP servers and other stateful objects are lost. bun --hot does not restart the process, so it preserves _some_ state across reloads to be less intrusive.
This implementation isn't particularly optimized. It re-transpiles files that haven't changed. It makes no attempt at incremental compilation. It's a starting point.
bun create
bun create is a fast way to create a new project from a template.
At the time of writing, bun create react app runs ~11x faster on my local computer than yarn create react-app app. bun create currently does no caching (though your npm client does)
Usage
Create a new Next.js project:
- ``` sh
- bun create next ./app
- ```
Create a new React project:
- ``` sh
- bun create react ./app
- ```
Create from a GitHub repo:
- ``` sh
- bun create ahfarmer/calculator ./app
- ```
To see a list of templates, run:
- ``` sh
- bun create
- ```
Format:
- ``` sh
- bun create github-user/repo-name destination
- bun create local-example-or-remote-example destination
- bun create /absolute/path/to-template-folder destination
- bun create https://github.com/github-user/repo-name destination
- bun create github.com/github-user/repo-name destination
- ```
Note: you don’t need bun create to use bun. You don’t need any configuration at all. This command exists to make it a little easier.
Local templates
If you have your own boilerplate you prefer using, copy it into $HOME/.bun-create/my-boilerplate-name.
Before checking bun’s templates on npmjs, bun create checks for a local folder matching the input in:
- $BUN_CREATE_DIR/
- $HOME/.bun-create/
- $(pwd)/.bun-create/
If a folder exists in any of those folders with the input, bun will use that instead of a remote template.
To create a local template, run:
- ``` sh
- mkdir -p $HOME/.bun-create/new-template-name
- echo '{"name":"new-template-name"}' > $HOME/.bun-create/new-template-name/package.json
- ```
This lets you run:
- ``` sh
- bun create new-template-name ./app
- ```
Now your new template should appear when you run:
- ``` sh
- bun create
- ```
Warning: unlike with remote templates, bun will delete the entire destination folder if it already exists.
Flags
Flag | Description |
---|---|
------------ | -------------------------------------- |
--npm | Use |
--yarn | Use |
--pnpm | Use |
--force | Overwrite |
--no-install | Skip |
--no-git | Don’t |
--open | Start |
Environment | Description |
---|---|
--------------------- | ------------------------------------------------------------------------------------------------------ |
GITHUB_API_DOMAIN | If |
GITHUB_API_TOKEN | This |
By default, bun create will cancel if there are existing files it would overwrite and it's a remote template. You can pass --force to disable this behavior.
Publishing a new template
Clone https://github.com/bun-community/create-templates/ and create a new folder in root directory with your new template. Thepackage.json must have a name that starts with @bun-examples/. Do not worry about publishing it, that will happen automatically after the PR is merged.
Make sure to include a .gitignore that includes node_modules so that node_modules aren’t checked in to git when people download the template.
Testing your new template
To test your new template, add it as a local template or pass the absolute path.
- ``` sh
- bun create /path/to/my/new/template destination-dir
- ```
Warning: This will always delete everything in destination-dir.
Config
The bun-create section of package.json is automatically removed from the package.json on disk. This lets you add create-only steps without waiting for an extra package to install.
There are currently three options:
- postinstall
- preinstall
- start (customize the displayed start command)
They can be an array of strings or one string. An array of steps will be executed in order.
Here is an example:
- ``` json
- {
- "name": "@bun-examples/next",
- "version": "0.0.31",
- "main": "index.js",
- "dependencies": {
- "next": "11.1.2",
- "react": "^17.0.2",
- "react-dom": "^17.0.2",
- "react-is": "^17.0.2"
- },
- "devDependencies": {
- "@types/react": "^17.0.19",
- "bun-framework-next": "^0.0.0-21",
- "typescript": "^4.3.5"
- },
- "bun-create": {
- "postinstall": ["bun bun --use next"],
- "start": "bun run echo 'Hello world!'"
- }
- }
- ```
By default, all commands run inside the environment exposed by the auto-detected npm client. This incurs a significant performance penalty, something like 150ms spent waiting for the npm client to start on each invocation.
Any command that starts with "bun " will be run without npm, relying on the first bun binary in $PATH.
How bun create works
When you run bun create ${template} ${destination}, here’s what happens:
IF remote template
1. GET registry.npmjs.org/@bun-examples/${template}/latest and parse it
2. GET registry.npmjs.org/@bun-examples/${template}/-/${template}-${latestVersion}.tgz
3. Decompress & extract ${template}-${latestVersion}.tgz into ${destination}
- If there are files that would overwrite, warn and exit unless --force is passed
IF GitHub repo
1. Download the tarball from GitHub’s API
2. Decompress & extract into ${destination}
- If there are files that would overwrite, warn and exit unless --force is passed
ELSE IF local template
1. Open local template folder
2. Delete destination directory recursively
3. Copy files recursively using the fastest system calls available (on macOS fcopyfile and Linux, copy_file_range). Do not copy or traverse into node_modules folder if exists (this alone makes it faster than cp)
4. Parse the `