bun - 信じられないほど高速なJavaScriptランタイム、バンドラー、トランスパイラー、パッケージマネージャーがすべて1つにまとめられています。

(Incredibly fast JavaScript runtime, bundler, transpiler and package manager – all in one.)

Created at: 2021-04-14 08:48:17
Language: Zig

バン

ロゴ

パンは新しいです:

  • fetch、WebSocket、その他いくつかの組み込みなどのWeb APIを備えたJavaScriptランタイム。bunにはJavaScriptCoreが組み込まれており、V8のような一般的なエンジンよりも高速でメモリ効率が高い傾向があります(埋め込むのは難しいですが)
  • JavaScript/TypeScript/JSX transpiler
  • JavaScript & CSS バンドラー
  • package.json スクリプトのタスクランナー
  • NPM互換パッケージマネージャー

オールインワンの高速で使いやすいツール。開発用の1,000 node_modulesの代わりに、必要なのはパンだけです。

BUNは実験的なソフトウェアですバンのDiscordに参加して助けを求め、まだ機能していないものを見てください。

今日、bunの主な焦点はbun.js:bunのJavaScriptランタイムです。

取り付ける

Native: (macOS x64 & Silicon, Linux x64, Windows Subsystem for Linux)

curl -fsSL https://bun.sh/install | bash

自作:(MacOSおよびLinux)

brew tap oven-sh/bun
brew install bun

Docker: (Linux x64)

docker pull jarredsumner/bun:edge
docker run --rm --init --ulimit memlock=-1:-1 jarredsumner/bun:edge

Linuxを使用している場合は、カーネルバージョン5.6以降を強くお勧めしますが、最小値は5.1です。

アップグレード

Bunの最新バージョンにアップグレードするには、次のコマンドを実行します。

bun upgrade

Bunは、コミットするたびにカナリアビルドを自動的にリリースします。最新の Canary ビルドにアップグレードするには、次のコマンドを実行します。

main

bun upgrade --canary

カナリアビルドの表示

カナリアビルドは自動テストなしでリリースされます

目次

パン.jsの使用 - 新しいJavaScriptランタイム環境

bun.jsは、パフォーマンス、開発者エクスペリエンス、JavaScriptエコシステムとの互換性に重点を置いています。

// http.ts
export default {
  port: 3000,
  fetch(request: Request) {
    return new Response("Hello World");
  },
};

// bun ./http.ts
要求数/秒 OS プロセッサ パンバージョン
260,000 マック アップルシリコンM1マックス 0.0.76
160,000 リナックス AMDのライゼン 5 3600 6-コア 2.2GHz 0.0.76
http_load_testで測定実行することによって:
./http_load_test  20 127.0.0.1 3000

bun.js は、可能な場合は新しい API を設計するのではなく、Web API の互換性を優先します。bun.js はいくつかのノード.js API も実装しています。

  • TypeScript & JSX のサポートはビルトインで、Bun の JavaScript トランスパイラーを搭載している。
  • ESM & CommonJS モジュールがサポートされています (内部的には bun.js は ESM を使用)
  • 多くのnpmパッケージは、bun.jsで「機能します」(ノードAPIをほとんど/まったく使用しない場合)
  • tsconfig.json は、package.json 内でネイティブにサポートされています。
    "paths"
    "exports"
  • fs
    、、およびノードから部分的に実装されています
    path
    process
  • フェッチレスポンスURLなどのWeb APIが組み込まれています
  • HTMLライターを使用すると、HTMLをパンに簡単に変換できます.js
  • ノードよりも4倍速く起動します(自分で試してみてください)
  • .env
    ファイルは自動的にロードされ、
    process.env
    Bun.env
  • トップレベル待機

ランタイムは、WebKit と Safari を強化する JavaScript エンジンである JavaScriptCore を使用します。ヘッダーURLなどの一部のWeb APIは、Safariの実装を直接使用します。

cat
Linux上の大きなファイルに対してGNUcatよりも2倍高速に動作するクローン

// 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

サーバー側のレンダリング反応:

// 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

例フォルダにはさらにいくつかのがあります。

さらに例を追加するPRは大歓迎です!

bun.jsの種類(エディタのオートコンプリート)

現時点で最高のドキュメントは、パンタイプのnpmパッケージのTypeScriptタイプです。ドキュメントサイトは近日公開予定です。

エディターで bun.js タイプのオートコンプリートを取得するには、

  1. thenpmパッケージをインストールします。
    bun-types
# yarn/npm/pnpm work too, "bun-types" is an ordinary npm package
bun add bun-types
  1. これをあなたに追加します:
    tsconfig.json
    jsconfig.json
{
  "compilerOptions": {
    "lib": ["ESNext"],
    "module": "esnext",
    "target": "esnext",
    "moduleResolution": "node",
    // "bun-types" is the important part
    "types": ["bun-types"]
  }
}

ここで型を表示することもできます。

タイプに貢献するには、オーブンシュ/パンタイプに向かいます。

ウェブ API のファスト・パス

bun.js には、Web API がサーバーと CLI のパフォーマンス要求を満たすようにする一般的なユース ケースへの高速パスがあります。

Bun.file(path)
遅延読み込みファイルを表すBlobを返します。

ファイルブロブを渡すと、Bunは自動的により高速なシステムコールを使用します。

Bun.write

const blob = Bun.file("input.txt");
await Bun.write("output.txt", blob);

Linuxでは、これはcopy_file_rangeシステムコールを使用し、macOSでは、これは(またはfcopyfile)になります。

clonefile

Bun.write
応答オブジェクトもサポートしています。自動的にBLOB に変換されます。

// Eventually, this will stream the response to disk but today it buffers
await Bun.write("index.html", await fetch("https://example.com"));

パンをパッケージマネージャーとして使用する

Linuxでは、パッケージを20倍〜100倍速くインストールする傾向があります。macOSでは、4倍から80倍のようなものです。

bun install
npm install

package.json からパッケージをインストールするには:

bun install

package.json にパッケージを追加または削除するには:

bun remove react
bun add preact
Linuxユーザーの場合:うまく機能するにはLinuxカーネル5.6以降が必要です
bun install

Linux カーネルの最小バージョンは 5.1 です。Linuxカーネル5.1〜5.5を使用している場合でも動作するはずですが、io_uringの操作をサポートしていないため、HTTPリクエストは遅くなります。

bun install
connect()

Ubuntu 20.04を使用している場合は、新しいカーネルをインストールする方法は次のとおりです。

# 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

タスクランナーとしてのパンの使用

各タスクでnpmクライアントが起動するのを170ミリ秒待つ代わりに、bunを6ミリ秒待ちます。

パンをタスクランナーとして使用するには、実行します代わりに。

bun run
npm run

# Instead of "npm run clean"
bun run clean

# This also works
bun clean

パッケージ.jsonを仮定すると、コマンドは次のようになります。

"clean"
"scripts"

{
  "name": "myapp",
  "scripts": {
    "clean": "rm -rf dist out node_modules"
  }
}

ブンで不和ボットを作成する

アプリケーションコマンド

アプリケーションコマンドは、Discordクライアントでアプリと対話するためのネイティブな方法です。さまざまなインターフェイスでアクセスできるコマンドには、チャット入力、メッセージのコンテキストメニュー(右上のメニューまたはメッセージの右クリック)、およびユーザーのコンテキストメニュー(ユーザーの右クリック)の3種類があります。

開始するには、インタラクションテンプレートを使用できます。

bun create discord-interactions my-interactions-bot
cd my-interactions-bot

Discordボット/アプリケーションをまだお持ちでない場合は、ここで作成できます(https://discord.com/developers/applications/me)

ボットをサーバーに招待するには、

https://discord.com/api/oauth2/authorize?client_id=<your_application_id>&scope=bot%20applications.commands

その後、アプリケーション ページからボットのトークン、公開キー、アプリケーション ID を取得し、ファイルに入れる必要があります。

.env.example

次に、対話を処理するhttpサーバーを実行できます。

bun install
mv .env.example .env

bun run.js # listening on port 1337

Discordは安全でないHTTPサーバーを受け入れないため、SSL証明書を提供するか、インタラクションサーバーを安全なリバースプロキシの背後に置く必要があります。開発では、ngrok/cloudflareトンネルを使用して、ローカルポートを安全なURLとして公開できます。

ネクストでパンを使う.js

パンを使用して新しいNext.jsアプリを作成するには:

bun create next ./app
cd app
bun dev # start dev server

既存の Next.js アプリを bun で使用するには:

bun add bun-framework-next
echo "framework = 'next'" > bunfig.toml
bun bun # bundle dependencies
bun dev # start dev server

Next.jsの機能の多くはサポートされていますが、すべてではありません。

まだ機能しないものは次のとおりです。

  • getStaticPaths
  • 同一起源内部
    fetch
    getStaticProps
    getServerSideProps
  • ロケール、ゾーン、(回避策: 変更
    assetPrefix
    --origin \"http://localhost:3000/assetPrefixInhere\"
    )
  • next/image
    はレギュラータグにポリフィルされます。
    <img src>
  • proxy
    そして他の何か
    next.config.js
  • APIルート、ミドルウェア(ただし、ミドルウェアはサポートが簡単です!同様のSSR API)
  • styled-jsx(技術的にはNextではありませんが.jsよく使用されます)
  • 反応サーバーコンポーネント

Next.jsを使用する場合、bunは自動的に構成を読み取り、(この順序で).andを介して自動的に置き換えられます。

.env.local
.env.development
.env
process.env.NEXT_PUBLIC_
process.env.NEXT_
--define

現時点では、新しい依存関係をインポートするたびに、再実行する必要があります。これは最終的に自動的に行われます。

node_modules
bun bun --use next

シングルページアプリでの bun の使用

プロジェクトフォルダのルート(場所):

package.json

bun bun ./entry-point-1.js ./entry-point-2.jsx
bun

デフォルトでは、ディレクトリ内のHTMLファイルを探し、それを提供します。ページに移動するブラウザーの場合、ファイル拡張子は URL では省略可能であり、ディレクトリに対して自動的に書き換えられます。

bun
public
.html
index.html

ルーティング元の例と、それらの照合方法を次に示します。

public/

開発サーバーの URL ファイルパス
/dir public/dir/index.html
/ public/index.html
/インデックス public/index.html
/こんにちは public/hi.html
/ファイル public/file.html
/font/Inter.woff2 public/font/inter.woff2
/こんにちは public/index.html

存在する場合、そのパス名にファイル拡張子が付いていない限り、404ページではなくデフォルトのページになります。

public/index.html

反応アプリの作成でパンを使用する

新しい React アプリを作成するには:

bun create react ./app
cd app
bun dev # start dev server

既存の React アプリを使用するには:

# 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

そこから、bunは開発サーバーのパスをソースファイルにマッピングするためにファイルシステムに依存します。すべての URL パスは、プロジェクト ルート (場所) に対する相対パスです。

package.json

ソース コード ファイルのパスのルーティングの例を次に示します。

開発サーバーの URL ファイルパス (cwd に対する相対パス)
/src/components/Button.tsx src/components/Button.tsx
/src/index.tsx src/index.tsx
/ページ/インデックス.js ページ/インデックス.js

ファイル拡張子をインパスに含める必要はありません。ファイル拡張子のないCommonJSスタイルのインポートパスが機能します。

import

パブリックディレクトリは、渡すことで上書きできます。

--public-dir="path-to-folder"

ディレクトリが指定されておらず、存在しない場合、bunは試行します。Ifは存在しませんが、パブリックディレクトリからは提供されません。passbunは現在のディレクトリから機能しますが、現在のディレクトリを最初ではなく最後にチェックします。

./public/
./static/
./static/
--public-dir=./

タイプスクリプトでパンを使用する

タイプスクリプトをバンでトランスパイルする

タイプスクリプトはうまくいきます。構成するものも、インストールする必要もありません。aorfileをインポートすると、bunはそれをJavaScriptにトランスパイルします。パンはまた、トランスパイルを含むまたはファイル。これはバンのTypeScriptトランスパイラーを搭載しているので、高速です。

.ts
.tsx
node_modules
.ts
.tsx

パンも読みます、を含むと。

tsconfig.json
baseUrl
paths

型定義の追加

TypeScript をグローバル API で動作させるには、プロジェクトに追加します。

bun-types

bun add -d bun-types

そしてあなたのフィールド:

types
tsconfig.json

{
  "compilerOptions": {
    "types": ["bun-types"]
  }
}

まだ実装されていません

BUNは非常に広い範囲のプロジェクトであり、まだ初期段階にあります。

Bunのロードマップを見ることができますが、計画されている追加のことがいくつかあります。

特徴
フェッチ API を使用したウェブストリーム パン.js
HTML を使用したウェブストリーム パン.js
npmの動作に一致するパッケージホイスト パンインストール
ソースマップ(バンドルされていないものがサポートされています) JSバンドラー
ソースマップ .CSS
JavaScript ミニファイア JSトランスパイラー
CSS ミニファイア .CSS
CSSパーサー(バンドルのみ) .CSS
木を揺らす ジャバスクリプト
木を揺らす .CSS
タイプスクリプトデコレータ TSトランスパイラー
@jsxPragma
コメント
JSトランスパイラー
ファイルの共有
.bun
バン
日付とタイムスタンプ TOML パーサー
高速更新のためのハッシュコンポーネント JSXトランスパイラー
JS Transpiler == JavaScript Transpiler TSトランスパイラー == TypeScript Transpiler

パッケージマネージャ ==
bun.js == JavaScript を実行する bun の JavaScriptCore インテグレーション。Node.js & Deno が V8 を埋め込む方法に似ています。
bun install

制限と使用目的

今日、bunは主にbun.js、つまりJavaScriptランタイムに焦点を当てています。

bunのバンドラーとトランスパイラーを別々に使用してブラウザやノード用にビルドすることもできますが、bunにはまだミニファイアがなく、ツリーシェイキングもサポートされていません。本番環境のブラウザビルドでは、おそらくesbuildやswcなどのツールを使用する必要があります。

長期的には、bunはNode.js、Webpack、Babel、yarn、PostCSS(本番環境)を置き換える予定です。

今後の破壊的変更

  • Bun の CLI フラグは、JavaScript ランタイムとしての bun のサポートを強化するように変更されます。それらは、bunが単なるフロントエンド開発ツールであったときに選ばれました。
  • Bunのバンドル形式は、プロダクションブラウザバンドルとオンデマンドプロダクションバンドルに対応するように変更されます

構成

bunfig.toml

bunfig.toml は bun の設定ファイルです。

これにより、毎回 CLI にフラグを渡す代わりに、ファイルから設定をロードできます。構成ファイルは CLI 引数が解析される前に読み込まれるため、CLI 引数でオーバーライドできます。

次に例を示します。

# 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: 各プロパティ名を一覧表示する

ローダー

ローダーは、インポートとファイル拡張子を変換と出力にマップする方法を決定します。

現在、bunは以下のローダーを実装しています。

インプット ローダー アウトプット
。.js JSX + JavaScript 。.js
.jsx JSX + JavaScript 。.js
.ts タイプスクリプト + ジャバスクリプト 。.js
.tsx TypeScript + JSX + JavaScript 。.js
.mjs ジャバスクリプト 。.js
.cjs ジャバスクリプト 。.js
.mts タイプスクリプト 。.js
.cts タイプスクリプト 。.js
.toml トムル 。.js
。.css .CSS 。.css
.env 環境 該当なし
.* ファイル

それ以外はすべて次のように扱われ、インポートを URL (またはパス) に置き換えます。

file
file

passingto によって、どのローダーがどの拡張機能にマップされるかを構成できます。例えば:

--loaders
bun

bun --loader=.js:js

これにより、ファイルのJSX変換が無効になります。

.js

JS の CSS (bun dev のみ)

JavaScript ライクなローダーで CSS をインポートする場合、CSS は特別な扱いを受けます。

デフォルトでは、bunは次のようなステートメントを変換します。

import "../styles/global.css";
いつ
platform
browser
globalThis.document?.dispatchEvent(
  new CustomEvent("onimportcss", {
    detail: "http://localhost:3000/styles/globals.css",
  })
);

それを ais に変換するためのイベント ハンドラーは、HMR が有効になっているときに自動的に登録されます。このイベント ハンドラーは、フレームワークまたはクライアント側コードで設定することでオフにできます。さらに、この方法でインポートされたすべての.cssファイルのリストを取得できます。

<link>
package.json
globalThis["Bun_disableCSSImports"] = true;
globalThis["__BUN"].allImportedStyles

いつ
platform
bun
//@import url("http://localhost:3000/styles/globals.css");

さらに、bun は、インポートされた CSS ファイルへの URL のフラット リストを返す SSR/SSG 用の API を公開します。その機能はです。

Bun.getImportedStyles()

// 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
});

これは、スタイルが設定されていないコンテンツのフラッシュを防ぐのに役立ちます。

CSS ローダー

BUN バンドルファイルを介してインポートされたファイルを 1 つのファイルに。今日、CSSを自動接頭辞付けしたり縮小したりすることはありません。1つのJavaScriptファイルにインポートされた複数のファイルは、1つのファイルにバンドルされません。それらをファイルからインポートする必要があります。

.css
@import
.css
.css

この入力:

@import url("./hi.css");
@import url("./hello.css");
@import url("./yo.css");

なる:

/* hi.css */
/* ...contents of hi.css */
/* hello.css */
/* ...contents of hello.css */
/* yo.css */
/* ...contents of yo.css */

CSS ランタイム

ホットCSSリロードをサポートするために、bunはスタイルシートがどのファイルで構成されているかをタグ付けする注釈をCSSに挿入します。ブラウザはこれを無視するため、スタイルには影響しません。

@supports

デフォルトでは、bunのランタイムコードは自動的にリッスンし、挿入します aそのスタイルシートを持つタグが存在しない場合。それがパンに相当する方法です作品。

onimportcss
event.detail
<link rel="stylesheet" href={${event.detail}}>
link
style-loader

フレームワーク

警告これにはまもなく破壊的変更が加えられます。これは、Bunがほとんど開発サーバーであり、JavaScriptランタイムではなかったときに設計されました。

フレームワークは、開発者が既存のツールでbunを使用できるように、bunを事前構成します。

フレームワークは、フレームワーク内のオブジェクトを介して構成されます(アプリケーションのオブジェクトではありません)。

framework
package.json
package.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"
          }
        }
      }
    }
  }
}

型定義を次に示します。

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[];
};

フレームワークを使用するには、合格します。

bun bun --use package-name

あなたのフレームワークはで始まるべきです。これは、人々が次のようなものを入力できるようにするためですそしてそれはチェックします最初。これは、Babelプラグインが開始する傾向がある方法と似ています。

package.json
name
bun-framework-
bun bun --use next
bun-framework-next
babel-plugin-

フレームワークを開発するために、あなたも行うことができます。

bun bun --use ./relative-path-to-framework

フレームワーク統合の追加に興味がある場合は、お問い合わせください。ここにはたくさんのことがあり、まだ完全には文書化されていません。

トラブルシューティング

M1(またはAppleシリコン)で実行されていないパン

次のようなメッセージが表示された場合

[1] 28447殺されたパンは次に作成します./テスト

これはおそらく、Appleシリコンでbunのx64バージョンを実行していることを意味します。これは、パンがロゼッタ経由で実行されている場合に発生します。ロゼッタは、バンが間接的に使用するAVX2命令をエミュレートできません。

修正は、Appleシリコン用に構築されたバージョンのbunを確実にインストールすることです。

エラー: 予期しない

次のようなエラーが表示される場合:

画像

これは通常、オープン・ファイル記述子の最大数が明示的に小さい数に設定されていることを意味します。デフォルトでは、bunは利用可能なファイル記述子の最大数を要求します(macOSでは32,000のようなものです)。しかし、以前にChokidarなどでulimitの問題に遭遇した場合は、インターネット上の誰かがあなたに走るようにアドバイスしたかもしれません。

ulimit -n 8096

残念ながら、そのアドバイスはハードリミットを下げます。これは、依存関係の多い大規模なリポジトリやプロジェクトで問題になる可能性があります。Chokidar(および他のウォッチャー)は電話をかけていないようです、それは彼らが(はるかに低い)ソフト制限に依存していることを意味します。

8096
setrlimit

この問題を解決するには:

  1. 呼び出すスクリプトをすべて削除してシェルを再起動します。
    ulimit -n
  2. 再試行し、それでもエラーが発生する場合は、次のように、ばかげて高い数値に設定してみてください。
    ulimit -n
    ulimit -n 2147483646
  3. もう一度やり直して、それでも問題が解決しない場合は、問題を開きます

解凍が必要です

Linuxにbunをインストールするには解凍が必要です。次のいずれかのコマンドを使用してインストールできます。

unzip

Debian / Ubuntu / Mint

sudo apt install unzip

レッドハット / セントOS / フェドーラ

sudo dnf install unzip

アーチ/マンジャロ

sudo pacman -S unzip

オープンSUSE

sudo zypper install unzip

パンのインストールがスタックしている

走って、パンの不和で私に送ってください。Linuxを使用している場合は、実行してログを添付すると便利です。

bun install --verbose 2> logs.txt
sudo perf trace bun install --silent

参考

bun install

bun install は高速なパッケージマネージャと npm クライアントです。

bun のインストールは、環境変数と CLI フラグを使用して構成できます。

bunfig.toml

でパンインストールを構成する
bunfig.toml

bunfig.toml
は、次のパスで検索されます。
bun install
bun remove
bun add

  1. $XDG_CONFIG_HOME/.bunfig.toml
    又は
    $HOME/.bunfig.toml
  2. ./bunfig.toml

両方が見つかった場合、結果はマージされます。

での構成はオプションです。BUNは一般的にゼロ構成にしようとしますが、それが常に可能であるとは限りません。

bunfig.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

TypeScriptタイプとして読みやすい場合は、次のようにします。

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;
}

環境変数を使用した構成

環境変数の優先順位は よりも高くなります。

bunfig.toml

名前 説明: __________
BUN_CONFIG_REGISTRY npmレジストリを設定します(デフォルト:https://registry.npmjs.org)
BUN_CONFIG_TOKEN 認証トークンを設定する(現在は何もしない)
BUN_CONFIG_LOCKFILE_SAVE_PATH ロックファイルを保存するファイルパス(デフォルト:bun.lockb)
BUN_CONFIG_YARN_LOCKFILE 糸v1スタイルの糸を保存します。
BUN_CONFIG_LINK_NATIVE_BINS Pointin package.json to a platform specific dependencies
bin
BUN_CONFIG_SKIP_SAVE_LOCKFILE ロックファイルを保存しない
BUN_CONFIG_SKIP_LOAD_LOCKFILE ロックファイルを読み込まない
BUN_CONFIG_SKIP_INSTALL_PACKAGES パッケージをインストールしない

bunは常に、ターゲットプラットフォームで利用可能な最速のインストール方法を使用しようとします。macOSでは、それはLinuxではそうです。使用するインストール方法を変更できますフラグ。使用不可またはエラーの場合、およびファイルをコピーするプラットフォーム固有の実装にフォールバックします。

clonefile
hardlink
--backend
clonefile
hardlink

bunはnpmからインストールされたパッケージを保存します。semver バージョンに aor atag がある場合は、代わりにその値のハッシュに置き換えられることに注意してください。これは、長いファイルパスによるエラーの可能性を減らすためですが、残念ながら、パッケージがディスク上のどこにインストールされたかを把握するのが複雑になります。

~/.bun/install/cache/${name}@${version}
build
pre

フォルダが存在する場合、インストールする前に、パンはandin予想されるnode_modulesフォルダが予想されるものと一致するandand。これは、インストールする必要があるかどうかを決定する方法です。カスタムJSONパーサーを使用しており、見つかるとすぐに解析を停止します。

node_modules
"name"
"version"
package/package.json
name
version
"name"
"version"

存在しない場合、または依存関係が変更された場合、tarballは解決中に熱心にダウンロードおよび抽出されます。

bun.lockb
package.json

存在し、変更されていない場合、bunは不足している依存関係を遅延してダウンロードします。一致するパッケージが予想される場所にすでに存在する場合、bunはtarballをダウンロードしようとしません。

bun.lockb
package.json
name
version
node_modules

プラットフォーム固有の依存関係?

bun は、npm からの正規化された値を、解決されたパッケージと共にロックファイルに格納します。実行時に現在のターゲットに対して無効になっているパッケージのダウンロード、抽出、およびインストールをスキップします。これは、最終的にインストールされたパッケージが変更されても、ロックファイルはプラットフォーム/アーキテクチャ間で変更されないことを意味します。

cpu
os

ピアの依存関係?

ピアの依存関係は yarn と同様に処理されます。ピアの依存関係を自動的にインストールせず、既存の依存関係を選択しようとします。

bun install

ロックファイル

bun.lockb
は BUN のバイナリロックファイル形式です。

なぜバイナリなのですか?

一言で言えば:パフォーマンス。BunのLockFileは信じられないほど速く保存&ロードし、通常ロックファイル内にあるものよりもはるかに多くのデータを保存します。

どうすれば検査できますか?

今のところ、最も簡単なのは実行することです。これにより、Yarn v1 スタイルの yarn.lock ファイルが出力されます。

bun install -y

ロックファイルには何が保存されていますか?

パッケージ、それらのパッケージのメタデータ、ホイストされたインストール順序、各パッケージの依存関係、それらの依存関係が解決されたパッケージ、整合性ハッシュ (使用可能な場合)、各パッケージが解決された内容、およびバージョン (または同等のもの)。

なぜ速いのですか?

すべてのデータに線形配列を使用します。パッケージは、自動インクリメント整数 ID またはパッケージ名のハッシュによって参照されます。8 文字を超える文字列は重複排除されます。ディスクに保存する前に、ロックファイルはガベージコレクションされ、パッケージツリーをたどり、依存関係の順序でパッケージのクローンを作成することで決定論的になります。

キャッシュ

キャッシュを削除するには:

rm -rf ~/.bun/install/cache

プラットフォーム固有のバックエンド

bun install
プラットフォームに応じて異なるシステムコールを使用して依存関係をインストールします。これはパフォーマンスの最適化です。を使用して特定のバックエンドを強制できますフラグ。
--backend

ハードリンクは、Linux のデフォルトのバックエンドです。ベンチマークは、Linuxで最速であることを示しました。

rm -rf node_modules
bun install --backend hardlink

クローンファイルは、macOSのデフォルトのバックエンドです。ベンチマークは、macOSで最速であることを示しました。macOSでのみ使用できます。

rm -rf node_modules
bun install --backend clonefile

clonefile_each_dirと似ていますが、ディレクトリごとに各ファイルを個別に複製する点が異なります。macOSでのみ利用可能であり、パフォーマンスが低下する傾向があります。とは異なり、これは1回のシステムコールでサブディレクトリを再帰的に複製しません。

clonefile
clonefile
clonefile

rm -rf node_modules
bun install --backend clonefile_each_dir

copyfileは、上記のいずれかが失敗した場合に使用されるフォールバックであり、最も低速です。macOSでは、それを使用しますそしてLinuxではそれを使用します。

fcopyfile()
copy_file_range()

rm -rf node_modules
bun install --backend copyfile

シンボリックリンクは通常、内部的に(そして最終的には)依存関係にのみ使用されます。無限ループを防ぐために、フォルダのシンボリックリンクをスキップします。

file:
link:
node_modules

でインストールした場合、Node.jsは、各依存関係に独自のnode_modulesフォルダーがあるか、パストしない限り、依存関係のnode_modulesを解決しません。ノード.js のドキュメント --preserve-symlinks を参照してください。

--backend=symlink
--preserve-symlinks
node

rm -rf node_modules
bun install --backend symlink

# https://nodejs.org/api/cli.html#--preserve-symlinks
node --preserve-symlinks ./my-file.js

Bunのランタイムは現在、同等のものを公開していませんが、そのコードは存在します。

--preserve-symlinks

npm レジストリ メタデータ

bunは、NPMレジストリ応答をキャッシュするためにバイナリ形式を使用します。これはJSONよりもはるかに高速に読み込まれ、ディスク上で小さくなる傾向があります。 これらのファイルが表示されます。ファイル名のパターンはです。これはハッシュであるため、スコープ付きパッケージ用に追加のディレクトリを作成する必要はありません。

~/.bun/install/cache/*.npm
${hash(packageName)}.npm

パンの使用法は無視します。これによりパフォーマンスは向上しますが、npmから最新のパッケージバージョンのメタデータを受信するには、bunが約5分古くなる可能性があります。

Cache-Control
Age

bun run

bun run
は高速スクリプトランナーです。毎回npmクライアントが起動するのを170ミリ秒待つ代わりに、bunを6ミリ秒待ちます。
package.json

デフォルトでは、呼び出されるスクリプトを出力します。

bun run

bun run clean
$ rm -rf node_modules/.cache dist

あなたはそれを無効にすることができます

--silent

bun run --silent clean

bun run ${script-name}
と同等のものを実行します。たとえば、スクリプトを実行すると、非bunプロセスがスピンアップすることがあります。
npm run script-name
bun run dev
dev
package.json

bun run ${javascript-file.js}
ファイルにノードシバンがない限り、bunで実行します。

追加の引数なしでリストを印刷するには:

scripts
bun run

# 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
シェル/タスクから環境変数を自動的にロードします。ファイルは、他のBUNと同じ優先度でロードされるため、次のようになります。
.env
.env

  1. .env.local
    が最初です
  2. if ( === ) else
    $NODE_ENV
    "production"
    .env.production
    .env.development
  3. .env

そこで予期しないことがある場合は、実行して環境変数のリストを取得できます。

bun run env

使用するデフォルトのシェルはですが、見つからない場合は試行し、それでも見つからない場合は試行します。これは現時点では構成できませんが、気になる場合は問題を報告してください。

bash
sh
zsh

bun run
自動的に親を追加し、一致するスクリプトがない場合は、代わりにそのバイナリをロードします。つまり、パッケージから実行可能ファイルを実行することもできます。
node_modules/.bin
$PATH

# 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

追加のフラグをタスクまたは実行可能ファイルに渡すには、次の 2 つの方法があります。

# 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
のようなライフサイクルフックをサポートします。存在する場合は、npm クライアントの動作に合わせて実行されます。失敗した場合、次のタスクは実行されません。現在、これらのライフサイクルタスクが存在する場合、そのファイルに問題が必要な場合にスキップするフラグはありません。
post${task}
pre{task}
pre${task}

bun --hot

bun --hot
BunのJavaScriptランタイムでコードのホットリロードを有効にします。これは、Bun v0.2.0で利用できる非常に実験的な機能です。

のようなファイルウォッチャーとは異なり、HTTPサーバーのようなステートフルなオブジェクトを実行し続けることができます。

nodemon
bun --hot

ブンv0.2.0 ノデーモン

スクリーンレコーディング 2022 AM:10-06 2 36 06

BunのHTTPサーバー(自動)で使用するには:

server.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" },
    });
  },
};

次に、次のコマンドを実行します。

bun --hot server.ts

次のものも使用できます。

bun run

bun run --hot server.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);

Bunの将来のバージョンでは、Vite'sisのサポートにより、ホットリロードのライフサイクル管理が改善され、エコシステムに合わせられる予定です。

import.meta.hot

ハウワークス
bun --hot

bun --hot
インポートされたファイルの変更を監視し、それらを再読み込みします。インポートされていないファイルは監視されず、監視もされません。
node_modules

リロード時に、内部キャッシュとESモジュールレジストリをリセットします()。

require
Loader.registry

そうしたら:

  • ガベージコレクタを同期的に実行します(実行時のパフォーマンスを犠牲にして、メモリリークを最小限に抑えるため)
  • Bunはすべてのコードを最初から再トランスパイルします(ソースマップを含む)
  • JavaScriptCore (エンジン) がコードを再評価します。

従来のファイルウォッチャーはプロセス全体を再起動するため、HTTPサーバーやその他のステートフルオブジェクトは失われます。プロセスは再起動されないため、リロード後も一部の状態が保持され、邪魔になりません。

bun --hot

この実装は特に最適化されていません。変更されていないファイルを再トランスパイルします。インクリメンタルコンパイルは試行されません。それが出発点です。

bun create

bun create
は、テンプレートから新しいプロジェクトをすばやく作成する方法です。

執筆時点では、ローカルコンピュータで~11倍高速に実行されます.現在キャッシュはありません(ただし、npmクライアントはキャッシュします)

bun create react app
yarn create react-app app
bun create

使い

新しい Next.js プロジェクトを作成します。

bun create next ./app

新しい React プロジェクトを作成します。

bun create react ./app

GitHub リポジトリから作成します。

bun create ahfarmer/calculator ./app

テンプレートの一覧を表示するには、次のコマンドを実行します。

bun create

形式:

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

注:パンを使用する必要はありません。構成はまったく必要ありません。このコマンドは、少し簡単にするために存在します。

bun create

ローカルテンプレート

使用したい独自の定型文がある場合は、それをコピーします。

$HOME/.bun-create/my-boilerplate-name

npmjsでbunのテンプレートをチェックする前に、入力に一致するローカルフォルダをチェックします。

bun create

  • $BUN_CREATE_DIR/
  • $HOME/.bun-create/
  • $(pwd)/.bun-create/

入力のあるフォルダのいずれかにフォルダが存在する場合、bunはリモートテンプレートの代わりにそれを使用します。

ローカルテンプレートを作成するには、次のコマンドを実行します。

mkdir -p $HOME/.bun-create/new-template-name
echo '{"name":"new-template-name"}' > $HOME/.bun-create/new-template-name/package.json

これにより、以下を実行できます。

bun create new-template-name ./app

これで、実行時に新しいテンプレートが表示されます。

bun create

警告:リモートテンプレートとは異なり、bunは宛先フォルダーがすでに存在する場合、それを削除します。

フラグ

説明: __________
--エヌピーエム タスクとインストールに使用します
npm
--糸 タスクとインストールに使用します
yarn
--pnpm タスクとインストールに使用します
pnpm
--力 既存のファイルを上書きする
--インストールなし インストールとタスクをスキップする
node_modules
--いいえ-git gitリポジトリを初期化しないでください
--開ける 開始 & 終了後にブラウザで開く
環境変数 説明: __________
GITHUB_API_DOMAIN GitHub エンタープライズまたはプロキシを使用している場合は、エンドポイントが GitHub go に要求するものを変更できます
GITHUB_API_TOKEN これは、プライベートリポジトリで動作するか、レート制限された場合に動作します
bun create

デフォルトでは、上書きされる既存のファイルがあり、それがリモートテンプレートである場合はキャンセルされます。あなたは渡すことができますこの動作を無効にするには。

bun create
--force

新しいテンプレートの公開

クローンを作成し https://github.com/bun-community/create-templates/ 新しいテンプレートを使用してルートディレクトリに新しいフォルダを作成します。the持っている必要がありますそれはから始まります。PRがマージされた後に自動的に発生する公開について心配する必要はありません。

package.json
name
@bun-examples/

必ず含めてくださいそれに含まれるので、人々がテンプレートをダウンロードするときにgitにチェックインされません。

.gitignore
node_modules
node_modules

新しいテンプレートのテスト

新しいテンプレートをテストするには、ローカルテンプレートとして追加するか、絶対パスを渡します。

bun create /path/to/my/new/template destination-dir

警告:これにより、宛先ディレクトリのすべてが常に削除されます

設定

theセクションは自動的に削除されますディスク上。これにより、追加のパッケージがインストールされるのを待たずに、作成専用の手順を追加できます。

bun-create
package.json
package.json

現在、次の 3 つのオプションがあります。

  • postinstall
  • preinstall
  • start
    (表示される開始コマンドをカスタマイズします)

文字列の配列または 1 つの文字列を指定できます。一連のステップが順番に実行されます。

次に例を示します。

{
  "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!'"
  }
}

既定では、すべてのコマンドは、自動検出された npm クライアントによって公開されている環境内で実行されます。これにより、呼び出しごとにnpmクライアントが起動するのを待つのに150ミリ秒かかるなど、パフォーマンスが大幅に低下します。

で始まるコマンドはnpmなしで実行され、最初のバイナリに依存します。

"bun "
bun
$PATH

ハウワークス
bun create

実行すると、次のようになります。

bun create ${template} ${destination}

IF リモートテンプレート

  1. GETそしてそれを解析する

    registry.npmjs.org/@bun-examples/${template}/latest

  2. 取得

    registry.npmjs.org/@bun-examples/${template}/-/${template}-${latestVersion}.tgz

  3. 解凍&抽出

    ${template}-${latestVersion}.tgz
    ${destination}

    • 上書き、警告、および終了するファイルがある場合は、渡されない限り終了します
      --force

IF GitHub repo

  1. GitHub の API から tarball をダウンロードする

  2. 解凍&抽出

    ${destination}

    • 上書き、警告、および終了するファイルがある場合は、渡されない限り終了します
      --force

ELSE IF ローカルテンプレート

  1. ローカルテンプレートフォルダを開く

  2. 宛先ディレクトリを再帰的に削除する

  3. 利用可能な最速のシステムコールを使用してファイルを再帰的にコピーします(macOSおよびLinuxの場合)。存在する場合は、フォルダにコピーまたはトラバースしないでください(これだけでも高速になります

    fcopyfile
    copy_file_range
    node_modules
    cp
    )

  4. を解析します(再び!)、更新するには、を削除しますセクションから更新を保存しますディスクに。

    package.json
    name
    ${basename(destination)}
    bun-create
    package.json
    package.json

    • NEXT.jsが検出された場合は、依存関係のリストに追加します
      bun-framework-next
    • [反応アプリの作成] が検出された場合は、/src/index にエントリ ポイントを追加します。{js,jsx,ts,tsx} to
      public/index.html
    • リレーが検出された場合は、リレーが機能するように追加します
      bun-macro-relay
  5. npmクライアントを自動検出し、優先し(v1)、最後に

    pnpm
    yarn
    npm

  6. npmクライアントで定義されたタスクを実行する

    "bun-create": { "preinstall" }

  7. rununlessis が渡されたか、package.json に依存関係がありません

    ${npmClient} install
    --no-install

  8. npmクライアントで定義されたタスクを実行する

    "bun-create": { "preinstall" }

  9. 走る

    git init; git add -A .; git commit -am "Initial Commit";

    • 名前を変更します。NPM は、パッケージに表示されないようにファイルを自動的に削除します。
      gitignore
      .gitignore
      .gitignore
    • 依存関係がある場合、これはインストール中に別のスレッドで同時に実行されますnode_modules
    • 利用可能な場合はlibgit2を使用することがテストされ、マイクロベンチマークで3倍遅く実行されました
  10. 完成です

misctools/publish-examples.js
すべての例を npm に公開します。

bun bun

実行インポートされたすべての依存関係を含むファイルを(再帰的に)生成します。

bun bun ./path-to.js
node_modules.bun

バンドルする理由

  • ブラウザーの場合、依存関係をバンドルせずにアプリ全体を読み込むのは、通常、低速です。高速バンドラーとトランスパイラーでは、ボトルネックは最終的に多くのネットワーク要求を同時に実行するWebブラウザの機能になります。これにはHTTP/3など多くの回避策がありますが、バンドルほど効果的なものはありません。反対の再現可能な証拠がある場合は、問題を送信してください。バンドルが必要なければもっと良いでしょう。
    <link rel="modulepreload">
  • サーバーでは、バンドルにより、JavaScript をロードするためのファイルシステム検索の数が減ります。ファイルシステムのルックアップはHTTPリクエストよりも高速ですが、それでもオーバーヘッドがあります。

何ですか?
.bun

注:この形式はまもなく変更される可能性があります

ファイルには次のものが含まれます。

.bun

  • バンドルされたすべてのソースコード
  • バンドルされたすべてのソースコードメタデータ
  • プロジェクトのメタデータと設定

ここにいくつかの質問ファイルの答えがあります:

.bun

  • インポートするとき、そのためのコードはどこにありますか?(解決せず、コードだけ)
    react/index.js
    .bun
  • パッケージのどのモジュールが使用されていますか?
  • どのフレームワークが使用されていますか?(例えば、次へ.js)
  • ルートディレクトリはどこにありますか?
  • インポートされた各依存関係の大きさはどれくらいですか?
  • バンドルの内容のハッシュは何ですか?(eタグ用)
  • このバンドルにエクスポートされたすべてのnpmパッケージの名前とバージョンは何ですか?
  • このプロジェクトでは、どのパッケージのどのモジュールが使用されていますか?(「プロジェクト」は、.bunを生成するために使用されるすべてのエントリポイントとして定義されます)

オールインワンファイル。

ビルドキャッシュに少し似ていますが、ビルド間で再利用できるように設計されています。

位置に依存しないコード

設計の観点から、フォーマットの最も重要な部分は、コードがどのように編成されるかです。各モジュールは、次のようなハッシュによってエクスポートされます。

.bun

// preact/dist/preact.module.js
export var $eb6819b = $$m({
  "preact/dist/preact.module.js": (module, exports) => {
    let n, l, u, i, t, o, r, f, e = {}, c = [], s = /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;
    // ... rest of code

これにより、バンドルされたモジュールの位置が独立します。理論的には、コードを再解析したり、新しいバンドルを生成したりすることなく、使用中の正確なモジュールのみをインポートできます。1 つのバンドルは、Web ページで使用されているモジュールのみを含む多数のバンドルに動的にすることができます。バイトオフセット付きのメタデータのおかげで、Webサーバーはsendfileを使用して各モジュールをブラウザにゼロコピーで送信できます。bun自体はまだそれほどスマートではありませんが、これらの最適化は本番環境で役立ち、Reactサーバーコンポーネントに非常に役立つ可能性があります。

内部のスキーマを確認するには、JavascriptBundleContainer を参照してください。メタデータを読み取るための JavaScript バインディングは、src/api/schema.js にあります。これはまだ実際にはAPIではありません。ファイルの一番下からバイナリデータを取得する部分がありません。いつか、これを他のツールでも使えるようにしたいです。

コードはどこにありますか?

.bun
ファイルは実行可能としてマークされます。

コードを印刷するには、実行しますターミナルで実行するか、実行します。

./node_modules.bun
bun ./path-to-node_modules.bun

コピー&ペースト可能な例を次に示します。

./node_modules.bun > node_modules.js

これは、すべてのファイルが次のように始まるときに機能します。

.bun

#!/usr/bin/env bun

bunを使用して本番環境にデプロイするには、ファイルからコードを取得し、Webサーバーがそれを見つけることができる場所(またはVercelまたはRailsアプリケーションを使用している場合は、フォルダ内)に固執する必要があります。

.bun
public

バイナリファイル形式であるため、VSCodeまたはvimで開くだけで奇妙にレンダリングされる可能性があります。

.bun

アドバンスド

既定では、アプリ コードまたは別の外部依存関係のいずれかにある外部依存関係のみをバンドルします。「外部依存関係」とは、「解決されたファイルパスと対応するJavaScriptのようなファイル」と定義されます。

bun bun
import
require
/node_modules/
package.json

afolder(つまり、すべてのシンボリックリンクに続く最終的な解決されたパス)にないパッケージをバンドルするようにbunを強制するには、ルートプロジェクトのwithsetにasectionを追加して、常にバンドルするパッケージ名の配列に追加します。次に例を示します。

node_modules
bun
package.json
alwaysBundle

{
  "name": "my-package-name-in-here",
  "bun": {
    "alwaysBundle": ["@mybigcompany/my-workspace-package"]
  }
}

バンドルされた依存関係は、ホットモジュールの再ロードの対象にはなりません。コードはブラウザとブンに逐語的に提供されます.js。ただし、将来的には、使用されているバンドルの一部にのみ分割される可能性があります。これは、現在のバージョンのファイルで可能ですが(必要なファイルを知っている限り)、まだ実装されていません。長期的には、内部に各モジュールのすべてが含まれます。

.bun
import
export

モジュールIDハッシュとは何ですか?

ここで使用されるハッシュ:

$eb6819b

export var $eb6819b = $$m({

次のように生成されます。

  1. Murmur3 の 32 ビット ハッシュ。これは、npmパッケージを一意に識別するハッシュです。
    package.name@package.version
  2. +.のWyhash 64は、「npmパッケージのルートに対して、モジュールはどこにインポートされますか?」を意味します。たとえば、インポートした場合、
    package.hash
    package_path
    package_path
    react/jsx-dev-runtime.js
    package_path
    jsx-dev-runtime.js
    react-dom/cjs/react-dom.development.js
    cjs/react-dom.development.js
  3. 上記で生成されたハッシュを
    u32

このモジュールIDハッシュの実装の詳細は、bunのバージョンによって異なります。重要な部分は、メタデータにモジュールID、パッケージパス、およびパッケージハッシュが含まれているため、他のツールがこれのいずれかを利用するかどうかは実際には重要ではありません。

bun upgrade

パンをアップグレードするには、実行します。

bun upgrade

最新バージョンのbunを自動的にダウンロードし、現在実行中のバージョンを上書きします。

これは、アップデーター用のbunリリースで最新バージョンのbunをチェックし、システム提供のライブラリを使用して解凍することで機能します(ゲートキーパーがmacOSで動作するようにするため)

unzip

何らかの理由で問題が発生した場合は、curlインストールスクリプトを使用することもできます。

curl https://bun.sh/install | bash

bunがすでにインストールされている場合でも機能します。

bunは単一のバイナリファイルとして配布されるため、手動で行うこともできます。

  • お使いのプラットフォーム用の最新バージョンのbunをbun-releases-for-updater(== macOS)でダウンロードしてください。
    darwin
  • フォルダを解凍します
  • バイナリを(または任意の場所)に移動します
    bun
    ~/.bun/bin

カナリアビルド

カナリアビルドはコミットごとに生成されます。

bunのカナリアビルドをインストールするには、次のコマンドを実行します。

bun upgrade --canary

このフラグは永続的ではありません (ただし、将来変更される可能性があります)。常にパンのカナリアビルドを実行する場合は、環境変数をシェルの起動スクリプトで。

BUN_CANARY
1

これにより、https://github.com/oven-sh/bun/releases/tag/canary からリリースzipがダウンロードされます。

bunの最新の公開バージョンに戻すには、次のコマンドを実行します。

bun upgrade

bun init

bun init
は、Bunで空のプロジェクトを開始する簡単な方法です。それは正気のデフォルトを推測し、複数回実行しても非破壊的です。

デモ

それは作成します:

  • a現在のディレクトリ名をデフォルトとする名前のファイル
    package.json
  • afile または afile (エントリ ポイントが TypeScript ファイルかどうかによって異なります)
    tsconfig.json
    jsconfig.json
  • デフォルトのエントリポイントは、存在しないか、またはaorフィールドを指定します
    index.ts
    index.{tsx, jsx, js, mts, mjs}
    package.json
    module
    main
  • ファイルファイル
    README.md

通行人の場合、質問せずに続行したいと想定されます。

-y
--yes

最後に、それは実行されますインストールします。

bun install
bun-types

ブンv0.1.7で追加されました。

どうですか?
bun init
bun create

bun init
は空白のプロジェクト用です。テンプレートを適用します。
bun create

bun completions

このコマンドは、入力候補 forand/or をインストールします。インストール時にすべて自動的に実行されます。どのシェルをインストールするかを決定するために、から読み取ります。それはあなたのシェルとOSのためのいくつかの一般的なシェル完了ディレクトリを試します。

zsh
fish
bun upgrade
$SHELL

入力候補を手動でコピーする場合は、次のコマンドを実行します。インストール先の入力候補ディレクトリがわかっている場合は、次のコマンドを実行します。

bun completions > path-to-file
bun completions /path/to/directory

ローダー API

Bun v0.1.11ではカスタムローダーが導入されています。

  • インポートして要求,,,,およびBunが組み込みローダーを実装していないその他のファイル拡張子。
    .svelte
    .vue
    .yaml
    .scss
    .less
  • ESM および CJS モジュールを動的に生成する

YAML ローダー viajs-yaml

これはanloader.loadersを使用すると、BunがESMおよびCJSモジュールに変換するJSオブジェクトを返すことができます。

"object"
object

プラグインの実装 (

my-yaml-plugin.js
)

import { plugin } from "bun";

plugin({
  name: "YAML",

  setup(builder) {
    const { load } = require("js-yaml");
    const { readFileSync } = require("fs");
    // Run this function on any import that ends with .yaml or .yml
    builder.onLoad({ filter: /\.(yaml|yml)$/ }, (args) => {
      // Read the YAML file from disk
      const text = readFileSync(args.path, "utf8");

      // parse the YAML file with js-yaml
      const exports = load(text);

      return {
        // Copy the keys and values from the parsed YAML file into the ESM module namespace object
        exports,

        // we're returning an object
        loader: "object",
      };
    });
  },
});

プラグインの使用法:

import "./my-yaml-plugin.js";
import { hello } from "./myfile.yaml";

console.log(hello); // "world"

svelte/コンパイラを使用したSvelteローダー

これは aloader で、JS 文字列を返すか、Bun が ESM & CJS モジュールに変換します。

"js"
ArrayBufferView

プラグインの実装 (

myplugin.js
)

import { plugin } from "bun";

await plugin({
  name: "svelte loader",
  async setup(builder) {
    const { compile } = await import("svelte/compiler");
    const { readFileSync } = await import("fs");

    // Register a loader for .svelte files
    builder.onLoad({ filter: /\.svelte$/ }, ({ path }) => ({
      // Run the Svelte compiler on the import path
      contents: compile(readFileSync(path, "utf8"), {
        filename: path,
        generate: "ssr",
      }).js.code,

      // Set the loader to "js"
      // This runs it through Bun's transpiler
      loader: "js",
    }));
  },
});

注:本番実装では、コンパイルされた出力をキャッシュし、追加のエラー処理を含める必要があります。

プラグインの使用法:

import "./myplugin.js";
import MySvelteComponent from "./component.svelte";

console.log(mySvelteComponent.render());

ローダー API リファレンス

BunのローダーAPIインターフェースは、esbuildに大まかに基づいています。一部のエスビルドプラグインは、Bunで「正しく機能」します。

MDX:

import { plugin } from "bun";
import { renderToStaticMarkup } from "react-dom/server";

// it's the esbuild plugin, but it works using Bun's transpiler.
import mdx from "@mdx-js/esbuild";

plugin(mdx());

import Foo from "./bar.mdx";
console.log(renderToStaticMarkup(<Foo />));

ローダーAPIの中核には、インポートパスに挿入されたプレフィックス paths.is インポートに一致するRegExp areand.is(esbuildとは異なり、Bunはトランスパイルされた出力にプレフィックスを挿入します)。たとえば、aofとaofを持つローダーがある場合、インポートパスはに変換されます。

filter
namespace
filter
namespace
filter
\.yaml$
namespace
yaml:
./myfile.yaml
yaml:./myfile.yaml

プラグイン機能

最上位レベルでは、エクスポート元の関数文字列とオブジェクトを受け取る関数が必要です。

plugin
"bun"
"name"
"setup"
builder

プラグインを自動的にアクティブ化するには、関数は次のようなインポートステートメントからのものである必要があります。

plugin

import { plugin } from "bun";

// This automatically activates on import
plugin({
  name: "my plugin",
  setup(builder) {},
});

/* Bun.plugin() does not automatically activate. */

内部関数では、次のことができます。

setup

  • を使用してローダーを登録する
    builder.onLoad()
  • を使用してリゾルバを登録する
    builder.onResolve()

内部的には、Bunのトランスパイラーは自動的に呼び出しを別々のファイルに変換します(ファイルごとに最大1つ)。これにより、アプリケーションの残りの部分がゼロ構成で実行される前にローダーをアクティブ化できます。

plugin()

builder.onLoad({ filter, namespace?: "optional-namespace" }, callback)

builder.onLoad()
一致する RegExp と文字列のローダーを登録します。
filter
namespace

この関数は、次のプロパティを含むオブジェクトで呼び出されます。

callback
args

  • path
    : ロードされるファイルのパス

今のところ、それが唯一のプロパティです。今後、さらに追加される可能性があります。

ローダーの種類

ローダーにはさまざまな種類があります。

  • "js"
    ,,,:これらのローダーは、Bunのトランスパイラーを介してソーステキストを実行します
    "jsx"
    "ts"
    "tsx"
  • "json"
    ,: これらのローダーは、Bun の組み込みパーサーを介してソーステキストを実行します
    "toml"
  • "object"
    : このローダーは、オブジェクトからモジュール名前空間オブジェクトにすべてのキーと値をコピーすることにより、新しい ECMAScript モジュールを ECMAScript モジュール レジストリに挿入します。
    "exports"

この関数は、サンドプロパティを含む戻り値を期待しています。 ローダーがそうでない限り。

callback
contents
loader
"object"

"contents"
はソースコードです。文字列または an を指定できます。
ArrayBufferView

"loader"
はローダーの種類です。かも,,,,,,かも。
"js"
"jsx"
"ts"
"tsx"
"json"
"toml"
"object"

ifis、関数は期待します代わりにオブジェクト。キーと値は、ESM モジュール名前空間オブジェクトにコピーされます。

"loader"
"object"
callback
"exports"
"contents"

"object"
ローダーは、YAML、JSON、またはその他のデータ形式を解析するときなど、戻り値がオブジェクトに解析される場合に便利です。ほとんどのローダー API では、値を文字列化して再度解析する必要があります。このローダーを使用すると、その手順をスキップできるため、パフォーマンスが向上し、場合によっては少し簡単になります。

Bun.serve
- 高速 HTTP サーバー

"bun!" と書くハローワールド HTTP サーバーの場合、Linux 上のノード.jsの約 2.5 倍/秒のリクエストを処理します。

Bun.serve

要求数/秒 実行中
~64,000 ノード 16
~160,000 バン

大きいほど良い

コード

バン:

Bun.serve({
  fetch(req: Request) {
    return new Response(`bun!`);
  },
  port: 3000,
});

ノード:

require("http")
  .createServer((req, res) => res.end("bun!"))
  .listen(8080);
画像

使い

bun.jsでHTTPサーバーを起動する2つの方法:

  1. export default
    関数を持つオブジェクト
    fetch

bunの起動に使用されるファイルにデフォルトのエクスポートがある場合、HTTPサーバーを起動します。

fetch

// hi.js
export default {
  fetch(req) {
    return new Response("HI!");
  },
};

// bun ./hi.js

fetch
要求オブジェクトを受け取り、応答または約束<応答>を返す必要があります。将来のバージョンでは、クッキーのようなものについて追加の引数があるかもしれません。

  1. Bun.serve
    HTTP サーバーを明示的に起動します。
Bun.serve({
  fetch(req) {
    return new Response("HI!");
  },
});

エラー処理

エラー処理のために、あなたは関数を得ます。

error

定義されていないか、aを返さない場合は、スタックトレースを含む例外ページが表示されます。

development: true
error
Response

画像

うまくいけば、bunがデバッガーをサポートするまで、bunの問題を簡単にデバッグできるようになります。このエラーページは、何に基づいています。

bun dev

エラー関数がレスポンスを返す場合は、代わりに提供されます

Bun.serve({
  fetch(req) {
    throw new Error("woops!");
  },
  error(error: Error) {
    return new Response("Uh oh!!\n" + error.toString(), { status: 500 });
  },
});

エラー関数自体がスローされ、開発がfalseの場合、一般的な500ページが表示されます

サーバーを停止するには、以下を呼び出します。

server.stop()

const server = Bun.serve({
  fetch() {
    return new Response("HI!");
  },
});

server.stop();

HTTPS with Bun.serve()

Bun.serve()
には、TLS (HTTPS) のサポートが組み込まれています。パスアンドHTTPSを有効にするオプション。
keyFile
certFile

例:

Bun.serve({
  fetch(req) {
    return new Response("Hello!!!");
  },
  /**
   * File path to a TLS key
   *
   * To enable TLS, this option is required.
   */
  keyFile: "./key.pem",
  /**
   * File path to a TLS certificate
   *
   * To enable TLS, this option is required.
   */
  certFile: "./cert.pem",

  /**
   * Optional SSL options
   */
  // passphrase?: string;
  // caFile?: string;
  // dhParamsFile?: string;
  // lowMemoryMode?: boolean;
});

WebSockets with Bun.serve()

Bun.serve()
サーバー側のウェブソケットのサポートが組み込まれています(Bun v0.2.1以降)。

顔立ち:

  • 圧縮 (パス
    perMessageDeflate: true
    )
  • ティッカー
  • Pubsub / MQTTライクなトピックによるブロードキャストサポート

それも速いです。Linux x64 のチャットルームの場合:

毎秒送信されるメッセージ 実行中 クライアント
~700,000 (
Bun.serve
) ブン v0.2.1 (x64)
16
~100,000 (
ws
) Node v18.10.0 (x64)
16

Here is an example that echoes back any message it receives:

Bun.serve({
  websocket: {
    message(ws, message) {
      ws.send(message);
    },
  },

  fetch(req, server) {
    // Upgrade to a ServerWebSocket if we can
    // This automatically checks for the `Sec-WebSocket-Key` header
    // meaning you don't have to check headers, you can just call `upgrade()`
    if (server.upgrade(req))
      // When upgrading, we return undefined since we don't want to send a Response
      return;

    return new Response("Regular HTTP response");
  },
});

Here is a more complete example:

type User = {
  name: string;
};

Bun.serve<User>({
  fetch(req, server) {
    if (req.url === "/chat") {
      if (
        server.upgrade(req, {
          // This User object becomes ws.data
          data: {
            name: new URL(req.url).searchParams.get("name") || "Friend",
          },
          // Pass along some headers to the client
          headers: {
            "Set-Cookie": "name=" + new URL(req.url).searchParams.get("name"),
          },
        })
      )
        return;
    }

    return new Response("Expected a websocket connection", { status: 400 });
  },

  websocket: {
    open(ws) {
      console.log("WebSocket opened");

      // subscribe to "the-group-chat" topic
      ws.subscribe("the-group-chat");
    },

    message(ws, message) {
      // In a group chat, we want to broadcast to everyone
      // so we use publish()
      ws.publish("the-group-chat", `${ws.data.name}: ${message}`);
    },

    close(ws, code, reason) {
      ws.publish("the-group-chat", `${ws.data.name} left the chat`);
    },

    drain(ws) {
      console.log("Please send me data. I am ready to receive it.");
    },

    // enable compression
    perMessageDeflate: true,
    /*
    * perMessageDeflate: {
       **
       * Enable compression on the {@link ServerWebSocket}
       *
       * @default false
       *
       * `true` is equivalent to `"shared"
       compress?: WebSocketCompressor | false | true;
       **
       * Configure decompression
       *
       * @default false
       *
       * `true` is equivalent to `"shared"
       decompress?: WebSocketCompressor | false | true;
    */

    /**
     * The maximum size of a message
     */
    // maxPayloadLength?: number;
    /**
     * After a connection has not received a message for this many seconds, it will be closed.
     * @default 120 (2 minutes)
     */
    // idleTimeout?: number;
    /**
     * The maximum number of bytes that can be buffered for a single connection.
     * @default 16MB
     */
    // backpressureLimit?: number;
    /**
     * Close the connection if the backpressure limit is reached.
     * @default false
     */
    // closeOnBackpressureLimit?: boolean;

    // this makes it so ws.data shows up as a Request object
  },
  // TLS is also supported with WebSockets
  /**
   * File path to a TLS key
   *
   * To enable TLS, this option is required.
   */
  // keyFile: "./key.pem",
  /**
   * File path to a TLS certificate
   *
   * To enable TLS, this option is required.
   */
  // certFile: "./cert.pem",
});

ServerWebSocket vs WebSocket

For server websocket connections, Bun exposes a class which is similar to the web-standard class used for websocket client connections, but with a few differences:

ServerWebSocket
WebSocket

Headers

ServerWebSocket
supports passing headers. This is useful for setting cookies or other headers that you want to send to the client before the connection is upgraded.

Bun.serve({
  fetch(req, server) {
    if (
      server.upgrade(req, { headers: { "Set-Cookie": "name=HiThereMyNameIs" } })
    )
      return;
  },
  websocket: {
    message(ws, message) {
      ws.send(message);
    },
  },
});

The web-standard API does not let you specify headers.

WebSocket

Publish/subscribe

ServerWebSocket
has , , and methods which let you broadcast the same message to all clients connected to a topic in one line of code.
publish()
subscribe()
unsubscribe

ws.publish("stock-prices/GOOG", `${price}`);
Backpressure

ServerWebSocket.send
returns a number that indicates:

  • 0
    if the message was dropped due to a connection issue
  • -1
    if the message was enqueued but there is backpressure
  • any other number indicates the number of bytes sent

This lets you have better control over backpressure in your server.

You can also enable/disable compression per message with the option:

compress

// this will compress
ws.send("Hello".repeat(1000), true);

WebSocket.send
returns and does not indicate backpressure, which can cause issues if you are sending a lot of data.
undefined

ServerWebSocket
also supports a callback that runs when the connection is ready to receive more data.
drain

Callbacks are per server instead of per socket

ServerWebSocket
expects you to pass a object to the method which has methods for , , , , and . This is different than the client-side class which extends (onmessage, onopen, onclose),
WebSocketHandler
Bun.serve()
open
message
close
drain
error
WebSocket
EventTarget

Clients tend to not have many socket connections open so an event-based API makes sense.

But servers tend to have many socket connections open, which means:

  • Time spent adding/removing event listeners for each connection adds up
  • Extra memory spent on storing references to callbacks function for each connection
  • Usually, people create new functions for each connection, which also means more memory

So, instead of using an event-based API, expects you to pass a single object with methods for each event in and it is reused for each connection.

ServerWebSocket
Bun.serve()

This leads to less memory usage and less time spent adding/removing event listeners.


The interface for is loosely based on what Cloudflare Workers does.

Bun.serve

The HTTP server and server-side websockets are based on uWebSockets.

Bun.spawn
– spawn a process

Bun.spawn
lets you quickly spawn a process. Available as of Bun v0.2.0.

import { spawn } from "bun";

const { stdout } = spawn(["esbuild"], {
  stdin: await fetch(
    "https://raw.githubusercontent.com/oven-sh/bun/main/examples/hashing.js"
  ),
});

const text = await new Response(stdout).text();
console.log(text); // "const input = "hello world".repeat(400); ..."

Bun.spawn spawns processes 60% faster than Node.js' .

child_process

bun spawn.mjs
cpu: Apple M1 Max
runtime: bun 0.2.0 (arm64-darwin)

benchmark              time (avg)             (minmax)       p75       p99      p995
--------------------------------------------------------- -----------------------------
spawnSync echo hi  888.14 µs/iter    (821.83 µs1.2 ms) 905.92 µs      1 ms   1.03 msnode spawn.node.mjs
cpu: Apple M1 Max
runtime: node v18.9.1 (arm64-darwin)

benchmark              time (avg)             (minmax)       p75       p99      p995
--------------------------------------------------------- -----------------------------
spawnSync echo hi    1.47 ms/iter     (1.14 ms2.64 ms)   1.57 ms   2.37 ms   2.52 ms

Synchronous example:

import { spawnSync } from "bun";

const { stdout } = spawnSync(["echo", "hi"]);

// When using spawnSync, stdout is a Buffer
// this lets you read from it synchronously
const text = stdout.toString();

console.log(text); // "hi\n"

You can pass an object as the second argument to customize the process:

import { spawn } from "bun";

const { stdout } = spawn(["printenv", "FOO"], {
  cwd: "/tmp",

  env: {
    ...process.env,
    FOO: "bar",
  },

  // Disable stdin
  stdin: null,

  // Allow us to read from stdout
  stdout: "pipe",

  // Point stderr to write to "/tmp/stderr.log"
  stderr: Bun.file("/tmp/stderr.log"),
});

const text = await new Response(stdout).text();
console.log(text); // "bar\n"

You can also pass a for :

Bun.file
stdin

import { spawn, file, write } from "bun";

await write("/tmp/foo.txt", "hi");
const { stdout } = spawn(["cat"], {
  // Set /tmp/foo.txt as stdin
  stdin: file("/tmp/foo.txt"),
});

const text = await new Response(stdout).text();
console.log(text); // "hi\n"

stdin
also accepts a TypedArray:

import { spawn } from "bun";

const { stdout } = spawn(["cat"], {
  stdin: new TextEncoder().encode("hi"),
  stdout: "pipe",
});

const text = await new Response(stdout).text();
console.log(text); // "hi\n"

Bun.spawn
also supports incrementally writing to stdin:

⚠️ This API is a little buggy right now

import { spawn } from "bun";

const { stdin, stdout } = spawn(["cat"], {
  stdin: "pipe",
  stdout: "pipe",
});

// You can pass it strings or TypedArrays
// Write "hi" to stdin
stdin.write("hi");

// By default, stdin is buffered so you need to call flush() to send it
stdin.flush(true);

// When you're done, call end()
stdin.end();

const text = await new Response(stdout).text();
console.log(text); // "hi\n"

Under the hood, and use

posix_spawn(3)
.
Bun.spawn
Bun.spawnSync

stdin

stdin
can be one of:

  • Bun.file()
  • null
    (no stdin)
  • ArrayBufferView
  • Response
    , with a buffered body or from . is not supported yet (TODO)
    Request
    fetch()
    ReadableStream
  • number
    (file descriptor)
  • "pipe"
    (default), which returns a for fast incremental writing
    FileSink
  • "inherit"
    which will inherit the parent's stdin

stdout and stderr

stdout
and can be one of:
stderr

  • Bun.file()
  • null
    (disable)
  • number
    (file descriptor)
  • "pipe"
    (default for ), returns a
    stdout
    ReadableStream
  • "inherit"
    (default for ) which will inherit the parent's stdout/stderr
    stderr

When to use

Bun.spawn
vs
Bun.spawnSync

There are three main differences between and .

Bun.spawn
Bun.spawnSync

  1. Bun.spawnSync
    blocks the event loop until the subprocess exits. For HTTP servers, you probably should avoid using but for CLI apps, you probably should use .
    Bun.spawnSync
    Bun.spawnSync

  2. spawnSync
    returns a different object for and so you can read the data synchronously.
    stdout
    stderr

spawn
spawnSync
ReadableStream
Buffer
  1. Bun.spawn
    supports incrementally writing to .
    stdin

If you need to read from or synchronously, you should use . Otherwise, is preferred.

stdout
stderr
Bun.spawnSync
Bun.spawn

More details

Bun.spawn
returns a object.
Subprocess

More complete types are available in

bun-types
.

interface Subprocess {
  readonly pid: number;
  readonly stdin: FileSink | undefined;
  readonly stdout: ReadableStream | number | undefined;
  readonly stderr: ReadableStream | number | undefined;

  readonly exitCode: number | undefined;

  // Wait for the process to exit
  readonly exited: Promise<number>;

  // Keep Bun's process alive until the subprocess exits
  ref(): void;

  // Don't keep Bun's process alive until the subprocess exits
  unref(): void;

  // Kill the process
  kill(code?: number): void;
  readonly killed: boolean;
}

Bun.which
– find the path to a binary

Find the path to an executable, similar to typing in your terminal.

which

const ls = Bun.which("ls");
console.log(ls); // "/usr/bin/ls"

Bun.which
defaults the to the current environment variable, but you can customize it
PATH
PATH

const ls = Bun.which("ls", {
  PATH: "/usr/local/bin:/usr/bin:/bin",
});
console.log(ls); // "/usr/bin/ls"

Bun.which
also accepts a option to search for the binary in a specific directory.
cwd

const ls = Bun.which("ls", {
  cwd: "/tmp",
  PATH: "",
});

console.log(ls); // null

Bun.listen
&
Bun.connect
- TCP/TLS sockets

Bun.listen
and is bun's native TCP & TLS socket API. Use it to implement database clients, game servers – anything that needs to communicate over TCP (instead of HTTP). This is a low-level API intended for library authors and for advanced use cases.
Bun.connect

Start a TCP server with :

Bun.listen

// The server
Bun.listen({
  hostname: "localhost",
  port: 8080,
  socket: {
    open(socket) {
      socket.write("hello world");
    },
    data(socket, data) {
      console.log(data instanceof Uint8Array); // true
    },
    drain(socket) {
      console.log("gimme more data");
    },
    close(socket) {
      console.log("goodbye!");
    },
  },
  // This is a TLS socket
  // certFile: "/path/to/cert.pem",
  // keyFile: "/path/to/key.pem",
});

Bun.connect
lets you create a TCP client:

// The client
Bun.connect({
  hostname: "localhost",
  port: 8080,

  socket: {
    open(socket) {
      socket.write("hello server, i'm the client!");
    },
    data(socket, message) {
      socket.write("thanks for the message! Sincerely, " + socket.data.name);
    },
    drain(socket) {
      console.log("my socket is ready for more data");
    },
    close(socket) {
      console.log("");
    },
    timeout(socket) {
      console.log("socket timed out");
    },
  },

  data: {
    name: "Clienty McClientface",
  },
});

Benchmark-driven API design

Bun's TCP socket API is designed to go fast.

Instead of using promises or assigning callbacks per socket instance (like Node.js' or the web-standard API), assign all callbacks one time

EventEmitter
WebSocket

This design decision was made after benchmarking. For performance-sensitive servers, promise-heavy APIs or assigning callbacks per socket instance can cause significant garbage collector pressure and increase memory usage. If you're using a TCP server API, you probably care more about performance.

Bun.listen({
  socket: {
    open(socket) {},
    data(socket, data) {},
    drain(socket) {},
    close(socket) {},
    error(socket, error) {},
  },
  hostname: "localhost",
  port: 8080,
});

Instead of having to allocate unique functions for each instance of a socket, we can use each callback once for all sockets. This is a small optimization, but it adds up.

How do you pass per-socket data to each socket object?

**data**
is a property on the & object that you can use to store per-socket data.
TCPSocket
TLSSocket

socket.data = { name: "Clienty McClientface" };

You can assign a default value to in the or options.

data
connect
listen

Bun.listen({
  socket: {
    open(socket) {
      console.log(socket.data); // { name: "Servery McServerface" }
    },
  },
  data: {
    name: "Servery McServerface",
  },
});

Hot-reloading TCP servers & clients

TCPSocket
(returned by and passed through callbacks in ) has a method that lets you reload the callbacks for all related sockets (either just the one for or all sockets for ):
Bun.connect
Bun.listen
reload
Bun.connect
Bun.listen

const socket = Bun.connect({
  hostname: "localhost",
  port: 8080,
  socket: {
    data(socket, msg) {
      console.log("wow i got a message!");

      // this will be called the next time the server sends a message
      socket.reload({
        data(socket) {
          console.log("okay, not so surprising this time");
        },
      });
    },
  },
});

No buffering

Currently, & in Bun do not buffer data. Adding support for corking (similar to ) is planned, but it means you will need to handle backpressure yourself using the callback.

TCPSocket
TLSSocket
ServerWebSocket
drain

Your TCP client/server will have abysmal performance if you don't consider buffering carefully.

For example, this:

socket.write("h");
socket.write("e");
socket.write("l");
socket.write("l");
socket.write("o");

Performs significantly worse than:

socket.write("hello");

To simplify this for now, consider using with the option:

ArrayBufferSink
{stream: true}

const sink = new ArrayBufferSink({ stream: true, highWaterMark: 1024 });

sink.write("h");
sink.write("e");
sink.write("l");
sink.write("l");
sink.write("o");

queueMicrotask(() => {
  var data = sink.flush();
  if (!socket.write(data)) {
    // put it back in the sink if the socket is full
    sink.write(data);
  }
});

Builtin buffering is planned in a future version of Bun.

Bun.peek
- read a promise without resolving it

Bun.peek
is a utility function that lets you read a promise's result without or , but only if the promise has already fulfilled or rejected.
await
.then

This function was added in Bun v0.2.2.

import { peek } from "bun";

const promise = Promise.resolve("hi");

// no await!
const result = peek(promise);

console.log(result); // "hi"

Bun.peek
is useful for performance-sensitive code that wants to reduce the number of extra microticks. It's an advanced API and you probably shouldn't use it unless you know what you're doing.

import { peek } from "bun";
import { expect, test } from "bun:test";

test("peek", () => {
  const promise = Promise.resolve(true);

  // no await necessary!
  expect(peek(promise)).toBe(true);

  // if we peek again, it returns the same value
  const again = peek(promise);
  expect(again).toBe(true);

  // if we peek a non-promise, it returns the value
  const value = peek(42);
  expect(value).toBe(42);

  // if we peek a pending promise, it returns the promise again
  const pending = new Promise(() => {});
  expect(peek(pending)).toBe(pending);

  // If we peek a rejected promise, it:
  // - returns the error
  // - does not mark the promise as handled
  const rejected = Promise.reject(
    new Error("Succesfully tested promise rejection")
  );
  expect(peek(rejected).message).toBe("Succesfully tested promise rejection");
});

peek.status
lets you read the status of a promise without resolving it.

import { peek } from "bun";
import { expect, test } from "bun:test";

test("peek.status", () => {
  const promise = Promise.resolve(true);
  expect(peek.status(promise)).toBe("fulfilled");

  const pending = new Promise(() => {});
  expect(peek.status(pending)).toBe("pending");

  const rejected = Promise.reject(new Error("oh nooo"));
  expect(peek.status(rejected)).toBe("rejected");
});

Bun.write
– optimizing I/O

Bun.write
lets you write, copy or pipe files automatically using the fastest system calls compatible with the input and platform.

interface Bun {
  write(
    destination: string | number | FileBlob,
    input: string | FileBlob | Blob | ArrayBufferView
  ): Promise<number>;
}
Output Input System Call Platform
file file copy_file_range Linux
file pipe sendfile Linux
pipe pipe splice Linux
terminal file sendfile Linux
terminal terminal sendfile Linux
socket file or pipe sendfile (if http, not https) Linux
file (path, doesn't exist) file (path) clonefile macOS
file file fcopyfile macOS
file Blob or string write macOS
file Blob or string write Linux

All this complexity is handled by a single function.

// Write "Hello World" to output.txt
await Bun.write("output.txt", "Hello World");
// log a file to stdout
await Bun.write(Bun.stdout, Bun.file("input.txt"));
// write the HTTP response body to disk
await Bun.write("index.html", await fetch("http://example.com"));
// this does the same thing
await Bun.write(Bun.file("index.html"), await fetch("http://example.com"));
// copy input.txt to output.txt
await Bun.write("output.txt", Bun.file("input.txt"));

bun:sqlite (SQLite3 module)

bun:sqlite
is a high-performance built-in SQLite3 module for bun.js.

  • Simple, synchronous API (synchronous is faster)
  • Transactions
  • Binding named & positional parameters
  • Prepared statements
  • Automatic type conversions ( becomes
    BLOB
    Uint8Array
    )
  • toString() prints as SQL

Installation:

# there's nothing to install
# bun:sqlite is built-in to bun.js

Example:

import { Database } from "bun:sqlite";

const db = new Database("mydb.sqlite");
db.run(
  "CREATE TABLE IF NOT EXISTS foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)"
);
db.run("INSERT INTO foo (greeting) VALUES (?)", "Welcome to bun!");
db.run("INSERT INTO foo (greeting) VALUES (?)", "Hello World!");

// get the first row
db.query("SELECT * FROM foo").get();
// { id: 1, greeting: "Welcome to bun!" }

// get all rows
db.query("SELECT * FROM foo").all();
// [
//   { id: 1, greeting: "Welcome to bun!" },
//   { id: 2, greeting: "Hello World!" },
// ]

// get all rows matching a condition
db.query("SELECT * FROM foo WHERE greeting = ?").all("Welcome to bun!");
// [
//   { id: 1, greeting: "Welcome to bun!" },
// ]

// get first row matching a named condition
db.query("SELECT * FROM foo WHERE greeting = $greeting").get({
  $greeting: "Welcome to bun!",
});
// [
//   { id: 1, greeting: "Welcome to bun!" },
// ]

bun:sqlite Benchmark

Database: Northwind Traders.

This benchmark can be run from ./bench/sqlite.

Here are results from an M1 Pro (64GB) on macOS 12.3.1.

SELECT * FROM "Order"

Library Runtime ms/iter
bun:sqlite3 Bun 0.0.83 14.31 (1x)
better-sqlite3 Node 18.0.0 40.81 (2.8x slower)
deno.land/x/sqlite Deno 1.21.2 125.96 (8.9x slower)

SELECT * FROM "Product"

Library Runtime us/iter
bun:sqlite3 Bun 0.0.83 33.85 (1x)
better-sqlite3 Node 18.0.0 121.09 (3.5x slower)
deno.land/x/sqlite Deno 1.21.2 187.64 (8.9x slower)

SELECT * FROM "OrderDetail"

Library Runtime ms/iter
bun:sqlite3 Bun 0.0.83 146.92 (1x)
better-sqlite3 Node 18.0.0 875.73 (5.9x slower)
deno.land/x/sqlite Deno 1.21.2 541.15 (3.6x slower)

In screenshot form (which has a different sorting order)

image

Getting started with bun:sqlite

bun:sqlite's API is loosely based on better-sqlite3, though the implementation is different.

bun:sqlite has two classes:

  • class Database
  • class Statement

Database

Calling opens or creates the SQLite database.

new Database(filename)

constructor(
      filename: string,
      options?:
        | number
        | {
            /**
             * Open the database as read-only (no write operations, no create).
             *
             * Equivalent to {@link constants.SQLITE_OPEN_READONLY}
             */
            readonly?: boolean;
            /**
             * Allow creating a new database
             *
             * Equivalent to {@link constants.SQLITE_OPEN_CREATE}
             */
            create?: boolean;
            /**
             * Open the database as read-write
             *
             * Equivalent to {@link constants.SQLITE_OPEN_READWRITE}
             */
            readwrite?: boolean;
          }
    );

To open or create a SQLite3 database:

import { Database } from "bun:sqlite";

const db = new Database("mydb.sqlite");

Open an in-memory database:

import { Database } from "bun:sqlite";

// all of these do the same thing
let db = new Database(":memory:");
let db = new Database();
let db = new Database("");

Open read-write and throw if the database doesn't exist:

import { Database } from "bun:sqlite";
const db = new Database("mydb.sqlite", { readwrite: true });

Open read-only and throw if the database doesn't exist:

import { Database } from "bun:sqlite";
const db = new Database("mydb.sqlite", { readonly: true });

Open read-write, don't throw if new file:

import { Database } from "bun:sqlite";
const db = new Database("mydb.sqlite", { readonly: true, create: true });

Open a database from a :

Uint8Array

import { Database } from "bun:sqlite";
import { readFileSync } from "fs";

// unlike passing a filepath, this will not persist any changes to disk
// it will be read-write but not persistent
const db = new Database(readFileSync("mydb.sqlite"));

Close a database:

let db = new Database();
db.close();

Note: is called automatically when the database is garbage collected. It is safe to call multiple times but has no effect after the first.

close()

Database.prototype.query

query(sql)
creates a for the given SQL and caches it, but does not execute it.
Statement

class Database {
  query(sql: string): Statement;
}

query
returns a object.
Statement

It performs the same operation as , except:

Database.prototype.prepare

  • query
    caches the prepared statement in the object
    Database
  • query
    doesn't bind parameters

This intended to make it easier for to be fast by default. Calling compiles a SQLite query, which can take some time, so it's better to cache those a little.

bun:sqlite
.prepare

You can bind parameters on any call to a statement.

import { Database } from "bun:sqlite";

// generate some data
let db = new Database();
db.run(
  "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)"
);
db.run("INSERT INTO foo (greeting) VALUES ($greeting)", {
  $greeting: "Welcome to bun",
});

// get the query
const stmt = db.query("SELECT * FROM foo WHERE greeting = ?");

// run the query
stmt.all("Welcome to bun!");
stmt.get("Welcome to bun!");
stmt.run("Welcome to bun!");

Database.prototype.prepare

prepare(sql)
creates a for the given SQL, but does not execute it.
Statement

Unlike , this does not cache the compiled query.

query()

import { Database } from "bun:sqlite";

// generate some data
let db = new Database();
db.run(
  "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)"
);

// compile the prepared statement
const stmt = db.prepare("SELECT * FROM foo WHERE bar = ?");

// run the prepared statement
stmt.all("baz");

Internally, this calls

sqlite3_prepare_v3
.

Database.prototype.exec & Database.prototype.run

exec
is for one-off executing a query which does not need to return anything. is an alias.
run

class Database {
  // exec is an alias for run
  exec(sql: string, ...params: ParamsType): void;
  run(sql: string, ...params: ParamsType): void;
}

This is useful for things like

Creating a table:

import { Database } from "bun:sqlite";

let db = new Database();
db.exec(
  "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)"
);

Inserting one row:

import { Database } from "bun:sqlite";

let db = new Database();
db.exec(
  "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)"
);

// insert one row
db.exec("INSERT INTO foo (greeting) VALUES ($greeting)", {
  $greeting: "Welcome to bun",
});

For queries which aren't intended to be run multiple times, it should be faster to use than or because it doesn't create a object.

exec()
prepare()
query()
Statement

Internally, this function calls

sqlite3_prepare
,
sqlite3_step
, and
sqlite3_finalize
.

Database.prototype.transaction

Creates a function that always runs inside a transaction. When the function is invoked, it will begin a new transaction. When the function returns, the transaction will be committed. If an exception is thrown, the transaction will be rolled back (and the exception will propagate as usual).

// setup
import { Database } from "bun:sqlite";
const db = Database.open(":memory:");
db.exec(
  "CREATE TABLE cats (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, age INTEGER)"
);

const insert = db.prepare("INSERT INTO cats (name, age) VALUES ($name, $age)");
const insertMany = db.transaction((cats) => {
  for (const cat of cats) insert.run(cat);
});

insertMany([
  { $name: "Joey", $age: 2 },
  { $name: "Sally", $age: 4 },
  { $name: "Junior", $age: 1 },
]);

Transaction functions can be called from inside other transaction functions. When doing so, the inner transaction becomes a savepoint.

// setup
import { Database } from "bun:sqlite";
const db = Database.open(":memory:");
db.exec(
  "CREATE TABLE expenses (id INTEGER PRIMARY KEY AUTOINCREMENT, note TEXT, dollars INTEGER);"
);
db.exec(
  "CREATE TABLE cats (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, age INTEGER)"
);
const newExpense = db.prepare(
  "INSERT INTO expenses (note, dollars) VALUES (?, ?)"
);
const insert = db.prepare("INSERT INTO cats (name, age) VALUES ($name, $age)");
const insertMany = db.transaction((cats) => {
  for (const cat of cats) insert.run(cat);
});

const adopt = db.transaction((cats) => {
  newExpense.run("adoption fees", 20);
  insertMany(cats); // nested transaction
});

adopt([
  { $name: "Joey", $age: 2 },
  { $name: "Sally", $age: 4 },
  { $name: "Junior", $age: 1 },
]);

Transactions also come with , , and versions.

deferred
immediate
exclusive

insertMany(cats); // uses "BEGIN"
insertMany.deferred(cats); // uses "BEGIN DEFERRED"
insertMany.immediate(cats); // uses "BEGIN IMMEDIATE"
insertMany.exclusive(cats); // uses "BEGIN EXCLUSIVE"

Any arguments passed to the transaction function will be forwarded to the wrapped function, and any values returned from the wrapped function will be returned from the transaction function. The wrapped function will also have access to the same binding as the transaction function.

bun:sqlite's transaction implementation is based on better-sqlite3 (along with this section of the docs), so thanks to Joshua Wise and better-sqlite3 contributors.

Database.prototype.serialize

SQLite has a built-in way to serialize and deserialize databases to and from memory.

bun:sqlite
fully supports it:

let db = new Database();

// write some data
db.run(
  "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)"
);
db.run("INSERT INTO foo VALUES (?)", "Welcome to bun!");
db.run("INSERT INTO foo VALUES (?)", "Hello World!");

const copy = db.serialize();
// => Uint8Array

const db2 = new Database(copy);
db2.query("SELECT * FROM foo").all();
// => [
//   { id: 1, greeting: "Welcome to bun!" },
//   { id: 2, greeting: "Hello World!" },
// ]

db.serialize()
returns a of the database.
Uint8Array

Internally, it calls

sqlite3_serialize
.

Database.prototype.loadExtension

bun:sqlite
supports SQLite extensions.

To load a SQLite extension, call :

Database.prototype.loadExtension(name)

import { Database } from "bun:sqlite";

let db = new Database();

db.loadExtension("myext");

If you're on macOS, you will need to first use a custom SQLite install (you can install with homebrew). By default, bun uses Apple's proprietary build of SQLite because it benchmarks about 50% faster. However, they disabled extension support, so you will need to have a custom build of SQLite to use extensions on macOS.

import { Database } from "bun:sqlite";

// on macOS, this must be run before any other calls to `Database`
// if called on linux, it will return true and do nothing
// on linux it will still check that a string was passed
Database.setCustomSQLite("/path/to/sqlite.dylib");

let db = new Database();

db.loadExtension("myext");

To install sqlite with homebrew:

brew install sqlite

Statement

Statement
is a prepared statement. Use it to run queries that get results.

TLDR:

You can bind parameters on any call to a statement. Named parameters and positional parameters are supported. Bound parameters are remembered between calls and reset the next time you pass parameters to bind.

import { Database } from "bun:sqlite";

// setup
let db = new Database();
db.run(
  "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT)"
);
db.run("INSERT INTO foo VALUES (?)", "Welcome to bun!");
db.run("INSERT INTO foo VALUES (?)", "Hello World!");

// Statement object
let statement = db.query("SELECT * FROM foo");

// returns all the rows
statement.all();

// returns the first row
statement.get();

// runs the query, without returning anything
statement.run();

Statement.all

Calling on a instance runs the query and returns the rows as an array of objects.

all()
Statement

import { Database } from "bun:sqlite";

// setup
let db = new Database();
db.run(
  "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)"
);
db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Welcome to bun!", 2);
db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Hello World!", 0);
db.run(
  "INSERT INTO foo (greeting, count) VALUES (?, ?)",
  "Welcome to bun!!!!",
  2
);

// Statement object
let statement = db.query("SELECT * FROM foo WHERE count = ?");

// return all the query results, binding 2 to the count parameter
statement.all(2);
// => [
//   { id: 1, greeting: "Welcome to bun!", count: 2 },
//   { id: 3, greeting: "Welcome to bun!!!!", count: 2 },
// ]

Internally, this calls

sqlite3_reset
and repeatedly calls
sqlite3_step
until it returns .
SQLITE_DONE

Statement.values

Calling on a instance runs the query and returns the rows as an array of arrays.

values()
Statement

import { Database } from "bun:sqlite";

// setup
let db = new Database();
db.run(
  "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)"
);
db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Welcome to bun!", 2);
db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Hello World!", 0);
db.run(
  "INSERT INTO foo (greeting, count) VALUES (?, ?)",
  "Welcome to bun!!!!",
  2
);

// Statement object
let statement = db.query("SELECT * FROM foo WHERE count = ?");

// return all the query results as an array of arrays, binding 2 to "count"
statement.values(2);
// => [
//   [ 1, "Welcome to bun!", 2 ],
//   [ 3, "Welcome to bun!!!!", 2 ],
// ]

// Statement object, but with named parameters
let statement = db.query("SELECT * FROM foo WHERE count = $count");

// return all the query results as an array of arrays, binding 2 to "count"
statement.values({ $count: 2 });
// => [
//   [ 1, "Welcome to bun!", 2 ],
//   [ 3, "Welcome to bun!!!!", 2 ],
// ]

Internally, this calls

sqlite3_reset
and repeatedly calls
sqlite3_step
until it returns .
SQLITE_DONE

Statement.get

Calling on a instance runs the query and returns the first result as an object.

get()
Statement

import { Database } from "bun:sqlite";

// setup
let db = new Database();
db.run(
  "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)"
);
db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Welcome to bun!", 2);
db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Hello World!", 0);
db.run(
  "INSERT INTO foo (greeting, count) VALUES (?, ?)",
  "Welcome to bun!!!!",
  2
);

// Statement object
let statement = db.query("SELECT * FROM foo WHERE count = ?");

// return the first row as an object, binding 2 to the count parameter
statement.get(2);
// => { id: 1, greeting: "Welcome to bun!", count: 2 }

// Statement object, but with named parameters
let statement = db.query("SELECT * FROM foo WHERE count = $count");

// return the first row as an object, binding 2 to the count parameter
statement.get({ $count: 2 });
// => { id: 1, greeting: "Welcome to bun!", count: 2 }

Internally, this calls

sqlite3_reset
and calls
sqlite3_step
once. Stepping through all the rows is not necessary when you only want the first row.

Statement.run

Calling on a instance runs the query and returns nothing.

run()
Statement

This is useful if you want to repeatedly run a query, but don't care about the results.

import { Database } from "bun:sqlite";

// setup
let db = new Database();
db.run(
  "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)"
);
db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Welcome to bun!", 2);
db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Hello World!", 0);
db.run(
  "INSERT INTO foo (greeting, count) VALUES (?, ?)",
  "Welcome to bun!!!!",
  2
);

// Statement object (TODO: use a better example query)
let statement = db.query("SELECT * FROM foo");

// run the query, returning nothing
statement.run();

Internally, this calls

sqlite3_reset
and calls
sqlite3_step
once. Stepping through all the rows is not necessary when you don't care about the results.

Statement.finalize

This method finalizes the statement, freeing any resources associated with it.

After a statement has been finalized, it cannot be used for any further queries. Any attempt to run the statement will throw an error. Calling it multiple times will have no effect.

It is a good idea to finalize a statement when you are done with it, but the garbage collector will do it for you if you don't.

import { Database } from "bun:sqlite";

// setup
let db = new Database();
db.run(
  "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)"
);
db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Welcome to bun!", 2);
db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Hello World!", 0);
db.run(
  "INSERT INTO foo (greeting, count) VALUES (?, ?)",
  "Welcome to bun!!!!",
  2
);

// Statement object
let statement = db.query("SELECT * FROM foo WHERE count = ?");

statement.finalize();

// this will throw
statement.run();

Statement.toString()

Calling on a instance prints the expanded SQL query. This is useful for debugging.

toString()
Statement

import { Database } from "bun:sqlite";

// setup
let db = new Database();
db.run(
  "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, greeting TEXT, count INTEGER)"
);
db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Welcome to bun!", 2);
db.run("INSERT INTO foo (greeting, count) VALUES (?, ?)", "Hello World!", 0);
db.run(
  "INSERT INTO foo (greeting, count) VALUES (?, ?)",
  "Welcome to bun!!!!",
  2
);

// Statement object
const statement = db.query("SELECT * FROM foo WHERE count = ?");

console.log(statement.toString());
// => "SELECT * FROM foo WHERE count = NULL"

statement.run(2); // bind the param

console.log(statement.toString());
// => "SELECT * FROM foo WHERE count = 2"

Internally, this calls

sqlite3_expanded_sql
.

Datatypes

JavaScript type SQLite type
string
TEXT
number
INTEGER
or
DECIMAL
boolean
INTEGER
(1 or 0)
Uint8Array
BLOB
Buffer
BLOB
bigint
INTEGER
null
NULL

bun:ffi
(Foreign Functions Interface)

bun:ffi
lets you efficiently call native libraries from JavaScript. It works with languages that support the C ABI (Zig, Rust, C/C++, C#, Nim, Kotlin, etc).

This snippet prints sqlite3's version number:

import { dlopen, FFIType, suffix } from "bun:ffi";

// `suffix` is either "dylib", "so", or "dll" depending on the platform
// you don't have to use "suffix", it's just there for convenience
const path = `libsqlite3.${suffix}`;

const {
  symbols: {
    // sqlite3_libversion is the function we will call
    sqlite3_libversion,
  },
} =
  // dlopen() expects:
  // 1. a library name or file path
  // 2. a map of symbols
  dlopen(path, {
    // `sqlite3_libversion` is a function that returns a string
    sqlite3_libversion: {
      // sqlite3_libversion takes no arguments
      args: [],
      // sqlite3_libversion returns a pointer to a string
      returns: FFIType.cstring,
    },
  });

console.log(`SQLite 3 version: ${sqlite3_libversion()}`);

Low-overhead FFI

3ns to go from JavaScript <> native code with (on my machine, an M1 Pro with 64GB of RAM)

bun:ffi

  • 5x faster than napi (Node v17.7.1)
  • 100x faster than Deno v1.21.1

As measured in this simple benchmark

Why is bun:ffi fast?

Bun generates & just-in-time compiles C bindings that efficiently convert values between JavaScript types and native types.

To compile C, Bun embeds TinyCC, a small and fast C compiler.

Usage

With Zig:

// add.zig
pub export fn add(a: i32, b: i32) i32 {
  return a + b;
}

To compile:

zig build-lib add.zig -dynamic -OReleaseFast

Pass the path to the shared library and the list of symbols you want to import.

dlopen

import { dlopen, FFIType, suffix } from "bun:ffi";

const path = `libadd.${suffix}`;

const lib = dlopen(path, {
  add: {
    args: [FFIType.i32, FFIType.i32],
    returns: FFIType.i32,
  },
});

lib.symbols.add(1, 2);

With Rust:

// add.rs
#[no_mangle]
pub extern "C" fn add(a: isize, b: isize) -> isize {
    a + b
}

To compile:

rustc --crate-type cdylib add.rs

Supported FFI types (
FFIType
)

FFIType
C Type Aliases
cstring
char*
ptr
void*
pointer
, ,
void*
char*
i8
int8_t
int8_t
i16
int16_t
int16_t
i32
int32_t
int32_t
,
int
i64
int64_t
int64_t
i64_fast
int64_t
u8
uint8_t
uint8_t
u16
uint16_t
uint16_t
u32
uint32_t
uint32_t
u64
uint64_t
uint64_t
u64_fast
uint64_t
f32
float
float
f64
double
double
bool
bool
char
char

Strings (
CString
)

JavaScript strings and C-like strings are different, and that complicates using strings with native libraries.

How are JavaScript strings and C strings different?

JavaScript strings:

  • UTF16 (2 bytes per letter) or potentially latin1, depending on the JavaScript engine & what characters are used
  • length
    stored separately
  • Immutable

C strings:

  • UTF8 (1 byte per letter), usually
  • The length is not stored. Instead, the string is null-terminated which means the length is the index of the first it finds
    \0
  • Mutable

To help with that, exports which extends JavaScript's built-in to support null-terminated strings and add a few extras:

bun:ffi
CString
String

class CString extends String {
  /**
   * Given a `ptr`, this will automatically search for the closing `\0` character and transcode from UTF-8 to UTF-16 if necessary.
   */
  constructor(ptr: number, byteOffset?: number, byteLength?: number): string;

  /**
   * The ptr to the C string
   *
   * This `CString` instance is a clone of the string, so it
   * is safe to continue using this instance after the `ptr` has been
   * freed.
   */
  ptr: number;
  byteOffset?: number;
  byteLength?: number;
}

To convert from a null-terminated string pointer to a JavaScript string:

const myString = new CString(ptr);

To convert from a pointer with a known length to a JavaScript string:

const myString = new CString(ptr, 0, byteLength);

new CString
clones the C string, so it is safe to continue using after has been freed.
myString
ptr

my_library_free(myString.ptr);

// this is safe because myString is a clone
console.log(myString);
Returning a string

When used in , coerces the pointer to a JavaScript . When used in , is identical to .

returns
FFIType.cstring
string
args
cstring
ptr

Function pointers (
CFunction
)

To call a function pointer from JavaScript, use

CFunction

This is useful if using Node-API (napi) with Bun, and you've already loaded some symbols.

import { CFunction } from "bun:ffi";

let myNativeLibraryGetVersion = /* somehow, you got this pointer */

const getVersion = new CFunction({
  returns: "cstring",
  args: [],
  ptr: myNativeLibraryGetVersion,
});
getVersion();

If you have multiple function pointers, you can define them all at once with :

linkSymbols

import { linkSymbols } from "bun:ffi";

// getVersionPtrs defined elsewhere
const [majorPtr, minorPtr, patchPtr] = getVersionPtrs();

const lib = linkSymbols({
  // Unlike with dlopen(), the names here can be whatever you want
  getMajor: {
    returns: "cstring",
    args: [],

    // Since this doesn't use dlsym(), you have to provide a valid ptr
    // That ptr could be a number or a bigint
    // An invalid pointer will crash your program.
    ptr: majorPtr,
  },
  getMinor: {
    returns: "cstring",
    args: [],
    ptr: minorPtr,
  },
  getPatch: {
    returns: "cstring",
    args: [],
    ptr: patchPtr,
  },
});

const [major, minor, patch] = [
  lib.symbols.getMajor(),
  lib.symbols.getMinor(),
  lib.symbols.getPatch(),
];

Pointers

Bun represents pointers as a in JavaScript.

number

How does a 64 bit pointer fit in a JavaScript number?

64-bit processors support up to 52 bits of addressable space.

JavaScript numbers support 53 bits of usable space, so that leaves us with about 11 bits of extra space.

Why not ?

BigInt

BigInt
is slower. JavaScript engines allocate a separate which means they can't just fit in a regular javascript value.
BigInt

If you pass a to a function, it will be converted to a

BigInt
number

To convert from a TypedArray to a pointer:

import { ptr } from "bun:ffi";
let myTypedArray = new Uint8Array(32);
const myPtr = ptr(myTypedArray);

To convert from a pointer to an ArrayBuffer:

import { ptr, toArrayBuffer } from "bun:ffi";
let myTypedArray = new Uint8Array(32);
const myPtr = ptr(myTypedArray);

// toArrayBuffer accepts a `byteOffset` and `byteLength`
// if `byteLength` is not provided, it is assumed to be a null-terminated pointer
myTypedArray = new Uint8Array(toArrayBuffer(myPtr, 0, 32), 0, 32);

To read data from a pointer

You have two options.

For long-lived pointers, a is the fastest option:

DataView

import { toArrayBuffer } from "bun:ffi";
let myDataView = new DataView(toArrayBuffer(myPtr, 0, 32));

console.log(
  myDataView.getUint8(0, true),
  myDataView.getUint8(1, true),
  myDataView.getUint8(2, true),
  myDataView.getUint8(3, true)
);

For short-lived pointers, is the fastest option:

read

Available in Bun v0.1.12+

import { read } from "bun:ffi";

console.log(
  // ptr, byteOffset
  read.u8(myPtr, 0),
  read.u8(myPtr, 1),
  read.u8(myPtr, 2),
  read.u8(myPtr, 3)
);

read
behaves similarly to , but it can be faster because it doesn't need to create a or .
DataView
DataView
ArrayBuffer

FFIType
read
function
ptr
read.ptr
i8
read.i8
i16
read.i16
i32
read.i32
i64
read.i64
u8
read.u8
u16
read.u16
u32
read.u32
u64
read.u64
f32
read.f32
f64
read.f64

Memory management with pointers:

bun:ffi
does not manage memory for you because it doesn't have the information necessary. You must free the memory when you're done with it.

From JavaScript:

If you want to track when a TypedArray is no longer in use from JavaScript, you can use a FinalizationRegistry.

From FFI (C, Rust, Zig, etc):

Available in Bun v0.1.8 and later.

If you want to track when a TypedArray is no longer in use from C or FFI, you can pass a callback and an optional context pointer to or . This function is called at some point later, once the garbage collector frees the underlying JavaScript object.

toArrayBuffer
toBuffer
ArrayBuffer

The expected signature is the same as in JavaScriptCore's C API:

typedef void (*JSTypedArrayBytesDeallocator)(void *bytes, void *deallocatorContext);
import { toArrayBuffer } from "bun:ffi";

// with a deallocatorContext:
toArrayBuffer(
  bytes,
  byteOffset,

  byteLength,

  // this is an optional pointer to a callback
  deallocatorContext,

  // this is a pointer to a function
  jsTypedArrayBytesDeallocator
);

// without a deallocatorContext:
toArrayBuffer(
  bytes,
  byteOffset,

  byteLength,

  // this is a pointer to a function
  jsTypedArrayBytesDeallocator
);

Pointers & memory safety

Using raw pointers outside of FFI is extremely not recommended.

A future version of bun may add a CLI flag to disable (or potentially a separate build of bun).

bun:ffi

Pointer alignment

If an API expects a pointer sized to something other than or , make sure the typed array is also that size.

char
u8

A is not exactly the same as due to alignment

u64*
[8]u8*

Passing a pointer

Where FFI functions expect a pointer, pass a TypedArray of equivalent size

Easymode:

import { dlopen, FFIType } from "bun:ffi";

const {
  symbols: { encode_png },
} = dlopen(myLibraryPath, {
  encode_png: {
    // FFIType's can be specified as strings too
    args: ["ptr", "u32", "u32"],
    returns: FFIType.ptr,
  },
});

const pixels = new Uint8ClampedArray(128 * 128 * 4);
pixels.fill(254);
pixels.subarray(0, 32 * 32 * 2).fill(0);

const out = encode_png(
  // pixels will be passed as a pointer
  pixels,

  128,
  128
);

The auto-generated wrapper converts the pointer to a TypedArray

Hardmode

If you don't want the automatic conversion or you want a pointer to a specific byte offset within the TypedArray, you can also directly get the pointer to the TypedArray:

import { dlopen, FFIType, ptr } from "bun:ffi";

const {
  symbols: { encode_png },
} = dlopen(myLibraryPath, {
  encode_png: {
    // FFIType's can be specified as strings too
    args: ["ptr", "u32", "u32"],
    returns: FFIType.ptr,
  },
});

const pixels = new Uint8ClampedArray(128 * 128 * 4);
pixels.fill(254);

// this returns a number! not a BigInt!
const myPtr = ptr(pixels);

const out = encode_png(
  myPtr,

  // dimensions:
  128,
  128
);
Reading pointers
const out = encode_png(
  // pixels will be passed as a pointer
  pixels,

  // dimensions:
  128,
  128
);

// assuming it is 0-terminated, it can be read like this:
let png = new Uint8Array(toArrayBuffer(out));

// save it to disk:
await Bun.write("out.png", png);
Not implemented yet

bun:ffi
has a few more things planned but not implemented yet:

  • callback functions
  • async functions

Node-API (napi)

Bun.js implements 90% of the APIs available in Node-API (napi).

You can see the status of this here.

Loading Node-API modules in Bun.js works the same as in Node.js:

const napi = require("./my-node-module.node");

You can also use :

process.dlopen

let mod = { exports: {} };
process.dlopen(mod, "./my-node-module.node");

As part of that work, Bun.js also polyfills the

detect-libc
package, which is used by many Node-API modules to detect which binding to .
.node
require

This implementation of Node-API is from scratch. It doesn't use any code from Node.js.

Some implementation details

When requiring a module, Bun's JavaScript transpiler transforms the expression into a call to :

*.node
require
import.meta.require

// this is the input
require("./my-node-module.node");

// this is the output
import.meta.require("./my-node-module.node");

Bun doesn't currently support dynamic requires, but is an escape hatch for that. It uses a JavaScriptCore built-in function.

import.meta.require

Bun.Transpiler

Bun.Transpiler
lets you use Bun's transpiler from JavaScript (available in Bun.js)

type Loader = "jsx" | "js" | "ts" | "tsx";

interface TranspilerOptions {
  // Replace key with value. Value must be a JSON string.
  // @example
  // ```
  // { "process.env.NODE_ENV": "\"production\"" }
  // ```
  define: Record<string, string>,

  // What is the default loader used for this transpiler?
  loader: Loader,

  // What platform are we targeting? This may affect how import and/or require is used
  platform: "browser" | "bun" | "macro" | "node",

  // TSConfig.json file as stringified JSON or an object
  // Use this to set a custom JSX factory, fragment, or import source
  // For example, if you want to use Preact instead of React. Or if you want to use Emotion.
  tsconfig: string | TSConfig,

  // Replace imports with macros
  macros: MacroMap,
}

// This lets you use macros
interface MacroMap {
  // @example
  // ```
  // {
  //   "react-relay": {
  //     "graphql": "bun-macro-relay/bun-macro-relay.tsx"
  //   }
  // }
  // ```
  [packagePath: string]: {
    [importItemName: string]: string,
  },
}

class Bun.Transpiler {
  constructor(options: TranspilerOptions)

  transform(code: string, loader?: Loader): Promise<string>
  transformSync(code: string, loader?: Loader): string

  scan(code: string): {exports: string[], imports: Import}
  scanImports(code: string): Import[]
}

type Import = {
  path: string,
  kind:
  // import foo from 'bar'; in JavaScript
  | "import-statement"
  // require("foo") in JavaScript
  | "require-call"
  // require.resolve("foo") in JavaScript
  | "require-resolve"
  // Dynamic import() in JavaScript
  | "dynamic-import"
  // @import() in CSS
  | "import-rule"
  // url() in CSS
  | "url-token"
  // The import was injected by Bun
  | "internal"
  // Entry point
  // Probably won't see this one
  | "entry-point"
}

const transpiler = new Bun.Transpiler({ loader: "jsx" });

Bun.Transpiler.transformSync

This lets you transpile JavaScript, TypeScript, TSX, and JSX using Bun's transpiler. It does not resolve modules.

It is synchronous and runs in the same thread as other JavaScript code.

const transpiler = new Bun.Transpiler({ loader: "jsx" });
transpiler.transformSync("<div>hi!</div>");
import { __require as require } from "bun:wrap";
import * as JSX from "react/jsx-dev-runtime";
var jsx = require(JSX).jsxDEV;

export default jsx(
  "div",
  {
    children: "hi!",
  },
  undefined,
  false,
  undefined,
  this
);

If a macro is used, it will be run in the same thread as the transpiler, but in a separate event loop from the rest of your application. Currently, globals between macros and regular code are shared, which means it is possible (but not recommended) to share states between macros and regular code. Attempting to use AST nodes outside of a macro is undefined behavior.

Bun.Transpiler.transform

This lets you transpile JavaScript, TypeScript, TSX, and JSX using Bun's transpiler. It does not resolve modules.

It is async and automatically runs in Bun's worker threadpool. That means, if you run it 100 times, it will run it across threads without blocking the main JavaScript thread.

Math.floor($cpu_count * 0.8)

If code uses a macro, it will potentially spawn a new copy of Bun.js' JavaScript runtime environment in that new thread.

Unless you're transpiling many large files, you should probably use . The cost of the threadpool will often take longer than actually transpiling code.

Bun.Transpiler.transformSync

const transpiler = new Bun.Transpiler({ loader: "jsx" });
await transpiler.transform("<div>hi!</div>");
import { __require as require } from "bun:wrap";
import * as JSX from "react/jsx-dev-runtime";
var jsx = require(JSX).jsxDEV;

export default jsx(
  "div",
  {
    children: "hi!",
  },
  undefined,
  false,
  undefined,
  this
);

You can also pass a as a string

Loader

await transpiler.transform("<div>hi!</div>", "tsx");

Bun.Transpiler.scan

This is a fast way to get a list of imports & exports used in a JavaScript/jsx or TypeScript/tsx file.

This function is synchronous.

const transpiler = new Bun.Transpiler({ loader: "ts" });

transpiler.scan(`
import React from 'react';
import Remix from 'remix';
import type {ReactNode} from 'react';

export const loader = () => import('./loader');
`);
{
  "exports": [
    "loader"
  ],
  "imports": [
    {
      "kind": "import-statement",
      "path": "react"
    },
    {
      "kind": "import-statement",
      "path": "remix"
    },
    {
      "kind": "dynamic-import",
      "path": "./loader"
    }
  ]
}

Bun.Transpiler.scanImports

This is a fast path for getting a list of imports used in a JavaScript/jsx or TypeScript/tsx file. It skips the visiting pass, which means it is faster but less accurate. You probably won't notice a difference between and often. You might notice it for very large files (megabytes).

Bun.Transpiler.scan
Bun.Transpiler.scanImports

This function is synchronous.

const transpiler = new Bun.Transpiler({ loader: "ts" });

transpiler.scanImports(`
import React from 'react';
import Remix from 'remix';
import type {ReactNode} from 'react';

export const loader = () => import('./loader');
`);
[
  {
    "kind": "import-statement",
    "path": "react"
  },
  {
    "kind": "import-statement",
    "path": "remix"
  },
  {
    "kind": "dynamic-import",
    "path": "./loader"
  }
]

Environment variables

  • GOMAXPROCS
    : For , this sets the maximum number of threads to use. If you’re experiencing an issue with , try setting to force bun to run single-threaded
    bun bun
    bun bun
    GOMAXPROCS=1
  • DISABLE_BUN_ANALYTICS=1
    this disables bun’s analytics. bun records bundle timings (so we can answer with data, "is bun getting faster?") and feature usage (e.g., "are people actually using macros?"). The request body size is about 60 bytes, so it’s not a lot of data
  • TMPDIR
    : Before completes, it stores the new in . If unset, defaults to the platform-specific temporary directory (on Linux, and on macOS
    bun bun
    .bun
    $TMPDIR
    TMPDIR
    /tmp
    /private/tmp
    )

Credits

  • While written in Zig instead of Go, bun’s JS transpiler, CSS lexer, and node module resolver source code is based on @evanw’s esbuild project. Evan did a fantastic job with esbuild.
  • The idea for the name "bun" came from @kipply

License

bun itself is MIT-licensed.

However, JavaScriptCore (and WebKit) is LGPL-2 and bun statically links it. WebCore files from WebKit are also licensed under LGPL2.

Per LGPL2:

(1) If you statically link against an LGPL’d library, you must also provide your application in an object (not necessarily source) format, so that a user has the opportunity to modify the library and relink the application.

You can find the patched version of WebKit used by bun here: https://github.com/oven-sh/webkit. If you would like to relink bun with changes:

  • git submodule update --init --recursive
  • make jsc
  • zig build

This compiles JavaScriptCore, compiles bun’s bindings for JavaScriptCore (which are the object files using JavaScriptCore) and outputs a new binary with your changes.

.cpp
bun

bun also statically links these libraries:

For compatibility reasons, these NPM packages are embedded into bun’s binary and injected if imported.

Developing bun

Some links you should read about JavaScriptCore, the JavaScript engine Bun uses:

To get your development environment configured, expect it to take 30-90 minutes :(

VSCode Dev Container (Linux)

The VSCode Dev Container in this repository is the easiest way to get started. It comes with Zig, JavaScriptCore, Zig Language Server, vscode-zig, and more pre-installed on an instance of Ubuntu.

To develop on Linux, the following is required:

To get started, in the repository, locally run:

bun

# devcontainer-build just sets the architecture so if you're on ARM64, it'll do the right thing.
make devcontainer-build

Next, open VS Code in the repository. To open the dev container, open the command palette (Ctrl + Shift + P) and run: .

bun
Dev Containers: Reopen in Container

You will then need to clone the GitHub repository inside that container.

Inside the container, run this:

# First time setup
gh repo clone oven-sh/bun . -- --depth=1 --progress -j8

# update all submodules except webkit because webkit takes awhile and it's already compiled for you.
git -c submodule."src/bun.js/WebKit".update=none submodule update --init --recursive --depth=1 --progress

# Compile bun dependencies (zig is already compiled)
make devcontainer

# Build bun for development
make dev

# Run bun
bun-debug

It is very similar to my own development environment (except I use macOS)

MacOS

Install LLVM 13 and homebrew dependencies:

brew install llvm@13 coreutils libtool cmake libiconv automake ninja gnu-sed pkg-config esbuild go rust

bun (& the version of Zig) need LLVM 13 and Clang 13 (clang is part of LLVM). Weird build & runtime errors will happen otherwise.

Make sure LLVM 13 is in your :

$PATH

which clang-13

If it is not, you will have to run this to link it:

export PATH="$(brew --prefix llvm@13)/bin:$HOME/.bun-tools/zig:$PATH"
export LDFLAGS="$LDFLAGS -L$(brew --prefix llvm@13)/lib"
export CPPFLAGS="$CPPFLAGS -I$(brew --prefix llvm@13)/include"

On fish that looks like

fish_add_path (brew --prefix llvm@13)/bin

Install Zig (macOS)

Note: you must use the same version of Zig used by Bun in oven-sh/zig. Installing from brew will not work. Installing the latest stable version of Zig won't work. If you don't use the same version Bun uses, you will get strange build errors and be sad because you put all this work into trying to get Bun to compile and it failed for weird reasons.

zig

To install the zig binary:

# Custom path for the custom zig install
mkdir -p $HOME/.bun-tools

# Requires jq & grab latest binary
curl -o zig.tar.gz -sL https://github.com/oven-sh/zig/releases/download/jul1/zig-macos-$(uname -m).tar.gz

# This will extract to $HOME/.bun-tools/zig
tar -xvf zig.tar.gz -C $HOME/.bun-tools/
rm zig.tar.gz

# Make sure it gets trusted
# If you get an error 'No such xattr: com.apple.quarantine', that means it's already trusted and you can continue
xattr -d com.apple.quarantine $HOME/.bun-tools/zig/zig

Now you'll need to add Zig to your PATH.

Using :

zsh

echo 'export PATH="$HOME/.bun-tools/zig:$PATH"' >> ~/.zshrc
source ~/.zshrc

Using :

fish

# Add to PATH (fish)
fish_add_path $HOME/.bun-tools/zig

Using :

bash

echo 'export PATH="$HOME/.bun-tools/zig:$PATH"' >> ~/.bash_profile
source ~/.bash_profile

The version of Zig used by Bun is not a fork, just a slightly older version. Zig is a new programming language and moves quickly.

Build bun (macOS)

If you're building on a macOS device, you'll need to have a valid Developer Certificate, or else the code signing step will fail. To check if you have one, open the app, go to the profile and search for . You should have at least one certificate with a name like . If you don't have one, follow this guide to get one.

Keychain Access
login
Apple Development
Apple Development: user@example.com (WDYABC123)

You can still work with the generated binary locally at even if the code signing fails.

packages/debug-bun-*/bun-debug

In :

bun

# If you omit --depth=1, `git submodule update` will take 17.5 minutes on 1gbps internet, mostly due to WebKit.
git submodule update --init --recursive --progress --depth=1 --checkout
make vendor identifier-cache bindings jsc dev

Verify it worked (macOS)

First ensure the node dependencies are installed

(cd test/snippets && npm i)
(cd test/scripts && npm i)

そうしたら

make test-dev-all

トラブルシューティング (macOS)

コンパイル時にエラーが表示された場合は、次のコマンドを実行します。

libarchive

brew install pkg-config

見つからないファイルに関するエラーが表示された場合は、ヘッダーを作成したことを確認してください

zig build obj

vscode-zig

注:これは開発コンテナに自動的にインストールされます

あなたはのフォークをインストールしたいと思うでしょうだからあなたは得るそしてボタン。

vscode-zig
Run test
Debug test

それを行うには:

curl -L https://github.com/Jarred-Sumner/vscode-zig/releases/download/fork-v1/zig-0.2.5.vsix > vscode-zig.vsix
code --install-extension vscode-zig.vsix

トラブルシューティング (一般)

ビルドプロセス中に発生した場合、これはメモリが不足しているか、スワップしたことを意味します。Bunは現在、コンパイルに約22GBのRAMを必要としています。

error: the build command failed with exit code 9