mirror of
https://github.com/oven-sh/bun
synced 2026-02-05 08:28:55 +00:00
Compare commits
364 Commits
claude/css
...
ali/react
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e3ee654f2 | ||
|
|
e306ac831e | ||
|
|
ed1eb21093 | ||
|
|
4a4a37796b | ||
|
|
7aef153f5d | ||
|
|
3cb64478d7 | ||
|
|
7dfed6b986 | ||
|
|
5972cf24cb | ||
|
|
329c79364d | ||
|
|
da5bf73494 | ||
|
|
fbd58db004 | ||
|
|
81999c26e6 | ||
|
|
dde1f9e610 | ||
|
|
c9b5ba1c96 | ||
|
|
74f4fcf2a6 | ||
|
|
031f12442d | ||
|
|
a20a718e48 | ||
|
|
33537b6ec1 | ||
|
|
952436fc4c | ||
|
|
31352bc646 | ||
|
|
ff84564c11 | ||
|
|
cc78a1bca1 | ||
|
|
3ca89abacf | ||
|
|
2f02e4e31d | ||
|
|
efb508e2ae | ||
|
|
86af3dd034 | ||
|
|
a6d3808ad8 | ||
|
|
2153fe4163 | ||
|
|
6977b36215 | ||
|
|
ceaab9eda3 | ||
|
|
f262e32368 | ||
|
|
69a76d44f9 | ||
|
|
fc9538baf1 | ||
|
|
72b7956385 | ||
|
|
62b296bb43 | ||
|
|
cb14f70a43 | ||
|
|
38511375f8 | ||
|
|
06cfe2ead1 | ||
|
|
4f219503fe | ||
|
|
b2353d687e | ||
|
|
65e66099f7 | ||
|
|
c534f0caa0 | ||
|
|
a873152aeb | ||
|
|
a17eb07d48 | ||
|
|
1381de4d18 | ||
|
|
8d8b037e94 | ||
|
|
f43a175f72 | ||
|
|
e1ad16f857 | ||
|
|
661a246039 | ||
|
|
d0fed20c89 | ||
|
|
1c6165e68a | ||
|
|
daefbfb453 | ||
|
|
971e4679cf | ||
|
|
49a9dc7ddf | ||
|
|
b5a5fea9ae | ||
|
|
243a237a62 | ||
|
|
49cfda12a8 | ||
|
|
ab579a3cc3 | ||
|
|
8cd9b4eae6 | ||
|
|
dd9860f501 | ||
|
|
756e590782 | ||
|
|
c62613e765 | ||
|
|
7f96bf8f13 | ||
|
|
cd800b02f5 | ||
|
|
de999f78ab | ||
|
|
24748104ce | ||
|
|
3fca3b97d9 | ||
|
|
4cec2ecdc6 | ||
|
|
cdeb7bfb00 | ||
|
|
98b24f5797 | ||
|
|
678843fb59 | ||
|
|
7227745249 | ||
|
|
472e2d379f | ||
|
|
16360c9432 | ||
|
|
1b3d0d5c40 | ||
|
|
799248bfb4 | ||
|
|
a2689c03e9 | ||
|
|
2a7e2c9cf3 | ||
|
|
4f0d2a5624 | ||
|
|
23230112b0 | ||
|
|
c2c2a1685a | ||
|
|
09716704bb | ||
|
|
2d3223c5a6 | ||
|
|
891ea726d6 | ||
|
|
11eddb2cf1 | ||
|
|
0cc63255b1 | ||
|
|
71a5f9fb26 | ||
|
|
b257967189 | ||
|
|
4e629753cc | ||
|
|
7204820f19 | ||
|
|
af3a1ffd46 | ||
|
|
2ff068dad2 | ||
|
|
927065238b | ||
|
|
fa727b22de | ||
|
|
f20b0ced8e | ||
|
|
17fdb5bcdf | ||
|
|
f65d89ff8b | ||
|
|
c1931c11fe | ||
|
|
67d27499c3 | ||
|
|
85db75611b | ||
|
|
61519b320d | ||
|
|
c129d683cd | ||
|
|
0b0ffbf250 | ||
|
|
c64dd684c8 | ||
|
|
5aa5906ccf | ||
|
|
c93d8cf12b | ||
|
|
1a1091fd2c | ||
|
|
bdf77f968c | ||
|
|
457b4a46b3 | ||
|
|
3b2bea9820 | ||
|
|
5b4b99e2c4 | ||
|
|
da2be3f582 | ||
|
|
7282e92e48 | ||
|
|
80c28b6280 | ||
|
|
e40238fdc2 | ||
|
|
166e961202 | ||
|
|
58ecff4e0c | ||
|
|
a548ae7038 | ||
|
|
bbaabedce6 | ||
|
|
f84f90c09f | ||
|
|
43a7b6518a | ||
|
|
c85ab5218e | ||
|
|
bade403361 | ||
|
|
047eecc90c | ||
|
|
f03a1ab1c9 | ||
|
|
1e3057045c | ||
|
|
e92fd08930 | ||
|
|
deb3e94948 | ||
|
|
1b01f7c0da | ||
|
|
5e256e4b1f | ||
|
|
fc6fdbe300 | ||
|
|
247629aded | ||
|
|
2894e8d309 | ||
|
|
cc84e271ff | ||
|
|
c07150d5b1 | ||
|
|
b0d3815cf9 | ||
|
|
f145d8c30c | ||
|
|
3a23965581 | ||
|
|
0b45b9c29e | ||
|
|
9d679811cd | ||
|
|
cda3eb5396 | ||
|
|
b17dccc6e0 | ||
|
|
99a80a6fe6 | ||
|
|
8b7bc0fe59 | ||
|
|
7e89ca3d2f | ||
|
|
d8fa01ed41 | ||
|
|
361cd05676 | ||
|
|
dbe15d3020 | ||
|
|
3a200e8097 | ||
|
|
2701292a9f | ||
|
|
61b1aded3e | ||
|
|
36a414c087 | ||
|
|
ac02036879 | ||
|
|
612d41185b | ||
|
|
59b34efea8 | ||
|
|
243f3652f1 | ||
|
|
11dc2fae56 | ||
|
|
0919f237a5 | ||
|
|
691e731404 | ||
|
|
1a19be07ee | ||
|
|
903ac7bdd5 | ||
|
|
0ac6b17d4a | ||
|
|
921e3578b1 | ||
|
|
101bcb1ea0 | ||
|
|
f691ea1e96 | ||
|
|
53208e2538 | ||
|
|
53299d78b1 | ||
|
|
363c4a5c06 | ||
|
|
6e6120640e | ||
|
|
6556138c7b | ||
|
|
aea7b196e6 | ||
|
|
b3f92b0889 | ||
|
|
dab797b834 | ||
|
|
a8ff3f8ac3 | ||
|
|
b516eedc67 | ||
|
|
bcea163fd2 | ||
|
|
a47cbef4ca | ||
|
|
90e68fa095 | ||
|
|
da0b090834 | ||
|
|
e6aced6637 | ||
|
|
ce560cd318 | ||
|
|
e554c4e1ca | ||
|
|
731f42ca72 | ||
|
|
f33a852a80 | ||
|
|
f5122bdbf1 | ||
|
|
c29c69b9b5 | ||
|
|
916d44fc45 | ||
|
|
421a4f37cd | ||
|
|
d0da7076e6 | ||
|
|
ea78d564da | ||
|
|
6338d55f70 | ||
|
|
d3bdc77274 | ||
|
|
ecd2fed665 | ||
|
|
28447ab578 | ||
|
|
3e798f1787 | ||
|
|
a64f073ad3 | ||
|
|
bb19610f0d | ||
|
|
ed4a887047 | ||
|
|
894a654e26 | ||
|
|
99dd08bccb | ||
|
|
7339d1841b | ||
|
|
1217e87379 | ||
|
|
704661e96f | ||
|
|
8e659b2dc8 | ||
|
|
93007de396 | ||
|
|
2166f0c200 | ||
|
|
1a0a081e75 | ||
|
|
2eb33628d1 | ||
|
|
56e9c92b4a | ||
|
|
34cfdf039a | ||
|
|
d4a9c7a161 | ||
|
|
b5c16dcc1b | ||
|
|
0ba166eea3 | ||
|
|
1920a7c63c | ||
|
|
d56005b520 | ||
|
|
2bd5d68047 | ||
|
|
9c5c4edac4 | ||
|
|
cae0673dc4 | ||
|
|
51e18d379f | ||
|
|
199781bf4f | ||
|
|
ffeb21c49b | ||
|
|
d54ffd8012 | ||
|
|
dca34819b6 | ||
|
|
b4add533e6 | ||
|
|
7afcc8416f | ||
|
|
1ef578a0b4 | ||
|
|
8be4fb61d0 | ||
|
|
208ac7fb60 | ||
|
|
29b6faadf8 | ||
|
|
99df2e071f | ||
|
|
a3d91477a8 | ||
|
|
52c3e2e3f8 | ||
|
|
a7e95718ac | ||
|
|
db2960d27b | ||
|
|
bbfac709cc | ||
|
|
41fbeacee1 | ||
|
|
24b2929c9a | ||
|
|
bf992731c6 | ||
|
|
eafc04cc5d | ||
|
|
95cacdc6be | ||
|
|
6cf46e67f6 | ||
|
|
f28670ac68 | ||
|
|
0df21d7f30 | ||
|
|
39e7e55802 | ||
|
|
a63888ed6d | ||
|
|
e50385879b | ||
|
|
20d2f3805e | ||
|
|
d984f8f5ad | ||
|
|
9ea2ec876e | ||
|
|
0919e45c23 | ||
|
|
fd41a41ab9 | ||
|
|
fc06e1cf14 | ||
|
|
1778713cbf | ||
|
|
02d9da73bd | ||
|
|
6562275d15 | ||
|
|
cccae0cc79 | ||
|
|
c10d184448 | ||
|
|
0f8a232466 | ||
|
|
f47df15c18 | ||
|
|
f2d3141767 | ||
|
|
c8b21f207d | ||
|
|
6357978b90 | ||
|
|
151d8bb413 | ||
|
|
42bfccee3c | ||
|
|
f219a29248 | ||
|
|
b588512237 | ||
|
|
3a42ad8b1f | ||
|
|
cc1fff363d | ||
|
|
ba5e4784aa | ||
|
|
3e747886aa | ||
|
|
9504d14b7a | ||
|
|
911b670621 | ||
|
|
679282b8c6 | ||
|
|
1f79bc15a3 | ||
|
|
80a945f03f | ||
|
|
43054c9a7f | ||
|
|
2fad71dd45 | ||
|
|
6b2c3e61ea | ||
|
|
43d447f9fe | ||
|
|
8b35b5634a | ||
|
|
811f0888c8 | ||
|
|
a5d7830862 | ||
|
|
ef17dc57e4 | ||
|
|
e58cb4511e | ||
|
|
d44b3db1cb | ||
|
|
857e25d88c | ||
|
|
6eee2eeaf6 | ||
|
|
cc3e4d8319 | ||
|
|
278c2e7fb6 | ||
|
|
e296928ab9 | ||
|
|
39c1bf38f5 | ||
|
|
91d30b4da0 | ||
|
|
c2812fff79 | ||
|
|
e0337f5649 | ||
|
|
d05768cc18 | ||
|
|
1ad67908fc | ||
|
|
f55e320f41 | ||
|
|
8e0cf4c5e0 | ||
|
|
5dcf8a8076 | ||
|
|
d6b155f056 | ||
|
|
5f8393cc99 | ||
|
|
ee7dfefbe0 | ||
|
|
6d132e628f | ||
|
|
ae9ecc99c9 | ||
|
|
eeecbfa790 | ||
|
|
862f7378e4 | ||
|
|
636e597b60 | ||
|
|
6abb9f81eb | ||
|
|
aa33b11a7a | ||
|
|
21266f5263 | ||
|
|
c5fc729fde | ||
|
|
03d1e48004 | ||
|
|
19b9c4a850 | ||
|
|
842503ecb1 | ||
|
|
cb9c45c26c | ||
|
|
917dcc846f | ||
|
|
0fb277a56e | ||
|
|
c343aca21e | ||
|
|
e89a0f3807 | ||
|
|
59f12d30b3 | ||
|
|
f0d4fa8b63 | ||
|
|
3fb0a824cb | ||
|
|
ab3566627d | ||
|
|
3906407e5d | ||
|
|
33447ef2db | ||
|
|
3760407908 | ||
|
|
c1f0ce277d | ||
|
|
bfe3041179 | ||
|
|
5b6344cf3c | ||
|
|
b4fdf41ea5 | ||
|
|
b9da6b71f9 | ||
|
|
87487468f3 | ||
|
|
cfdeb42023 | ||
|
|
20e4c094ac | ||
|
|
17be416250 | ||
|
|
9745f01041 | ||
|
|
16131f92e1 | ||
|
|
59a4d0697b | ||
|
|
78a2ae44aa | ||
|
|
7f295919a9 | ||
|
|
1d0984b5c4 | ||
|
|
dfa93a8ede | ||
|
|
c8773c5e30 | ||
|
|
0f74fafc59 | ||
|
|
47d6e161fe | ||
|
|
160625c37c | ||
|
|
1b9b686772 | ||
|
|
6f3e098bac | ||
|
|
4c6b296a7c | ||
|
|
2ab962bf6b | ||
|
|
f556fc987c | ||
|
|
3a1b12ee61 | ||
|
|
a952b4200e | ||
|
|
24485fb432 | ||
|
|
b10fda0487 | ||
|
|
740cdaba3d | ||
|
|
68be15361a | ||
|
|
c57be8dcdb | ||
|
|
5115a88126 | ||
|
|
e992b804c8 | ||
|
|
b92555e099 | ||
|
|
381848cd69 | ||
|
|
61f9845f80 | ||
|
|
abc52da7bb |
19
.aikido
19
.aikido
@@ -1,19 +0,0 @@
|
||||
exclude:
|
||||
paths:
|
||||
- test
|
||||
- scripts
|
||||
- bench
|
||||
- packages/bun-lambda
|
||||
- packages/bun-release
|
||||
- packages/bun-wasm
|
||||
- packages/bun-vscode
|
||||
- packages/bun-plugin-yaml
|
||||
- packages/bun-plugin-svelte
|
||||
- packages/bun-native-plugin-rs
|
||||
- packages/bun-native-bundler-plugin-api
|
||||
- packages/bun-inspector-protocol
|
||||
- packages/bun-inspector-frontend
|
||||
- packages/bun-error
|
||||
- packages/bun-debug-adapter-protocol
|
||||
- packages/bun-build-mdx-rs
|
||||
- packages/@types/bun
|
||||
@@ -48,14 +48,6 @@ RUN --mount=type=tmpfs,target=/tmp \
|
||||
wget -O /tmp/cmake.sh "$cmake_url" && \
|
||||
sh /tmp/cmake.sh --skip-license --prefix=/usr
|
||||
|
||||
RUN --mount=type=tmpfs,target=/tmp \
|
||||
sccache_version="0.12.0" && \
|
||||
arch=$(uname -m) && \
|
||||
sccache_url="https://github.com/mozilla/sccache/releases/download/v${sccache_version}/sccache-v${sccache_version}-${arch}-unknown-linux-musl.tar.gz" && \
|
||||
wget -O /tmp/sccache.tar.gz "$sccache_url" && \
|
||||
tar -xzf /tmp/sccache.tar.gz -C /tmp && \
|
||||
install -m755 /tmp/sccache-v${sccache_version}-${arch}-unknown-linux-musl/sccache /usr/local/bin
|
||||
|
||||
RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 130 \
|
||||
--slave /usr/bin/g++ g++ /usr/bin/g++-13 \
|
||||
--slave /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-13 \
|
||||
@@ -118,14 +110,14 @@ ARG BUILDKITE_AGENT_TAGS
|
||||
|
||||
|
||||
# Install Rust nightly
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
|
||||
&& export PATH=$HOME/.cargo/bin:$PATH \
|
||||
&& rustup install nightly \
|
||||
&& rustup default nightly
|
||||
|
||||
|
||||
RUN ARCH=$(if [ "$TARGETARCH" = "arm64" ]; then echo "arm64"; else echo "amd64"; fi) && \
|
||||
echo "Downloading buildkite" && \
|
||||
echo "Downloading buildkite" && \
|
||||
curl -fsSL "https://github.com/buildkite/agent/releases/download/v3.87.0/buildkite-agent-linux-${ARCH}-3.87.0.tar.gz" -o /tmp/buildkite-agent.tar.gz && \
|
||||
mkdir -p /tmp/buildkite-agent && \
|
||||
tar -xzf /tmp/buildkite-agent.tar.gz -C /tmp/buildkite-agent && \
|
||||
@@ -133,20 +125,6 @@ RUN ARCH=$(if [ "$TARGETARCH" = "arm64" ]; then echo "arm64"; else echo "amd64";
|
||||
|
||||
RUN mkdir -p /var/cache/buildkite-agent /var/log/buildkite-agent /var/run/buildkite-agent /etc/buildkite-agent /var/lib/buildkite-agent/cache/bun
|
||||
|
||||
# The following is necessary to configure buildkite to use a stable
|
||||
# checkout directory. sccache hashes absolute paths into its cache keys,
|
||||
# so if buildkite uses a different checkout path each time (which it does
|
||||
# by default), sccache will be useless.
|
||||
RUN mkdir -p -m 755 /var/lib/buildkite-agent/hooks && \
|
||||
cat <<'EOF' > /var/lib/buildkite-agent/hooks/environment
|
||||
#!/bin/sh
|
||||
set -efu
|
||||
|
||||
export BUILDKITE_BUILD_CHECKOUT_PATH=/var/lib/buildkite-agent/build
|
||||
EOF
|
||||
|
||||
RUN chmod 744 /var/lib/buildkite-agent/hooks/environment
|
||||
|
||||
COPY ../*/agent.mjs /var/bun/scripts/
|
||||
|
||||
ENV BUN_INSTALL_CACHE=/var/lib/buildkite-agent/cache/bun
|
||||
@@ -169,7 +147,7 @@ COPY . /workspace/bun
|
||||
|
||||
|
||||
# Install Rust nightly
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
|
||||
&& export PATH=$HOME/.cargo/bin:$PATH \
|
||||
&& rustup install nightly \
|
||||
&& rustup default nightly
|
||||
@@ -183,4 +161,4 @@ RUN --mount=type=tmpfs,target=/workspace/bun/build \
|
||||
ls -la \
|
||||
&& bun run build:release \
|
||||
&& mkdir -p /target \
|
||||
&& cp -r /workspace/bun/build/release/bun /target/bun
|
||||
&& cp -r /workspace/bun/build/release/bun /target/bun
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
getEmoji,
|
||||
getEnv,
|
||||
getLastSuccessfulBuild,
|
||||
getSecret,
|
||||
isBuildkite,
|
||||
isBuildManual,
|
||||
isFork,
|
||||
@@ -31,7 +30,7 @@ import {
|
||||
} from "../scripts/utils.mjs";
|
||||
|
||||
/**
|
||||
* @typedef {"linux" | "darwin" | "windows" | "freebsd"} Os
|
||||
* @typedef {"linux" | "darwin" | "windows"} Os
|
||||
* @typedef {"aarch64" | "x64"} Arch
|
||||
* @typedef {"musl"} Abi
|
||||
* @typedef {"debian" | "ubuntu" | "alpine" | "amazonlinux"} Distro
|
||||
@@ -114,7 +113,6 @@ const buildPlatforms = [
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.22" },
|
||||
{ os: "windows", arch: "x64", release: "2019" },
|
||||
{ os: "windows", arch: "x64", baseline: true, release: "2019" },
|
||||
{ os: "freebsd", arch: "x64", release: "14.3" },
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -225,7 +223,7 @@ function getImageName(platform, options) {
|
||||
* @param {number} [limit]
|
||||
* @link https://buildkite.com/docs/pipelines/command-step#retry-attributes
|
||||
*/
|
||||
function getRetry() {
|
||||
function getRetry(limit = 0) {
|
||||
return {
|
||||
manual: {
|
||||
permit_on_passed: true,
|
||||
@@ -294,7 +292,7 @@ function getEc2Agent(platform, options, ec2Options) {
|
||||
* @returns {string}
|
||||
*/
|
||||
function getCppAgent(platform, options) {
|
||||
const { os, arch } = platform;
|
||||
const { os, arch, distro } = platform;
|
||||
|
||||
if (os === "darwin") {
|
||||
return {
|
||||
@@ -315,7 +313,7 @@ function getCppAgent(platform, options) {
|
||||
* @returns {string}
|
||||
*/
|
||||
function getLinkBunAgent(platform, options) {
|
||||
const { os, arch } = platform;
|
||||
const { os, arch, distro } = platform;
|
||||
|
||||
if (os === "darwin") {
|
||||
return {
|
||||
@@ -354,7 +352,14 @@ function getZigPlatform() {
|
||||
* @param {PipelineOptions} options
|
||||
* @returns {Agent}
|
||||
*/
|
||||
function getZigAgent(_platform, options) {
|
||||
function getZigAgent(platform, options) {
|
||||
const { arch } = platform;
|
||||
|
||||
// Uncomment to restore to using macOS on-prem for Zig.
|
||||
// return {
|
||||
// queue: "build-zig",
|
||||
// };
|
||||
|
||||
return getEc2Agent(getZigPlatform(), options, {
|
||||
instanceType: "r8g.large",
|
||||
});
|
||||
@@ -456,6 +461,23 @@ function getBuildCommand(target, options, label) {
|
||||
return `bun run build:${buildProfile}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Platform} platform
|
||||
* @param {PipelineOptions} options
|
||||
* @returns {Step}
|
||||
*/
|
||||
function getBuildVendorStep(platform, options) {
|
||||
return {
|
||||
key: `${getTargetKey(platform)}-build-vendor`,
|
||||
label: `${getTargetLabel(platform)} - build-vendor`,
|
||||
agents: getCppAgent(platform, options),
|
||||
retry: getRetry(),
|
||||
cancel_on_build_failing: isMergeQueue(),
|
||||
env: getBuildEnv(platform, options),
|
||||
command: `${getBuildCommand(platform, options)} --target dependencies`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Platform} platform
|
||||
* @param {PipelineOptions} options
|
||||
@@ -505,9 +527,9 @@ function getBuildZigStep(platform, options) {
|
||||
const toolchain = getBuildToolchain(platform);
|
||||
return {
|
||||
key: `${getTargetKey(platform)}-build-zig`,
|
||||
retry: getRetry(),
|
||||
label: `${getTargetLabel(platform)} - build-zig`,
|
||||
agents: getZigAgent(platform, options),
|
||||
retry: getRetry(),
|
||||
cancel_on_build_failing: isMergeQueue(),
|
||||
env: getBuildEnv(platform, options),
|
||||
command: `${getBuildCommand(platform, options)} --target bun-zig --toolchain ${toolchain}`,
|
||||
@@ -557,6 +579,7 @@ function getBuildBunStep(platform, options) {
|
||||
/**
|
||||
* @typedef {Object} TestOptions
|
||||
* @property {string} [buildId]
|
||||
* @property {boolean} [unifiedTests]
|
||||
* @property {string[]} [testFiles]
|
||||
* @property {boolean} [dryRun]
|
||||
*/
|
||||
@@ -569,7 +592,7 @@ function getBuildBunStep(platform, options) {
|
||||
*/
|
||||
function getTestBunStep(platform, options, testOptions = {}) {
|
||||
const { os, profile } = platform;
|
||||
const { buildId, testFiles } = testOptions;
|
||||
const { buildId, unifiedTests, testFiles } = testOptions;
|
||||
|
||||
const args = [`--step=${getTargetKey(platform)}-build-bun`];
|
||||
if (buildId) {
|
||||
@@ -591,7 +614,7 @@ function getTestBunStep(platform, options, testOptions = {}) {
|
||||
agents: getTestAgent(platform, options),
|
||||
retry: getRetry(),
|
||||
cancel_on_build_failing: isMergeQueue(),
|
||||
parallelism: os === "darwin" ? 2 : 10,
|
||||
parallelism: unifiedTests ? undefined : os === "darwin" ? 2 : 10,
|
||||
timeout_in_minutes: profile === "asan" || os === "windows" ? 45 : 30,
|
||||
env: {
|
||||
ASAN_OPTIONS: "allow_user_segv_handler=1:disable_coredump=0:detect_leaks=0",
|
||||
@@ -660,7 +683,7 @@ function getReleaseStep(buildPlatforms, options) {
|
||||
agents: {
|
||||
queue: "test-darwin",
|
||||
},
|
||||
depends_on: buildPlatforms.filter(p => p.os !== "freebsd").map(platform => `${getTargetKey(platform)}-build-bun`),
|
||||
depends_on: buildPlatforms.map(platform => `${getTargetKey(platform)}-build-bun`),
|
||||
env: {
|
||||
CANARY: revision,
|
||||
},
|
||||
@@ -773,6 +796,8 @@ function getBenchmarkStep() {
|
||||
* @property {Platform[]} [buildPlatforms]
|
||||
* @property {Platform[]} [testPlatforms]
|
||||
* @property {string[]} [testFiles]
|
||||
* @property {boolean} [unifiedBuilds]
|
||||
* @property {boolean} [unifiedTests]
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -943,6 +968,22 @@ function getOptionsStep() {
|
||||
default: "false",
|
||||
options: booleanOptions,
|
||||
},
|
||||
{
|
||||
key: "unified-builds",
|
||||
select: "Do you want to build each platform in a single step?",
|
||||
hint: "If true, builds will not be split into separate steps (this will likely slow down the build)",
|
||||
required: false,
|
||||
default: "false",
|
||||
options: booleanOptions,
|
||||
},
|
||||
{
|
||||
key: "unified-tests",
|
||||
select: "Do you want to run tests in a single step?",
|
||||
hint: "If true, tests will not be split into separate steps (this will be very slow)",
|
||||
required: false,
|
||||
default: "false",
|
||||
options: booleanOptions,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -1008,6 +1049,8 @@ async function getPipelineOptions() {
|
||||
buildImages: parseBoolean(options["build-images"]),
|
||||
publishImages: parseBoolean(options["publish-images"]),
|
||||
testFiles: parseArray(options["test-files"]),
|
||||
unifiedBuilds: parseBoolean(options["unified-builds"]),
|
||||
unifiedTests: parseBoolean(options["unified-tests"]),
|
||||
buildPlatforms: buildPlatformKeys?.length
|
||||
? buildPlatformKeys.flatMap(key => buildProfiles.map(profile => ({ ...buildPlatformsMap.get(key), profile })))
|
||||
: Array.from(buildPlatformsMap.values()),
|
||||
@@ -1073,7 +1116,7 @@ async function getPipeline(options = {}) {
|
||||
const imagePlatforms = new Map(
|
||||
buildImages || publishImages
|
||||
? [...buildPlatforms, ...testPlatforms]
|
||||
.filter(({ os }) => os !== "darwin")
|
||||
.filter(({ os }) => os === "linux" || os === "windows")
|
||||
.map(platform => [getImageKey(platform), platform])
|
||||
: [],
|
||||
);
|
||||
@@ -1089,7 +1132,7 @@ async function getPipeline(options = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
let { skipBuilds, forceBuilds, dryRun } = options;
|
||||
let { skipBuilds, forceBuilds, unifiedBuilds, dryRun } = options;
|
||||
dryRun = dryRun || !!buildImages;
|
||||
|
||||
/** @type {string | undefined} */
|
||||
@@ -1107,13 +1150,10 @@ async function getPipeline(options = {}) {
|
||||
const includeASAN = !isMainBranch();
|
||||
|
||||
if (!buildId) {
|
||||
let relevantBuildPlatforms = includeASAN
|
||||
const relevantBuildPlatforms = includeASAN
|
||||
? buildPlatforms
|
||||
: buildPlatforms.filter(({ profile }) => profile !== "asan");
|
||||
|
||||
// run build-image but no build-bun yet
|
||||
relevantBuildPlatforms = relevantBuildPlatforms.filter(({ os }) => os !== "freebsd");
|
||||
|
||||
steps.push(
|
||||
...relevantBuildPlatforms.map(target => {
|
||||
const imageKey = getImageKey(target);
|
||||
@@ -1123,16 +1163,13 @@ async function getPipeline(options = {}) {
|
||||
dependsOn.push(`${imageKey}-build-image`);
|
||||
}
|
||||
|
||||
const steps = [];
|
||||
steps.push(getBuildCppStep(target, options));
|
||||
steps.push(getBuildZigStep(target, options));
|
||||
steps.push(getLinkBunStep(target, options));
|
||||
|
||||
return getStepWithDependsOn(
|
||||
{
|
||||
key: getTargetKey(target),
|
||||
group: getTargetLabel(target),
|
||||
steps,
|
||||
steps: unifiedBuilds
|
||||
? [getBuildBunStep(target, options)]
|
||||
: [getBuildCppStep(target, options), getBuildZigStep(target, options), getLinkBunStep(target, options)],
|
||||
},
|
||||
...dependsOn,
|
||||
);
|
||||
@@ -1141,13 +1178,13 @@ async function getPipeline(options = {}) {
|
||||
}
|
||||
|
||||
if (!isMainBranch()) {
|
||||
const { skipTests, forceTests, testFiles } = options;
|
||||
const { skipTests, forceTests, unifiedTests, testFiles } = options;
|
||||
if (!skipTests || forceTests) {
|
||||
steps.push(
|
||||
...testPlatforms.map(target => ({
|
||||
key: getTargetKey(target),
|
||||
group: getTargetLabel(target),
|
||||
steps: [getTestBunStep(target, options, { testFiles, buildId })],
|
||||
steps: [getTestBunStep(target, options, { unifiedTests, testFiles, buildId })],
|
||||
})),
|
||||
);
|
||||
}
|
||||
@@ -1190,43 +1227,6 @@ async function main() {
|
||||
console.log("Generated options:", options);
|
||||
}
|
||||
|
||||
startGroup("Querying GitHub for files...");
|
||||
if (options && isBuildkite && !isMainBranch()) {
|
||||
/** @type {string[]} */
|
||||
let allFiles = [];
|
||||
/** @type {string[]} */
|
||||
let newFiles = [];
|
||||
let prFileCount = 0;
|
||||
try {
|
||||
console.log("on buildkite: collecting new files from PR");
|
||||
const per_page = 50;
|
||||
const { BUILDKITE_PULL_REQUEST } = process.env;
|
||||
for (let i = 1; i <= 10; i++) {
|
||||
const res = await fetch(
|
||||
`https://api.github.com/repos/oven-sh/bun/pulls/${BUILDKITE_PULL_REQUEST}/files?per_page=${per_page}&page=${i}`,
|
||||
{ headers: { Authorization: `Bearer ${getSecret("GITHUB_TOKEN")}` } },
|
||||
);
|
||||
const doc = await res.json();
|
||||
console.log(`-> page ${i}, found ${doc.length} items`);
|
||||
if (doc.length === 0) break;
|
||||
for (const { filename, status } of doc) {
|
||||
prFileCount += 1;
|
||||
allFiles.push(filename);
|
||||
if (status !== "added") continue;
|
||||
newFiles.push(filename);
|
||||
}
|
||||
if (doc.length < per_page) break;
|
||||
}
|
||||
console.log(`- PR ${BUILDKITE_PULL_REQUEST}, ${prFileCount} files, ${newFiles.length} new files`);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
if (allFiles.every(filename => filename.startsWith("docs/"))) {
|
||||
console.log(`- PR is only docs, skipping tests!`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
startGroup("Generating pipeline...");
|
||||
const pipeline = await getPipeline(options);
|
||||
if (!pipeline) {
|
||||
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -16,7 +16,6 @@
|
||||
*.map text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
|
||||
*.md text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
|
||||
*.mdc text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
|
||||
*.mdx text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
|
||||
*.mjs text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
|
||||
*.mts text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
|
||||
|
||||
@@ -48,6 +47,7 @@ examples/**/* linguist-documentation
|
||||
|
||||
vendor/*.c linguist-vendored
|
||||
vendor/brotli/** linguist-vendored
|
||||
packages/bun-framework-react/vendor/** linguist-vendored -diff -merge
|
||||
|
||||
test/js/node/test/fixtures linguist-vendored
|
||||
test/js/node/test/common linguist-vendored
|
||||
|
||||
24
.github/workflows/docs.yml
vendored
Normal file
24
.github/workflows/docs.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "packages/bun-types/**.d.ts"
|
||||
- "CONTRIBUTING.md"
|
||||
- "src/cli/install.sh"
|
||||
- "src/cli/install.ps1"
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'oven-sh' }}
|
||||
steps:
|
||||
# redeploy Vercel site when a file in `docs` changes
|
||||
# using VERCEL_DEPLOY_HOOK environment variable
|
||||
- name: Trigger Webhook
|
||||
run: |
|
||||
curl -v ${{ secrets.VERCEL_DEPLOY_HOOK }}
|
||||
2
.github/workflows/format.yml
vendored
2
.github/workflows/format.yml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
pull_request:
|
||||
merge_group:
|
||||
env:
|
||||
BUN_VERSION: "1.3.2"
|
||||
BUN_VERSION: "1.2.20"
|
||||
LLVM_VERSION: "19.1.7"
|
||||
LLVM_VERSION_MAJOR: "19"
|
||||
|
||||
|
||||
19
.github/workflows/typos.yml
vendored
Normal file
19
.github/workflows/typos.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Typos
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
docs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Spellcheck
|
||||
uses: crate-ci/typos@v1.29.4
|
||||
with:
|
||||
files: docs/**/*
|
||||
@@ -7,8 +7,4 @@ src/react-refresh.js
|
||||
*.min.js
|
||||
test/snippets
|
||||
test/js/node/test
|
||||
test/napi/node-napi-tests
|
||||
bun.lock
|
||||
|
||||
# the output codeblocks need to stay minified
|
||||
docs/bundler/minifier.mdx
|
||||
|
||||
26
CLAUDE.md
26
CLAUDE.md
@@ -38,36 +38,16 @@ If no valid issue number is provided, find the best existing file to modify inst
|
||||
|
||||
### Writing Tests
|
||||
|
||||
Tests use Bun's Jest-compatible test runner with proper test fixtures.
|
||||
|
||||
- For **single-file tests**, prefer `-e` over `tempDir`.
|
||||
- For **multi-file tests**, prefer `tempDir` and `Bun.spawn`.
|
||||
Tests use Bun's Jest-compatible test runner with proper test fixtures:
|
||||
|
||||
```typescript
|
||||
import { test, expect } from "bun:test";
|
||||
import { bunEnv, bunExe, normalizeBunSnapshot, tempDir } from "harness";
|
||||
|
||||
test("(single-file test) my feature", async () => {
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "-e", "console.log('Hello, world!')"],
|
||||
env: bunEnv,
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([
|
||||
proc.stdout.text(),
|
||||
proc.stderr.text(),
|
||||
proc.exited,
|
||||
]);
|
||||
|
||||
expect(normalizeBunSnapshot(stdout)).toMatchInlineSnapshot(`"Hello, world!"`);
|
||||
expect(exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test("(multi-file test) my feature", async () => {
|
||||
test("my feature", async () => {
|
||||
// Create temp directory with test files
|
||||
using dir = tempDir("test-prefix", {
|
||||
"index.js": `import { foo } from "./foo.ts"; foo();`,
|
||||
"foo.ts": `export function foo() { console.log("foo"); }`,
|
||||
"index.js": `console.log("hello");`,
|
||||
});
|
||||
|
||||
// Spawn Bun process
|
||||
|
||||
@@ -24,6 +24,7 @@ if(CMAKE_HOST_APPLE)
|
||||
include(SetupMacSDK)
|
||||
endif()
|
||||
include(SetupLLVM)
|
||||
include(SetupCcache)
|
||||
|
||||
# --- Project ---
|
||||
|
||||
@@ -47,16 +48,6 @@ include(SetupEsbuild)
|
||||
include(SetupZig)
|
||||
include(SetupRust)
|
||||
|
||||
find_program(SCCACHE_PROGRAM sccache)
|
||||
if(SCCACHE_PROGRAM AND NOT DEFINED ENV{NO_SCCACHE})
|
||||
include(SetupSccache)
|
||||
else()
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if(CCACHE_PROGRAM)
|
||||
include(SetupCcache)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Generate dependency versions header
|
||||
include(GenerateDependencyVersions)
|
||||
|
||||
|
||||
@@ -23,19 +23,19 @@ Using your system's package manager, install Bun's dependencies:
|
||||
{% codetabs group="os" %}
|
||||
|
||||
```bash#macOS (Homebrew)
|
||||
$ brew install automake cmake coreutils gnu-sed go icu4c libiconv libtool ninja pkg-config rust ruby sccache
|
||||
$ brew install automake ccache cmake coreutils gnu-sed go icu4c libiconv libtool ninja pkg-config rust ruby
|
||||
```
|
||||
|
||||
```bash#Ubuntu/Debian
|
||||
$ sudo apt install curl wget lsb-release software-properties-common cargo cmake git golang libtool ninja-build pkg-config rustc ruby-full xz-utils
|
||||
$ sudo apt install curl wget lsb-release software-properties-common cargo ccache cmake git golang libtool ninja-build pkg-config rustc ruby-full xz-utils
|
||||
```
|
||||
|
||||
```bash#Arch
|
||||
$ sudo pacman -S base-devel cmake git go libiconv libtool make ninja pkg-config python rust sed unzip ruby
|
||||
$ sudo pacman -S base-devel ccache cmake git go libiconv libtool make ninja pkg-config python rust sed unzip ruby
|
||||
```
|
||||
|
||||
```bash#Fedora
|
||||
$ sudo dnf install cargo clang19 llvm19 lld19 cmake git golang libtool ninja-build pkg-config rustc ruby libatomic-static libstdc++-static sed unzip which libicu-devel 'perl(Math::BigInt)'
|
||||
$ sudo dnf install cargo clang19 llvm19 lld19 ccache cmake git golang libtool ninja-build pkg-config rustc ruby libatomic-static libstdc++-static sed unzip which libicu-devel 'perl(Math::BigInt)'
|
||||
```
|
||||
|
||||
```bash#openSUSE Tumbleweed
|
||||
@@ -65,44 +65,6 @@ $ brew install bun
|
||||
|
||||
{% /codetabs %}
|
||||
|
||||
### Optional: Install `sccache`
|
||||
|
||||
sccache is used to cache compilation artifacts, significantly speeding up builds. It must be installed with S3 support:
|
||||
|
||||
```bash
|
||||
# For macOS
|
||||
$ brew install sccache
|
||||
|
||||
# For Linux. Note that the version in your package manager may not have S3 support.
|
||||
$ cargo install sccache --features=s3
|
||||
```
|
||||
|
||||
This will install `sccache` with S3 support. Our build scripts will automatically detect and use `sccache` with our shared S3 cache. **Note**: Not all versions of `sccache` are compiled with S3 support, hence we recommend installing it via `cargo`.
|
||||
|
||||
#### Registering AWS Credentials for `sccache` (Core Developers Only)
|
||||
|
||||
Core developers have write access to the shared S3 cache. To enable write access, you must log in with AWS credentials. The easiest way to do this is to use the [`aws` CLI](https://aws.amazon.com/cli/) and invoke [`aws configure` to provide your AWS security info](https://docs.aws.amazon.com/cli/latest/reference/configure/).
|
||||
|
||||
The `cmake` scripts should automatically detect your AWS credentials from the environment or the `~/.aws/credentials` file.
|
||||
|
||||
<details>
|
||||
<summary>Logging in to the `aws` CLI</summary>
|
||||
|
||||
1. Install the AWS CLI by following [the official guide](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html).
|
||||
2. Log in to your AWS account console. A team member should provide you with your credentials.
|
||||
3. Click your name in the top right > Security credentials.
|
||||
4. Scroll to "Access keys" and create a new access key.
|
||||
5. Run `aws configure` in your terminal and provide the access key ID and secret access key when prompted.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Common Issues You May Encounter</summary>
|
||||
|
||||
- To confirm that the cache is being used, you can use the `sccache --show-stats` command right after a build. This will expose very useful statistics, including cache hits/misses.
|
||||
- If you have multiple AWS profiles configured, ensure that the correct profile is set in the `AWS_PROFILE` environment variable.
|
||||
- `sccache` follows a server-client model. If you run into weird issues where `sccache` refuses to use S3, even though you have AWS credentials configured, try killing any running `sccache` servers with `sccache --stop-server` and then re-running the build.
|
||||
</details>
|
||||
|
||||
## Install LLVM
|
||||
|
||||
Bun requires LLVM 19 (`clang` is part of LLVM). This version requirement is to match WebKit (precompiled), as mismatching versions will cause memory allocation failures at runtime. In most cases, you can install LLVM through your system package manager:
|
||||
@@ -201,7 +163,7 @@ Bun generally takes about 2.5 minutes to compile a debug build when there are Zi
|
||||
- Batch up your changes
|
||||
- Ensure zls is running with incremental watching for LSP errors (if you use VSCode and install Zig and run `bun run build` once to download Zig, this should just work)
|
||||
- Prefer using the debugger ("CodeLLDB" in VSCode) to step through the code.
|
||||
- Use debug logs. `BUN_DEBUG_<scope>=1` will enable debug logging for the corresponding `Output.scoped(.<scope>, .hidden)` logs. You can also set `BUN_DEBUG_QUIET_LOGS=1` to disable all debug logging that isn't explicitly enabled. To dump debug logs into a file, `BUN_DEBUG=<path-to-file>.log`. Debug logs are aggressively removed in release builds.
|
||||
- Use debug logs. `BUN_DEBUG_<scope>=1` will enable debug logging for the corresponding `Output.scoped(.<scope>, .hidden)` logs. You can also set `BUN_DEBUG_QUIET_LOGS=1` to disable all debug logging that isn't explicitly enabled. To dump debug lgos into a file, `BUN_DEBUG=<path-to-file>.log`. Debug logs are aggressively removed in release builds.
|
||||
- src/js/\*\*.ts changes are pretty much instant to rebuild. C++ changes are a bit slower, but still much faster than the Zig code (Zig is one compilation unit, C++ is many).
|
||||
|
||||
## Code generation scripts
|
||||
@@ -369,6 +331,15 @@ $ bun run build -DUSE_STATIC_LIBATOMIC=OFF
|
||||
|
||||
The built version of Bun may not work on other systems if compiled this way.
|
||||
|
||||
### ccache conflicts with building TinyCC on macOS
|
||||
|
||||
If you run into issues with `ccache` when building TinyCC, try reinstalling ccache
|
||||
|
||||
```bash
|
||||
brew uninstall ccache
|
||||
brew install ccache
|
||||
```
|
||||
|
||||
## Using bun-debug
|
||||
|
||||
- Disable logging: `BUN_DEBUG_QUIET_LOGS=1 bun-debug ...` (to disable all debug logging)
|
||||
|
||||
10
README.md
10
README.md
@@ -54,7 +54,7 @@ Bun supports Linux (x64 & arm64), macOS (x64 & Apple Silicon) and Windows (x64).
|
||||
curl -fsSL https://bun.com/install | bash
|
||||
|
||||
# on windows
|
||||
powershell -c "irm bun.sh/install.ps1 | iex"
|
||||
powershell -c "irm bun.com/install.ps1 | iex"
|
||||
|
||||
# with npm
|
||||
npm install -g bun
|
||||
@@ -104,13 +104,13 @@ bun upgrade --canary
|
||||
- [File types (Loaders)](https://bun.com/docs/runtime/loaders)
|
||||
- [TypeScript](https://bun.com/docs/runtime/typescript)
|
||||
- [JSX](https://bun.com/docs/runtime/jsx)
|
||||
- [Environment variables](https://bun.com/docs/runtime/environment-variables)
|
||||
- [Environment variables](https://bun.com/docs/runtime/env)
|
||||
- [Bun APIs](https://bun.com/docs/runtime/bun-apis)
|
||||
- [Web APIs](https://bun.com/docs/runtime/web-apis)
|
||||
- [Node.js compatibility](https://bun.com/docs/runtime/nodejs-compat)
|
||||
- [Node.js compatibility](https://bun.com/docs/runtime/nodejs-apis)
|
||||
- [Single-file executable](https://bun.com/docs/bundler/executables)
|
||||
- [Plugins](https://bun.com/docs/runtime/plugins)
|
||||
- [Watch mode / Hot Reloading](https://bun.com/docs/runtime/watch-mode)
|
||||
- [Watch mode / Hot Reloading](https://bun.com/docs/runtime/hot)
|
||||
- [Module resolution](https://bun.com/docs/runtime/modules)
|
||||
- [Auto-install](https://bun.com/docs/runtime/autoimport)
|
||||
- [bunfig.toml](https://bun.com/docs/runtime/bunfig)
|
||||
@@ -230,7 +230,7 @@ bun upgrade --canary
|
||||
|
||||
- Ecosystem
|
||||
- [Use React and JSX](https://bun.com/guides/ecosystem/react)
|
||||
- [Use Gel with Bun](https://bun.com/guides/ecosystem/gel)
|
||||
- [Use EdgeDB with Bun](https://bun.com/guides/ecosystem/edgedb)
|
||||
- [Use Prisma with Bun](https://bun.com/guides/ecosystem/prisma)
|
||||
- [Add Sentry to a Bun app](https://bun.com/guides/ecosystem/sentry)
|
||||
- [Create a Discord bot](https://bun.com/guides/ecosystem/discordjs)
|
||||
|
||||
@@ -13,4 +13,7 @@ export function run(opts = {}) {
|
||||
}
|
||||
|
||||
export const bench = Mitata.bench;
|
||||
export const group = Mitata.group;
|
||||
|
||||
export function group(_name, fn) {
|
||||
return Mitata.group(fn);
|
||||
}
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
import { bench, group, run } from "../runner.mjs";
|
||||
|
||||
const runAll = !process.argv.includes("--simple");
|
||||
|
||||
const small = new Uint8Array(1024);
|
||||
const medium = new Uint8Array(1024 * 100);
|
||||
const large = new Uint8Array(1024 * 1024);
|
||||
|
||||
for (let i = 0; i < large.length; i++) {
|
||||
const value = Math.floor(Math.sin(i / 100) * 128 + 128);
|
||||
if (i < small.length) small[i] = value;
|
||||
if (i < medium.length) medium[i] = value;
|
||||
large[i] = value;
|
||||
}
|
||||
|
||||
const format = new Intl.NumberFormat("en-US", { notation: "compact", unit: "byte" });
|
||||
|
||||
async function compress(data, format) {
|
||||
const cs = new CompressionStream(format);
|
||||
const writer = cs.writable.getWriter();
|
||||
const reader = cs.readable.getReader();
|
||||
|
||||
writer.write(data);
|
||||
writer.close();
|
||||
|
||||
const chunks = [];
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
chunks.push(value);
|
||||
}
|
||||
|
||||
const result = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));
|
||||
let offset = 0;
|
||||
for (const chunk of chunks) {
|
||||
result.set(chunk, offset);
|
||||
offset += chunk.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function decompress(data, format) {
|
||||
const ds = new DecompressionStream(format);
|
||||
const writer = ds.writable.getWriter();
|
||||
const reader = ds.readable.getReader();
|
||||
|
||||
writer.write(data);
|
||||
writer.close();
|
||||
|
||||
const chunks = [];
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
chunks.push(value);
|
||||
}
|
||||
|
||||
const result = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));
|
||||
let offset = 0;
|
||||
for (const chunk of chunks) {
|
||||
result.set(chunk, offset);
|
||||
offset += chunk.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function roundTrip(data, format) {
|
||||
const compressed = await compress(data, format);
|
||||
return await decompress(compressed, format);
|
||||
}
|
||||
|
||||
const formats = ["deflate", "gzip", "deflate-raw"];
|
||||
if (runAll) formats.push("brotli", "zstd");
|
||||
|
||||
// Small data benchmarks (1KB)
|
||||
group(`CompressionStream ${format.format(small.length)}`, () => {
|
||||
for (const fmt of formats) {
|
||||
try {
|
||||
new CompressionStream(fmt);
|
||||
bench(fmt, async () => await compress(small, fmt));
|
||||
} catch (e) {
|
||||
// Skip unsupported formats
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Medium data benchmarks (100KB)
|
||||
group(`CompressionStream ${format.format(medium.length)}`, () => {
|
||||
for (const fmt of formats) {
|
||||
try {
|
||||
new CompressionStream(fmt);
|
||||
bench(fmt, async () => await compress(medium, fmt));
|
||||
} catch (e) {}
|
||||
}
|
||||
});
|
||||
|
||||
// Large data benchmarks (1MB)
|
||||
group(`CompressionStream ${format.format(large.length)}`, () => {
|
||||
for (const fmt of formats) {
|
||||
try {
|
||||
new CompressionStream(fmt);
|
||||
bench(fmt, async () => await compress(large, fmt));
|
||||
} catch (e) {
|
||||
// Skip unsupported formats
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const compressedData = {};
|
||||
for (const fmt of formats) {
|
||||
try {
|
||||
compressedData[fmt] = {
|
||||
small: await compress(small, fmt),
|
||||
medium: await compress(medium, fmt),
|
||||
large: await compress(large, fmt),
|
||||
};
|
||||
} catch (e) {
|
||||
// Skip unsupported formats
|
||||
}
|
||||
}
|
||||
|
||||
group(`DecompressionStream ${format.format(small.length)}`, () => {
|
||||
for (const fmt of formats) {
|
||||
if (compressedData[fmt]) {
|
||||
bench(fmt, async () => await decompress(compressedData[fmt].small, fmt));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
group(`DecompressionStream ${format.format(medium.length)}`, () => {
|
||||
for (const fmt of formats) {
|
||||
if (compressedData[fmt]) {
|
||||
bench(fmt, async () => await decompress(compressedData[fmt].medium, fmt));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
group(`DecompressionStream ${format.format(large.length)}`, () => {
|
||||
for (const fmt of formats) {
|
||||
if (compressedData[fmt]) {
|
||||
bench(fmt, async () => await decompress(compressedData[fmt].large, fmt));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
group(`roundtrip ${format.format(large.length)}`, () => {
|
||||
for (const fmt of formats) {
|
||||
try {
|
||||
new CompressionStream(fmt);
|
||||
bench(fmt, async () => await roundTrip(large, fmt));
|
||||
} catch (e) {
|
||||
// Skip unsupported formats
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await run();
|
||||
@@ -11,10 +11,10 @@ const builtin = ${JSON.stringify(builtin)};
|
||||
const now = performance.now();
|
||||
require(builtin);
|
||||
const end = performance.now();
|
||||
process.stdout.write(JSON.stringify({ builtin, time: end - now }) + "\\n");
|
||||
process.stdout.write(JSON.stringify({builtin, time: end - now}) + "\\n");
|
||||
`,
|
||||
);
|
||||
spawnSync(process.execPath, [path], {
|
||||
const result = spawnSync(typeof Bun !== "undefined" ? "bun" : "node", [path], {
|
||||
stdio: ["inherit", "inherit", "inherit"],
|
||||
env: {
|
||||
...process.env,
|
||||
|
||||
57
build.zig
57
build.zig
@@ -18,6 +18,22 @@ const OperatingSystem = @import("src/env.zig").OperatingSystem;
|
||||
|
||||
const pathRel = fs.path.relative;
|
||||
|
||||
/// When updating this, make sure to adjust SetupZig.cmake
|
||||
const recommended_zig_version = "0.14.0";
|
||||
|
||||
// comptime {
|
||||
// if (!std.mem.eql(u8, builtin.zig_version_string, recommended_zig_version)) {
|
||||
// @compileError(
|
||||
// "" ++
|
||||
// "Bun requires Zig version " ++ recommended_zig_version ++ ", but you have " ++
|
||||
// builtin.zig_version_string ++ ". This is automatically configured via Bun's " ++
|
||||
// "CMake setup. You likely meant to run `bun run build`. If you are trying to " ++
|
||||
// "upgrade the Zig compiler, edit ZIG_COMMIT in cmake/tools/SetupZig.cmake or " ++
|
||||
// "comment this error out.",
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
const zero_sha = "0000000000000000000000000000000000000000";
|
||||
|
||||
const BunBuildOptions = struct {
|
||||
@@ -32,7 +48,6 @@ const BunBuildOptions = struct {
|
||||
/// enable debug logs in release builds
|
||||
enable_logs: bool = false,
|
||||
enable_asan: bool,
|
||||
enable_fuzzilli: bool,
|
||||
enable_valgrind: bool,
|
||||
use_mimalloc: bool,
|
||||
tracy_callstack_depth: u16,
|
||||
@@ -82,10 +97,9 @@ const BunBuildOptions = struct {
|
||||
opts.addOption(bool, "baseline", this.isBaseline());
|
||||
opts.addOption(bool, "enable_logs", this.enable_logs);
|
||||
opts.addOption(bool, "enable_asan", this.enable_asan);
|
||||
opts.addOption(bool, "enable_fuzzilli", this.enable_fuzzilli);
|
||||
opts.addOption(bool, "enable_valgrind", this.enable_valgrind);
|
||||
opts.addOption(bool, "use_mimalloc", this.use_mimalloc);
|
||||
opts.addOption([]const u8, "reported_nodejs_version", b.fmt("{f}", .{this.reported_nodejs_version}));
|
||||
opts.addOption([]const u8, "reported_nodejs_version", b.fmt("{}", .{this.reported_nodejs_version}));
|
||||
opts.addOption(bool, "zig_self_hosted_backend", this.no_llvm);
|
||||
opts.addOption(bool, "override_no_export_cpp_apis", this.override_no_export_cpp_apis);
|
||||
|
||||
@@ -120,8 +134,8 @@ pub fn getOSVersionMin(os: OperatingSystem) ?Target.Query.OsVersion {
|
||||
|
||||
pub fn getOSGlibCVersion(os: OperatingSystem) ?Version {
|
||||
return switch (os) {
|
||||
// Compiling with a newer glibc than this will break certain cloud environments. See symbols.test.ts.
|
||||
.linux => .{ .major = 2, .minor = 26, .patch = 0 },
|
||||
// Compiling with a newer glibc than this will break certain cloud environments.
|
||||
.linux => .{ .major = 2, .minor = 27, .patch = 0 },
|
||||
|
||||
else => null,
|
||||
};
|
||||
@@ -257,7 +271,6 @@ pub fn build(b: *Build) !void {
|
||||
.tracy_callstack_depth = b.option(u16, "tracy_callstack_depth", "") orelse 10,
|
||||
.enable_logs = b.option(bool, "enable_logs", "Enable logs in release") orelse false,
|
||||
.enable_asan = b.option(bool, "enable_asan", "Enable asan") orelse false,
|
||||
.enable_fuzzilli = b.option(bool, "enable_fuzzilli", "Enable fuzzilli instrumentation") orelse false,
|
||||
.enable_valgrind = b.option(bool, "enable_valgrind", "Enable valgrind") orelse false,
|
||||
.use_mimalloc = b.option(bool, "use_mimalloc", "Use mimalloc as default allocator") orelse false,
|
||||
.llvm_codegen_threads = b.option(u32, "llvm_codegen_threads", "Number of threads to use for LLVM codegen") orelse 1,
|
||||
@@ -277,16 +290,14 @@ pub fn build(b: *Build) !void {
|
||||
var o = build_options;
|
||||
var unit_tests = b.addTest(.{
|
||||
.name = "bun-test",
|
||||
.optimize = build_options.optimize,
|
||||
.root_source_file = b.path("src/unit_test.zig"),
|
||||
.test_runner = .{ .path = b.path("src/main_test.zig"), .mode = .simple },
|
||||
.root_module = b.createModule(.{
|
||||
.optimize = build_options.optimize,
|
||||
.root_source_file = b.path("src/unit_test.zig"),
|
||||
.target = build_options.target,
|
||||
.omit_frame_pointer = false,
|
||||
.strip = false,
|
||||
}),
|
||||
.target = build_options.target,
|
||||
.use_llvm = !build_options.no_llvm,
|
||||
.use_lld = if (build_options.os == .mac) false else !build_options.no_llvm,
|
||||
.omit_frame_pointer = false,
|
||||
.strip = false,
|
||||
});
|
||||
configureObj(b, &o, unit_tests);
|
||||
// Setting `linker_allow_shlib_undefined` causes the linker to ignore
|
||||
@@ -320,7 +331,6 @@ pub fn build(b: *Build) !void {
|
||||
var step = b.step("check", "Check for semantic analysis errors");
|
||||
var bun_check_obj = addBunObject(b, &build_options);
|
||||
bun_check_obj.generated_bin = null;
|
||||
// bun_check_obj.use_llvm = false;
|
||||
step.dependOn(&bun_check_obj.step);
|
||||
|
||||
// The default install step will run zig build check. This is so ZLS
|
||||
@@ -493,7 +503,6 @@ fn addMultiCheck(
|
||||
.no_llvm = root_build_options.no_llvm,
|
||||
.enable_asan = root_build_options.enable_asan,
|
||||
.enable_valgrind = root_build_options.enable_valgrind,
|
||||
.enable_fuzzilli = root_build_options.enable_fuzzilli,
|
||||
.use_mimalloc = root_build_options.use_mimalloc,
|
||||
.override_no_export_cpp_apis = root_build_options.override_no_export_cpp_apis,
|
||||
};
|
||||
@@ -607,22 +616,15 @@ fn configureObj(b: *Build, opts: *BunBuildOptions, obj: *Compile) void {
|
||||
obj.llvm_codegen_threads = opts.llvm_codegen_threads orelse 0;
|
||||
}
|
||||
|
||||
obj.no_link_obj = opts.os != .windows;
|
||||
|
||||
obj.no_link_obj = true;
|
||||
|
||||
if (opts.enable_asan and !enableFastBuild(b)) {
|
||||
if (@hasField(Build.Module, "sanitize_address")) {
|
||||
if (opts.enable_fuzzilli) {
|
||||
obj.sanitize_coverage_trace_pc_guard = true;
|
||||
}
|
||||
obj.root_module.sanitize_address = true;
|
||||
} else {
|
||||
const fail_step = b.addFail("asan is not supported on this platform");
|
||||
obj.step.dependOn(&fail_step.step);
|
||||
}
|
||||
} else if (opts.enable_fuzzilli) {
|
||||
const fail_step = b.addFail("fuzzilli requires asan");
|
||||
obj.step.dependOn(&fail_step.step);
|
||||
}
|
||||
obj.bundle_compiler_rt = false;
|
||||
obj.bundle_ubsan_rt = false;
|
||||
@@ -777,13 +779,6 @@ fn addInternalImports(b: *Build, mod: *Module, opts: *BunBuildOptions) void {
|
||||
mod.addImport("cpp", cppImport);
|
||||
cppImport.addImport("bun", mod);
|
||||
}
|
||||
{
|
||||
const ciInfoImport = b.createModule(.{
|
||||
.root_source_file = (std.Build.LazyPath{ .cwd_relative = opts.codegen_path }).path(b, "ci_info.zig"),
|
||||
});
|
||||
mod.addImport("ci_info", ciInfoImport);
|
||||
ciInfoImport.addImport("bun", mod);
|
||||
}
|
||||
inline for (.{
|
||||
.{ .import = "completions-bash", .file = b.path("completions/bun.bash") },
|
||||
.{ .import = "completions-zsh", .file = b.path("completions/bun.zsh") },
|
||||
@@ -809,7 +804,7 @@ fn addInternalImports(b: *Build, mod: *Module, opts: *BunBuildOptions) void {
|
||||
fn propagateImports(source_mod: *Module) !void {
|
||||
var seen = std.AutoHashMap(*Module, void).init(source_mod.owner.graph.arena);
|
||||
defer seen.deinit();
|
||||
var queue = std.array_list.Managed(*Module).init(source_mod.owner.graph.arena);
|
||||
var queue = std.ArrayList(*Module).init(source_mod.owner.graph.arena);
|
||||
defer queue.deinit();
|
||||
try queue.appendSlice(source_mod.import_table.values());
|
||||
while (queue.pop()) |mod| {
|
||||
|
||||
52
bun.lock
52
bun.lock
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "bun",
|
||||
@@ -32,10 +31,17 @@
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19",
|
||||
},
|
||||
},
|
||||
},
|
||||
"overrides": {
|
||||
"@types/bun": "workspace:packages/@types/bun",
|
||||
"@types/node": "24.3.1",
|
||||
"bun-types": "workspace:packages/bun-types",
|
||||
},
|
||||
"packages": {
|
||||
@@ -85,13 +91,13 @@
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
|
||||
|
||||
"@lezer/common": ["@lezer/common@1.3.0", "", {}, "sha512-L9X8uHCYU310o99L3/MpJKYxPzXPOS7S0NmBaM7UO/x2Kb2WbmMLSkfvdr1KxRIFYOpbY0Jhn7CfLSUDzL8arQ=="],
|
||||
"@lezer/common": ["@lezer/common@1.2.3", "", {}, "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA=="],
|
||||
|
||||
"@lezer/cpp": ["@lezer/cpp@1.1.3", "", { "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0" } }, "sha512-ykYvuFQKGsRi6IcE+/hCSGUhb/I4WPjd3ELhEblm2wS2cOznDFzO+ubK2c+ioysOnlZ3EduV+MVQFCPzAIoY3w=="],
|
||||
|
||||
"@lezer/highlight": ["@lezer/highlight@1.2.3", "", { "dependencies": { "@lezer/common": "^1.3.0" } }, "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g=="],
|
||||
"@lezer/highlight": ["@lezer/highlight@1.2.1", "", { "dependencies": { "@lezer/common": "^1.0.0" } }, "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA=="],
|
||||
|
||||
"@lezer/lr": ["@lezer/lr@1.4.3", "", { "dependencies": { "@lezer/common": "^1.0.0" } }, "sha512-yenN5SqAxAPv/qMnpWW0AT7l+SxVrgG+u0tNsRQWqbrz66HIl8DnEbBObvy21J5K7+I1v7gsAnlE2VQ5yYVSeA=="],
|
||||
"@lezer/lr": ["@lezer/lr@1.4.2", "", { "dependencies": { "@lezer/common": "^1.0.0" } }, "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA=="],
|
||||
|
||||
"@octokit/app": ["@octokit/app@14.1.0", "", { "dependencies": { "@octokit/auth-app": "^6.0.0", "@octokit/auth-unauthenticated": "^5.0.0", "@octokit/core": "^5.0.0", "@octokit/oauth-app": "^6.0.0", "@octokit/plugin-paginate-rest": "^9.0.0", "@octokit/types": "^12.0.0", "@octokit/webhooks": "^12.0.4" } }, "sha512-g3uEsGOQCBl1+W1rgfwoRFUIR6PtvB2T1E4RpygeUU5LrLvlOqcxrt5lfykIeRpUPpupreGJUYl70fqMDXdTpw=="],
|
||||
|
||||
@@ -145,7 +151,7 @@
|
||||
|
||||
"@sentry/types": ["@sentry/types@7.120.4", "", {}, "sha512-cUq2hSSe6/qrU6oZsEP4InMI5VVdD86aypE+ENrQ6eZEVLTCYm1w6XhW1NvIu3UuWh7gZec4a9J7AFpYxki88Q=="],
|
||||
|
||||
"@types/aws-lambda": ["@types/aws-lambda@8.10.159", "", {}, "sha512-SAP22WSGNN12OQ8PlCzGzRCZ7QDCwI85dQZbmpz7+mAk+L7j+wI7qnvmdKh+o7A5LaOp6QnOZ2NJphAZQTTHQg=="],
|
||||
"@types/aws-lambda": ["@types/aws-lambda@8.10.152", "", {}, "sha512-soT/c2gYBnT5ygwiHPmd9a1bftj462NWVk2tKCc1PYHSIacB2UwbTS2zYG4jzag1mRDuzg/OjtxQjQ2NKRB6Rw=="],
|
||||
|
||||
"@types/btoa-lite": ["@types/btoa-lite@1.0.2", "", {}, "sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg=="],
|
||||
|
||||
@@ -155,7 +161,9 @@
|
||||
|
||||
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
|
||||
|
||||
"@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="],
|
||||
"@types/node": ["@types/node@24.3.1", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g=="],
|
||||
|
||||
"@types/react": ["@types/react@19.1.10", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg=="],
|
||||
|
||||
"aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="],
|
||||
|
||||
@@ -185,9 +193,11 @@
|
||||
|
||||
"constant-case": ["constant-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", "upper-case": "^2.0.2" } }, "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
"deprecation": ["deprecation@2.3.1", "", {}, "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="],
|
||||
|
||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||
"detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
|
||||
|
||||
"dot-case": ["dot-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w=="],
|
||||
|
||||
@@ -211,29 +221,27 @@
|
||||
|
||||
"jws": ["jws@3.2.2", "", { "dependencies": { "jwa": "^1.4.1", "safe-buffer": "^5.0.1" } }, "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA=="],
|
||||
|
||||
"lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="],
|
||||
"lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="],
|
||||
|
||||
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="],
|
||||
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="],
|
||||
|
||||
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="],
|
||||
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="],
|
||||
|
||||
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="],
|
||||
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="],
|
||||
|
||||
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="],
|
||||
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="],
|
||||
|
||||
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="],
|
||||
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="],
|
||||
|
||||
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="],
|
||||
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="],
|
||||
|
||||
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="],
|
||||
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="],
|
||||
|
||||
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="],
|
||||
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="],
|
||||
|
||||
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="],
|
||||
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="],
|
||||
|
||||
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="],
|
||||
|
||||
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="],
|
||||
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="],
|
||||
|
||||
"lodash.includes": ["lodash.includes@4.3.0", "", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="],
|
||||
|
||||
@@ -289,7 +297,7 @@
|
||||
|
||||
"scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
|
||||
|
||||
"semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||
"semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||
|
||||
"sentence-case": ["sentence-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", "upper-case-first": "^2.0.2" } }, "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg=="],
|
||||
|
||||
@@ -305,7 +313,7 @@
|
||||
|
||||
"uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="],
|
||||
|
||||
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
||||
"undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
|
||||
|
||||
"universal-github-app-jwt": ["universal-github-app-jwt@1.2.0", "", { "dependencies": { "@types/jsonwebtoken": "^9.0.0", "jsonwebtoken": "^9.0.2" } }, "sha512-dncpMpnsKBk0eetwfN8D8OUHGfiDhhJ+mtsbMl+7PfW7mYjiH8LIcqRmYMtzYLgSh47HjfdBtrBwIQ/gizKR3g=="],
|
||||
|
||||
|
||||
@@ -10,4 +10,4 @@ preload = "./test/preload.ts"
|
||||
|
||||
[install]
|
||||
linker = "isolated"
|
||||
minimumReleaseAge = 259200 # three days
|
||||
minimumReleaseAge = 1
|
||||
|
||||
@@ -51,23 +51,6 @@ if(ENABLE_ASAN)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENABLE_FUZZILLI)
|
||||
register_compiler_flags(
|
||||
DESCRIPTION "Enable coverage instrumentation for fuzzing"
|
||||
-fsanitize-coverage=trace-pc-guard
|
||||
)
|
||||
|
||||
register_linker_flags(
|
||||
DESCRIPTION "Link coverage instrumentation"
|
||||
-fsanitize-coverage=trace-pc-guard
|
||||
)
|
||||
|
||||
register_compiler_flags(
|
||||
DESCRIPTION "Enable fuzzilli-specific code"
|
||||
-DFUZZILLI_ENABLED
|
||||
)
|
||||
endif()
|
||||
|
||||
# --- Optimization level ---
|
||||
if(DEBUG)
|
||||
register_compiler_flags(
|
||||
|
||||
@@ -125,8 +125,7 @@ setx(CWD ${CMAKE_SOURCE_DIR})
|
||||
setx(BUILD_PATH ${CMAKE_BINARY_DIR})
|
||||
|
||||
optionx(CACHE_PATH FILEPATH "The path to the cache directory" DEFAULT ${BUILD_PATH}/cache)
|
||||
optionx(CACHE_STRATEGY "auto|distributed|local|none" "The strategy to use for caching" DEFAULT
|
||||
"auto")
|
||||
optionx(CACHE_STRATEGY "read-write|read-only|write-only|none" "The strategy to use for caching" DEFAULT "read-write")
|
||||
|
||||
optionx(CI BOOL "If CI is enabled" DEFAULT OFF)
|
||||
optionx(ENABLE_ANALYSIS BOOL "If static analysis targets should be enabled" DEFAULT OFF)
|
||||
@@ -142,39 +141,9 @@ optionx(TMP_PATH FILEPATH "The path to the temporary directory" DEFAULT ${BUILD_
|
||||
|
||||
# --- Helper functions ---
|
||||
|
||||
# list_filter_out_regex()
|
||||
#
|
||||
# Description:
|
||||
# Filters out elements from a list that match a regex pattern.
|
||||
#
|
||||
# Arguments:
|
||||
# list - The list of strings to traverse
|
||||
# pattern - The regex pattern to filter out
|
||||
# touched - A variable to set if any items were removed
|
||||
function(list_filter_out_regex list pattern touched)
|
||||
set(result_list "${${list}}")
|
||||
set(keep_list)
|
||||
set(was_modified OFF)
|
||||
|
||||
foreach(line IN LISTS result_list)
|
||||
if(line MATCHES "${pattern}")
|
||||
set(was_modified ON)
|
||||
else()
|
||||
list(APPEND keep_list ${line})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(${list} "${keep_list}" PARENT_SCOPE)
|
||||
set(${touched} ${was_modified} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# setenv()
|
||||
# Description:
|
||||
# Sets an environment variable during the build step, and writes it to a .env file.
|
||||
#
|
||||
# See Also:
|
||||
# unsetenv()
|
||||
#
|
||||
# Arguments:
|
||||
# variable string - The variable to set
|
||||
# value string - The value to set the variable to
|
||||
@@ -187,7 +156,13 @@ function(setenv variable value)
|
||||
|
||||
if(EXISTS ${ENV_PATH})
|
||||
file(STRINGS ${ENV_PATH} ENV_FILE ENCODING UTF-8)
|
||||
list_filter_out_regex(ENV_FILE "^${variable}=" ENV_MODIFIED)
|
||||
|
||||
foreach(line ${ENV_FILE})
|
||||
if(line MATCHES "^${variable}=")
|
||||
list(REMOVE_ITEM ENV_FILE ${line})
|
||||
set(ENV_MODIFIED ON)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(ENV_MODIFIED)
|
||||
list(APPEND ENV_FILE "${variable}=${value}")
|
||||
@@ -203,28 +178,6 @@ function(setenv variable value)
|
||||
message(STATUS "Set ENV ${variable}: ${value}")
|
||||
endfunction()
|
||||
|
||||
# See setenv()
|
||||
# Description:
|
||||
# Exact opposite of setenv().
|
||||
# Arguments:
|
||||
# variable string - The variable to unset.
|
||||
# See Also:
|
||||
# setenv()
|
||||
function(unsetenv variable)
|
||||
set(ENV_PATH ${BUILD_PATH}/.env)
|
||||
if(NOT EXISTS ${ENV_PATH})
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(STRINGS ${ENV_PATH} ENV_FILE ENCODING UTF-8)
|
||||
list_filter_out_regex(ENV_FILE "^${variable}=" ENV_MODIFIED)
|
||||
|
||||
if(ENV_MODIFIED)
|
||||
list(JOIN ENV_FILE "\n" ENV_FILE)
|
||||
file(WRITE ${ENV_PATH} ${ENV_FILE})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# satisfies_range()
|
||||
# Description:
|
||||
# Check if a version satisfies a version range or list of ranges
|
||||
@@ -357,7 +310,7 @@ function(find_command)
|
||||
${FIND_VALIDATOR}
|
||||
)
|
||||
|
||||
if(FIND_REQUIRED AND ${FIND_VARIABLE} MATCHES "NOTFOUND")
|
||||
if(NOT FIND_REQUIRED STREQUAL "OFF" AND ${FIND_VARIABLE} MATCHES "NOTFOUND")
|
||||
set(error "Command not found: \"${FIND_NAME}\"")
|
||||
|
||||
if(FIND_VERSION)
|
||||
|
||||
@@ -127,8 +127,6 @@ if (NOT ENABLE_ASAN)
|
||||
set(ENABLE_ZIG_ASAN OFF)
|
||||
endif()
|
||||
|
||||
optionx(ENABLE_FUZZILLI BOOL "If fuzzilli support should be enabled" DEFAULT OFF)
|
||||
|
||||
if(RELEASE AND LINUX AND CI AND NOT ENABLE_ASSERTIONS AND NOT ENABLE_ASAN)
|
||||
set(DEFAULT_LTO ON)
|
||||
else()
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
"paths": ["src/bake/*.ts", "src/bake/*/*.{ts,css}"],
|
||||
"exclude": ["src/bake/generated.ts"]
|
||||
},
|
||||
{
|
||||
"output": "BunFrameworkReactSources.txt",
|
||||
"paths": ["packages/bun-framework-react/*.{ts,tsx,js,jsx}", "packages/bun-framework-react/src/**/*.{ts,tsx,js,jsx}"]
|
||||
},
|
||||
{
|
||||
"output": "BindgenSources.txt",
|
||||
"paths": ["src/**/*.bind.ts"]
|
||||
|
||||
@@ -34,6 +34,26 @@ register_command(
|
||||
ALWAYS_RUN
|
||||
)
|
||||
|
||||
if(GIT_CHANGED_SOURCES)
|
||||
set(CLANG_FORMAT_CHANGED_SOURCES)
|
||||
foreach(source ${CLANG_FORMAT_SOURCES})
|
||||
list(FIND GIT_CHANGED_SOURCES ${source} index)
|
||||
if(NOT ${index} EQUAL -1)
|
||||
list(APPEND CLANG_FORMAT_CHANGED_SOURCES ${source})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(CLANG_FORMAT_CHANGED_SOURCES)
|
||||
set(CLANG_FORMAT_DIFF_COMMAND ${CLANG_FORMAT_PROGRAM}
|
||||
-i # edits files in-place
|
||||
--verbose
|
||||
${CLANG_FORMAT_CHANGED_SOURCES}
|
||||
)
|
||||
else()
|
||||
set(CLANG_FORMAT_DIFF_COMMAND ${CMAKE_COMMAND} -E echo "No changed files for clang-format")
|
||||
endif()
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
clang-format-diff
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
set(CLANG_TIDY_SOURCES ${BUN_C_SOURCES} ${BUN_CXX_SOURCES})
|
||||
|
||||
set(CLANG_TIDY_COMMAND ${CLANG_TIDY_PROGRAM}
|
||||
-p ${BUILD_PATH}
|
||||
-p ${BUILD_PATH}
|
||||
--config-file=${CWD}/.clang-tidy
|
||||
)
|
||||
|
||||
@@ -40,6 +40,27 @@ register_command(
|
||||
ALWAYS_RUN
|
||||
)
|
||||
|
||||
if(GIT_CHANGED_SOURCES)
|
||||
set(CLANG_TIDY_CHANGED_SOURCES)
|
||||
foreach(source ${CLANG_TIDY_SOURCES})
|
||||
list(FIND GIT_CHANGED_SOURCES ${source} index)
|
||||
if(NOT ${index} EQUAL -1)
|
||||
list(APPEND CLANG_TIDY_CHANGED_SOURCES ${source})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(CLANG_TIDY_CHANGED_SOURCES)
|
||||
set(CLANG_TIDY_DIFF_COMMAND ${CLANG_TIDY_PROGRAM}
|
||||
${CLANG_TIDY_CHANGED_SOURCES}
|
||||
--fix
|
||||
--fix-errors
|
||||
--fix-notes
|
||||
)
|
||||
else()
|
||||
set(CLANG_TIDY_DIFF_COMMAND ${CMAKE_COMMAND} -E echo "No changed files for clang-tidy")
|
||||
endif()
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
clang-tidy-diff
|
||||
|
||||
@@ -92,6 +92,26 @@ register_command(
|
||||
ALWAYS_RUN
|
||||
)
|
||||
|
||||
if(GIT_CHANGED_SOURCES)
|
||||
set(PRETTIER_CHANGED_SOURCES)
|
||||
foreach(source ${PRETTIER_SOURCES})
|
||||
list(FIND GIT_CHANGED_SOURCES ${source} index)
|
||||
if(NOT ${index} EQUAL -1)
|
||||
list(APPEND PRETTIER_CHANGED_SOURCES ${source})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(PRETTIER_CHANGED_SOURCES)
|
||||
set(PRETTIER_DIFF_COMMAND ${PRETTIER_COMMAND}
|
||||
--write
|
||||
--plugin=prettier-plugin-organize-imports
|
||||
${PRETTIER_CHANGED_SOURCES}
|
||||
)
|
||||
else()
|
||||
set(PRETTIER_DIFF_COMMAND ${CMAKE_COMMAND} -E echo "No changed files for prettier")
|
||||
endif()
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
prettier-diff
|
||||
|
||||
@@ -25,6 +25,25 @@ register_command(
|
||||
ALWAYS_RUN
|
||||
)
|
||||
|
||||
if(GIT_CHANGED_SOURCES)
|
||||
set(ZIG_FORMAT_CHANGED_SOURCES)
|
||||
foreach(source ${ZIG_FORMAT_SOURCES})
|
||||
list(FIND GIT_CHANGED_SOURCES ${source} index)
|
||||
if(NOT ${index} EQUAL -1)
|
||||
list(APPEND ZIG_FORMAT_CHANGED_SOURCES ${source})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(ZIG_FORMAT_CHANGED_SOURCES)
|
||||
set(ZIG_FORMAT_DIFF_COMMAND ${ZIG_EXECUTABLE}
|
||||
fmt
|
||||
${ZIG_FORMAT_CHANGED_SOURCES}
|
||||
)
|
||||
else()
|
||||
set(ZIG_FORMAT_DIFF_COMMAND ${CMAKE_COMMAND} -E echo "No changed files for zig-format")
|
||||
endif()
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
zig-format-diff
|
||||
|
||||
@@ -317,10 +317,6 @@ set(BUN_CPP_OUTPUTS
|
||||
${CODEGEN_PATH}/cpp.zig
|
||||
)
|
||||
|
||||
set(BUN_CI_INFO_OUTPUTS
|
||||
${CODEGEN_PATH}/ci_info.zig
|
||||
)
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
bun-cppbind
|
||||
@@ -338,21 +334,6 @@ register_command(
|
||||
${BUN_CPP_OUTPUTS}
|
||||
)
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
bun-ci-info
|
||||
COMMENT
|
||||
"Generating CI info"
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE}
|
||||
${CWD}/src/codegen/ci_info.ts
|
||||
${CODEGEN_PATH}/ci_info.zig
|
||||
SOURCES
|
||||
${BUN_JAVASCRIPT_CODEGEN_SOURCES}
|
||||
OUTPUTS
|
||||
${BUN_CI_INFO_OUTPUTS}
|
||||
)
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
bun-js-modules
|
||||
@@ -631,7 +612,6 @@ set(BUN_ZIG_GENERATED_SOURCES
|
||||
${BUN_ZIG_GENERATED_CLASSES_OUTPUTS}
|
||||
${BUN_JAVASCRIPT_OUTPUTS}
|
||||
${BUN_CPP_OUTPUTS}
|
||||
${BUN_CI_INFO_OUTPUTS}
|
||||
${BUN_BINDGENV2_ZIG_OUTPUTS}
|
||||
)
|
||||
|
||||
@@ -695,7 +675,6 @@ register_command(
|
||||
-Dcpu=${ZIG_CPU}
|
||||
-Denable_logs=$<IF:$<BOOL:${ENABLE_LOGS}>,true,false>
|
||||
-Denable_asan=$<IF:$<BOOL:${ENABLE_ZIG_ASAN}>,true,false>
|
||||
-Denable_fuzzilli=$<IF:$<BOOL:${ENABLE_FUZZILLI}>,true,false>
|
||||
-Denable_valgrind=$<IF:$<BOOL:${ENABLE_VALGRIND}>,true,false>
|
||||
-Duse_mimalloc=$<IF:$<BOOL:${USE_MIMALLOC_AS_DEFAULT_ALLOCATOR}>,true,false>
|
||||
-Dllvm_codegen_threads=${LLVM_ZIG_CODEGEN_THREADS}
|
||||
|
||||
@@ -49,6 +49,3 @@ else()
|
||||
setenv(CCACHE_MAXSIZE 100G)
|
||||
setenv(CCACHE_SLOPPINESS "pch_defines,time_macros,locale,random_seed,clang_index_store,gcno_cwd")
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -4,9 +4,41 @@ find_command(
|
||||
COMMAND
|
||||
git
|
||||
REQUIRED
|
||||
${CI}
|
||||
OFF
|
||||
)
|
||||
|
||||
if(NOT GIT_PROGRAM)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(GIT_DIFF_COMMAND ${GIT_PROGRAM} diff --no-color --name-only --diff-filter=AMCR origin/main HEAD)
|
||||
|
||||
execute_process(
|
||||
COMMAND
|
||||
${GIT_DIFF_COMMAND}
|
||||
WORKING_DIRECTORY
|
||||
${CWD}
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
OUTPUT_VARIABLE
|
||||
GIT_DIFF
|
||||
ERROR_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_VARIABLE
|
||||
GIT_DIFF_ERROR
|
||||
RESULT_VARIABLE
|
||||
GIT_DIFF_RESULT
|
||||
)
|
||||
|
||||
if(NOT GIT_DIFF_RESULT EQUAL 0)
|
||||
message(WARNING "Command failed: ${GIT_DIFF_COMMAND} ${GIT_DIFF_ERROR}")
|
||||
return()
|
||||
endif()
|
||||
|
||||
string(REPLACE "\n" ";" GIT_CHANGED_SOURCES "${GIT_DIFF}")
|
||||
|
||||
if(CI)
|
||||
set(GIT_CHANGED_SOURCES "${GIT_CHANGED_SOURCES}")
|
||||
message(STATUS "Set GIT_CHANGED_SOURCES: ${GIT_CHANGED_SOURCES}")
|
||||
endif()
|
||||
|
||||
list(TRANSFORM GIT_CHANGED_SOURCES PREPEND ${CWD}/)
|
||||
list(LENGTH GIT_CHANGED_SOURCES GIT_CHANGED_SOURCES_COUNT)
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
# Setup sccache as the C and C++ compiler launcher to speed up builds by caching
|
||||
if(CACHE_STRATEGY STREQUAL "none")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(SCCACHE_SHARED_CACHE_REGION "us-west-1")
|
||||
set(SCCACHE_SHARED_CACHE_BUCKET "bun-build-sccache-store")
|
||||
|
||||
# Function to check if the system AWS credentials have access to the sccache S3 bucket.
|
||||
function(check_aws_credentials OUT_VAR)
|
||||
# Install dependencies first
|
||||
execute_process(
|
||||
COMMAND ${BUN_EXECUTABLE} install --frozen-lockfile
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/scripts/build-cache
|
||||
RESULT_VARIABLE INSTALL_EXIT_CODE
|
||||
OUTPUT_VARIABLE INSTALL_OUTPUT
|
||||
ERROR_VARIABLE INSTALL_ERROR
|
||||
)
|
||||
|
||||
if(NOT INSTALL_EXIT_CODE EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to install dependencies in scripts/build-cache\n"
|
||||
"Exit code: ${INSTALL_EXIT_CODE}\n"
|
||||
"Output: ${INSTALL_OUTPUT}\n"
|
||||
"Error: ${INSTALL_ERROR}")
|
||||
endif()
|
||||
|
||||
# Check AWS credentials
|
||||
execute_process(
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE}
|
||||
run
|
||||
have-access.ts
|
||||
--bucket ${SCCACHE_SHARED_CACHE_BUCKET}
|
||||
--region ${SCCACHE_SHARED_CACHE_REGION}
|
||||
WORKING_DIRECTORY
|
||||
${CMAKE_SOURCE_DIR}/scripts/build-cache
|
||||
RESULT_VARIABLE HAVE_ACCESS_EXIT_CODE
|
||||
)
|
||||
|
||||
if(HAVE_ACCESS_EXIT_CODE EQUAL 0)
|
||||
set(HAS_CREDENTIALS TRUE)
|
||||
else()
|
||||
set(HAS_CREDENTIALS FALSE)
|
||||
endif()
|
||||
|
||||
set(${OUT_VAR} ${HAS_CREDENTIALS} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Configure sccache to use the local cache only.
|
||||
function(sccache_configure_local_filesystem)
|
||||
unsetenv(SCCACHE_BUCKET)
|
||||
unsetenv(SCCACHE_REGION)
|
||||
setenv(SCCACHE_DIR "${CACHE_PATH}/sccache")
|
||||
endfunction()
|
||||
|
||||
# Configure sccache to use the distributed cache (S3 + local).
|
||||
function(sccache_configure_distributed)
|
||||
setenv(SCCACHE_BUCKET "${SCCACHE_SHARED_CACHE_BUCKET}")
|
||||
setenv(SCCACHE_REGION "${SCCACHE_SHARED_CACHE_REGION}")
|
||||
setenv(SCCACHE_DIR "${CACHE_PATH}/sccache")
|
||||
endfunction()
|
||||
|
||||
function(sccache_configure_environment_ci)
|
||||
if(CACHE_STRATEGY STREQUAL "auto" OR CACHE_STRATEGY STREQUAL "distributed")
|
||||
check_aws_credentials(HAS_AWS_CREDENTIALS)
|
||||
if(HAS_AWS_CREDENTIALS)
|
||||
sccache_configure_distributed()
|
||||
message(NOTICE "sccache: Using distributed cache strategy.")
|
||||
else()
|
||||
message(FATAL_ERROR "CI CACHE_STRATEGY is set to '${CACHE_STRATEGY}', but no valid AWS "
|
||||
"credentials were found. Note that 'auto' requires AWS credentials to access the shared "
|
||||
"cache in CI.")
|
||||
endif()
|
||||
elseif(CACHE_STRATEGY STREQUAL "local")
|
||||
# We disallow this because we want our CI runs to always used the shared cache to accelerate
|
||||
# builds.
|
||||
# none, distributed and auto are all okay.
|
||||
#
|
||||
# If local is configured, it's as good as "none", so this is probably user error.
|
||||
message(FATAL_ERROR "CI CACHE_STRATEGY is set to 'local', which is not allowed.")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(sccache_configure_environment_developer)
|
||||
# Local environments can use any strategy they like. S3 is set up in such a way so as to clean
|
||||
# itself from old entries automatically.
|
||||
if (CACHE_STRATEGY STREQUAL "auto" OR CACHE_STRATEGY STREQUAL "local")
|
||||
# In the local environment, we prioritize using the local cache. This is because sccache takes
|
||||
# into consideration the whole absolute path of the files being compiled, and it's very
|
||||
# unlikely users will have the same absolute paths on their local machines.
|
||||
sccache_configure_local_filesystem()
|
||||
message(NOTICE "sccache: Using local cache strategy.")
|
||||
elseif(CACHE_STRATEGY STREQUAL "distributed")
|
||||
check_aws_credentials(HAS_AWS_CREDENTIALS)
|
||||
if(HAS_AWS_CREDENTIALS)
|
||||
sccache_configure_distributed()
|
||||
message(NOTICE "sccache: Using distributed cache strategy.")
|
||||
else()
|
||||
message(FATAL_ERROR "CACHE_STRATEGY is set to 'distributed', but no valid AWS credentials "
|
||||
"were found.")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
find_command(VARIABLE SCCACHE_PROGRAM COMMAND sccache REQUIRED ${CI})
|
||||
if(NOT SCCACHE_PROGRAM)
|
||||
message(WARNING "sccache not found. Your builds will be slower.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(SCCACHE_ARGS CMAKE_C_COMPILER_LAUNCHER CMAKE_CXX_COMPILER_LAUNCHER)
|
||||
foreach(arg ${SCCACHE_ARGS})
|
||||
setx(${arg} ${SCCACHE_PROGRAM})
|
||||
list(APPEND CMAKE_ARGS -D${arg}=${${arg}})
|
||||
endforeach()
|
||||
|
||||
setenv(SCCACHE_LOG "info")
|
||||
|
||||
if (CI)
|
||||
sccache_configure_environment_ci()
|
||||
else()
|
||||
sccache_configure_environment_developer()
|
||||
endif()
|
||||
@@ -20,7 +20,7 @@ else()
|
||||
unsupported(CMAKE_SYSTEM_NAME)
|
||||
endif()
|
||||
|
||||
set(ZIG_COMMIT "c1423ff3fc7064635773a4a4616c5bf986eb00fe")
|
||||
set(ZIG_COMMIT "55fdbfa0c86be86b68d43a4ba761e6909eb0d7b2")
|
||||
optionx(ZIG_TARGET STRING "The zig target to use" DEFAULT ${DEFAULT_ZIG_TARGET})
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
@@ -55,7 +55,13 @@ optionx(ZIG_OBJECT_FORMAT "obj|bc" "Output file format for Zig object files" DEF
|
||||
optionx(ZIG_LOCAL_CACHE_DIR FILEPATH "The path to local the zig cache directory" DEFAULT ${CACHE_PATH}/zig/local)
|
||||
optionx(ZIG_GLOBAL_CACHE_DIR FILEPATH "The path to the global zig cache directory" DEFAULT ${CACHE_PATH}/zig/global)
|
||||
|
||||
optionx(ZIG_COMPILER_SAFE BOOL "Download a ReleaseSafe build of the Zig compiler." DEFAULT ${CI})
|
||||
if(CI)
|
||||
set(ZIG_COMPILER_SAFE_DEFAULT ON)
|
||||
else()
|
||||
set(ZIG_COMPILER_SAFE_DEFAULT OFF)
|
||||
endif()
|
||||
|
||||
optionx(ZIG_COMPILER_SAFE BOOL "Download a ReleaseSafe build of the Zig compiler." DEFAULT ${ZIG_COMPILER_SAFE_DEFAULT})
|
||||
|
||||
setenv(ZIG_LOCAL_CACHE_DIR ${ZIG_LOCAL_CACHE_DIR})
|
||||
setenv(ZIG_GLOBAL_CACHE_DIR ${ZIG_GLOBAL_CACHE_DIR})
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<p align="center">
|
||||
<a href="https://bun.com">
|
||||
<img src="https://github.com/user-attachments/assets/50282090-adfd-4ddb-9e27-c30753c6b161" alt="Logo" height="170" />
|
||||
</a>
|
||||
</p>
|
||||
<h1 align="center">Bun Documentation</h1>
|
||||
|
||||
Official documentation for Bun: the fast, all-in-one JavaScript runtime.
|
||||
|
||||
## Development
|
||||
|
||||
Install the [Mintlify CLI](https://www.npmjs.com/package/mint) to preview the documentation locally:
|
||||
|
||||
```bash
|
||||
bun install -g mint
|
||||
```
|
||||
|
||||
Run the development server:
|
||||
|
||||
```bash
|
||||
mint dev
|
||||
```
|
||||
|
||||
The site will be available at `http://localhost:3000`.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please open an issue or submit a pull request.
|
||||
@@ -1,23 +1,41 @@
|
||||
---
|
||||
title: Binary Data
|
||||
description: Working with binary data in JavaScript
|
||||
---
|
||||
|
||||
This page is intended as an introduction to working with binary data in JavaScript. Bun implements a number of data types and utilities for working with binary data, most of which are Web-standard. Any Bun-specific APIs will be noted as such.
|
||||
|
||||
Below is a quick "cheat sheet" that doubles as a table of contents. Click an item in the left column to jump to that section.
|
||||
|
||||
| Class | Description |
|
||||
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [`TypedArray`](#typedarray) | A family of classes that provide an `Array`-like interface for interacting with binary data. Includes `Uint8Array`, `Uint16Array`, `Int8Array`, and more. |
|
||||
| [`Buffer`](#buffer) | A subclass of `Uint8Array` that implements a wide range of convenience methods. Unlike the other elements in this table, this is a Node.js API (which Bun implements). It can't be used in the browser. |
|
||||
| [`DataView`](#dataview) | A class that provides a `get/set` API for writing some number of bytes to an `ArrayBuffer` at a particular byte offset. Often used reading or writing binary protocols. |
|
||||
| [`Blob`](#blob) | A readonly blob of binary data usually representing a file. Has a MIME `type`, a `size`, and methods for converting to `ArrayBuffer`, `ReadableStream`, and string. |
|
||||
| [`File`](#file) | A subclass of `Blob` that represents a file. Has a `name` and `lastModified` timestamp. There is experimental support in Node.js v20. |
|
||||
| [`BunFile`](#bunfile) | _Bun only_. A subclass of `Blob` that represents a lazily-loaded file on disk. Created with `Bun.file(path)`. |
|
||||
{% table %}
|
||||
|
||||
---
|
||||
|
||||
- [`TypedArray`](#typedarray)
|
||||
- A family of classes that provide an `Array`-like interface for interacting with binary data. Includes `Uint8Array`, `Uint16Array`, `Int8Array`, and more.
|
||||
|
||||
---
|
||||
|
||||
- [`Buffer`](#buffer)
|
||||
- A subclass of `Uint8Array` that implements a wide range of convenience methods. Unlike the other elements in this table, this is a Node.js API (which Bun implements). It can't be used in the browser.
|
||||
|
||||
---
|
||||
|
||||
- [`DataView`](#dataview)
|
||||
- A class that provides a `get/set` API for writing some number of bytes to an `ArrayBuffer` at a particular byte offset. Often used reading or writing binary protocols.
|
||||
|
||||
---
|
||||
|
||||
- [`Blob`](#blob)
|
||||
- A readonly blob of binary data usually representing a file. Has a MIME `type`, a `size`, and methods for converting to `ArrayBuffer`, `ReadableStream`, and string.
|
||||
|
||||
---
|
||||
|
||||
- [`File`](#file)
|
||||
- A subclass of `Blob` that represents a file. Has a `name` and `lastModified` timestamp. There is experimental support in Node.js v20.
|
||||
|
||||
---
|
||||
|
||||
- [`BunFile`](#bunfile)
|
||||
- _Bun only_. A subclass of `Blob` that represents a lazily-loaded file on disk. Created with `Bun.file(path)`.
|
||||
|
||||
{% /table %}
|
||||
|
||||
## `ArrayBuffer` and views
|
||||
|
||||
Until 2009, there was no language-native way to store and manipulate binary data in JavaScript. ECMAScript v5 introduced a range of new mechanisms for this. The most fundamental building block is `ArrayBuffer`, a simple data structure that represents a sequence of bytes in memory.
|
||||
@@ -80,28 +98,70 @@ dv.setFloat64(0, 3.1415);
|
||||
|
||||
The following methods are available on `DataView`:
|
||||
|
||||
| Getters | Setters |
|
||||
| -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [`getBigInt64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getBigInt64) | [`setBigInt64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setBigInt64) |
|
||||
| [`getBigUint64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getBigUint64) | [`setBigUint64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setBigUint64) |
|
||||
| [`getFloat32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat32) | [`setFloat32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat32) |
|
||||
| [`getFloat64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat64) | [`setFloat64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat64) |
|
||||
| [`getInt16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt16) | [`setInt16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt16) |
|
||||
| [`getInt32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt32) | [`setInt32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt32) |
|
||||
| [`getInt8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt8) | [`setInt8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt8) |
|
||||
| [`getUint16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint16) | [`setUint16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint16) |
|
||||
| [`getUint32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint32) | [`setUint32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint32) |
|
||||
| [`getUint8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint8) | [`setUint8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint8) |
|
||||
{% table %}
|
||||
|
||||
- Getters
|
||||
- Setters
|
||||
|
||||
---
|
||||
|
||||
- [`getBigInt64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getBigInt64)
|
||||
- [`setBigInt64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setBigInt64)
|
||||
|
||||
---
|
||||
|
||||
- [`getBigUint64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getBigUint64)
|
||||
- [`setBigUint64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setBigUint64)
|
||||
|
||||
---
|
||||
|
||||
- [`getFloat32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat32)
|
||||
- [`setFloat32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat32)
|
||||
|
||||
---
|
||||
|
||||
- [`getFloat64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat64)
|
||||
- [`setFloat64()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat64)
|
||||
|
||||
---
|
||||
|
||||
- [`getInt16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt16)
|
||||
- [`setInt16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt16)
|
||||
|
||||
---
|
||||
|
||||
- [`getInt32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt32)
|
||||
- [`setInt32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt32)
|
||||
|
||||
---
|
||||
|
||||
- [`getInt8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt8)
|
||||
- [`setInt8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt8)
|
||||
|
||||
---
|
||||
|
||||
- [`getUint16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint16)
|
||||
- [`setUint16()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint16)
|
||||
|
||||
---
|
||||
|
||||
- [`getUint32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint32)
|
||||
- [`setUint32()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint32)
|
||||
|
||||
---
|
||||
|
||||
- [`getUint8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint8)
|
||||
- [`setUint8()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint8)
|
||||
|
||||
{% /table %}
|
||||
|
||||
### `TypedArray`
|
||||
|
||||
Typed arrays are a family of classes that provide an `Array`-like interface for interacting with data in an `ArrayBuffer`. Whereas a `DataView` lets you write numbers of varying size at a particular offset, a `TypedArray` interprets the underlying bytes as an array of numbers, each of a fixed size.
|
||||
|
||||
<Note>
|
||||
It's common to refer to this family of classes collectively by their shared superclass `TypedArray`. This class as
|
||||
_internal_ to JavaScript; you can't directly create instances of it, and `TypedArray` is not defined in the global
|
||||
scope. Think of it as an `interface` or an abstract class.
|
||||
</Note>
|
||||
{% callout %}
|
||||
**Note** — It's common to refer to this family of classes collectively by their shared superclass `TypedArray`. This class as _internal_ to JavaScript; you can't directly create instances of it, and `TypedArray` is not defined in the global scope. Think of it as an `interface` or an abstract class.
|
||||
{% /callout %}
|
||||
|
||||
```ts
|
||||
const buffer = new ArrayBuffer(3);
|
||||
@@ -122,32 +182,121 @@ The top row contains the raw bytes, and the later rows contain how these bytes w
|
||||
|
||||
The following classes are typed arrays, along with a description of how they interpret the bytes in an `ArrayBuffer`:
|
||||
|
||||
Here's the first table formatted as a markdown table:
|
||||
{% table %}
|
||||
|
||||
| Class | Description |
|
||||
| ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Every one (1) byte is interpreted as an unsigned 8-bit integer. Range 0 to 255. |
|
||||
| [`Uint16Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint16Array) | Every two (2) bytes are interpreted as an unsigned 16-bit integer. Range 0 to 65535. |
|
||||
| [`Uint32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array) | Every four (4) bytes are interpreted as an unsigned 32-bit integer. Range 0 to 4294967295. |
|
||||
| [`Int8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int8Array) | Every one (1) byte is interpreted as a signed 8-bit integer. Range -128 to 127. |
|
||||
| [`Int16Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int16Array) | Every two (2) bytes are interpreted as a signed 16-bit integer. Range -32768 to 32767. |
|
||||
| [`Int32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int32Array) | Every four (4) bytes are interpreted as a signed 32-bit integer. Range -2147483648 to 2147483647. |
|
||||
| [`Float16Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float16Array) | Every two (2) bytes are interpreted as a 16-bit floating point number. Range -6.104e5 to 6.55e4. |
|
||||
| [`Float32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array) | Every four (4) bytes are interpreted as a 32-bit floating point number. Range -3.4e38 to 3.4e38. |
|
||||
| [`Float64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float64Array) | Every eight (8) bytes are interpreted as a 64-bit floating point number. Range -1.7e308 to 1.7e308. |
|
||||
| [`BigInt64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt64Array) | Every eight (8) bytes are interpreted as a signed `BigInt`. Range -9223372036854775808 to 9223372036854775807 (though `BigInt` is capable of representing larger numbers). |
|
||||
| [`BigUint64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigUint64Array) | Every eight (8) bytes are interpreted as an unsigned `BigInt`. Range 0 to 18446744073709551615 (though `BigInt` is capable of representing larger numbers). |
|
||||
| [`Uint8ClampedArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray) | Same as `Uint8Array`, but automatically "clamps" to the range 0-255 when assigning a value to an element. |
|
||||
- Class
|
||||
- Description
|
||||
|
||||
---
|
||||
|
||||
- [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array)
|
||||
- Every one (1) byte is interpreted as an unsigned 8-bit integer. Range 0 to 255.
|
||||
|
||||
---
|
||||
|
||||
- [`Uint16Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint16Array)
|
||||
- Every two (2) bytes are interpreted as an unsigned 16-bit integer. Range 0 to 65535.
|
||||
|
||||
---
|
||||
|
||||
- [`Uint32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array)
|
||||
- Every four (4) bytes are interpreted as an unsigned 32-bit integer. Range 0 to 4294967295.
|
||||
|
||||
---
|
||||
|
||||
- [`Int8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int8Array)
|
||||
- Every one (1) byte is interpreted as a signed 8-bit integer. Range -128 to 127.
|
||||
|
||||
---
|
||||
|
||||
- [`Int16Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int16Array)
|
||||
- Every two (2) bytes are interpreted as a signed 16-bit integer. Range -32768 to 32767.
|
||||
|
||||
---
|
||||
|
||||
- [`Int32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int32Array)
|
||||
- Every four (4) bytes are interpreted as a signed 32-bit integer. Range -2147483648 to 2147483647.
|
||||
|
||||
---
|
||||
|
||||
- [`Float16Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float16Array)
|
||||
- Every two (2) bytes are interpreted as a 16-bit floating point number. Range -6.104e5 to 6.55e4.
|
||||
|
||||
---
|
||||
|
||||
- [`Float32Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float32Array)
|
||||
- Every four (4) bytes are interpreted as a 32-bit floating point number. Range -3.4e38 to 3.4e38.
|
||||
|
||||
---
|
||||
|
||||
- [`Float64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Float64Array)
|
||||
- Every eight (8) bytes are interpreted as a 64-bit floating point number. Range -1.7e308 to 1.7e308.
|
||||
|
||||
---
|
||||
|
||||
- [`BigInt64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt64Array)
|
||||
- Every eight (8) bytes are interpreted as a signed `BigInt`. Range -9223372036854775808 to 9223372036854775807 (though `BigInt` is capable of representing larger numbers).
|
||||
|
||||
---
|
||||
|
||||
- [`BigUint64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigUint64Array)
|
||||
- Every eight (8) bytes are interpreted as an unsigned `BigInt`. Range 0 to 18446744073709551615 (though `BigInt` is capable of representing larger numbers).
|
||||
|
||||
---
|
||||
|
||||
- [`Uint8ClampedArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray)
|
||||
- Same as `Uint8Array`, but automatically "clamps" to the range 0-255 when assigning a value to an element.
|
||||
|
||||
{% /table %}
|
||||
|
||||
The table below demonstrates how the bytes in an `ArrayBuffer` are interpreted when viewed using different typed array classes.
|
||||
|
||||
| | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 |
|
||||
| ---------------- | ------------------- | ---------- | ------------------- | ---------- | -------------------- | ---------- | -------------------- | ---------- |
|
||||
| `ArrayBuffer` | `00000000` | `00000001` | `00000010` | `00000011` | `00000100` | `00000101` | `00000110` | `00000111` |
|
||||
| `Uint8Array` | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|
||||
| `Uint16Array` | 256 (`1 * 256 + 0`) | | 770 (`3 * 256 + 2`) | | 1284 (`5 * 256 + 4`) | | 1798 (`7 * 256 + 6`) | |
|
||||
| `Uint32Array` | 50462976 | | | | 117835012 | | | |
|
||||
| `BigUint64Array` | 506097522914230528n | | | | | | | |
|
||||
{% table %}
|
||||
|
||||
---
|
||||
|
||||
- `ArrayBuffer`
|
||||
- `00000000`
|
||||
- `00000001`
|
||||
- `00000010`
|
||||
- `00000011`
|
||||
- `00000100`
|
||||
- `00000101`
|
||||
- `00000110`
|
||||
- `00000111`
|
||||
|
||||
---
|
||||
|
||||
- `Uint8Array`
|
||||
- 0
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 4
|
||||
- 5
|
||||
- 6
|
||||
- 7
|
||||
|
||||
---
|
||||
|
||||
- `Uint16Array`
|
||||
- 256 (`1 * 256 + 0`) {% colspan=2 %}
|
||||
- 770 (`3 * 256 + 2`) {% colspan=2 %}
|
||||
- 1284 (`5 * 256 + 4`) {% colspan=2 %}
|
||||
- 1798 (`7 * 256 + 6`) {% colspan=2 %}
|
||||
|
||||
---
|
||||
|
||||
- `Uint32Array`
|
||||
- 50462976 {% colspan=4 %}
|
||||
- 117835012 {% colspan=4 %}
|
||||
|
||||
---
|
||||
|
||||
- `BigUint64Array`
|
||||
- 506097522914230528n {% colspan=8 %}
|
||||
|
||||
{% /table %}
|
||||
|
||||
To create a typed array from a pre-defined `ArrayBuffer`:
|
||||
|
||||
@@ -319,7 +468,9 @@ const file = Bun.file("index.txt");
|
||||
|
||||
### `File`
|
||||
|
||||
<Warning>Browser only. Experimental support in Node.js 20.</Warning>
|
||||
{% callout %}
|
||||
Browser only. Experimental support in Node.js 20.
|
||||
{% /callout %}
|
||||
|
||||
[`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) is a subclass of `Blob` that adds a `name` and `lastModified` property. It's commonly used in the browser to represent files uploaded via a `<input type="file">` element. Node.js and Bun implement `File`.
|
||||
|
||||
@@ -339,21 +490,15 @@ const file = new File(["<html>Hello</html>"], "index.html", {
|
||||
|
||||
Refer to the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/Blob) for complete docs information.
|
||||
|
||||
---
|
||||
|
||||
## Streams
|
||||
|
||||
Streams are an important abstraction for working with binary data without loading it all into memory at once. They are commonly used for reading and writing files, sending and receiving network requests, and processing large amounts of data.
|
||||
|
||||
Bun implements the Web APIs [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) and [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream).
|
||||
|
||||
<Note>
|
||||
Bun also implements the `node:stream` module, including
|
||||
[`Readable`](https://nodejs.org/api/stream.html#stream_readable_streams),
|
||||
[`Writable`](https://nodejs.org/api/stream.html#stream_writable_streams), and
|
||||
[`Duplex`](https://nodejs.org/api/stream.html#stream_duplex_and_transform_streams). For complete documentation, refer
|
||||
to the Node.js docs.
|
||||
</Note>
|
||||
{% callout %}
|
||||
Bun also implements the `node:stream` module, including [`Readable`](https://nodejs.org/api/stream.html#stream_readable_streams), [`Writable`](https://nodejs.org/api/stream.html#stream_writable_streams), and [`Duplex`](https://nodejs.org/api/stream.html#stream_duplex_and_transform_streams). For complete documentation, refer to the Node.js docs.
|
||||
{% /callout %}
|
||||
|
||||
To create a simple readable stream:
|
||||
|
||||
@@ -372,15 +517,12 @@ The contents of this stream can be read chunk-by-chunk with `for await` syntax.
|
||||
```ts
|
||||
for await (const chunk of stream) {
|
||||
console.log(chunk);
|
||||
// => "hello"
|
||||
// => "world"
|
||||
}
|
||||
|
||||
// => "hello"
|
||||
// => "world"
|
||||
```
|
||||
|
||||
For a more complete discussion of streams in Bun, see [API > Streams](/runtime/streams).
|
||||
|
||||
---
|
||||
For a more complete discussion of streams in Bun, see [API > Streams](https://bun.com/docs/api/streams).
|
||||
|
||||
## Conversion
|
||||
|
||||
@@ -432,6 +574,12 @@ Array.from(new Uint8Array(buf));
|
||||
new Blob([buf], { type: "text/plain" });
|
||||
```
|
||||
|
||||
<!-- #### To `File`
|
||||
|
||||
```ts
|
||||
new File([buf], "filename.txt", { type: "text/plain", lastModified: Date.now() });
|
||||
``` -->
|
||||
|
||||
#### To `ReadableStream`
|
||||
|
||||
The following snippet creates a `ReadableStream` and enqueues the entire `ArrayBuffer` as a single chunk.
|
||||
@@ -445,7 +593,7 @@ new ReadableStream({
|
||||
});
|
||||
```
|
||||
|
||||
<Accordion title="With chunking">
|
||||
{% details summary="With chunking" %}
|
||||
To stream the `ArrayBuffer` in chunks, use a `Uint8Array` view and enqueue each chunk.
|
||||
|
||||
```ts
|
||||
@@ -462,7 +610,7 @@ new ReadableStream({
|
||||
});
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
|
||||
### From `TypedArray`
|
||||
|
||||
@@ -509,6 +657,12 @@ Array.from(arr);
|
||||
new Blob([arr.buffer], { type: "text/plain" });
|
||||
```
|
||||
|
||||
<!-- #### To `File`
|
||||
|
||||
```ts
|
||||
new File([arr.buffer], "filename.txt", { type: "text/plain", lastModified: Date.now() });
|
||||
``` -->
|
||||
|
||||
#### To `ReadableStream`
|
||||
|
||||
```ts
|
||||
@@ -520,8 +674,7 @@ new ReadableStream({
|
||||
});
|
||||
```
|
||||
|
||||
<Accordion title="With chunking">
|
||||
|
||||
{% details summary="With chunking" %}
|
||||
To stream the `ArrayBuffer` in chunks, split the `TypedArray` into chunks and enqueue each one individually.
|
||||
|
||||
```ts
|
||||
@@ -535,7 +688,7 @@ new ReadableStream({
|
||||
});
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
|
||||
### From `DataView`
|
||||
|
||||
@@ -582,6 +735,12 @@ Array.from(view);
|
||||
new Blob([view.buffer], { type: "text/plain" });
|
||||
```
|
||||
|
||||
<!-- #### To `File`
|
||||
|
||||
```ts
|
||||
new File([view.buffer], "filename.txt", { type: "text/plain", lastModified: Date.now() });
|
||||
``` -->
|
||||
|
||||
#### To `ReadableStream`
|
||||
|
||||
```ts
|
||||
@@ -593,7 +752,7 @@ new ReadableStream({
|
||||
});
|
||||
```
|
||||
|
||||
<Accordion title="With chunking">
|
||||
{% details summary="With chunking" %}
|
||||
To stream the `ArrayBuffer` in chunks, split the `DataView` into chunks and enqueue each one individually.
|
||||
|
||||
```ts
|
||||
@@ -607,7 +766,7 @@ new ReadableStream({
|
||||
});
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
|
||||
### From `Buffer`
|
||||
|
||||
@@ -661,6 +820,12 @@ Array.from(buf);
|
||||
new Blob([buf], { type: "text/plain" });
|
||||
```
|
||||
|
||||
<!-- #### To `File`
|
||||
|
||||
```ts
|
||||
new File([buf], "filename.txt", { type: "text/plain", lastModified: Date.now() });
|
||||
``` -->
|
||||
|
||||
#### To `ReadableStream`
|
||||
|
||||
```ts
|
||||
@@ -672,7 +837,7 @@ new ReadableStream({
|
||||
});
|
||||
```
|
||||
|
||||
<Accordion title="With chunking">
|
||||
{% details summary="With chunking" %}
|
||||
To stream the `ArrayBuffer` in chunks, split the `Buffer` into chunks and enqueue each one individually.
|
||||
|
||||
```ts
|
||||
@@ -686,7 +851,7 @@ new ReadableStream({
|
||||
});
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
|
||||
### From `Blob`
|
||||
|
||||
@@ -736,6 +901,8 @@ Array.from(await blob.bytes());
|
||||
blob.stream();
|
||||
```
|
||||
|
||||
<!-- ### From `File` -->
|
||||
|
||||
### From `ReadableStream`
|
||||
|
||||
It's common to use [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) as a convenient intermediate representation to make it easier to convert `ReadableStream` to other formats.
|
||||
@@ -837,6 +1004,14 @@ Bun.readableStreamToArray(stream);
|
||||
new Response(stream).blob();
|
||||
```
|
||||
|
||||
<!-- #### To `File`
|
||||
|
||||
```ts
|
||||
new Response(stream)
|
||||
.blob()
|
||||
.then(blob => new File([blob], "filename.txt", { type: "text/plain", lastModified: Date.now() }));
|
||||
``` -->
|
||||
|
||||
#### To `ReadableStream`
|
||||
|
||||
To split a `ReadableStream` into two streams that can be consumed independently:
|
||||
@@ -844,3 +1019,20 @@ To split a `ReadableStream` into two streams that can be consumed independently:
|
||||
```ts
|
||||
const [a, b] = stream.tee();
|
||||
```
|
||||
|
||||
<!-- - Use Buffer
|
||||
- TextEncoder
|
||||
- `Bun.ArrayBufferSink`
|
||||
- ReadableStream
|
||||
- AsyncIterator
|
||||
- TypedArray vs ArrayBuffer vs DataView
|
||||
- Bun.indexOfLine
|
||||
- “direct” readablestream
|
||||
- readable stream has assumptions about
|
||||
- its very generic
|
||||
- all data is copies and queued
|
||||
- direct : no queueing
|
||||
- just a write function
|
||||
- you can write strings
|
||||
- more synchronous
|
||||
- corking works better -->
|
||||
@@ -1,19 +1,12 @@
|
||||
---
|
||||
title: C Compiler
|
||||
description: Compile and run C from JavaScript with low overhead
|
||||
---
|
||||
|
||||
`bun:ffi` has experimental support for compiling and running C from JavaScript with low overhead.
|
||||
|
||||
---
|
||||
|
||||
## Usage (cc in `bun:ffi`)
|
||||
|
||||
See the [introduction blog post](https://bun.com/blog/compile-and-run-c-in-js) for more information.
|
||||
|
||||
JavaScript:
|
||||
|
||||
```ts hello.ts icon="file-code"
|
||||
```ts#hello.js
|
||||
import { cc } from "bun:ffi";
|
||||
import source from "./hello.c" with { type: "file" };
|
||||
|
||||
@@ -34,7 +27,7 @@ console.log("What is the answer to the universe?", hello());
|
||||
|
||||
C source:
|
||||
|
||||
```c hello.c
|
||||
```c#hello.c
|
||||
int hello() {
|
||||
return 42;
|
||||
}
|
||||
@@ -42,8 +35,8 @@ int hello() {
|
||||
|
||||
When you run `hello.js`, it will print:
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
bun hello.js
|
||||
```sh
|
||||
$ bun hello.js
|
||||
What is the answer to the universe? 42
|
||||
```
|
||||
|
||||
@@ -51,7 +44,7 @@ Under the hood, `cc` uses [TinyCC](https://bellard.org/tcc/) to compile the C co
|
||||
|
||||
### Primitive types
|
||||
|
||||
The same `FFIType` values in [`dlopen`](/runtime/ffi) are supported in `cc`.
|
||||
The same `FFIType` values in [`dlopen`](/docs/api/ffi) are supported in `cc`.
|
||||
|
||||
| `FFIType` | C Type | Aliases |
|
||||
| ---------- | -------------- | --------------------------- |
|
||||
@@ -87,7 +80,7 @@ You can also pass a `napi_env` to receive the N-API environment used to call the
|
||||
|
||||
For example, if you have a string in C, you can return it to JavaScript like this:
|
||||
|
||||
```ts hello.ts
|
||||
```ts#hello.js
|
||||
import { cc } from "bun:ffi";
|
||||
import source from "./hello.c" with { type: "file" };
|
||||
|
||||
@@ -108,7 +101,7 @@ const result = hello();
|
||||
|
||||
And in C:
|
||||
|
||||
```c hello.c
|
||||
```c#hello.c
|
||||
#include <node/node_api.h>
|
||||
|
||||
napi_value hello(napi_env env) {
|
||||
@@ -120,7 +113,7 @@ napi_value hello(napi_env env) {
|
||||
|
||||
You can also use this to return other types like objects and arrays:
|
||||
|
||||
```c hello.c
|
||||
```c#hello.c
|
||||
#include <node/node_api.h>
|
||||
|
||||
napi_value hello(napi_env env) {
|
||||
@@ -196,7 +189,7 @@ type Defines = Record<string, string>;
|
||||
cc({
|
||||
source: "hello.c",
|
||||
define: {
|
||||
NDEBUG: "1",
|
||||
"NDEBUG": "1",
|
||||
},
|
||||
});
|
||||
```
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
title: Color
|
||||
description: Format colors as CSS, ANSI, numbers, hex strings, and more
|
||||
---
|
||||
|
||||
`Bun.color(input, outputFormat?)` leverages Bun's CSS parser to parse, normalize, and convert colors from user input to a variety of output formats, including:
|
||||
|
||||
| Format | Example |
|
||||
@@ -247,7 +242,7 @@ Bun.color([255, 0, 0], "HEX"); // "#FF0000"
|
||||
|
||||
Like many of Bun's APIs, you can use macros to invoke `Bun.color` at bundle-time for use in client-side JavaScript builds:
|
||||
|
||||
```ts client-side.ts
|
||||
```ts#client-side.ts
|
||||
import { color } from "bun" with { type: "macro" };
|
||||
|
||||
console.log(color("#f00", "css"));
|
||||
@@ -1,14 +1,6 @@
|
||||
---
|
||||
title: Console
|
||||
description: The console object in Bun
|
||||
---
|
||||
|
||||
<Note>
|
||||
Bun provides a browser- and Node.js-compatible [console](https://developer.mozilla.org/en-US/docs/Web/API/console)
|
||||
global. This page only documents Bun-native APIs.
|
||||
</Note>
|
||||
|
||||
---
|
||||
{% callout %}
|
||||
**Note** — Bun provides a browser- and Node.js-compatible [console](https://developer.mozilla.org/en-US/docs/Web/API/console) global. This page only documents Bun-native APIs.
|
||||
{% /callout %}
|
||||
|
||||
## Object inspection depth
|
||||
|
||||
@@ -27,13 +19,11 @@ console.log(nested);
|
||||
|
||||
The CLI flag takes precedence over the configuration file setting.
|
||||
|
||||
---
|
||||
|
||||
## Reading from stdin
|
||||
|
||||
In Bun, the `console` object can be used as an `AsyncIterable` to sequentially read lines from `process.stdin`.
|
||||
|
||||
```ts adder.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
for await (const line of console) {
|
||||
console.log(line);
|
||||
}
|
||||
@@ -41,7 +31,7 @@ for await (const line of console) {
|
||||
|
||||
This is useful for implementing interactive programs, like the following addition calculator.
|
||||
|
||||
```ts adder.ts icon="/icons/typescript.svg"
|
||||
```ts#adder.ts
|
||||
console.log(`Let's add some numbers!`);
|
||||
console.write(`Count: 0\n> `);
|
||||
|
||||
@@ -54,8 +44,8 @@ for await (const line of console) {
|
||||
|
||||
To run the file:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun adder.ts
|
||||
```bash
|
||||
$ bun adder.ts
|
||||
Let's add some numbers!
|
||||
Count: 0
|
||||
> 5
|
||||
@@ -1,15 +1,10 @@
|
||||
---
|
||||
title: Cookies
|
||||
description: Use Bun's native APIs for working with HTTP cookies
|
||||
---
|
||||
|
||||
Bun provides native APIs for working with HTTP cookies through `Bun.Cookie` and `Bun.CookieMap`. These APIs offer fast, easy-to-use methods for parsing, generating, and manipulating cookies in HTTP requests and responses.
|
||||
|
||||
## CookieMap class
|
||||
|
||||
`Bun.CookieMap` provides a Map-like interface for working with collections of cookies. It implements the `Iterable` interface, allowing you to use it with `for...of` loops and other iteration methods.
|
||||
|
||||
```ts title="cookies.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Empty cookie map
|
||||
const cookies = new Bun.CookieMap();
|
||||
|
||||
@@ -33,7 +28,7 @@ const cookies3 = new Bun.CookieMap([
|
||||
|
||||
In Bun's HTTP server, the `cookies` property on the request object (in `routes`) is an instance of `CookieMap`:
|
||||
|
||||
```ts title="server.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const server = Bun.serve({
|
||||
routes: {
|
||||
"/": req => {
|
||||
@@ -68,7 +63,7 @@ console.log("Server listening at: " + server.url);
|
||||
|
||||
Retrieves a cookie by name. Returns `null` if the cookie doesn't exist.
|
||||
|
||||
```ts title="get-cookie.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Get by name
|
||||
const cookie = cookies.get("session");
|
||||
|
||||
@@ -81,7 +76,7 @@ if (cookie != null) {
|
||||
|
||||
Checks if a cookie with the given name exists.
|
||||
|
||||
```ts title="has-cookie.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Check if cookie exists
|
||||
if (cookies.has("session")) {
|
||||
// Cookie exists
|
||||
@@ -96,7 +91,7 @@ if (cookies.has("session")) {
|
||||
|
||||
Adds or updates a cookie in the map. Cookies default to `{ path: "/", sameSite: "lax" }`.
|
||||
|
||||
```ts title="set-cookie.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Set by name and value
|
||||
cookies.set("session", "abc123");
|
||||
|
||||
@@ -119,7 +114,7 @@ cookies.set(cookie);
|
||||
|
||||
Removes a cookie from the map. When applied to a Response, this adds a cookie with an empty string value and an expiry date in the past. A cookie will only delete successfully on the browser if the domain and path is the same as it was when the cookie was created.
|
||||
|
||||
```ts title="delete-cookie.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Delete by name using default domain and path.
|
||||
cookies.delete("session");
|
||||
|
||||
@@ -135,7 +130,7 @@ cookies.delete({
|
||||
|
||||
Converts the cookie map to a serializable format.
|
||||
|
||||
```ts title="cookie-to-json.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const json = cookies.toJSON();
|
||||
```
|
||||
|
||||
@@ -145,7 +140,7 @@ Returns an array of values for Set-Cookie headers that can be used to apply all
|
||||
|
||||
When using `Bun.serve()`, you don't need to call this method explicitly. Any changes made to the `req.cookies` map are automatically applied to the response headers. This method is primarily useful when working with other HTTP server implementations.
|
||||
|
||||
```js title="node-server.js" icon="file-code"
|
||||
```js
|
||||
import { createServer } from "node:http";
|
||||
import { CookieMap } from "bun";
|
||||
|
||||
@@ -172,7 +167,7 @@ server.listen(3000, () => {
|
||||
|
||||
`CookieMap` provides several methods for iteration:
|
||||
|
||||
```ts title="iterate-cookies.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Iterate over [name, cookie] entries
|
||||
for (const [name, value] of cookies) {
|
||||
console.log(`${name}: ${value}`);
|
||||
@@ -205,7 +200,7 @@ cookies.forEach((value, name) => {
|
||||
|
||||
Returns the number of cookies in the map.
|
||||
|
||||
```ts title="cookie-size.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
console.log(cookies.size); // Number of cookies
|
||||
```
|
||||
|
||||
@@ -213,7 +208,7 @@ console.log(cookies.size); // Number of cookies
|
||||
|
||||
`Bun.Cookie` represents an HTTP cookie with its name, value, and attributes.
|
||||
|
||||
```ts title="cookie-class.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { Cookie } from "bun";
|
||||
|
||||
// Create a basic cookie
|
||||
@@ -243,7 +238,7 @@ const objCookie = new Bun.Cookie({
|
||||
|
||||
### Constructors
|
||||
|
||||
```ts title="constructors.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Basic constructor with name/value
|
||||
new Bun.Cookie(name: string, value: string);
|
||||
|
||||
@@ -259,7 +254,7 @@ new Bun.Cookie(options: CookieInit);
|
||||
|
||||
### Properties
|
||||
|
||||
```ts title="cookie-properties.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
cookie.name; // string - Cookie name
|
||||
cookie.value; // string - Cookie value
|
||||
cookie.domain; // string | null - Domain scope (null if not specified)
|
||||
@@ -278,7 +273,7 @@ cookie.httpOnly; // boolean - Accessible only via HTTP (not JavaScript)
|
||||
|
||||
Checks if the cookie has expired.
|
||||
|
||||
```ts title="is-expired.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Expired cookie (Date in the past)
|
||||
const expiredCookie = new Bun.Cookie("name", "value", {
|
||||
expires: new Date(Date.now() - 1000),
|
||||
@@ -302,7 +297,7 @@ console.log(sessionCookie.isExpired()); // false
|
||||
|
||||
Returns a string representation of the cookie suitable for a `Set-Cookie` header.
|
||||
|
||||
```ts title="serialize-cookie.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const cookie = new Bun.Cookie("session", "abc123", {
|
||||
domain: "example.com",
|
||||
path: "/admin",
|
||||
@@ -322,7 +317,7 @@ console.log(cookie.toString());
|
||||
|
||||
Converts the cookie to a plain object suitable for JSON serialization.
|
||||
|
||||
```ts title="cookie-json.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const cookie = new Bun.Cookie("session", "abc123", {
|
||||
secure: true,
|
||||
httpOnly: true,
|
||||
@@ -349,7 +344,7 @@ const jsonString = JSON.stringify(cookie);
|
||||
|
||||
Parses a cookie string into a `Cookie` instance.
|
||||
|
||||
```ts title="parse-cookie.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const cookie = Bun.Cookie.parse("name=value; Path=/; Secure; SameSite=Lax");
|
||||
|
||||
console.log(cookie.name); // "name"
|
||||
@@ -363,7 +358,7 @@ console.log(cookie.sameSite); // "lax"
|
||||
|
||||
Factory method to create a cookie.
|
||||
|
||||
```ts title="cookie-from.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const cookie = Bun.Cookie.from("session", "abc123", {
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
@@ -373,7 +368,7 @@ const cookie = Bun.Cookie.from("session", "abc123", {
|
||||
|
||||
## Types
|
||||
|
||||
```ts title="types.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
interface CookieInit {
|
||||
name?: string;
|
||||
value?: string;
|
||||
@@ -1,9 +1,4 @@
|
||||
---
|
||||
title: DNS
|
||||
description: Use Bun's DNS module to resolve DNS records
|
||||
---
|
||||
|
||||
Bun implements it's own `dns` module, and the `node:dns` module.
|
||||
Bun implements the `node:dns` module.
|
||||
|
||||
```ts
|
||||
import * as dns from "node:dns";
|
||||
@@ -13,17 +8,9 @@ console.log(addrs);
|
||||
// => [{ address: "172.67.161.226", family: 4, ttl: 0 }, ...]
|
||||
```
|
||||
|
||||
```ts
|
||||
import { dns } from "bun";
|
||||
|
||||
dns.prefetch("bun.com", 443);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DNS caching in Bun
|
||||
|
||||
Bun supports DNS caching. This cache makes repeated connections to the same hosts faster.
|
||||
In Bun v1.1.9, we added support for DNS caching. This cache makes repeated connections to the same hosts faster.
|
||||
|
||||
At the time of writing, we cache up to 255 entries for a maximum of 30 seconds (each). If any connections to a host fail, we remove the entry from the cache. When multiple connections are made to the same host simultaneously, DNS lookups are deduplicated to avoid making multiple requests for the same host.
|
||||
|
||||
@@ -52,7 +39,9 @@ An example where you might want to use this is a database driver. When your appl
|
||||
|
||||
### `dns.prefetch`
|
||||
|
||||
<Warning>This API is experimental and may change in the future.</Warning>
|
||||
{% callout %}
|
||||
**🚧** — This API is experimental and may change in the future.
|
||||
{% /callout %}
|
||||
|
||||
To prefetch a DNS entry, you can use the `dns.prefetch` API. This API is useful when you know you'll need to connect to a host soon and want to avoid the initial DNS lookup.
|
||||
|
||||
@@ -73,18 +62,28 @@ await fetch("https://bun.com");
|
||||
|
||||
### `dns.getCacheStats()`
|
||||
|
||||
<Warning>This API is experimental and may change in the future.</Warning>
|
||||
{% callout %}
|
||||
**🚧** — This API is experimental and may change in the future.
|
||||
{% /callout %}
|
||||
|
||||
To get the current cache stats, you can use the `dns.getCacheStats` API. This API returns an object with the following properties:
|
||||
To get the current cache stats, you can use the `dns.getCacheStats` API.
|
||||
|
||||
This API returns an object with the following properties:
|
||||
|
||||
```ts
|
||||
{
|
||||
cacheHitsCompleted: number; // Cache hits completed
|
||||
cacheHitsInflight: number; // Cache hits in flight
|
||||
cacheMisses: number; // Cache misses
|
||||
size: number; // Number of items in the DNS cache
|
||||
errors: number; // Number of times a connection failed
|
||||
totalCount: number; // Number of times a connection was requested at all (including cache hits and misses)
|
||||
// Cache hits
|
||||
cacheHitsCompleted: number;
|
||||
cacheHitsInflight: number;
|
||||
cacheMisses: number;
|
||||
// Number of items in the DNS cache
|
||||
size: number;
|
||||
|
||||
// Number of times a connection failed
|
||||
errors: number;
|
||||
|
||||
// Number of times a connection was requested at all (including cache hits and misses)
|
||||
totalCount: number;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
title: Fetch
|
||||
description: Send HTTP requests with Bun's fetch API
|
||||
---
|
||||
|
||||
Bun implements the WHATWG `fetch` standard, with some extensions to meet the needs of server-side JavaScript.
|
||||
|
||||
Bun also implements `node:http`, but `fetch` is generally recommended instead.
|
||||
@@ -273,7 +268,7 @@ const response = await fetch("s3://my-bucket/path/to/object", {
|
||||
|
||||
Note: Only PUT and POST methods support request bodies when using S3. For uploads, Bun automatically uses multipart upload for streaming bodies.
|
||||
|
||||
You can read more about Bun's S3 support in the [S3](/runtime/s3) documentation.
|
||||
You can read more about Bun's S3 support in the [S3](https://bun.com/docs/api/s3) documentation.
|
||||
|
||||
#### File URLs - `file://`
|
||||
|
||||
@@ -342,7 +337,7 @@ This will print the request and response headers to your terminal:
|
||||
```sh
|
||||
[fetch] > HTTP/1.1 GET http://example.com/
|
||||
[fetch] > Connection: keep-alive
|
||||
[fetch] > User-Agent: Bun/1.3.3
|
||||
[fetch] > User-Agent: Bun/$BUN_LATEST_VERSION
|
||||
[fetch] > Accept: */*
|
||||
[fetch] > Host: example.com
|
||||
[fetch] > Accept-Encoding: gzip, deflate, br, zstd
|
||||
@@ -388,7 +383,7 @@ dns.prefetch("bun.com");
|
||||
|
||||
By default, Bun caches and deduplicates DNS queries in-memory for up to 30 seconds. You can see the cache stats by calling `dns.getCacheStats()`:
|
||||
|
||||
To learn more about DNS caching in Bun, see the [DNS caching](/runtime/networking/dns) documentation.
|
||||
To learn more about DNS caching in Bun, see the [DNS caching](https://bun.com/docs/api/dns) documentation.
|
||||
|
||||
### Preconnect to a host
|
||||
|
||||
@@ -407,7 +402,7 @@ Note: calling `fetch` immediately after `fetch.preconnect` will not make your re
|
||||
To preconnect to a host at startup, you can pass `--fetch-preconnect`:
|
||||
|
||||
```sh
|
||||
bun --fetch-preconnect https://bun.com ./my-script.ts
|
||||
$ bun --fetch-preconnect https://bun.com ./my-script.ts
|
||||
```
|
||||
|
||||
This is sort of like `<link rel="preconnect">` in HTML.
|
||||
@@ -430,7 +425,7 @@ When the limit is exceeded, the requests are queued and sent as soon as the next
|
||||
You can increase the maximum number of simultaneous connections via the `BUN_CONFIG_MAX_HTTP_REQUESTS` environment variable:
|
||||
|
||||
```sh
|
||||
BUN_CONFIG_MAX_HTTP_REQUESTS=512 bun ./my-script.ts
|
||||
$ BUN_CONFIG_MAX_HTTP_REQUESTS=512 bun ./my-script.ts
|
||||
```
|
||||
|
||||
The max value for this limit is currently set to 65,336. The maximum port number is 65,535, so it's quite difficult for any one computer to exceed this limit.
|
||||
@@ -1,17 +1,9 @@
|
||||
---
|
||||
title: FFI
|
||||
description: Use Bun's FFI module to efficiently call native libraries from JavaScript
|
||||
---
|
||||
|
||||
<Warning>
|
||||
`bun:ffi` is **experimental**, with known bugs and limitations, and should not be relied on in production. The most
|
||||
stable way to interact with native code from Bun is to write a [Node-API module](/runtime/node-api).
|
||||
</Warning>
|
||||
{% callout %}
|
||||
**⚠️ Warning** — `bun:ffi` is **experimental**, with known bugs and limitations, and should not be relied on in production. The most stable way to interact with native code from Bun is to write a [Node-API module](/docs/api/node-api).
|
||||
{% /callout %}
|
||||
|
||||
Use the built-in `bun:ffi` module to efficiently call native libraries from JavaScript. It works with languages that support the C ABI (Zig, Rust, C/C++, C#, Nim, Kotlin, etc).
|
||||
|
||||
---
|
||||
|
||||
## dlopen usage (`bun:ffi`)
|
||||
|
||||
To print the version number of `sqlite3`:
|
||||
@@ -41,23 +33,20 @@ const {
|
||||
console.log(`SQLite 3 version: ${sqlite3_libversion()}`);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance
|
||||
|
||||
According to [our benchmark](https://github.com/oven-sh/bun/tree/main/bench/ffi), `bun:ffi` is roughly 2-6x faster than Node.js FFI via `Node-API`.
|
||||
|
||||
<Image src="/images/ffi.png" height="400" />
|
||||
{% image src="/images/ffi.png" height="400" /%}
|
||||
|
||||
Bun generates & just-in-time compiles C bindings that efficiently convert values between JavaScript types and native types. To compile C, Bun embeds [TinyCC](https://github.com/TinyCC/tinycc), a small and fast C compiler.
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
### Zig
|
||||
|
||||
```zig add.zig icon="file-code"
|
||||
```zig
|
||||
// add.zig
|
||||
pub export fn add(a: i32, b: i32) i32 {
|
||||
return a + b;
|
||||
}
|
||||
@@ -65,8 +54,8 @@ pub export fn add(a: i32, b: i32) i32 {
|
||||
|
||||
To compile:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
zig build-lib add.zig -dynamic -OReleaseFast
|
||||
```bash
|
||||
$ zig build-lib add.zig -dynamic -OReleaseFast
|
||||
```
|
||||
|
||||
Pass a path to the shared library and a map of symbols to import into `dlopen`:
|
||||
@@ -100,7 +89,7 @@ pub extern "C" fn add(a: i32, b: i32) -> i32 {
|
||||
To compile:
|
||||
|
||||
```bash
|
||||
rustc --crate-type cdylib add.rs
|
||||
$ rustc --crate-type cdylib add.rs
|
||||
```
|
||||
|
||||
### C++
|
||||
@@ -116,11 +105,9 @@ extern "C" int32_t add(int32_t a, int32_t b) {
|
||||
To compile:
|
||||
|
||||
```bash
|
||||
zig build-lib add.cpp -dynamic -lc -lc++
|
||||
$ zig build-lib add.cpp -dynamic -lc -lc++
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## FFI types
|
||||
|
||||
The following `FFIType` values are supported.
|
||||
@@ -150,13 +137,11 @@ The following `FFIType` values are supported.
|
||||
|
||||
Note: `buffer` arguments must be a `TypedArray` or `DataView`.
|
||||
|
||||
---
|
||||
|
||||
## Strings
|
||||
|
||||
JavaScript strings and C-like strings are different, and that complicates using strings with native libraries.
|
||||
|
||||
<Accordion title="How are JavaScript strings and C strings different?">
|
||||
{% details summary="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
|
||||
@@ -169,7 +154,7 @@ C strings:
|
||||
- The length is not stored. Instead, the string is null-terminated which means the length is the index of the first `\0` it finds
|
||||
- Mutable
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
|
||||
To solve this, `bun:ffi` exports `CString` which extends JavaScript's built-in `String` to support null-terminated strings and add a few extras:
|
||||
|
||||
@@ -216,11 +201,13 @@ console.log(myString);
|
||||
|
||||
When used in `returns`, `FFIType.cstring` coerces the pointer to a JavaScript `string`. When used in `args`, `FFIType.cstring` is identical to `ptr`.
|
||||
|
||||
---
|
||||
|
||||
## Function pointers
|
||||
|
||||
<Note>Async functions are not yet supported</Note>
|
||||
{% callout %}
|
||||
|
||||
**Note** — Async functions are not yet supported.
|
||||
|
||||
{% /callout %}
|
||||
|
||||
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.
|
||||
|
||||
@@ -268,11 +255,13 @@ const lib = linkSymbols({
|
||||
},
|
||||
});
|
||||
|
||||
const [major, minor, patch] = [lib.symbols.getMajor(), lib.symbols.getMinor(), lib.symbols.getPatch()];
|
||||
const [major, minor, patch] = [
|
||||
lib.symbols.getMajor(),
|
||||
lib.symbols.getMinor(),
|
||||
lib.symbols.getPatch(),
|
||||
];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Callbacks
|
||||
|
||||
Use `JSCallback` to create JavaScript callback functions that can be passed to C/FFI functions. The C/FFI function can call into the JavaScript/TypeScript code. This is useful for asynchronous code or whenever you want to call into JavaScript code from C.
|
||||
@@ -290,10 +279,13 @@ const {
|
||||
},
|
||||
});
|
||||
|
||||
const searchIterator = new JSCallback((ptr, length) => /hello/.test(new CString(ptr, length)), {
|
||||
returns: "bool",
|
||||
args: ["ptr", "usize"],
|
||||
});
|
||||
const searchIterator = new JSCallback(
|
||||
(ptr, length) => /hello/.test(new CString(ptr, length)),
|
||||
{
|
||||
returns: "bool",
|
||||
args: ["ptr", "usize"],
|
||||
},
|
||||
);
|
||||
|
||||
const str = Buffer.from("wwutwutwutwutwutwutwutwutwutwutut\0", "utf8");
|
||||
if (search(ptr(str), searchIterator)) {
|
||||
@@ -313,17 +305,21 @@ When you're done with a JSCallback, you should call `close()` to free the memory
|
||||
|
||||
`JSCallback` has experimental support for thread-safe callbacks. This will be needed if you pass a callback function into a different thread from its instantiation context. You can enable it with the optional `threadsafe` parameter.
|
||||
|
||||
Currently, thread-safe callbacks work best when run from another thread that is running JavaScript code, i.e. a [`Worker`](/runtime/workers). A future version of Bun will enable them to be called from any thread (such as new threads spawned by your native library that Bun is not aware of).
|
||||
Currently, thread-safe callbacks work best when run from another thread that is running JavaScript code, i.e. a [`Worker`](/docs/api/workers). A future version of Bun will enable them to be called from any thread (such as new threads spawned by your native library that Bun is not aware of).
|
||||
|
||||
```ts
|
||||
const searchIterator = new JSCallback((ptr, length) => /hello/.test(new CString(ptr, length)), {
|
||||
returns: "bool",
|
||||
args: ["ptr", "usize"],
|
||||
threadsafe: true, // Optional. Defaults to `false`
|
||||
});
|
||||
const searchIterator = new JSCallback(
|
||||
(ptr, length) => /hello/.test(new CString(ptr, length)),
|
||||
{
|
||||
returns: "bool",
|
||||
args: ["ptr", "usize"],
|
||||
threadsafe: true, // Optional. Defaults to `false`
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
<Note>
|
||||
{% callout %}
|
||||
|
||||
**⚡️ Performance tip** — For a slight performance boost, directly pass `JSCallback.prototype.ptr` instead of the `JSCallback` object:
|
||||
|
||||
```ts
|
||||
@@ -344,21 +340,17 @@ setOnResolve(onResolve.ptr);
|
||||
setOnResolve(onResolve);
|
||||
```
|
||||
|
||||
</Note>
|
||||
|
||||
---
|
||||
{% /callout %}
|
||||
|
||||
## Pointers
|
||||
|
||||
Bun represents [pointers](<https://en.wikipedia.org/wiki/Pointer_(computer_programming)>) as a `number` in JavaScript.
|
||||
|
||||
<Accordion title="How does a 64 bit pointer fit in a JavaScript number?">
|
||||
|
||||
{% details summary="How does a 64 bit pointer fit in a JavaScript number?" %}
|
||||
64-bit processors support up to [52 bits of addressable space](https://en.wikipedia.org/wiki/64-bit_computing#Limits_of_processors). [JavaScript numbers](https://en.wikipedia.org/wiki/Double-precision_floating-point_format#IEEE_754_double-precision_binary_floating-point_format:_binary64) 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 `BigInt` which means they can't fit into a regular JavaScript value. If you pass a `BigInt` to a function, it will be converted to a `number`
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
|
||||
To convert from a `TypedArray` to a pointer:
|
||||
|
||||
@@ -511,7 +503,7 @@ const out = encode_png(
|
||||
|
||||
The [auto-generated wrapper](https://github.com/oven-sh/bun/blob/6a65631cbdcae75bfa1e64323a6ad613a922cd1a/src/bun.js/ffi.exports.js#L180-L182) converts the pointer to a `TypedArray`.
|
||||
|
||||
<Accordion title="Hardmode">
|
||||
{% details summary="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`:
|
||||
|
||||
@@ -543,7 +535,7 @@ const out = encode_png(
|
||||
);
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
|
||||
### Reading pointers
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
---
|
||||
title: File I/O
|
||||
description: Bun provides a set of optimized APIs for reading and writing files.
|
||||
---
|
||||
{% callout %}
|
||||
|
||||
<Note>
|
||||
<!-- **Note** — The `Bun.file` and `Bun.write` APIs documented on this page are heavily optimized and represent the recommended way to perform file-system tasks using Bun. Existing Node.js projects may use Bun's [nearly complete](https://bun.com/docs/runtime/nodejs-apis#node-fs) implementation of the [`node:fs`](https://nodejs.org/api/fs.html) module. -->
|
||||
|
||||
The `Bun.file` and `Bun.write` APIs documented on this page are heavily optimized and represent the recommended way to perform file-system tasks using Bun. For operations that are not yet available with `Bun.file`, such as `mkdir` or `readdir`, you can use Bun's [nearly complete](/runtime/nodejs-compat#node-fs) implementation of the [`node:fs`](https://nodejs.org/api/fs.html) module.
|
||||
**Note** — The `Bun.file` and `Bun.write` APIs documented on this page are heavily optimized and represent the recommended way to perform file-system tasks using Bun. For operations that are not yet available with `Bun.file`, such as `mkdir` or `readdir`, you can use Bun's [nearly complete](https://bun.com/docs/runtime/nodejs-apis#node-fs) implementation of the [`node:fs`](https://nodejs.org/api/fs.html) module.
|
||||
|
||||
</Note>
|
||||
{% /callout %}
|
||||
|
||||
---
|
||||
Bun provides a set of optimized APIs for reading and writing files.
|
||||
|
||||
## Reading files (`Bun.file()`)
|
||||
|
||||
@@ -29,7 +26,6 @@ The reference conforms to the [`Blob`](https://developer.mozilla.org/en-US/docs/
|
||||
const foo = Bun.file("foo.txt");
|
||||
|
||||
await foo.text(); // contents as a string
|
||||
await foo.json(); // contents as a JSON object
|
||||
await foo.stream(); // contents as ReadableStream
|
||||
await foo.arrayBuffer(); // contents as ArrayBuffer
|
||||
await foo.bytes(); // contents as Uint8Array
|
||||
@@ -74,8 +70,6 @@ You can delete a file by calling the `.delete()` function.
|
||||
await Bun.file("logs.json").delete();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Writing files (`Bun.write()`)
|
||||
|
||||
`Bun.write(destination, data): Promise<number>`
|
||||
@@ -98,22 +92,88 @@ The second argument is the data to be written. It can be any of the following:
|
||||
|
||||
All possible permutations are handled using the fastest available system calls on the current platform.
|
||||
|
||||
<Accordion title="See syscalls">
|
||||
{% details summary="See syscalls" %}
|
||||
|
||||
| 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 (doesn't exist) | file (path) | clonefile | macOS |
|
||||
| file (exists) | file | fcopyfile | macOS |
|
||||
| file | Blob or string | write | macOS |
|
||||
| file | Blob or string | write | Linux |
|
||||
{% table %}
|
||||
|
||||
</Accordion>
|
||||
- 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 (doesn't exist)
|
||||
- file (path)
|
||||
- clonefile
|
||||
- macOS
|
||||
|
||||
---
|
||||
|
||||
- file (exists)
|
||||
- file
|
||||
- fcopyfile
|
||||
- macOS
|
||||
|
||||
---
|
||||
|
||||
- file
|
||||
- Blob or string
|
||||
- write
|
||||
- macOS
|
||||
|
||||
---
|
||||
|
||||
- file
|
||||
- Blob or string
|
||||
- write
|
||||
- Linux
|
||||
|
||||
{% /table %}
|
||||
|
||||
{% /details %}
|
||||
|
||||
To write a string to disk:
|
||||
|
||||
@@ -124,7 +184,7 @@ await Bun.write("output.txt", data);
|
||||
|
||||
To copy a file to another location on disk:
|
||||
|
||||
```ts
|
||||
```js
|
||||
const input = Bun.file("input.txt");
|
||||
const output = Bun.file("output.txt"); // doesn't exist yet!
|
||||
await Bun.write(output, input);
|
||||
@@ -152,8 +212,6 @@ const response = await fetch("https://bun.com");
|
||||
await Bun.write("index.html", response);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Incremental writing with `FileSink`
|
||||
|
||||
Bun provides a native incremental file writing API called `FileSink`. To retrieve a `FileSink` instance from a `BunFile`:
|
||||
@@ -201,8 +259,6 @@ writer.unref();
|
||||
writer.ref();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Directories
|
||||
|
||||
Bun's implementation of `node:fs` is fast, and we haven't implemented a Bun-specific API for reading directories just yet. For now, you should use `node:fs` for working with directories in Bun.
|
||||
@@ -239,15 +295,13 @@ import { mkdir } from "node:fs/promises";
|
||||
await mkdir("path/to/dir", { recursive: true });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Benchmarks
|
||||
|
||||
The following is a 3-line implementation of the Linux `cat` command.
|
||||
|
||||
```ts cat.ts icon="/icons/typescript.svg"
|
||||
```ts#cat.ts
|
||||
// Usage
|
||||
// bun ./cat.ts ./path-to-file
|
||||
// $ bun ./cat.ts ./path-to-file
|
||||
|
||||
import { resolve } from "path";
|
||||
|
||||
@@ -257,15 +311,13 @@ await Bun.write(Bun.stdout, Bun.file(path));
|
||||
|
||||
To run the file:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun ./cat.ts ./path-to-file
|
||||
```bash
|
||||
$ bun ./cat.ts ./path-to-file
|
||||
```
|
||||
|
||||
It runs 2x faster than GNU `cat` for large files on Linux.
|
||||
|
||||
<Frame></Frame>
|
||||
|
||||
---
|
||||
{% image src="/images/cat.jpg" /%}
|
||||
|
||||
## Reference
|
||||
|
||||
@@ -279,7 +331,13 @@ interface Bun {
|
||||
|
||||
write(
|
||||
destination: string | number | BunFile | URL,
|
||||
input: string | Blob | ArrayBuffer | SharedArrayBuffer | TypedArray | Response,
|
||||
input:
|
||||
| string
|
||||
| Blob
|
||||
| ArrayBuffer
|
||||
| SharedArrayBuffer
|
||||
| TypedArray
|
||||
| Response,
|
||||
): Promise<number>;
|
||||
}
|
||||
|
||||
@@ -296,7 +354,9 @@ interface BunFile {
|
||||
}
|
||||
|
||||
export interface FileSink {
|
||||
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
|
||||
write(
|
||||
chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer,
|
||||
): number;
|
||||
flush(): number | Promise<number>;
|
||||
end(error?: Error): number | Promise<number>;
|
||||
start(options?: { highWaterMark?: number }): void;
|
||||
@@ -1,9 +1,4 @@
|
||||
---
|
||||
title: File System Router
|
||||
description: Bun provides a fast API for resolving routes against file-system paths
|
||||
---
|
||||
|
||||
This API is primarily intended for library authors. At the moment only Next.js-style file-system routing is supported, but other styles may be added in the future.
|
||||
Bun provides a fast API for resolving routes against file-system paths. This API is primarily intended for library authors. At the moment only Next.js-style file-system routing is supported, but other styles may be added in the future.
|
||||
|
||||
## Next.js-style
|
||||
|
||||
@@ -21,14 +16,13 @@ pages
|
||||
|
||||
The `FileSystemRouter` can be used to resolve routes against this directory:
|
||||
|
||||
```ts router.ts
|
||||
```ts
|
||||
const router = new Bun.FileSystemRouter({
|
||||
style: "nextjs",
|
||||
dir: "./pages",
|
||||
origin: "https://mydomain.com",
|
||||
assetPrefix: "_next/static/"
|
||||
});
|
||||
|
||||
router.match("/");
|
||||
|
||||
// =>
|
||||
19
docs/api/file.md
Normal file
19
docs/api/file.md
Normal file
@@ -0,0 +1,19 @@
|
||||
Bun.js has fast paths for common use cases that make Web APIs live up to the performance demands of servers and CLIs.
|
||||
|
||||
`Bun.file(path)` returns a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) that represents a lazily-loaded file.
|
||||
|
||||
When you pass a file blob to `Bun.write`, Bun automatically uses a faster system call:
|
||||
|
||||
```js
|
||||
const blob = Bun.file("input.txt");
|
||||
await Bun.write("output.txt", blob);
|
||||
```
|
||||
|
||||
On Linux, this uses the [`copy_file_range`](https://man7.org/linux/man-pages/man2/copy_file_range.2.html) syscall and on macOS, this becomes `clonefile` (or [`fcopyfile`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/copyfile.3.html)).
|
||||
|
||||
`Bun.write` also supports [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects. It automatically converts to a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob).
|
||||
|
||||
```js
|
||||
// Eventually, this will stream the response to disk but today it buffers
|
||||
await Bun.write("index.html", await fetch("https://example.com"));
|
||||
```
|
||||
@@ -1,7 +1,4 @@
|
||||
---
|
||||
title: Glob
|
||||
description: Use Bun's fast native implementation of file globbing
|
||||
---
|
||||
Bun includes a fast native implementation of file globbing.
|
||||
|
||||
## Quickstart
|
||||
|
||||
387
docs/api/globals.md
Normal file
387
docs/api/globals.md
Normal file
@@ -0,0 +1,387 @@
|
||||
Bun implements the following globals.
|
||||
|
||||
{% table %}
|
||||
|
||||
- Global
|
||||
- Source
|
||||
- Notes
|
||||
|
||||
---
|
||||
|
||||
- [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`alert`](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert)
|
||||
- Web
|
||||
- Intended for command-line tools
|
||||
|
||||
---
|
||||
|
||||
- [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`Buffer`](https://nodejs.org/api/buffer.html#class-buffer)
|
||||
- Node.js
|
||||
- See [Node.js > `Buffer`](https://bun.com/docs/runtime/nodejs-apis#node-buffer)
|
||||
|
||||
---
|
||||
|
||||
- `Bun`
|
||||
- Bun
|
||||
- Subject to change as additional APIs are added
|
||||
|
||||
---
|
||||
|
||||
- [`ByteLengthQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/ByteLengthQueuingStrategy)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm)
|
||||
- Web
|
||||
- Intended for command-line tools
|
||||
|
||||
---
|
||||
|
||||
- [`__dirname`](https://nodejs.org/api/globals.html#__dirname)
|
||||
- Node.js
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`__filename`](https://nodejs.org/api/globals.html#__filename)
|
||||
- Node.js
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`atob()`](https://developer.mozilla.org/en-US/docs/Web/API/atob)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`btoa()`](https://developer.mozilla.org/en-US/docs/Web/API/btoa)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- `BuildMessage`
|
||||
- Bun
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`clearImmediate()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearImmediate)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`clearInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearInterval)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`clearTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearTimeout)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`console`](https://developer.mozilla.org/en-US/docs/Web/API/console)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`CountQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/CountQueuingStrategy)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`Crypto`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`crypto`](https://developer.mozilla.org/en-US/docs/Web/API/crypto)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`CryptoKey`](https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event)
|
||||
- Web
|
||||
- Also [`ErrorEvent`](https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent) [`CloseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) [`MessageEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent).
|
||||
|
||||
---
|
||||
|
||||
- [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`exports`](https://nodejs.org/api/globals.html#exports)
|
||||
- Node.js
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`global`](https://nodejs.org/api/globals.html#global)
|
||||
- Node.js
|
||||
- See [Node.js > `global`](https://bun.com/docs/runtime/nodejs-apis#global).
|
||||
|
||||
---
|
||||
|
||||
- [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis)
|
||||
- Cross-platform
|
||||
- Aliases to `global`
|
||||
|
||||
---
|
||||
|
||||
- [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`HTMLRewriter`](https://bun.com/docs/api/html-rewriter)
|
||||
- Cloudflare
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`JSON`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`MessageEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`module`](https://nodejs.org/api/globals.html#module)
|
||||
- Node.js
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`performance`](https://developer.mozilla.org/en-US/docs/Web/API/performance)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`process`](https://nodejs.org/api/process.html)
|
||||
- Node.js
|
||||
- See [Node.js > `process`](https://bun.com/docs/runtime/nodejs-apis#node-process)
|
||||
|
||||
---
|
||||
|
||||
- [`prompt`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt)
|
||||
- Web
|
||||
- Intended for command-line tools
|
||||
|
||||
---
|
||||
|
||||
- [`queueMicrotask()`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`ReadableByteStreamController`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableByteStreamController)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`ReadableStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultController)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`ReadableStreamDefaultReader`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`reportError`](https://developer.mozilla.org/en-US/docs/Web/API/reportError)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`require()`](https://nodejs.org/api/globals.html#require)
|
||||
- Node.js
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- `ResolveMessage`
|
||||
- Bun
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`setImmediate()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`ShadowRealm`](https://github.com/tc39/proposal-shadowrealm)
|
||||
- Web
|
||||
- Stage 3 proposal
|
||||
|
||||
---
|
||||
|
||||
- [`SubtleCrypto`](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`DOMException`](https://developer.mozilla.org/en-US/docs/Web/API/DOMException)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`TransformStream`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`TransformStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStreamDefaultController)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`WebAssembly`](https://nodejs.org/api/globals.html#webassembly)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`WritableStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultController)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`WritableStreamDefaultWriter`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter)
|
||||
- Web
|
||||
-
|
||||
|
||||
{% /table %}
|
||||
@@ -1,14 +1,8 @@
|
||||
---
|
||||
title: Hashing
|
||||
description: Bun provides a set of utility functions for hashing and verifying passwords with various cryptographically secure algorithms
|
||||
---
|
||||
{% callout %}
|
||||
|
||||
<Note>
|
||||
Bun implements the `createHash` and `createHmac` functions from [`node:crypto`](https://nodejs.org/api/crypto.html) in
|
||||
addition to the Bun-native APIs documented below.
|
||||
</Note>
|
||||
Bun implements the `createHash` and `createHmac` functions from [`node:crypto`](https://nodejs.org/api/crypto.html) in addition to the Bun-native APIs documented below.
|
||||
|
||||
---
|
||||
{% /callout %}
|
||||
|
||||
## `Bun.password`
|
||||
|
||||
@@ -138,8 +132,6 @@ The format is composed of:
|
||||
- `salt`: `$xXnlSvPh4ym5KYmxKAuuHVlDvy2QGHBNuI6bJJrRDOs`
|
||||
- `hash`: `$2YY6M48XmHn+s5NoBaL+ficzXajq2Yj8wut3r0vnrwI`
|
||||
|
||||
---
|
||||
|
||||
## `Bun.hash`
|
||||
|
||||
`Bun.hash` is a collection of utilities for _non-cryptographic_ hashing. Non-cryptographic hashing algorithms are optimized for speed of computation over collision-resistance or security.
|
||||
@@ -186,14 +178,13 @@ Bun.hash.murmur64v2("data", 1234);
|
||||
Bun.hash.rapidhash("data", 1234);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `Bun.CryptoHasher`
|
||||
|
||||
`Bun.CryptoHasher` is a general-purpose utility class that lets you incrementally compute a hash of string or binary data using a range of cryptographic hash algorithms. The following algorithms are supported:
|
||||
|
||||
- `"blake2b256"`
|
||||
- `"blake2b512"`
|
||||
- `"blake2s256"`
|
||||
- `"md4"`
|
||||
- `"md5"`
|
||||
- `"ripemd160"`
|
||||
@@ -230,11 +221,24 @@ hasher.update(new ArrayBuffer(10));
|
||||
|
||||
If a `string` is passed, an optional second parameter can be used to specify the encoding (default `'utf-8'`). The following encodings are supported:
|
||||
|
||||
| Category | Encodings |
|
||||
| -------------------------- | ------------------------------------------- |
|
||||
| Binary encodings | `"base64"` `"base64url"` `"hex"` `"binary"` |
|
||||
| Character encodings | `"utf8"` `"utf-8"` `"utf16le"` `"latin1"` |
|
||||
| Legacy character encodings | `"ascii"` `"binary"` `"ucs2"` `"ucs-2"` |
|
||||
{% table %}
|
||||
|
||||
---
|
||||
|
||||
- Binary encodings
|
||||
- `"base64"` `"base64url"` `"hex"` `"binary"`
|
||||
|
||||
---
|
||||
|
||||
- Character encodings
|
||||
- `"utf8"` `"utf-8"` `"utf16le"` `"latin1"`
|
||||
|
||||
---
|
||||
|
||||
- Legacy character encodings
|
||||
- `"ascii"` `"binary"` `"ucs2"` `"ucs-2"`
|
||||
|
||||
{% /table %}
|
||||
|
||||
```ts
|
||||
hasher.update("hello world"); // defaults to utf8
|
||||
@@ -1,12 +1,5 @@
|
||||
---
|
||||
title: HTMLRewriter
|
||||
description: Use Bun's HTMLRewriter to transform HTML documents with CSS selectors
|
||||
---
|
||||
|
||||
HTMLRewriter lets you use CSS selectors to transform HTML documents. It works with `Request`, `Response`, as well as `string`. Bun's implementation is based on Cloudflare's [lol-html](https://github.com/cloudflare/lol-html).
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
A common usecase is rewriting URLs in HTML content. Here's an example that rewrites image sources and link URLs to use a CDN domain:
|
||||
@@ -16,12 +9,16 @@ A common usecase is rewriting URLs in HTML content. Here's an example that rewri
|
||||
const rewriter = new HTMLRewriter().on("img", {
|
||||
element(img) {
|
||||
// Famous rickroll video thumbnail
|
||||
img.setAttribute("src", "https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg");
|
||||
img.setAttribute(
|
||||
"src",
|
||||
"https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
|
||||
);
|
||||
|
||||
// Wrap the image in a link to the video
|
||||
img.before('<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">', {
|
||||
html: true,
|
||||
});
|
||||
img.before(
|
||||
'<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">',
|
||||
{ html: true },
|
||||
);
|
||||
img.after("</a>", { html: true });
|
||||
|
||||
// Add some fun alt text
|
||||
@@ -46,22 +43,21 @@ console.log(result);
|
||||
|
||||
This replaces all images with a thumbnail of Rick Astley and wraps each `<img>` in a link, producing a diff like this:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```html
|
||||
```html-diff
|
||||
<html>
|
||||
<body>
|
||||
<img src="/cat.jpg" /> <!-- [!code --] -->
|
||||
<img src="dog.png" /> <!-- [!code --] -->
|
||||
<img src="https://example.com/bird.webp" /> <!-- [!code --] -->
|
||||
<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank"> <!-- [!code ++] -->
|
||||
<img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll" /> <!-- [!code ++] -->
|
||||
</a> <!-- [!code ++] -->
|
||||
<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank"> <!-- [!code ++] -->
|
||||
<img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll" /> <!-- [!code ++] -->
|
||||
</a> <!-- [!code ++] -->
|
||||
<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank"> <!-- [!code ++] -->
|
||||
<img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll" /> <!-- [!code ++] -->
|
||||
</a> <!-- [!code ++] -->
|
||||
- <img src="/cat.jpg">
|
||||
- <img src="dog.png">
|
||||
- <img src="https://example.com/bird.webp">
|
||||
+ <a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">
|
||||
+ <img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll">
|
||||
+ </a>
|
||||
+ <a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">
|
||||
+ <img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll">
|
||||
+ </a>
|
||||
+ <a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">
|
||||
+ <img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll">
|
||||
+ </a>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
@@ -184,7 +180,10 @@ rewriter.on("div", {
|
||||
el.setInnerContent(""); // Clear content
|
||||
|
||||
// Position manipulation
|
||||
el.before("Content before").after("Content after").prepend("First child").append("Last child");
|
||||
el.before("Content before")
|
||||
.after("Content after")
|
||||
.prepend("First child")
|
||||
.append("Last child");
|
||||
|
||||
// HTML content insertion
|
||||
el.before("<span>before</span>", { html: true })
|
||||
@@ -256,7 +255,11 @@ rewriter.on("*", {
|
||||
console.log(comment.removed); // Whether comment was removed
|
||||
|
||||
// Manipulation
|
||||
comment.before("Before comment").after("After comment").replace("New comment").remove();
|
||||
comment
|
||||
.before("Before comment")
|
||||
.after("After comment")
|
||||
.replace("New comment")
|
||||
.remove();
|
||||
|
||||
// HTML content insertion
|
||||
comment
|
||||
@@ -326,8 +329,6 @@ try {
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## See also
|
||||
|
||||
You can also read the [Cloudflare documentation](https://developers.cloudflare.com/workers/runtime-apis/html-rewriter/), which this API is intended to be compatible with.
|
||||
1408
docs/api/http.md
Normal file
1408
docs/api/http.md
Normal file
File diff suppressed because it is too large
Load Diff
69
docs/api/import-meta.md
Normal file
69
docs/api/import-meta.md
Normal file
@@ -0,0 +1,69 @@
|
||||
The `import.meta` object is a way for a module to access information about itself. It's part of the JavaScript language, but its contents are not standardized. Each "host" (browser, runtime, etc) is free to implement any properties it wishes on the `import.meta` object.
|
||||
|
||||
Bun implements the following properties.
|
||||
|
||||
```ts#/path/to/project/file.ts
|
||||
import.meta.dir; // => "/path/to/project"
|
||||
import.meta.file; // => "file.ts"
|
||||
import.meta.path; // => "/path/to/project/file.ts"
|
||||
import.meta.url; // => "file:///path/to/project/file.ts"
|
||||
|
||||
import.meta.main; // `true` if this file is directly executed by `bun run`
|
||||
// `false` otherwise
|
||||
|
||||
import.meta.resolve("zod"); // => "file:///path/to/project/node_modules/zod/index.js"
|
||||
```
|
||||
|
||||
{% table %}
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.dir`
|
||||
- Absolute path to the directory containing the current file, e.g. `/path/to/project`. Equivalent to `__dirname` in CommonJS modules (and Node.js)
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.dirname`
|
||||
- An alias to `import.meta.dir`, for Node.js compatibility
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.env`
|
||||
- An alias to `process.env`.
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.file`
|
||||
- The name of the current file, e.g. `index.tsx`
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.path`
|
||||
- Absolute path to the current file, e.g. `/path/to/project/index.ts`. Equivalent to `__filename` in CommonJS modules (and Node.js)
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.filename`
|
||||
- An alias to `import.meta.path`, for Node.js compatibility
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.main`
|
||||
- Indicates whether the current file is the entrypoint to the current `bun` process. Is the file being directly executed by `bun run` or is it being imported?
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.resolve`
|
||||
- Resolve a module specifier (e.g. `"zod"` or `"./file.tsx"`) to a url. Equivalent to [`import.meta.resolve` in browsers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta#resolve)
|
||||
|
||||
```ts
|
||||
import.meta.resolve("zod");
|
||||
// => "file:///path/to/project/node_modules/zod/index.ts"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.url`
|
||||
- A `string` url to the current file, e.g. `file:///path/to/project/index.ts`. Equivalent to [`import.meta.url` in browsers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta#url)
|
||||
|
||||
{% /table %}
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
title: Node-API
|
||||
description: Use Bun's Node-API module to build native add-ons to Node.js
|
||||
---
|
||||
|
||||
Node-API is an interface for building native add-ons to Node.js. Bun implements 95% of this interface from scratch, so most existing Node-API extensions will work with Bun out of the box. Track the completion status of it in [this issue](https://github.com/oven-sh/bun/issues/158).
|
||||
|
||||
As in Node.js, `.node` files (Node-API modules) can be required directly in Bun.
|
||||
@@ -1,13 +1,6 @@
|
||||
---
|
||||
title: Redis
|
||||
description: Use Bun's native Redis client with a Promise-based API
|
||||
---
|
||||
Bun provides native bindings for working with Redis databases with a modern, Promise-based API. The interface is designed to be simple and performant, with built-in connection management, fully typed responses, and TLS support. **New in Bun v1.2.9**
|
||||
|
||||
<Note>Bun's Redis client supports Redis server versions 7.2 and up.</Note>
|
||||
|
||||
Bun provides native bindings for working with Redis databases with a modern, Promise-based API. The interface is designed to be simple and performant, with built-in connection management, fully typed responses, and TLS support.
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { redis } from "bun";
|
||||
|
||||
// Set a key
|
||||
@@ -28,13 +21,11 @@ const exists = await redis.exists("greeting");
|
||||
await redis.del("greeting");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
To use the Redis client, you first need to create a connection:
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { redis, RedisClient } from "bun";
|
||||
|
||||
// Using the default client (reads connection info from environment)
|
||||
@@ -58,7 +49,7 @@ By default, the client reads connection information from the following environme
|
||||
|
||||
The Redis client automatically handles connections in the background:
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// No connection is made until a command is executed
|
||||
const client = new RedisClient();
|
||||
|
||||
@@ -74,7 +65,7 @@ client.close();
|
||||
|
||||
You can also manually control the connection lifecycle:
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const client = new RedisClient();
|
||||
|
||||
// Explicitly connect
|
||||
@@ -87,13 +78,11 @@ await client.set("key", "value");
|
||||
client.close();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Basic Operations
|
||||
|
||||
### String Operations
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Set a key
|
||||
await redis.set("user:1:name", "Alice");
|
||||
|
||||
@@ -119,7 +108,7 @@ const ttl = await redis.ttl("session:123");
|
||||
|
||||
### Numeric Operations
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Set initial value
|
||||
await redis.set("counter", "0");
|
||||
|
||||
@@ -132,9 +121,16 @@ await redis.decr("counter");
|
||||
|
||||
### Hash Operations
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Set multiple fields in a hash
|
||||
await redis.hmset("user:123", ["name", "Alice", "email", "alice@example.com", "active", "true"]);
|
||||
await redis.hmset("user:123", [
|
||||
"name",
|
||||
"Alice",
|
||||
"email",
|
||||
"alice@example.com",
|
||||
"active",
|
||||
"true",
|
||||
]);
|
||||
|
||||
// Get multiple fields from a hash
|
||||
const userFields = await redis.hmget("user:123", ["name", "email"]);
|
||||
@@ -153,7 +149,7 @@ await redis.hincrbyfloat("user:123", "score", 1.5);
|
||||
|
||||
### Set Operations
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Add member to set
|
||||
await redis.sadd("tags", "javascript");
|
||||
|
||||
@@ -173,25 +169,23 @@ const randomTag = await redis.srandmember("tags");
|
||||
const poppedTag = await redis.spop("tags");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pub/Sub
|
||||
|
||||
Bun provides native bindings for the [Redis
|
||||
Pub/Sub](https://redis.io/docs/latest/develop/pubsub/) protocol. **New in Bun
|
||||
1.2.23**
|
||||
|
||||
<Warning>
|
||||
The Redis Pub/Sub feature is experimental. Although we expect it to be stable, we're currently actively looking for
|
||||
feedback and areas for improvement.
|
||||
</Warning>
|
||||
{% callout %}
|
||||
**🚧** — The Redis Pub/Sub feature is experimental. Although we expect it to be
|
||||
stable, we're currently actively looking for feedback and areas for improvement.
|
||||
{% /callout %}
|
||||
|
||||
### Basic Usage
|
||||
|
||||
To get started publishing messages, you can set up a publisher in
|
||||
`publisher.ts`:
|
||||
|
||||
```typescript publisher.ts icon="/icons/typescript.svg"
|
||||
```typescript#publisher.ts
|
||||
import { RedisClient } from "bun";
|
||||
|
||||
const writer = new RedisClient("redis://localhost:6739");
|
||||
@@ -204,7 +198,7 @@ writer.close();
|
||||
|
||||
In another file, create the subscriber in `subscriber.ts`:
|
||||
|
||||
```typescript subscriber.ts icon="/icons/typescript.svg"
|
||||
```typescript#subscriber.ts
|
||||
import { RedisClient } from "bun";
|
||||
|
||||
const listener = new RedisClient("redis://localhost:6739");
|
||||
@@ -217,40 +211,40 @@ await listener.subscribe("general", (message, channel) => {
|
||||
|
||||
In one shell, run your subscriber:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```bash
|
||||
bun run subscriber.ts
|
||||
```
|
||||
|
||||
and, in another, run your publisher:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```bash
|
||||
bun run publisher.ts
|
||||
```
|
||||
|
||||
<Note>
|
||||
The subscription mode takes over the `RedisClient` connection. A
|
||||
{% callout %}
|
||||
**Note:** The subscription mode takes over the `RedisClient` connection. A
|
||||
client with subscriptions can only call `RedisClient.prototype.subscribe()`. In
|
||||
other words, applications which need to message Redis need a separate
|
||||
connection, acquirable through `.duplicate()`:
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```typescript
|
||||
import { RedisClient } from "bun";
|
||||
|
||||
const redis = new RedisClient("redis://localhost:6379");
|
||||
await redis.connect();
|
||||
const subscriber = await redis.duplicate(); // [!code ++]
|
||||
const subscriber = await redis.duplicate();
|
||||
|
||||
await subscriber.subscribe("foo", () => {});
|
||||
await redis.set("bar", "baz");
|
||||
```
|
||||
|
||||
</Note>
|
||||
{% /callout %}
|
||||
|
||||
### Publishing
|
||||
|
||||
Publishing messages is done through the `publish()` method:
|
||||
|
||||
```typescript redis.ts icon="/icons/typescript.svg"
|
||||
```typescript
|
||||
await client.publish(channelName, message);
|
||||
```
|
||||
|
||||
@@ -259,13 +253,13 @@ await client.publish(channelName, message);
|
||||
The Bun `RedisClient` allows you to subscribe to channels through the
|
||||
`.subscribe()` method:
|
||||
|
||||
```typescript redis.ts icon="/icons/typescript.svg"
|
||||
```typescript
|
||||
await client.subscribe(channel, (message, channel) => {});
|
||||
```
|
||||
|
||||
You can unsubscribe through the `.unsubscribe()` method:
|
||||
|
||||
```typescript redis.ts icon="/icons/typescript.svg"
|
||||
```typescript
|
||||
await client.unsubscribe(); // Unsubscribe from all channels.
|
||||
await client.unsubscribe(channel); // Unsubscribe a particular channel.
|
||||
await client.unsubscribe(channel, listener); // Unsubscribe a particular listener.
|
||||
@@ -277,16 +271,19 @@ await client.unsubscribe(channel, listener); // Unsubscribe a particular listene
|
||||
|
||||
The client automatically pipelines commands, improving performance by sending multiple commands in a batch and processing responses as they arrive.
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Commands are automatically pipelined by default
|
||||
const [infoResult, listResult] = await Promise.all([redis.get("user:1:name"), redis.get("user:2:email")]);
|
||||
const [infoResult, listResult] = await Promise.all([
|
||||
redis.get("user:1:name"),
|
||||
redis.get("user:2:email"),
|
||||
]);
|
||||
```
|
||||
|
||||
To disable automatic pipelining, you can set the `enableAutoPipelining` option to `false`:
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const client = new RedisClient("redis://localhost:6379", {
|
||||
enableAutoPipelining: false, // [!code ++]
|
||||
enableAutoPipelining: false,
|
||||
});
|
||||
```
|
||||
|
||||
@@ -294,7 +291,7 @@ const client = new RedisClient("redis://localhost:6379", {
|
||||
|
||||
When you need to use commands that don't have convenience methods, you can use the `send` method:
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Run any Redis command
|
||||
const info = await redis.send("INFO", []);
|
||||
|
||||
@@ -311,7 +308,7 @@ The `send` method allows you to use any Redis command, even ones that don't have
|
||||
|
||||
You can register handlers for connection events:
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const client = new RedisClient();
|
||||
|
||||
// Called when successfully connected to Redis server
|
||||
@@ -331,7 +328,7 @@ client.close();
|
||||
|
||||
### Connection Status and Monitoring
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Check if connected
|
||||
console.log(client.connected); // boolean indicating connection status
|
||||
|
||||
@@ -376,13 +373,11 @@ The following commands disable automatic pipelining:
|
||||
- `UNSUBSCRIBE`
|
||||
- `UNPSUBSCRIBE`
|
||||
|
||||
---
|
||||
|
||||
## Connection Options
|
||||
|
||||
When creating a client, you can pass various options to configure the connection:
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const client = new RedisClient("redis://localhost:6379", {
|
||||
// Connection timeout in milliseconds (default: 10000)
|
||||
connectionTimeout: 5000,
|
||||
@@ -425,13 +420,11 @@ When a connection is lost, the client automatically attempts to reconnect with e
|
||||
- Queued if `enableOfflineQueue` is true (default)
|
||||
- Rejected immediately if `enableOfflineQueue` is false
|
||||
|
||||
---
|
||||
|
||||
## Supported URL Formats
|
||||
|
||||
The Redis client supports various URL formats:
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Standard Redis URL
|
||||
new RedisClient("redis://localhost:6379");
|
||||
new RedisClient("redis://localhost:6379");
|
||||
@@ -457,13 +450,11 @@ new RedisClient("redis+tls+unix:///path/to/socket");
|
||||
new RedisClient("redis+tls+unix:///path/to/socket");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
The Redis client throws typed errors for different scenarios:
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
try {
|
||||
await redis.get("non-existent-key");
|
||||
} catch (error) {
|
||||
@@ -483,13 +474,11 @@ Common error codes:
|
||||
- `ERR_REDIS_AUTHENTICATION_FAILED` - Failed to authenticate with the server
|
||||
- `ERR_REDIS_INVALID_RESPONSE` - Received an invalid response from the server
|
||||
|
||||
---
|
||||
|
||||
## Example Use Cases
|
||||
|
||||
### Caching
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
async function getUserWithCache(userId) {
|
||||
const cacheKey = `user:${userId}`;
|
||||
|
||||
@@ -512,7 +501,7 @@ async function getUserWithCache(userId) {
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
async function rateLimit(ip, limit = 100, windowSecs = 3600) {
|
||||
const key = `ratelimit:${ip}`;
|
||||
|
||||
@@ -534,13 +523,20 @@ async function rateLimit(ip, limit = 100, windowSecs = 3600) {
|
||||
|
||||
### Session Storage
|
||||
|
||||
```ts redis.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
async function createSession(userId, data) {
|
||||
const sessionId = crypto.randomUUID();
|
||||
const key = `session:${sessionId}`;
|
||||
|
||||
// Store session with expiration
|
||||
await redis.hmset(key, ["userId", userId.toString(), "created", Date.now().toString(), "data", JSON.stringify(data)]);
|
||||
await redis.hmset(key, [
|
||||
"userId",
|
||||
userId.toString(),
|
||||
"created",
|
||||
Date.now().toString(),
|
||||
"data",
|
||||
JSON.stringify(data),
|
||||
]);
|
||||
await redis.expire(key, 86400); // 24 hours
|
||||
|
||||
return sessionId;
|
||||
@@ -553,7 +549,11 @@ async function getSession(sessionId) {
|
||||
const exists = await redis.exists(key);
|
||||
if (!exists) return null;
|
||||
|
||||
const [userId, created, data] = await redis.hmget(key, ["userId", "created", "data"]);
|
||||
const [userId, created, data] = await redis.hmget(key, [
|
||||
"userId",
|
||||
"created",
|
||||
"data",
|
||||
]);
|
||||
|
||||
return {
|
||||
userId: Number(userId),
|
||||
@@ -563,19 +563,33 @@ async function getSession(sessionId) {
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Bun's Redis client is implemented in Zig and uses the Redis Serialization Protocol (RESP3). It manages connections efficiently and provides automatic reconnection with exponential backoff.
|
||||
|
||||
The client supports pipelining commands, meaning multiple commands can be sent without waiting for the replies to previous commands. This significantly improves performance when sending multiple commands in succession.
|
||||
|
||||
### RESP3 Protocol Support
|
||||
|
||||
Bun's Redis client uses the newer RESP3 protocol by default, which provides more data types and features compared to RESP2:
|
||||
|
||||
- Better error handling with typed errors
|
||||
- Native Boolean responses
|
||||
- Map/Dictionary responses (key-value objects)
|
||||
- Set responses
|
||||
- Double (floating point) values
|
||||
- BigNumber support for large integer values
|
||||
|
||||
When connecting to Redis servers using older versions that don't support RESP3, the client automatically fallbacks to compatible modes.
|
||||
|
||||
## Limitations and Future Plans
|
||||
|
||||
Current limitations of the Redis client we are planning to address in future versions:
|
||||
|
||||
- Transactions (MULTI/EXEC) must be done through raw commands for now
|
||||
- [ ] Transactions (MULTI/EXEC) must be done through raw commands for now
|
||||
- [ ] Streams are supported but without dedicated methods
|
||||
- [ ] Pub/Sub does not currently support binary data, nor pattern-based
|
||||
subscriptions.
|
||||
|
||||
Unsupported features:
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
---
|
||||
title: S3
|
||||
description: Bun provides fast, native bindings for interacting with S3-compatible object storage services.
|
||||
---
|
||||
|
||||
Production servers often read, upload, and write files to S3-compatible object storage services instead of the local filesystem. Historically, that means local filesystem APIs you use in development can't be used in production. When you use Bun, things are different.
|
||||
|
||||
{% callout %}
|
||||
|
||||
### Bun's S3 API is fast
|
||||
|
||||
<Frame caption="Left: Bun v1.1.44. Right: Node.js v23.6.0">
|
||||
<img src="/images/bun-s3-node.gif" alt="Bun's S3 API is fast" />
|
||||
</Frame>
|
||||
{% image src="https://bun.com/bun-s3-node.gif" alt="Bun's S3 API is fast" caption="Left: Bun v1.1.44. Right: Node.js v23.6.0" /%}
|
||||
|
||||
{% /callout %}
|
||||
|
||||
Bun provides fast, native bindings for interacting with S3-compatible object storage services. Bun's S3 API is designed to be simple and feel similar to fetch's `Response` and `Blob` APIs (like Bun's local filesystem APIs).
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { s3, write, S3Client } from "bun";
|
||||
|
||||
// Bun.s3 reads environment variables for credentials
|
||||
@@ -55,7 +52,7 @@ There are several ways to interact with Bun's S3 API.
|
||||
|
||||
To explicitly set credentials, pass them to the `Bun.S3Client` constructor.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { S3Client } from "bun";
|
||||
|
||||
const client = new S3Client({
|
||||
@@ -77,7 +74,7 @@ const client = new S3Client({
|
||||
|
||||
The **`file`** method in `S3Client` returns a **lazy reference to a file on S3**.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// A lazy reference to a file on S3
|
||||
const s3file: S3File = client.file("123.json");
|
||||
```
|
||||
@@ -88,7 +85,7 @@ Like `Bun.file(path)`, the `S3Client`'s `file` method is synchronous. It does ze
|
||||
|
||||
If you've used the `fetch` API, you're familiar with the `Response` and `Blob` APIs. `S3File` extends `Blob`. The same methods that work on `Blob` also work on `S3File`.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Read an S3File as text
|
||||
const text = await s3file.text();
|
||||
|
||||
@@ -120,7 +117,7 @@ These helper methods not only simplify the API, they also make it faster.
|
||||
|
||||
Writing to S3 is just as simple.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Write a string (replacing the file)
|
||||
await s3file.write("Hello World!");
|
||||
|
||||
@@ -149,7 +146,7 @@ await Bun.write(s3file, "Hello World!");
|
||||
|
||||
Bun automatically handles multipart uploads for large files and provides streaming capabilities. The same API that works for local files also works for S3 files.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Write a large file
|
||||
const bigFile = Buffer.alloc(10 * 1024 * 1024); // 10MB
|
||||
const writer = s3file.writer({
|
||||
@@ -169,8 +166,6 @@ for (let i = 0; i < 10; i++) {
|
||||
await writer.end();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Presigning URLs
|
||||
|
||||
When your production service needs to let users upload files to your server, it's often more reliable for the user to upload directly to S3 instead of your server acting as an intermediary.
|
||||
@@ -179,7 +174,7 @@ To facilitate this, you can presign URLs for S3 files. This generates a URL with
|
||||
|
||||
The default behaviour is to generate a `GET` URL that expires in 24 hours. Bun attempts to infer the content type from the file extension. If inference is not possible, it will default to `application/octet-stream`.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { s3 } from "bun";
|
||||
|
||||
// Generate a presigned URL that expires in 24 hours (default)
|
||||
@@ -203,7 +198,7 @@ const presignedFile = myFile.presign({
|
||||
|
||||
To set an ACL (access control list) on a presigned URL, pass the `acl` option:
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const url = s3file.presign({
|
||||
acl: "public-read",
|
||||
expiresIn: 3600,
|
||||
@@ -227,7 +222,7 @@ You can pass any of the following ACLs:
|
||||
|
||||
To set an expiration time for a presigned URL, pass the `expiresIn` option.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const url = s3file.presign({
|
||||
// Seconds
|
||||
expiresIn: 3600, // 1 hour
|
||||
@@ -244,7 +239,7 @@ const url = s3file.presign({
|
||||
|
||||
To set the HTTP method for a presigned URL, pass the `method` option.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const url = s3file.presign({
|
||||
method: "PUT",
|
||||
// method: "DELETE",
|
||||
@@ -259,14 +254,14 @@ const url = s3file.presign({
|
||||
|
||||
To quickly redirect users to a presigned URL for an S3 file, pass an `S3File` instance to a `Response` object as the body.
|
||||
|
||||
This will automatically redirect the user to the presigned URL for the S3 file, saving you the memory, time, and bandwidth cost of downloading the file to your server and sending it back to the user.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const response = new Response(s3file);
|
||||
console.log(response);
|
||||
```
|
||||
|
||||
```txt
|
||||
This will automatically redirect the user to the presigned URL for the S3 file, saving you the memory, time, and bandwidth cost of downloading the file to your server and sending it back to the user.
|
||||
|
||||
```ts
|
||||
Response (0 KB) {
|
||||
ok: false,
|
||||
url: "",
|
||||
@@ -280,8 +275,6 @@ Response (0 KB) {
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Support for S3-Compatible Services
|
||||
|
||||
Bun's S3 implementation works with any S3-compatible storage service. Just specify the appropriate endpoint:
|
||||
@@ -290,7 +283,7 @@ Bun's S3 implementation works with any S3-compatible storage service. Just speci
|
||||
|
||||
AWS S3 is the default. You can also pass a `region` option instead of an `endpoint` option for AWS S3.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { S3Client } from "bun";
|
||||
|
||||
// AWS S3
|
||||
@@ -307,7 +300,7 @@ const s3 = new S3Client({
|
||||
|
||||
To use Bun's S3 client with [Google Cloud Storage](https://cloud.google.com/storage), set `endpoint` to `"https://storage.googleapis.com"` in the `S3Client` constructor.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={8}
|
||||
```ts
|
||||
import { S3Client } from "bun";
|
||||
|
||||
// Google Cloud Storage
|
||||
@@ -323,7 +316,7 @@ const gcs = new S3Client({
|
||||
|
||||
To use Bun's S3 client with [Cloudflare R2](https://developers.cloudflare.com/r2/), set `endpoint` to the R2 endpoint in the `S3Client` constructor. The R2 endpoint includes your account ID.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={8}
|
||||
```ts
|
||||
import { S3Client } from "bun";
|
||||
|
||||
// CloudFlare R2
|
||||
@@ -339,7 +332,7 @@ const r2 = new S3Client({
|
||||
|
||||
To use Bun's S3 client with [DigitalOcean Spaces](https://www.digitalocean.com/products/spaces/), set `endpoint` to the DigitalOcean Spaces endpoint in the `S3Client` constructor.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={8}
|
||||
```ts
|
||||
import { S3Client } from "bun";
|
||||
|
||||
const spaces = new S3Client({
|
||||
@@ -355,7 +348,7 @@ const spaces = new S3Client({
|
||||
|
||||
To use Bun's S3 client with [MinIO](https://min.io/), set `endpoint` to the URL that MinIO is running on in the `S3Client` constructor.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={10}
|
||||
```ts
|
||||
import { S3Client } from "bun";
|
||||
|
||||
const minio = new S3Client({
|
||||
@@ -371,9 +364,9 @@ const minio = new S3Client({
|
||||
|
||||
### Using Bun's S3Client with supabase
|
||||
|
||||
To use Bun's S3 client with [supabase](https://supabase.com/), set `endpoint` to the supabase endpoint in the `S3Client` constructor. The supabase endpoint includes your account ID and /storage/v1/s3 path. Make sure to set Enable connection via S3 protocol on in the supabase dashboard in `https://supabase.com/dashboard/project/<account-id>/settings/storage` and to set the region informed in the same section.
|
||||
To use Bun's S3 client with [supabase](https://supabase.com/), set `endpoint` to the supabase endpoint in the `S3Client` constructor. The supabase endpoint includes your account ID and /storage/v1/s3 path. Make sure to set Enable connection via S3 protocol on in the supabase dashboard in https://supabase.com/dashboard/project/<account-id>/settings/storage and to set the region informed in the same section.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={3-10}
|
||||
```ts
|
||||
import { S3Client } from "bun";
|
||||
|
||||
const supabase = new S3Client({
|
||||
@@ -387,15 +380,9 @@ const supabase = new S3Client({
|
||||
|
||||
### Using Bun's S3Client with S3 Virtual Hosted-Style endpoints
|
||||
|
||||
When using a S3 Virtual Hosted-Style endpoint, you need to set the `virtualHostedStyle` option to `true`.
|
||||
When using a S3 Virtual Hosted-Style endpoint, you need to set the `virtualHostedStyle` option to `true` and if no endpoint is provided, Bun will use region and bucket to infer the endpoint to AWS S3, if no region is provided it will use `us-east-1`. If you provide a the endpoint, there are no need to provide the bucket name.
|
||||
|
||||
<Note>
|
||||
- If you don’t specify an endpoint, Bun will automatically determine the AWS S3 endpoint using the provided region and
|
||||
bucket. - If no region is specified, Bun defaults to us-east-1. - If you explicitly provide an endpoint, you don’t
|
||||
need to specify a bucket name.
|
||||
</Note>
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={17, 25}
|
||||
```ts
|
||||
import { S3Client } from "bun";
|
||||
|
||||
// AWS S3 endpoint inferred from region and bucket
|
||||
@@ -403,7 +390,7 @@ const s3 = new S3Client({
|
||||
accessKeyId: "access-key",
|
||||
secretAccessKey: "secret-key",
|
||||
bucket: "my-bucket",
|
||||
virtualHostedStyle: true, // [!code ++]
|
||||
virtualHostedStyle: true,
|
||||
// endpoint: "https://my-bucket.s3.us-east-1.amazonaws.com",
|
||||
// region: "us-east-1",
|
||||
});
|
||||
@@ -413,7 +400,7 @@ const s3WithEndpoint = new S3Client({
|
||||
accessKeyId: "access-key",
|
||||
secretAccessKey: "secret-key",
|
||||
endpoint: "https://<bucket-name>.s3.<region>.amazonaws.com",
|
||||
virtualHostedStyle: true, // [!code ++]
|
||||
virtualHostedStyle: true,
|
||||
});
|
||||
|
||||
// Cloudflare R2
|
||||
@@ -421,12 +408,10 @@ const r2WithEndpoint = new S3Client({
|
||||
accessKeyId: "access-key",
|
||||
secretAccessKey: "secret-key",
|
||||
endpoint: "https://<bucket-name>.<account-id>.r2.cloudflarestorage.com",
|
||||
virtualHostedStyle: true, // [!code ++]
|
||||
virtualHostedStyle: true,
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Credentials
|
||||
|
||||
Credentials are one of the hardest parts of using S3, and we've tried to make it as easy as possible. By default, Bun reads the following environment variables for credentials.
|
||||
@@ -451,7 +436,7 @@ If the `S3_*` environment variable is not set, Bun will also check for the `AWS_
|
||||
| `bucket` | `AWS_BUCKET` |
|
||||
| `sessionToken` | `AWS_SESSION_TOKEN` |
|
||||
|
||||
These environment variables are read from [`.env` files](/runtime/environment-variables) or from the process environment at initialization time (`process.env` is not used for this).
|
||||
These environment variables are read from [`.env` files](/docs/runtime/env) or from the process environment at initialization time (`process.env` is not used for this).
|
||||
|
||||
These defaults are overridden by the options you pass to `s3.file(credentials)`, `new Bun.S3Client(credentials)`, or any of the methods that accept credentials. So if, for example, you use the same credentials for different buckets, you can set the credentials once in your `.env` file and then pass `bucket: "my-bucket"` to the `s3.file()` function without having to specify all the credentials again.
|
||||
|
||||
@@ -459,7 +444,7 @@ These defaults are overridden by the options you pass to `s3.file(credentials)`,
|
||||
|
||||
When you're not using environment variables or using multiple buckets, you can create a `S3Client` object to explicitly set credentials.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={3-11}
|
||||
```ts
|
||||
import { S3Client } from "bun";
|
||||
|
||||
const client = new S3Client({
|
||||
@@ -489,14 +474,13 @@ await file.delete();
|
||||
|
||||
To upload or write a file to S3, call `write` on the `S3Client` instance.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={8, 9}
|
||||
```ts
|
||||
const client = new Bun.S3Client({
|
||||
accessKeyId: "your-access-key",
|
||||
secretAccessKey: "your-secret-key",
|
||||
endpoint: "https://s3.us-east-1.amazonaws.com",
|
||||
bucket: "my-bucket",
|
||||
});
|
||||
|
||||
await client.write("my-file.txt", "Hello World!");
|
||||
await client.write("my-file.txt", new Response("Hello World!"));
|
||||
|
||||
@@ -508,7 +492,7 @@ await client.write("my-file.txt", new Response("Hello World!"));
|
||||
|
||||
To delete a file from S3, call `delete` on the `S3Client` instance.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={7}
|
||||
```ts
|
||||
const client = new Bun.S3Client({
|
||||
accessKeyId: "your-access-key",
|
||||
secretAccessKey: "your-secret-key",
|
||||
@@ -524,7 +508,7 @@ await client.delete("my-file.txt");
|
||||
|
||||
To check if a file exists in S3, call `exists` on the `S3Client` instance.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={7}
|
||||
```ts
|
||||
const client = new Bun.S3Client({
|
||||
accessKeyId: "your-access-key",
|
||||
secretAccessKey: "your-secret-key",
|
||||
@@ -540,7 +524,7 @@ const exists = await client.exists("my-file.txt");
|
||||
|
||||
`S3File` instances are created by calling the `S3Client` instance method or the `s3.file()` function. Like `Bun.file()`, `S3File` instances are lazy. They don't refer to something that necessarily exists at the time of creation. That's why all the methods that don't involve network requests are fully synchronous.
|
||||
|
||||
```ts Type Reference icon="/icons/typescript.svg" expandable
|
||||
```ts
|
||||
interface S3File extends Blob {
|
||||
slice(start: number, end?: number): S3File;
|
||||
exists(): Promise<boolean>;
|
||||
@@ -552,7 +536,14 @@ interface S3File extends Blob {
|
||||
arrayBuffer(): Promise<ArrayBuffer>;
|
||||
stream(options: S3Options): ReadableStream;
|
||||
write(
|
||||
data: string | Uint8Array | ArrayBuffer | Blob | ReadableStream | Response | Request,
|
||||
data:
|
||||
| string
|
||||
| Uint8Array
|
||||
| ArrayBuffer
|
||||
| Blob
|
||||
| ReadableStream
|
||||
| Response
|
||||
| Request,
|
||||
options?: BlobPropertyBag,
|
||||
): Promise<number>;
|
||||
|
||||
@@ -589,7 +580,7 @@ That means using `S3File` instances with `fetch()`, `Response`, and other web AP
|
||||
|
||||
To read a partial range of a file, you can use the `slice` method.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={1}
|
||||
```ts
|
||||
const partial = s3file.slice(0, 1024);
|
||||
|
||||
// Read the partial range as a Uint8Array
|
||||
@@ -605,7 +596,7 @@ Internally, this works by using the HTTP `Range` header to request only the byte
|
||||
|
||||
To delete a file from S3, you can use the `delete` method.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={1}
|
||||
```ts
|
||||
await s3file.delete();
|
||||
// await s3File.unlink();
|
||||
```
|
||||
@@ -633,7 +624,7 @@ The `S3Client` class provides several static methods for interacting with S3.
|
||||
|
||||
To write data directly to a path in the bucket, you can use the `S3Client.write` static method.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={12, 15-18, 22, 25-29}
|
||||
```ts
|
||||
import { S3Client } from "bun";
|
||||
|
||||
const credentials = {
|
||||
@@ -671,7 +662,7 @@ This is equivalent to calling `new S3Client(credentials).write("my-file.txt", "H
|
||||
|
||||
To generate a presigned URL for an S3 file, you can use the `S3Client.presign` static method.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={11-14}
|
||||
```ts
|
||||
import { S3Client } from "bun";
|
||||
|
||||
const credentials = {
|
||||
@@ -694,7 +685,7 @@ This is equivalent to calling `new S3Client(credentials).presign("my-file.txt",
|
||||
|
||||
To list some or all (up to 1,000) objects in a bucket, you can use the `S3Client.list` static method.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={12, 15-20, 24-29}
|
||||
```ts
|
||||
import { S3Client } from "bun";
|
||||
|
||||
const credentials = {
|
||||
@@ -733,7 +724,7 @@ This is equivalent to calling `new S3Client(credentials).list()`.
|
||||
|
||||
To check if an S3 file exists, you can use the `S3Client.exists` static method.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={11}
|
||||
```ts
|
||||
import { S3Client } from "bun";
|
||||
|
||||
const credentials = {
|
||||
@@ -749,13 +740,12 @@ const exists = await S3Client.exists("my-file.txt", credentials);
|
||||
|
||||
The same method also works on `S3File` instances.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight=7}
|
||||
```ts
|
||||
import { s3 } from "bun";
|
||||
|
||||
const s3file = s3.file("my-file.txt", {
|
||||
// ...credentials,
|
||||
...credentials,
|
||||
});
|
||||
|
||||
const exists = await s3file.exists();
|
||||
```
|
||||
|
||||
@@ -763,7 +753,7 @@ const exists = await s3file.exists();
|
||||
|
||||
To quickly check the size of S3 file without downloading it, you can use the `S3Client.size` static method.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={11}
|
||||
```ts
|
||||
import { S3Client } from "bun";
|
||||
|
||||
const credentials = {
|
||||
@@ -783,7 +773,7 @@ This is equivalent to calling `new S3Client(credentials).size("my-file.txt")`.
|
||||
|
||||
To get the size, etag, and other metadata of an S3 file, you can use the `S3Client.stat` static method.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { S3Client } from "bun";
|
||||
|
||||
const credentials = {
|
||||
@@ -795,22 +785,19 @@ const credentials = {
|
||||
};
|
||||
|
||||
const stat = await S3Client.stat("my-file.txt", credentials);
|
||||
```
|
||||
|
||||
```txt
|
||||
{
|
||||
etag: "\"7a30b741503c0b461cc14157e2df4ad8\"",
|
||||
lastModified: 2025-01-07T00:19:10.000Z,
|
||||
size: 1024,
|
||||
type: "text/plain;charset=utf-8",
|
||||
}
|
||||
// {
|
||||
// etag: "\"7a30b741503c0b461cc14157e2df4ad8\"",
|
||||
// lastModified: 2025-01-07T00:19:10.000Z,
|
||||
// size: 1024,
|
||||
// type: "text/plain;charset=utf-8",
|
||||
// }
|
||||
```
|
||||
|
||||
### `S3Client.delete` (static)
|
||||
|
||||
To delete an S3 file, you can use the `S3Client.delete` static method.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={10, 15}
|
||||
```ts
|
||||
import { S3Client } from "bun";
|
||||
|
||||
const credentials = {
|
||||
@@ -832,14 +819,14 @@ await S3Client.unlink("my-file.txt", credentials);
|
||||
|
||||
To make it easier to use the same code for local files and S3 files, the `s3://` protocol is supported in `fetch` and `Bun.file()`.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const response = await fetch("s3://my-bucket/my-file.txt");
|
||||
const file = Bun.file("s3://my-bucket/my-file.txt");
|
||||
```
|
||||
|
||||
You can additionally pass `s3` options to the `fetch` and `Bun.file` functions.
|
||||
|
||||
```ts s3.ts icon="/icons/typescript.svg" highlight={2-6}
|
||||
```ts
|
||||
const response = await fetch("s3://my-bucket/my-file.txt", {
|
||||
s3: {
|
||||
accessKeyId: "your-access-key",
|
||||
@@ -847,7 +834,7 @@ const response = await fetch("s3://my-bucket/my-file.txt", {
|
||||
endpoint: "https://s3.us-east-1.amazonaws.com",
|
||||
},
|
||||
headers: {
|
||||
range: "bytes=0-1023",
|
||||
"range": "bytes=0-1023",
|
||||
},
|
||||
});
|
||||
```
|
||||
@@ -1,40 +1,30 @@
|
||||
---
|
||||
title: Secrets
|
||||
description: Use Bun's Secrets API to store and retrieve sensitive credentials securely
|
||||
---
|
||||
|
||||
Store and retrieve sensitive credentials securely using the operating system's native credential storage APIs.
|
||||
|
||||
<Warning>This API is new and experimental. It may change in the future.</Warning>
|
||||
**Experimental:** This API is new and experimental. It may change in the future.
|
||||
|
||||
```typescript index.ts icon="/icons/typescript.svg"
|
||||
```typescript
|
||||
import { secrets } from "bun";
|
||||
|
||||
let githubToken: string | null = await secrets.get({
|
||||
const githubToken = await secrets.get({
|
||||
service: "my-cli-tool",
|
||||
name: "github-token",
|
||||
});
|
||||
|
||||
if (!githubToken) {
|
||||
githubToken = prompt("Please enter your GitHub token");
|
||||
|
||||
const response = await fetch("https://api.github.com/name", {
|
||||
headers: { "Authorization": `token ${githubToken}` },
|
||||
});
|
||||
console.log("Please enter your GitHub token");
|
||||
} else {
|
||||
await secrets.set({
|
||||
service: "my-cli-tool",
|
||||
name: "github-token",
|
||||
value: githubToken,
|
||||
value: prompt("Please enter your GitHub token"),
|
||||
});
|
||||
console.log("GitHub token stored");
|
||||
}
|
||||
|
||||
const response = await fetch("https://api.github.com/name", {
|
||||
headers: { Authorization: `token ${githubToken}` },
|
||||
});
|
||||
|
||||
console.log(`Logged in as ${(await response.json()).login}`);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
`Bun.secrets` provides a cross-platform API for managing sensitive credentials that CLI tools and development applications typically store in plaintext files like `~/.npmrc`, `~/.aws/credentials`, or `.env` files. It uses:
|
||||
@@ -45,12 +35,7 @@ console.log(`Logged in as ${(await response.json()).login}`);
|
||||
|
||||
All operations are asynchronous and non-blocking, running on Bun's threadpool.
|
||||
|
||||
<Note>
|
||||
In the future, we may add an additional `provider` option to make this better for production deployment secrets, but
|
||||
today this API is mostly useful for local development tools.
|
||||
</Note>
|
||||
|
||||
---
|
||||
Note: in the future, we may add an additional `provider` option to make this better for production deployment secrets, but today this API is mostly useful for local development tools.
|
||||
|
||||
## API
|
||||
|
||||
@@ -127,8 +112,6 @@ const deleted = await Bun.secrets.delete({
|
||||
|
||||
- `Promise<boolean>` - `true` if a credential was deleted, `false` if not found
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Storing CLI Tool Credentials
|
||||
@@ -160,7 +143,7 @@ const token = await Bun.secrets.get({
|
||||
if (token) {
|
||||
const response = await fetch("https://api.github.com/name", {
|
||||
headers: {
|
||||
Authorization: `token ${token}`,
|
||||
"Authorization": `token ${token}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -235,8 +218,6 @@ await Bun.secrets.set({
|
||||
// The old password is replaced
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Platform Behavior
|
||||
|
||||
### macOS (Keychain)
|
||||
@@ -278,8 +259,6 @@ await Bun.secrets.set({
|
||||
- macOS: Keychain Access must be available
|
||||
- Windows: Credential Manager service must be enabled
|
||||
|
||||
---
|
||||
|
||||
## Comparison with Environment Variables
|
||||
|
||||
Unlike environment variables, `Bun.secrets`:
|
||||
@@ -292,8 +271,6 @@ Unlike environment variables, `Bun.secrets`:
|
||||
- ❌ Requires OS credential service
|
||||
- ❌ Not very useful for deployment secrets (use environment variables in production)
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use descriptive service names**: Match the tool or application name
|
||||
@@ -317,8 +294,6 @@ Unlike environment variables, `Bun.secrets`:
|
||||
- ✅ Personal API keys for testing
|
||||
- ❌ Production servers (use proper secret management)
|
||||
|
||||
---
|
||||
|
||||
## TypeScript
|
||||
|
||||
```typescript
|
||||
@@ -337,3 +312,8 @@ namespace Bun {
|
||||
const secrets: Secrets;
|
||||
}
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Environment Variables](./env.md) - For deployment configuration
|
||||
- [Bun.password](./password.md) - For password hashing and verification
|
||||
@@ -1,17 +1,12 @@
|
||||
---
|
||||
title: Semver
|
||||
description: Use Bun's semantic versioning API
|
||||
---
|
||||
|
||||
Bun implements a semantic versioning API which can be used to compare versions and determine if a version is compatible with another range of versions. The versions and ranges are designed to be compatible with `node-semver`, which is used by npm clients.
|
||||
|
||||
It's about 20x faster than `node-semver`.
|
||||
|
||||
<Frame></Frame>
|
||||

|
||||
|
||||
Currently, this API provides two functions:
|
||||
Currently, this API provides two functions :
|
||||
|
||||
## `Bun.semver.satisfies(version: string, range: string): boolean`
|
||||
#### `Bun.semver.satisfies(version: string, range: string): boolean`
|
||||
|
||||
Returns `true` if `version` satisfies `range`, otherwise `false`.
|
||||
|
||||
@@ -36,7 +31,7 @@ semver.satisfies("1.0.0", "1.0.0 - 1.0.1"); // true
|
||||
|
||||
If `range` is invalid, it returns false. If `version` is invalid, it returns false.
|
||||
|
||||
## `Bun.semver.order(versionA: string, versionB: string): 0 | 1 | -1`
|
||||
#### `Bun.semver.order(versionA: string, versionB: string): 0 | 1 | -1`
|
||||
|
||||
Returns `0` if `versionA` and `versionB` are equal, `1` if `versionA` is greater than `versionB`, and `-1` if `versionA` is less than `versionB`.
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
---
|
||||
title: Spawn
|
||||
description: Spawn child processes with `Bun.spawn` or `Bun.spawnSync`
|
||||
---
|
||||
Spawn child processes with `Bun.spawn` or `Bun.spawnSync`.
|
||||
|
||||
## Spawn a process (`Bun.spawn()`)
|
||||
|
||||
@@ -32,25 +29,68 @@ By default, the input stream of the subprocess is undefined; it can be configure
|
||||
|
||||
```ts
|
||||
const proc = Bun.spawn(["cat"], {
|
||||
stdin: await fetch("https://raw.githubusercontent.com/oven-sh/bun/main/examples/hashing.js"),
|
||||
stdin: await fetch(
|
||||
"https://raw.githubusercontent.com/oven-sh/bun/main/examples/hashing.js",
|
||||
),
|
||||
});
|
||||
|
||||
const text = await proc.stdout.text();
|
||||
console.log(text); // "const input = "hello world".repeat(400); ..."
|
||||
```
|
||||
|
||||
| Value | Description |
|
||||
| ------------------------ | ------------------------------------------------ |
|
||||
| `null` | **Default.** Provide no input to the subprocess |
|
||||
| `"pipe"` | Return a `FileSink` for fast incremental writing |
|
||||
| `"inherit"` | Inherit the `stdin` of the parent process |
|
||||
| `Bun.file()` | Read from the specified file |
|
||||
| `TypedArray \| DataView` | Use a binary buffer as input |
|
||||
| `Response` | Use the response `body` as input |
|
||||
| `Request` | Use the request `body` as input |
|
||||
| `ReadableStream` | Use a readable stream as input |
|
||||
| `Blob` | Use a blob as input |
|
||||
| `number` | Read from the file with a given file descriptor |
|
||||
{% table %}
|
||||
|
||||
---
|
||||
|
||||
- `null`
|
||||
- **Default.** Provide no input to the subprocess
|
||||
|
||||
---
|
||||
|
||||
- `"pipe"`
|
||||
- Return a `FileSink` for fast incremental writing
|
||||
|
||||
---
|
||||
|
||||
- `"inherit"`
|
||||
- Inherit the `stdin` of the parent process
|
||||
|
||||
---
|
||||
|
||||
- `Bun.file()`
|
||||
- Read from the specified file.
|
||||
|
||||
---
|
||||
|
||||
- `TypedArray | DataView`
|
||||
- Use a binary buffer as input.
|
||||
|
||||
---
|
||||
|
||||
- `Response`
|
||||
- Use the response `body` as input.
|
||||
|
||||
---
|
||||
|
||||
- `Request`
|
||||
- Use the request `body` as input.
|
||||
|
||||
---
|
||||
|
||||
- `ReadableStream`
|
||||
- Use a readable stream as input.
|
||||
|
||||
---
|
||||
|
||||
- `Blob`
|
||||
- Use a blob as input.
|
||||
|
||||
---
|
||||
|
||||
- `number`
|
||||
- Read from the file with a given file descriptor.
|
||||
|
||||
{% /table %}
|
||||
|
||||
The `"pipe"` option lets incrementally write to the subprocess's input stream from the parent process.
|
||||
|
||||
@@ -89,7 +129,7 @@ const proc = Bun.spawn(["cat"], {
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const output = await proc.stdout.text();
|
||||
const output = await new Response(proc.stdout).text();
|
||||
console.log(output); // "Hello from ReadableStream!"
|
||||
```
|
||||
|
||||
@@ -100,24 +140,45 @@ You can read results from the subprocess via the `stdout` and `stderr` propertie
|
||||
```ts
|
||||
const proc = Bun.spawn(["bun", "--version"]);
|
||||
const text = await proc.stdout.text();
|
||||
console.log(text); // => "1.3.3\n"
|
||||
console.log(text); // => "$BUN_LATEST_VERSION\n"
|
||||
```
|
||||
|
||||
Configure the output stream by passing one of the following values to `stdout/stderr`:
|
||||
|
||||
| Value | Description |
|
||||
| ------------ | --------------------------------------------------------------------------------------------------- |
|
||||
| `"pipe"` | **Default for `stdout`.** Pipe the output to a `ReadableStream` on the returned `Subprocess` object |
|
||||
| `"inherit"` | **Default for `stderr`.** Inherit from the parent process |
|
||||
| `"ignore"` | Discard the output |
|
||||
| `Bun.file()` | Write to the specified file |
|
||||
| `number` | Write to the file with the given file descriptor |
|
||||
{% table %}
|
||||
|
||||
---
|
||||
|
||||
- `"pipe"`
|
||||
- **Default for `stdout`.** Pipe the output to a `ReadableStream` on the returned `Subprocess` object.
|
||||
|
||||
---
|
||||
|
||||
- `"inherit"`
|
||||
- **Default for `stderr`.** Inherit from the parent process.
|
||||
|
||||
---
|
||||
|
||||
- `"ignore"`
|
||||
- Discard the output.
|
||||
|
||||
---
|
||||
|
||||
- `Bun.file()`
|
||||
- Write to the specified file.
|
||||
|
||||
---
|
||||
|
||||
- `number`
|
||||
- Write to the file with the given file descriptor.
|
||||
|
||||
{% /table %}
|
||||
|
||||
## Exit handling
|
||||
|
||||
Use the `onExit` callback to listen for the process exiting or being killed.
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const proc = Bun.spawn(["bun", "--version"], {
|
||||
onExit(proc, exitCode, signalCode, error) {
|
||||
// exit handler
|
||||
@@ -127,7 +188,7 @@ const proc = Bun.spawn(["bun", "--version"], {
|
||||
|
||||
For convenience, the `exited` property is a `Promise` that resolves when the process exits.
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const proc = Bun.spawn(["bun", "--version"]);
|
||||
|
||||
await proc.exited; // resolves when process exit
|
||||
@@ -138,7 +199,7 @@ proc.signalCode; // null | "SIGABRT" | "SIGALRM" | ...
|
||||
|
||||
To kill a process:
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const proc = Bun.spawn(["bun", "--version"]);
|
||||
proc.kill();
|
||||
proc.killed; // true
|
||||
@@ -149,7 +210,7 @@ proc.kill("SIGTERM"); // specify a signal name
|
||||
|
||||
The parent `bun` process will not terminate until all child processes have exited. Use `proc.unref()` to detach the child process from the parent.
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const proc = Bun.spawn(["bun", "--version"]);
|
||||
proc.unref();
|
||||
```
|
||||
@@ -158,7 +219,7 @@ proc.unref();
|
||||
|
||||
You can get information about the process's resource usage after it has exited:
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const proc = Bun.spawn(["bun", "--version"]);
|
||||
await proc.exited;
|
||||
|
||||
@@ -172,7 +233,7 @@ console.log(`CPU time (system): ${usage.cpuTime.system} µs`);
|
||||
|
||||
You can abort a subprocess using an `AbortSignal`:
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
|
||||
@@ -189,7 +250,7 @@ controller.abort();
|
||||
|
||||
You can set a timeout for a subprocess to automatically terminate after a specific duration:
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Kill the process after 5 seconds
|
||||
const proc = Bun.spawn({
|
||||
cmd: ["sleep", "10"],
|
||||
@@ -201,7 +262,7 @@ await proc.exited; // Will resolve after 5 seconds
|
||||
|
||||
By default, timed-out processes are killed with the `SIGTERM` signal. You can specify a different signal with the `killSignal` option:
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// Kill the process with SIGKILL after 5 seconds
|
||||
const proc = Bun.spawn({
|
||||
cmd: ["sleep", "10"],
|
||||
@@ -216,10 +277,10 @@ The `killSignal` option also controls which signal is sent when an AbortSignal i
|
||||
|
||||
For spawnSync, you can limit the maximum number of bytes of output before the process is killed:
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
// Kill 'yes' after it emits over 100 bytes of output
|
||||
```ts
|
||||
// KIll 'yes' after it emits over 100 bytes of output
|
||||
const result = Bun.spawnSync({
|
||||
cmd: ["yes"], // or ["bun", "exec", "yes"] on Windows
|
||||
cmd: ["yes"], // or ["bun", "exec", "yes"] on windows
|
||||
maxBuffer: 100,
|
||||
});
|
||||
// process exits
|
||||
@@ -229,7 +290,7 @@ const result = Bun.spawnSync({
|
||||
|
||||
Bun supports direct inter-process communication channel between two `bun` processes. To receive messages from a spawned Bun subprocess, specify an `ipc` handler.
|
||||
|
||||
```ts parent.ts icon="/icons/typescript.svg"
|
||||
```ts#parent.ts
|
||||
const child = Bun.spawn(["bun", "child.ts"], {
|
||||
ipc(message) {
|
||||
/**
|
||||
@@ -241,13 +302,13 @@ const child = Bun.spawn(["bun", "child.ts"], {
|
||||
|
||||
The parent process can send messages to the subprocess using the `.send()` method on the returned `Subprocess` instance. A reference to the sending subprocess is also available as the second argument in the `ipc` handler.
|
||||
|
||||
```ts parent.ts icon="/icons/typescript.svg"
|
||||
```ts#parent.ts
|
||||
const childProc = Bun.spawn(["bun", "child.ts"], {
|
||||
ipc(message, childProc) {
|
||||
/**
|
||||
* The message received from the sub process
|
||||
**/
|
||||
childProc.send("Respond to child");
|
||||
childProc.send("Respond to child")
|
||||
},
|
||||
});
|
||||
|
||||
@@ -256,17 +317,17 @@ childProc.send("I am your father"); // The parent can send messages to the child
|
||||
|
||||
Meanwhile the child process can send messages to its parent using with `process.send()` and receive messages with `process.on("message")`. This is the same API used for `child_process.fork()` in Node.js.
|
||||
|
||||
```ts child.ts
|
||||
```ts#child.ts
|
||||
process.send("Hello from child as string");
|
||||
process.send({ message: "Hello from child as object" });
|
||||
|
||||
process.on("message", message => {
|
||||
process.on("message", (message) => {
|
||||
// print message from parent
|
||||
console.log(message);
|
||||
});
|
||||
```
|
||||
|
||||
```ts child.ts
|
||||
```ts#child.ts
|
||||
// send a string
|
||||
process.send("Hello from child as string");
|
||||
|
||||
@@ -289,7 +350,7 @@ childProc.disconnect();
|
||||
|
||||
To use IPC between a `bun` process and a Node.js process, set `serialization: "json"` in `Bun.spawn`. This is because Node.js and Bun use different JavaScript engines with different object serialization formats.
|
||||
|
||||
```js bun-node-ipc.js icon="file-code"
|
||||
```js#bun-node-ipc.js
|
||||
if (typeof Bun !== "undefined") {
|
||||
const prefix = `[bun ${process.versions.bun} 🐇]`;
|
||||
const node = Bun.spawn({
|
||||
@@ -313,8 +374,6 @@ if (typeof Bun !== "undefined") {
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Blocking API (`Bun.spawnSync()`)
|
||||
|
||||
Bun provides a synchronous equivalent of `Bun.spawn` called `Bun.spawnSync`. This is a blocking API that supports the same inputs and parameters as `Bun.spawn`. It returns a `SyncSubprocess` object, which differs from `Subprocess` in a few ways.
|
||||
@@ -332,35 +391,23 @@ console.log(proc.stdout.toString());
|
||||
|
||||
As a rule of thumb, the asynchronous `Bun.spawn` API is better for HTTP servers and apps, and `Bun.spawnSync` is better for building command-line tools.
|
||||
|
||||
---
|
||||
|
||||
## Benchmarks
|
||||
|
||||
<Note>
|
||||
⚡️ Under the hood, `Bun.spawn` and `Bun.spawnSync` use
|
||||
[`posix_spawn(3)`](https://man7.org/linux/man-pages/man3/posix_spawn.3.html).
|
||||
</Note>
|
||||
{%callout%}
|
||||
⚡️ Under the hood, `Bun.spawn` and `Bun.spawnSync` use [`posix_spawn(3)`](https://man7.org/linux/man-pages/man3/posix_spawn.3.html).
|
||||
{%/callout%}
|
||||
|
||||
Bun's `spawnSync` spawns processes 60% faster than the Node.js `child_process` module.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun spawn.mjs
|
||||
```
|
||||
|
||||
```txt
|
||||
```bash
|
||||
$ bun spawn.mjs
|
||||
cpu: Apple M1 Max
|
||||
runtime: bun 1.x (arm64-darwin)
|
||||
|
||||
benchmark time (avg) (min … max) p75 p99 p995
|
||||
--------------------------------------------------------- -----------------------------
|
||||
spawnSync echo hi 888.14 µs/iter (821.83 µs … 1.2 ms) 905.92 µs 1 ms 1.03 ms
|
||||
```
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
node spawn.node.mjs
|
||||
```
|
||||
|
||||
```txt
|
||||
$ node spawn.node.mjs
|
||||
cpu: Apple M1 Max
|
||||
runtime: node v18.9.1 (arm64-darwin)
|
||||
|
||||
@@ -369,19 +416,22 @@ benchmark time (avg) (min … max) p75 p99
|
||||
spawnSync echo hi 1.47 ms/iter (1.14 ms … 2.64 ms) 1.57 ms 2.37 ms 2.52 ms
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Reference
|
||||
|
||||
A reference of the Spawn API and types are shown below. The real types have complex generics to strongly type the `Subprocess` streams with the options passed to `Bun.spawn` and `Bun.spawnSync`. For full details, find these types as defined [bun.d.ts](https://github.com/oven-sh/bun/blob/main/packages/bun-types/bun.d.ts).
|
||||
|
||||
```ts See Typescript Definitions expandable
|
||||
```ts
|
||||
interface Bun {
|
||||
spawn(command: string[], options?: SpawnOptions.OptionsObject): Subprocess;
|
||||
spawnSync(command: string[], options?: SpawnOptions.OptionsObject): SyncSubprocess;
|
||||
spawnSync(
|
||||
command: string[],
|
||||
options?: SpawnOptions.OptionsObject,
|
||||
): SyncSubprocess;
|
||||
|
||||
spawn(options: { cmd: string[] } & SpawnOptions.OptionsObject): Subprocess;
|
||||
spawnSync(options: { cmd: string[] } & SpawnOptions.OptionsObject): SyncSubprocess;
|
||||
spawnSync(
|
||||
options: { cmd: string[] } & SpawnOptions.OptionsObject,
|
||||
): SyncSubprocess;
|
||||
}
|
||||
|
||||
namespace SpawnOptions {
|
||||
@@ -1,11 +1,6 @@
|
||||
---
|
||||
title: SQL
|
||||
description: Bun provides native bindings for working with SQL databases through a unified Promise-based API that supports PostgreSQL, MySQL, and SQLite.
|
||||
---
|
||||
Bun provides native bindings for working with SQL databases through a unified Promise-based API that supports PostgreSQL, MySQL, and SQLite. The interface is designed to be simple and performant, using tagged template literals for queries and offering features like connection pooling, transactions, and prepared statements.
|
||||
|
||||
The interface is designed to be simple and performant, using tagged template literals for queries and offering features like connection pooling, transactions, and prepared statements.
|
||||
|
||||
```ts title="db.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { sql, SQL } from "bun";
|
||||
|
||||
// PostgreSQL (default)
|
||||
@@ -30,25 +25,35 @@ const sqliteResults = await sqlite`
|
||||
`;
|
||||
```
|
||||
|
||||
### Features
|
||||
{% features title="Features" %}
|
||||
|
||||
- Tagged template literals to protect against SQL injection
|
||||
- Transactions
|
||||
- Named & positional parameters
|
||||
- Connection pooling
|
||||
- `BigInt` support
|
||||
- SASL Auth support (SCRAM-SHA-256), MD5, and Clear Text
|
||||
- Connection timeouts
|
||||
- Returning rows as data objects, arrays of arrays, or Buffer
|
||||
- Binary protocol support makes it faster
|
||||
- TLS support (and auth mode)
|
||||
- Automatic configuration with environment variable
|
||||
{% icon size=20 name="Shield" /%} Tagged template literals to protect against SQL injection
|
||||
|
||||
---
|
||||
{% icon size=20 name="GitMerge" /%} Transactions
|
||||
|
||||
{% icon size=20 name="Variable" /%} Named & positional parameters
|
||||
|
||||
{% icon size=20 name="Network" /%} Connection pooling
|
||||
|
||||
{% icon size=20 name="Binary" /%} `BigInt` support
|
||||
|
||||
{% icon size=20 name="Key" /%} SASL Auth support (SCRAM-SHA-256), MD5, and Clear Text
|
||||
|
||||
{% icon size=20 name="Timer" /%} Connection timeouts
|
||||
|
||||
{% icon size=20 name="Database" /%} Returning rows as data objects, arrays of arrays, or `Buffer`
|
||||
|
||||
{% icon size=20 name="Code" /%} Binary protocol support makes it faster
|
||||
|
||||
{% icon size=20 name="Lock" /%} TLS support (and auth mode)
|
||||
|
||||
{% icon size=20 name="Settings" /%} Automatic configuration with environment variable
|
||||
|
||||
{% /features %}
|
||||
|
||||
## Database Support
|
||||
|
||||
`Bun.SQL` provides a unified API for multiple database systems:
|
||||
Bun.SQL provides a unified API for multiple database systems:
|
||||
|
||||
### PostgreSQL
|
||||
|
||||
@@ -58,7 +63,7 @@ PostgreSQL is used when:
|
||||
- The connection string explicitly uses `postgres://` or `postgresql://` protocols
|
||||
- No connection string is provided and environment variables point to PostgreSQL
|
||||
|
||||
```ts title="db.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { sql } from "bun";
|
||||
// Uses PostgreSQL if DATABASE_URL is not set or is a PostgreSQL URL
|
||||
await sql`SELECT ...`;
|
||||
@@ -72,7 +77,7 @@ await pg`SELECT ...`;
|
||||
|
||||
MySQL support is built into Bun.SQL, providing the same tagged template literal interface with full compatibility for MySQL 5.7+ and MySQL 8.0+:
|
||||
|
||||
```ts title="db.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { SQL } from "bun";
|
||||
|
||||
// MySQL connection
|
||||
@@ -106,7 +111,7 @@ const newUsers = [
|
||||
await mysql`INSERT INTO users ${mysql(newUsers)}`;
|
||||
```
|
||||
|
||||
<Accordion title="MySQL Connection String Formats">
|
||||
{% details summary="MySQL Connection String Formats" %}
|
||||
|
||||
MySQL accepts various URL formats for connection strings:
|
||||
|
||||
@@ -125,9 +130,9 @@ new SQL("mysql://user:pass@localhost/db?ssl=true");
|
||||
new SQL("mysql://user:pass@/database?socket=/var/run/mysqld/mysqld.sock");
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
|
||||
<Accordion title="MySQL-Specific Features">
|
||||
{% details summary="MySQL-Specific Features" %}
|
||||
|
||||
MySQL databases support:
|
||||
|
||||
@@ -139,7 +144,7 @@ MySQL databases support:
|
||||
- **Connection attributes**: Client information sent to server for monitoring
|
||||
- **Query pipelining**: Execute multiple prepared statements without waiting for responses
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
|
||||
### SQLite
|
||||
|
||||
@@ -153,19 +158,19 @@ const memory = new SQL(":memory:");
|
||||
const memory2 = new SQL("sqlite://:memory:");
|
||||
|
||||
// File-based database
|
||||
const sql1 = new SQL("sqlite://myapp.db");
|
||||
const db = new SQL("sqlite://myapp.db");
|
||||
|
||||
// Using options object
|
||||
const sql2 = new SQL({
|
||||
const db2 = new SQL({
|
||||
adapter: "sqlite",
|
||||
filename: "./data/app.db",
|
||||
});
|
||||
|
||||
// For simple filenames, specify adapter explicitly
|
||||
const sql3 = new SQL("myapp.db", { adapter: "sqlite" });
|
||||
const db3 = new SQL("myapp.db", { adapter: "sqlite" });
|
||||
```
|
||||
|
||||
<Accordion title="SQLite Connection String Formats">
|
||||
{% details summary="SQLite Connection String Formats" %}
|
||||
|
||||
SQLite accepts various URL formats for connection strings:
|
||||
|
||||
@@ -194,18 +199,16 @@ new SQL("sqlite://data.db?mode=rw"); // Read-write mode (no create)
|
||||
new SQL("sqlite://data.db?mode=rwc"); // Read-write-create mode (default)
|
||||
```
|
||||
|
||||
<Note>
|
||||
Simple filenames without a protocol (like `"myapp.db"`) require explicitly specifying `{ adapter: "sqlite" }` to avoid ambiguity with PostgreSQL.
|
||||
</Note>
|
||||
**Note:** Simple filenames without a protocol (like `"myapp.db"`) require explicitly specifying `{ adapter: "sqlite" }` to avoid ambiguity with PostgreSQL.
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
|
||||
<Accordion title="SQLite-Specific Options">
|
||||
{% details summary="SQLite-Specific Options" %}
|
||||
|
||||
SQLite databases support additional configuration options:
|
||||
|
||||
```ts
|
||||
const sql = new SQL({
|
||||
const db = new SQL({
|
||||
adapter: "sqlite",
|
||||
filename: "app.db",
|
||||
|
||||
@@ -226,9 +229,9 @@ Query parameters in the URL are parsed to set these options:
|
||||
- `?mode=rw` → `readonly: false, create: false`
|
||||
- `?mode=rwc` → `readonly: false, create: true` (default)
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
|
||||
## Inserting data
|
||||
### Inserting data
|
||||
|
||||
You can pass JavaScript values directly to the SQL template literal and escaping will be handled for you.
|
||||
|
||||
@@ -284,8 +287,6 @@ await sql`INSERT INTO users ${sql(user, "name", "email")}`;
|
||||
// Only inserts name and email columns, ignoring other fields
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Query Results
|
||||
|
||||
By default, Bun's SQL client returns query results as arrays of objects, where each object represents a row with column names as keys. However, there are cases where you might want the data in a different format. The client provides two additional methods for this purpose.
|
||||
@@ -319,8 +320,6 @@ const rows = await sql`SELECT * FROM users`.raw();
|
||||
console.log(rows); // [[Buffer, Buffer], [Buffer, Buffer], [Buffer, Buffer]]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SQL Fragments
|
||||
|
||||
A common need in database applications is the ability to construct queries dynamically based on runtime conditions. Bun provides safe ways to do this without risking SQL injection.
|
||||
@@ -392,9 +391,7 @@ await sql`SELECT * FROM products WHERE ids = ANY(${sql.array([1, 2, 3])})`;
|
||||
// Generates: SELECT * FROM products WHERE ids = ANY(ARRAY[1, 2, 3])
|
||||
```
|
||||
|
||||
<Note>`sql.array` is PostgreSQL-only. Multi-dimensional arrays and NULL elements may not be supported yet.</Note>
|
||||
|
||||
---
|
||||
**Note**: `sql.array` is PostgreSQL-only. Multi-dimensional arrays and NULL elements may not be supported yet.
|
||||
|
||||
## `sql``.simple()`
|
||||
|
||||
@@ -434,22 +431,27 @@ const result = await sql.unsafe(`
|
||||
`);
|
||||
|
||||
// Using parameters (only one command is allowed)
|
||||
const result = await sql.unsafe("SELECT " + dangerous + " FROM users WHERE id = $1", [id]);
|
||||
const result = await sql.unsafe(
|
||||
"SELECT " + dangerous + " FROM users WHERE id = $1",
|
||||
[id],
|
||||
);
|
||||
```
|
||||
|
||||
#### What is SQL Injection?
|
||||
|
||||
{% image href="https://xkcd.com/327/" src="https://imgs.xkcd.com/comics/exploits_of_a_mom.png" /%}
|
||||
|
||||
### Execute and Cancelling Queries
|
||||
|
||||
Bun's SQL is lazy, which means it will only start executing when awaited or executed with `.execute()`.
|
||||
You can cancel a query that is currently executing by calling the `cancel()` method on the query object.
|
||||
|
||||
```ts
|
||||
const query = sql`SELECT * FROM users`.execute();
|
||||
const query = await sql`SELECT * FROM users`.execute();
|
||||
setTimeout(() => query.cancel(), 100);
|
||||
await query;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Database Environment Variables
|
||||
|
||||
`sql` connection parameters can be configured using environment variables. The client checks these variables in a specific order of precedence and automatically detects the database type based on the connection string format.
|
||||
@@ -571,8 +573,6 @@ DATABASE_URL="file:///absolute/path/to/db.sqlite"
|
||||
|
||||
**Note:** PostgreSQL-specific environment variables (`POSTGRES_URL`, `PGHOST`, etc.) are ignored when using SQLite.
|
||||
|
||||
---
|
||||
|
||||
## Runtime Preconnection
|
||||
|
||||
Bun can preconnect to PostgreSQL at startup to improve performance by establishing database connections before your application code runs. This is useful for reducing connection latency on the first database query.
|
||||
@@ -590,8 +590,6 @@ bun --sql-preconnect --hot index.js
|
||||
|
||||
The `--sql-preconnect` flag will automatically establish a PostgreSQL connection using your configured environment variables at startup. If the connection fails, it won't crash your application - the error will be handled gracefully.
|
||||
|
||||
---
|
||||
|
||||
## Connection Options
|
||||
|
||||
You can configure your database connection manually by passing options to the SQL constructor. Options vary depending on the database adapter:
|
||||
@@ -601,7 +599,7 @@ You can configure your database connection manually by passing options to the SQ
|
||||
```ts
|
||||
import { SQL } from "bun";
|
||||
|
||||
const sql = new SQL({
|
||||
const db = new SQL({
|
||||
// Required for MySQL when using options object
|
||||
adapter: "mysql",
|
||||
|
||||
@@ -622,13 +620,12 @@ const sql = new SQL({
|
||||
connectionTimeout: 30, // Timeout when establishing new connections
|
||||
|
||||
// SSL/TLS options
|
||||
ssl: "prefer", // or "disable", "require", "verify-ca", "verify-full"
|
||||
// tls: {
|
||||
// rejectUnauthorized: true,
|
||||
// ca: "path/to/ca.pem",
|
||||
// key: "path/to/key.pem",
|
||||
// cert: "path/to/cert.pem",
|
||||
// },
|
||||
tls: {
|
||||
rejectUnauthorized: true,
|
||||
ca: "path/to/ca.pem",
|
||||
key: "path/to/key.pem",
|
||||
cert: "path/to/cert.pem",
|
||||
},
|
||||
|
||||
// Callbacks
|
||||
onconnect: client => {
|
||||
@@ -649,7 +646,7 @@ const sql = new SQL({
|
||||
```ts
|
||||
import { SQL } from "bun";
|
||||
|
||||
const sql = new SQL({
|
||||
const db = new SQL({
|
||||
// Connection details (adapter is auto-detected as PostgreSQL)
|
||||
url: "postgres://user:pass@localhost:5432/dbname",
|
||||
|
||||
@@ -694,7 +691,7 @@ const sql = new SQL({
|
||||
```ts
|
||||
import { SQL } from "bun";
|
||||
|
||||
const sql = new SQL({
|
||||
const db = new SQL({
|
||||
// Required for SQLite
|
||||
adapter: "sqlite",
|
||||
filename: "./data/app.db", // or ":memory:" for in-memory database
|
||||
@@ -718,16 +715,14 @@ const sql = new SQL({
|
||||
});
|
||||
```
|
||||
|
||||
<Accordion title="SQLite Connection Notes">
|
||||
{% details summary="SQLite Connection Notes" %}
|
||||
|
||||
- **Connection Pooling**: SQLite doesn't use connection pooling as it's a file-based database. Each `SQL` instance represents a single connection.
|
||||
- **Transactions**: SQLite supports nested transactions through savepoints, similar to PostgreSQL.
|
||||
- **Concurrent Access**: SQLite handles concurrent access through file locking. Use WAL mode for better concurrency.
|
||||
- **Memory Databases**: Using `:memory:` creates a temporary database that exists only for the connection lifetime.
|
||||
|
||||
</Accordion>
|
||||
|
||||
---
|
||||
{% /details %}
|
||||
|
||||
## Dynamic passwords
|
||||
|
||||
@@ -744,8 +739,6 @@ const sql = new SQL(url, {
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SQLite-Specific Features
|
||||
|
||||
### Query Execution
|
||||
@@ -801,8 +794,6 @@ await sqlite`
|
||||
await sqlite`INSERT INTO flexible VALUES (${1}, ${"text"}, ${123.45}, ${Buffer.from("binary")})`;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Transactions
|
||||
|
||||
To start a new transaction, use `sql.begin`. This method works for both PostgreSQL and SQLite. For PostgreSQL, it reserves a dedicated connection from the pool. For SQLite, it begins a transaction on the single connection.
|
||||
@@ -878,8 +869,6 @@ await sql.commitDistributed("tx1");
|
||||
await sql.rollbackDistributed("tx1");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
Bun supports SCRAM-SHA-256 (SASL), MD5, and Clear Text authentication. SASL is recommended for better security. Check [Postgres SASL Authentication](https://www.postgresql.org/docs/current/sasl-authentication.html) for more information.
|
||||
@@ -914,17 +903,17 @@ The SSL mode can also be specified in connection strings:
|
||||
const sql = new SQL("postgres://user:password@localhost/mydb?sslmode=prefer");
|
||||
|
||||
// Using verify-full mode
|
||||
const sql = new SQL("postgres://user:password@localhost/mydb?sslmode=verify-full");
|
||||
const sql = new SQL(
|
||||
"postgres://user:password@localhost/mydb?sslmode=verify-full",
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Connection Pooling
|
||||
|
||||
Bun's SQL client automatically manages a connection pool, which is a pool of database connections that are reused for multiple queries. This helps to reduce the overhead of establishing and closing connections for each query, and it also helps to manage the number of concurrent connections to the database.
|
||||
|
||||
```ts
|
||||
const sql = new SQL({
|
||||
const db = new SQL({
|
||||
// Pool configuration
|
||||
max: 20, // Maximum 20 concurrent connections
|
||||
idleTimeout: 30, // Close idle connections after 30s
|
||||
@@ -936,7 +925,7 @@ const sql = new SQL({
|
||||
No connection will be made until a query is made.
|
||||
|
||||
```ts
|
||||
const sql = Bun.SQL(); // no connection are created
|
||||
const sql = Bun.sql(); // no connection are created
|
||||
|
||||
await sql`...`; // pool is started until max is reached (if possible), first available connection is used
|
||||
await sql`...`; // previous connection is reused
|
||||
@@ -952,8 +941,6 @@ await sql.close({ timeout: 5 }); // wait 5 seconds and close all connections fro
|
||||
await sql.close({ timeout: 0 }); // close all connections from the pool immediately
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Reserved Connections
|
||||
|
||||
Bun enables you to reserve a connection from the pool, and returns a client that wraps the single connection. This can be used for running queries on an isolated connection.
|
||||
@@ -976,8 +963,6 @@ try {
|
||||
} // Automatically released
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Prepared Statements
|
||||
|
||||
By default, Bun's SQL client automatically creates named prepared statements for queries where it can be inferred that the query is static. This provides better performance. However, you can change this behavior by setting `prepare: false` in the connection options:
|
||||
@@ -1006,8 +991,6 @@ You might want to use `prepare: false` when:
|
||||
|
||||
Note that disabling prepared statements may impact performance for queries that are executed frequently with different parameters, as the server needs to parse and plan each query from scratch.
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
The client provides typed errors for different failure scenarios. Errors are database-specific and extend from base error classes:
|
||||
@@ -1037,7 +1020,7 @@ try {
|
||||
}
|
||||
```
|
||||
|
||||
<Accordion title="PostgreSQL-Specific Error Codes">
|
||||
{% details summary="PostgreSQL-Specific Error Codes" %}
|
||||
|
||||
### PostgreSQL Connection Errors
|
||||
|
||||
@@ -1104,13 +1087,13 @@ try {
|
||||
| `ERR_POSTGRES_UNSAFE_TRANSACTION` | Unsafe transaction operation detected |
|
||||
| `ERR_POSTGRES_INVALID_TRANSACTION_STATE` | Invalid transaction state |
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
|
||||
### SQLite-Specific Errors
|
||||
|
||||
SQLite errors provide error codes and numbers that correspond to SQLite's standard error codes:
|
||||
|
||||
<Accordion title="Common SQLite Error Codes">
|
||||
{% details summary="Common SQLite Error Codes" %}
|
||||
|
||||
| Error Code | errno | Description |
|
||||
| ------------------- | ----- | ---------------------------------------------------- |
|
||||
@@ -1147,9 +1130,7 @@ try {
|
||||
}
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
---
|
||||
{% /details %}
|
||||
|
||||
## Numbers and BigInt
|
||||
|
||||
@@ -1164,8 +1145,6 @@ console.log(typeof x, x); // "string" "9223372036854777"
|
||||
console.log(typeof y, y); // "number" 12345
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## BigInt Instead of Strings
|
||||
|
||||
If you need large numbers as BigInt instead of strings, you can enable this by setting the `bigint` option to `true` when initializing the SQL client:
|
||||
@@ -1180,8 +1159,6 @@ const [{ x }] = await sql`SELECT 9223372036854777 as x`;
|
||||
console.log(typeof x, x); // "bigint" 9223372036854777n
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Roadmap
|
||||
|
||||
There's still some things we haven't finished yet.
|
||||
@@ -1190,8 +1167,6 @@ There's still some things we haven't finished yet.
|
||||
- Column name transforms (e.g. `snake_case` to `camelCase`). This is mostly blocked on a unicode-aware implementation of changing the case in C++ using WebKit's `WTF::String`.
|
||||
- Column type transforms
|
||||
|
||||
---
|
||||
|
||||
## Database-Specific Features
|
||||
|
||||
#### Authentication Methods
|
||||
@@ -1303,8 +1278,6 @@ We also haven't implemented some of the more uncommon features like:
|
||||
- Point & PostGIS types
|
||||
- All the multi-dimensional integer array types (only a couple of the types are supported)
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns & Best Practices
|
||||
|
||||
### Working with MySQL Result Sets
|
||||
@@ -1315,7 +1288,8 @@ const result = await mysql`INSERT INTO users (name) VALUES (${"Alice"})`;
|
||||
console.log(result.lastInsertRowid); // MySQL's LAST_INSERT_ID()
|
||||
|
||||
// Handling affected rows
|
||||
const updated = await mysql`UPDATE users SET active = ${false} WHERE age < ${18}`;
|
||||
const updated =
|
||||
await mysql`UPDATE users SET active = ${false} WHERE age < ${18}`;
|
||||
console.log(updated.affectedRows); // Number of rows updated
|
||||
|
||||
// Using MySQL-specific functions
|
||||
@@ -1348,47 +1322,43 @@ try {
|
||||
4. **Index properly**: MySQL relies heavily on indexes for query performance
|
||||
5. **Use `utf8mb4` charset**: It's set by default and handles all Unicode characters
|
||||
|
||||
---
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Why is this `Bun.sql` and not `Bun.postgres`?">
|
||||
The plan was to add more database drivers in the future. Now with MySQL support added, this unified API supports PostgreSQL, MySQL, and SQLite.
|
||||
</Accordion>
|
||||
<Accordion title="How do I know which database adapter is being used?">
|
||||
The adapter is automatically detected from the connection string:
|
||||
> Why is this `Bun.sql` and not `Bun.postgres`?
|
||||
|
||||
- URLs starting with `mysql://` or `mysql2://` use MySQL
|
||||
- URLs matching SQLite patterns (`:memory:`, `sqlite://`, `file://`) use SQLite
|
||||
- Everything else defaults to PostgreSQL
|
||||
</Accordion>
|
||||
<Accordion title="Are MySQL stored procedures supported?">
|
||||
Yes, stored procedures are fully supported including OUT parameters and multiple result sets:
|
||||
The plan was to add more database drivers in the future. Now with MySQL support added, this unified API supports PostgreSQL, MySQL, and SQLite.
|
||||
|
||||
```ts
|
||||
// Call stored procedure
|
||||
const results = await mysql`CALL GetUserStats(${userId}, @total_orders)`;
|
||||
> How do I know which database adapter is being used?
|
||||
|
||||
// Get OUT parameter
|
||||
const outParam = await mysql`SELECT @total_orders as total`;
|
||||
```
|
||||
</Accordion>
|
||||
<Accordion title="Can I use MySQL-specific SQL syntax?">
|
||||
Yes, you can use any MySQL-specific syntax:
|
||||
The adapter is automatically detected from the connection string:
|
||||
|
||||
```ts
|
||||
// MySQL-specific syntax works fine
|
||||
await mysql`SET @user_id = ${userId}`;
|
||||
await mysql`SHOW TABLES`;
|
||||
await mysql`DESCRIBE users`;
|
||||
await mysql`EXPLAIN SELECT * FROM users WHERE id = ${id}`;
|
||||
```
|
||||
</Accordion>
|
||||
- URLs starting with `mysql://` or `mysql2://` use MySQL
|
||||
- URLs matching SQLite patterns (`:memory:`, `sqlite://`, `file://`) use SQLite
|
||||
- Everything else defaults to PostgreSQL
|
||||
|
||||
</AccordionGroup>
|
||||
> Are MySQL stored procedures supported?
|
||||
|
||||
---
|
||||
Yes, stored procedures are fully supported including OUT parameters and multiple result sets:
|
||||
|
||||
```ts
|
||||
// Call stored procedure
|
||||
const results = await mysql`CALL GetUserStats(${userId}, @total_orders)`;
|
||||
|
||||
// Get OUT parameter
|
||||
const outParam = await mysql`SELECT @total_orders as total`;
|
||||
```
|
||||
|
||||
> Can I use MySQL-specific SQL syntax?
|
||||
|
||||
Yes, you can use any MySQL-specific syntax:
|
||||
|
||||
```ts
|
||||
// MySQL-specific syntax works fine
|
||||
await mysql`SET @user_id = ${userId}`;
|
||||
await mysql`SHOW TABLES`;
|
||||
await mysql`DESCRIBE users`;
|
||||
await mysql`EXPLAIN SELECT * FROM users WHERE id = ${id}`;
|
||||
```
|
||||
|
||||
## Why not just use an existing library?
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
---
|
||||
title: SQLite
|
||||
description: Bun natively implements a high-performance SQLite3 driver.
|
||||
---
|
||||
|
||||
Bun natively implements a high-performance [SQLite3](https://www.sqlite.org/) driver. To use it import from the built-in `bun:sqlite` module.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { Database } from "bun:sqlite";
|
||||
|
||||
const db = new Database(":memory:");
|
||||
const query = db.query("select 'Hello world' as message;");
|
||||
query.get();
|
||||
```
|
||||
|
||||
```txt
|
||||
{ message: "Hello world" }
|
||||
query.get(); // => { message: "Hello world" }
|
||||
```
|
||||
|
||||
The API is simple, synchronous, and fast. Credit to [better-sqlite3](https://github.com/JoshuaWise/better-sqlite3) and its contributors for inspiring the API of `bun:sqlite`.
|
||||
@@ -32,18 +23,13 @@ Features include:
|
||||
|
||||
The `bun:sqlite` module is roughly 3-6x faster than `better-sqlite3` and 8-9x faster than `deno.land/x/sqlite` for read queries. Each driver was benchmarked against the [Northwind Traders](https://github.com/jpwhite3/northwind-SQLite3/blob/46d5f8a64f396f87cd374d1600dbf521523980e8/Northwind_large.sqlite.zip) dataset. View and run the [benchmark source](https://github.com/oven-sh/bun/tree/main/bench/sqlite).
|
||||
|
||||
<Frame caption="Benchmarked on an M1 MacBook Pro (64GB) running macOS 12.3.1">
|
||||

|
||||
</Frame>
|
||||
|
||||
---
|
||||
{% image width="738" alt="SQLite benchmarks for Bun, better-sqlite3, and deno.land/x/sqlite" src="https://user-images.githubusercontent.com/709451/168459263-8cd51ca3-a924-41e9-908d-cf3478a3b7f3.png" caption="Benchmarked on an M1 MacBook Pro (64GB) running macOS 12.3.1" /%}
|
||||
|
||||
## Database
|
||||
|
||||
To open or create a SQLite3 database:
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { Database } from "bun:sqlite";
|
||||
|
||||
const db = new Database("mydb.sqlite");
|
||||
@@ -51,7 +37,7 @@ const db = new Database("mydb.sqlite");
|
||||
|
||||
To open an in-memory database:
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { Database } from "bun:sqlite";
|
||||
|
||||
// all of these do the same thing
|
||||
@@ -62,50 +48,64 @@ const db = new Database("");
|
||||
|
||||
To open in `readonly` mode:
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={2}
|
||||
```ts
|
||||
import { Database } from "bun:sqlite";
|
||||
const db = new Database("mydb.sqlite", { readonly: true });
|
||||
```
|
||||
|
||||
To create the database if the file doesn't exist:
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={2}
|
||||
```ts
|
||||
import { Database } from "bun:sqlite";
|
||||
const db = new Database("mydb.sqlite", { create: true });
|
||||
```
|
||||
|
||||
### Strict mode
|
||||
|
||||
{% callout %}
|
||||
Added in Bun v1.1.14
|
||||
{% /callout %}
|
||||
|
||||
By default, `bun:sqlite` requires binding parameters to include the `$`, `:`, or `@` prefix, and does not throw an error if a parameter is missing.
|
||||
|
||||
To instead throw an error when a parameter is missing and allow binding without a prefix, set `strict: true` on the `Database` constructor:
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={3}
|
||||
<!-- prettier-ignore -->
|
||||
```ts
|
||||
import { Database } from "bun:sqlite";
|
||||
|
||||
const strict = new Database(":memory:", { strict: true });
|
||||
const strict = new Database(
|
||||
":memory:",
|
||||
{ strict: true }
|
||||
);
|
||||
|
||||
// throws error because of the typo:
|
||||
const query = strict.query("SELECT $message;").all({ messag: "Hello world" });
|
||||
const query = strict
|
||||
.query("SELECT $message;")
|
||||
.all({ message: "Hello world" });
|
||||
|
||||
const notStrict = new Database(":memory:");
|
||||
const notStrict = new Database(
|
||||
":memory:"
|
||||
);
|
||||
// does not throw error:
|
||||
notStrict.query("SELECT $message;").all({ messag: "Hello world" });
|
||||
notStrict
|
||||
.query("SELECT $message;")
|
||||
.all({ message: "Hello world" });
|
||||
```
|
||||
|
||||
### Load via ES module import
|
||||
|
||||
You can also use an import attribute to load a database.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={1}
|
||||
import db from "./mydb.sqlite" with { type: "sqlite" };
|
||||
```ts
|
||||
import db from "./mydb.sqlite" with { "type": "sqlite" };
|
||||
|
||||
console.log(db.query("select * from users LIMIT 1").get());
|
||||
```
|
||||
|
||||
This is equivalent to the following:
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { Database } from "bun:sqlite";
|
||||
const db = new Database("./mydb.sqlite");
|
||||
```
|
||||
@@ -114,7 +114,7 @@ const db = new Database("./mydb.sqlite");
|
||||
|
||||
To close a database connection, but allow existing queries to finish, call `.close(false)`:
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={3}
|
||||
```ts
|
||||
const db = new Database();
|
||||
// ... do stuff
|
||||
db.close(false);
|
||||
@@ -122,40 +122,33 @@ db.close(false);
|
||||
|
||||
To close the database and throw an error if there are any pending queries, call `.close(true)`:
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={3}
|
||||
```ts
|
||||
const db = new Database();
|
||||
// ... do stuff
|
||||
db.close(true);
|
||||
```
|
||||
|
||||
<Note>
|
||||
`close(false)` is called automatically when the database is garbage collected. It is safe to call multiple times but
|
||||
has no effect after the first.
|
||||
</Note>
|
||||
Note: `close(false)` is called automatically when the database is garbage collected. It is safe to call multiple times but has no effect after the first.
|
||||
|
||||
### `using` statement
|
||||
|
||||
You can use the `using` statement to ensure that a database connection is closed when the `using` block is exited.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={4, 5}
|
||||
```ts
|
||||
import { Database } from "bun:sqlite";
|
||||
|
||||
{
|
||||
using db = new Database("mydb.sqlite");
|
||||
using query = db.query("select 'Hello world' as message;");
|
||||
console.log(query.get());
|
||||
console.log(query.get()); // => { message: "Hello world" }
|
||||
}
|
||||
```
|
||||
|
||||
```txt
|
||||
{ message: "Hello world" }
|
||||
```
|
||||
|
||||
### `.serialize()`
|
||||
|
||||
`bun:sqlite` supports SQLite's built-in mechanism for [serializing](https://www.sqlite.org/c3ref/serialize.html) and [deserializing](https://www.sqlite.org/c3ref/deserialize.html) databases to and from memory.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={2}
|
||||
```ts
|
||||
const olddb = new Database("mydb.sqlite");
|
||||
const contents = olddb.serialize(); // => Uint8Array
|
||||
const newdb = Database.deserialize(contents);
|
||||
@@ -167,21 +160,20 @@ Internally, `.serialize()` calls [`sqlite3_serialize`](https://www.sqlite.org/c3
|
||||
|
||||
Use the `db.query()` method on your `Database` instance to [prepare](https://www.sqlite.org/c3ref/prepare.html) a SQL query. The result is a `Statement` instance that will be cached on the `Database` instance. _The query will not be executed._
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const query = db.query(`select "Hello world" as message`);
|
||||
```
|
||||
|
||||
<Note>
|
||||
Use the `.prepare()` method to prepare a query _without_ caching it on the `Database` instance.
|
||||
{% callout %}
|
||||
|
||||
```ts
|
||||
// compile the prepared statement
|
||||
const query = db.prepare("SELECT * FROM foo WHERE bar = ?");
|
||||
```
|
||||
**Note** — Use the `.prepare()` method to prepare a query _without_ caching it on the `Database` instance.
|
||||
|
||||
</Note>
|
||||
```ts
|
||||
// compile the prepared statement
|
||||
const query = db.prepare("SELECT * FROM foo WHERE bar = ?");
|
||||
```
|
||||
|
||||
---
|
||||
{% /callout %}
|
||||
|
||||
## WAL mode
|
||||
|
||||
@@ -189,18 +181,15 @@ SQLite supports [write-ahead log mode](https://www.sqlite.org/wal.html) (WAL) wh
|
||||
|
||||
To enable WAL mode, run this pragma query at the beginning of your application:
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
db.exec("PRAGMA journal_mode = WAL;");
|
||||
```
|
||||
|
||||
<Accordion title="What is WAL mode?">
|
||||
{% details summary="What is WAL mode" %}
|
||||
In WAL mode, writes to the database are written directly to a separate file called the "WAL file" (write-ahead log). This file will be later integrated into the main database file. Think of it as a buffer for pending writes. Refer to the [SQLite docs](https://www.sqlite.org/wal.html) for a more detailed overview.
|
||||
|
||||
On macOS, WAL files may be persistent by default. This is not a bug, it is how macOS configured the system version of SQLite.
|
||||
|
||||
</Accordion>
|
||||
|
||||
---
|
||||
{% /details %}
|
||||
|
||||
## Statements
|
||||
|
||||
@@ -208,13 +197,13 @@ A `Statement` is a _prepared query_, which means it's been parsed and compiled i
|
||||
|
||||
Create a statement with the `.query` method on your `Database` instance.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const query = db.query(`select "Hello world" as message`);
|
||||
```
|
||||
|
||||
Queries can contain parameters. These can be numerical (`?1`) or named (`$param` or `:param` or `@param`).
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const query = db.query(`SELECT ?1, ?2;`);
|
||||
const query = db.query(`SELECT $param1, $param2;`);
|
||||
```
|
||||
@@ -225,28 +214,32 @@ Values are bound to these parameters when the query is executed. A `Statement` c
|
||||
|
||||
To bind values to a statement, pass an object to the `.all()`, `.get()`, `.run()`, or `.values()` method.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={2}
|
||||
```ts
|
||||
const query = db.query(`select $message;`);
|
||||
query.all({ $message: "Hello world" });
|
||||
```
|
||||
|
||||
You can bind using positional parameters too:
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const query = db.query(`select ?1;`);
|
||||
query.all("Hello world");
|
||||
```
|
||||
|
||||
#### `strict: true` lets you bind values without prefixes
|
||||
|
||||
{% callout %}
|
||||
Added in Bun v1.1.14
|
||||
{% /callout %}
|
||||
|
||||
By default, the `$`, `:`, and `@` prefixes are **included** when binding values to named parameters. To bind without these prefixes, use the `strict` option in the `Database` constructor.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { Database } from "bun:sqlite";
|
||||
|
||||
const db = new Database(":memory:", {
|
||||
// bind values without prefixes
|
||||
strict: true, // [!code ++]
|
||||
strict: true,
|
||||
});
|
||||
|
||||
const query = db.query(`select $message;`);
|
||||
@@ -262,13 +255,10 @@ query.all({ message: "Hello world" });
|
||||
|
||||
Use `.all()` to run a query and get back the results as an array of objects.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={2}
|
||||
```ts
|
||||
const query = db.query(`select $message;`);
|
||||
query.all({ $message: "Hello world" });
|
||||
```
|
||||
|
||||
```txt
|
||||
[{ message: "Hello world" }]
|
||||
// => [{ message: "Hello world" }]
|
||||
```
|
||||
|
||||
Internally, this calls [`sqlite3_reset`](https://www.sqlite.org/capi3ref.html#sqlite3_reset) and repeatedly calls [`sqlite3_step`](https://www.sqlite.org/capi3ref.html#sqlite3_step) until it returns `SQLITE_DONE`.
|
||||
@@ -277,13 +267,10 @@ Internally, this calls [`sqlite3_reset`](https://www.sqlite.org/capi3ref.html#sq
|
||||
|
||||
Use `.get()` to run a query and get back the first result as an object.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={2}
|
||||
```ts
|
||||
const query = db.query(`select $message;`);
|
||||
query.get({ $message: "Hello world" });
|
||||
```
|
||||
|
||||
```txt
|
||||
{ $message: "Hello world" }
|
||||
// => { $message: "Hello world" }
|
||||
```
|
||||
|
||||
Internally, this calls [`sqlite3_reset`](https://www.sqlite.org/capi3ref.html#sqlite3_reset) followed by [`sqlite3_step`](https://www.sqlite.org/capi3ref.html#sqlite3_step) until it no longer returns `SQLITE_ROW`. If the query returns no rows, `undefined` is returned.
|
||||
@@ -292,27 +279,32 @@ Internally, this calls [`sqlite3_reset`](https://www.sqlite.org/capi3ref.html#sq
|
||||
|
||||
Use `.run()` to run a query and get back `undefined`. This is useful for schema-modifying queries (e.g. `CREATE TABLE`) or bulk write operations.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={2}
|
||||
```ts
|
||||
const query = db.query(`create table foo;`);
|
||||
query.run();
|
||||
```
|
||||
|
||||
```txt
|
||||
{
|
||||
lastInsertRowid: 0,
|
||||
changes: 0,
|
||||
}
|
||||
// {
|
||||
// lastInsertRowid: 0,
|
||||
// changes: 0,
|
||||
// }
|
||||
```
|
||||
|
||||
Internally, this calls [`sqlite3_reset`](https://www.sqlite.org/capi3ref.html#sqlite3_reset) and calls [`sqlite3_step`](https://www.sqlite.org/capi3ref.html#sqlite3_step) once. Stepping through all the rows is not necessary when you don't care about the results.
|
||||
|
||||
{% callout %}
|
||||
Since Bun v1.1.14, `.run()` returns an object with two properties: `lastInsertRowid` and `changes`.
|
||||
{% /callout %}
|
||||
|
||||
The `lastInsertRowid` property returns the ID of the last row inserted into the database. The `changes` property is the number of rows affected by the query.
|
||||
|
||||
### `.as(Class)` - Map query results to a class
|
||||
|
||||
{% callout %}
|
||||
Added in Bun v1.1.14
|
||||
{% /callout %}
|
||||
|
||||
Use `.as(Class)` to run a query and get back the results as instances of a class. This lets you attach methods & getters/setters to results.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={10}
|
||||
```ts
|
||||
class Movie {
|
||||
title: string;
|
||||
year: number;
|
||||
@@ -325,14 +317,8 @@ class Movie {
|
||||
const query = db.query("SELECT title, year FROM movies").as(Movie);
|
||||
const movies = query.all();
|
||||
const first = query.get();
|
||||
|
||||
console.log(movies[0].isMarvel);
|
||||
console.log(first.isMarvel);
|
||||
```
|
||||
|
||||
```txt
|
||||
true
|
||||
true
|
||||
console.log(movies[0].isMarvel); // => true
|
||||
console.log(first.isMarvel); // => true
|
||||
```
|
||||
|
||||
As a performance optimization, the class constructor is not called, default initializers are not run, and private fields are not accessible. This is more like using `Object.create` than `new`. The class's prototype is assigned to the object, methods are attached, and getters/setters are set up, but the constructor is not called.
|
||||
@@ -343,7 +329,7 @@ The database columns are set as properties on the class instance.
|
||||
|
||||
Use `.iterate()` to run a query and incrementally return results. This is useful for large result sets that you want to process one row at a time without loading all the results into memory.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={2}
|
||||
```ts
|
||||
const query = db.query("SELECT * FROM foo");
|
||||
for (const row of query.iterate()) {
|
||||
console.log(row);
|
||||
@@ -352,30 +338,29 @@ for (const row of query.iterate()) {
|
||||
|
||||
You can also use the `@@iterator` protocol:
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={2}
|
||||
```ts
|
||||
const query = db.query("SELECT * FROM foo");
|
||||
for (const row of query) {
|
||||
console.log(row);
|
||||
}
|
||||
```
|
||||
|
||||
This feature was added in Bun v1.1.31.
|
||||
|
||||
### `.values()`
|
||||
|
||||
Use `values()` to run a query and get back all results as an array of arrays.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={3, 4}
|
||||
```ts
|
||||
const query = db.query(`select $message;`);
|
||||
|
||||
query.values({ $message: "Hello world" });
|
||||
query.values(2);
|
||||
```
|
||||
|
||||
```txt
|
||||
[
|
||||
[ "Iron Man", 2008 ],
|
||||
[ "The Avengers", 2012 ],
|
||||
[ "Ant-Man: Quantumania", 2023 ],
|
||||
]
|
||||
query.values(2);
|
||||
// [
|
||||
// [ "Iron Man", 2008 ],
|
||||
// [ "The Avengers", 2012 ],
|
||||
// [ "Ant-Man: Quantumania", 2023 ],
|
||||
// ]
|
||||
```
|
||||
|
||||
Internally, this calls [`sqlite3_reset`](https://www.sqlite.org/capi3ref.html#sqlite3_reset) and repeatedly calls [`sqlite3_step`](https://www.sqlite.org/capi3ref.html#sqlite3_step) until it returns `SQLITE_DONE`.
|
||||
@@ -384,7 +369,7 @@ Internally, this calls [`sqlite3_reset`](https://www.sqlite.org/capi3ref.html#sq
|
||||
|
||||
Use `.finalize()` to destroy a `Statement` and free any resources associated with it. Once finalized, a `Statement` cannot be executed again. Typically, the garbage collector will do this for you, but explicit finalization may be useful in performance-sensitive applications.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={3}
|
||||
```ts
|
||||
const query = db.query("SELECT title, year FROM movies");
|
||||
const movies = query.all();
|
||||
query.finalize();
|
||||
@@ -394,7 +379,7 @@ query.finalize();
|
||||
|
||||
Calling `toString()` on a `Statement` instance prints the expanded SQL query. This is useful for debugging.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={6, 9, 12}
|
||||
```ts
|
||||
import { Database } from "bun:sqlite";
|
||||
|
||||
// setup
|
||||
@@ -415,34 +400,42 @@ Internally, this calls [`sqlite3_expanded_sql`](https://www.sqlite.org/capi3ref.
|
||||
|
||||
Queries can contain parameters. These can be numerical (`?1`) or named (`$param` or `:param` or `@param`). Bind values to these parameters when executing the query:
|
||||
|
||||
```ts title="query.ts" icon="/icons/typescript.svg"
|
||||
{% codetabs %}
|
||||
|
||||
```ts#Query
|
||||
const query = db.query("SELECT * FROM foo WHERE bar = $bar");
|
||||
const results = query.all({
|
||||
$bar: "bar",
|
||||
});
|
||||
```
|
||||
|
||||
```txt
|
||||
[{ "$bar": "bar" }]
|
||||
```json#Results
|
||||
[
|
||||
{ "$bar": "bar" }
|
||||
]
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
|
||||
Numbered (positional) parameters work too:
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg"
|
||||
{% codetabs %}
|
||||
|
||||
```ts#Query
|
||||
const query = db.query("SELECT ?1, ?2");
|
||||
const results = query.all("hello", "goodbye");
|
||||
```
|
||||
|
||||
```txt
|
||||
```ts#Results
|
||||
[
|
||||
{
|
||||
"?1": "hello",
|
||||
"?2": "goodbye",
|
||||
},
|
||||
];
|
||||
{
|
||||
"?1": "hello",
|
||||
"?2": "goodbye"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
{% /codetabs %}
|
||||
|
||||
## Integers
|
||||
|
||||
@@ -454,25 +447,26 @@ By default, `bun:sqlite` returns integers as `number` types. If you need to hand
|
||||
|
||||
### `safeIntegers: true`
|
||||
|
||||
{% callout %}
|
||||
Added in Bun v1.1.14
|
||||
{% /callout %}
|
||||
|
||||
When `safeIntegers` is `true`, `bun:sqlite` will return integers as `bigint` types:
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={3}
|
||||
```ts
|
||||
import { Database } from "bun:sqlite";
|
||||
|
||||
const db = new Database(":memory:", { safeIntegers: true });
|
||||
const query = db.query(`SELECT ${BigInt(Number.MAX_SAFE_INTEGER) + 102n} as max_int`);
|
||||
const query = db.query(
|
||||
`SELECT ${BigInt(Number.MAX_SAFE_INTEGER) + 102n} as max_int`,
|
||||
);
|
||||
const result = query.get();
|
||||
|
||||
console.log(result.max_int);
|
||||
```
|
||||
|
||||
```txt
|
||||
9007199254741093n
|
||||
console.log(result.max_int); // => 9007199254741093n
|
||||
```
|
||||
|
||||
When `safeIntegers` is `true`, `bun:sqlite` will throw an error if a `bigint` value in a bound parameter exceeds 64 bits:
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={3}
|
||||
```ts
|
||||
import { Database } from "bun:sqlite";
|
||||
|
||||
const db = new Database(":memory:", { safeIntegers: true });
|
||||
@@ -483,38 +477,30 @@ const query = db.query("INSERT INTO test (value) VALUES ($value)");
|
||||
try {
|
||||
query.run({ $value: BigInt(Number.MAX_SAFE_INTEGER) ** 2n });
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
console.log(e.message); // => BigInt value '81129638414606663681390495662081' is out of range
|
||||
}
|
||||
```
|
||||
|
||||
```txt
|
||||
BigInt value '81129638414606663681390495662081' is out of range
|
||||
```
|
||||
|
||||
### `safeIntegers: false` (default)
|
||||
|
||||
When `safeIntegers` is `false`, `bun:sqlite` will return integers as `number` types and truncate any bits beyond 53:
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={3}
|
||||
```ts
|
||||
import { Database } from "bun:sqlite";
|
||||
|
||||
const db = new Database(":memory:", { safeIntegers: false });
|
||||
const query = db.query(`SELECT ${BigInt(Number.MAX_SAFE_INTEGER) + 102n} as max_int`);
|
||||
const query = db.query(
|
||||
`SELECT ${BigInt(Number.MAX_SAFE_INTEGER) + 102n} as max_int`,
|
||||
);
|
||||
const result = query.get();
|
||||
console.log(result.max_int);
|
||||
console.log(result.max_int); // => 9007199254741092
|
||||
```
|
||||
|
||||
```txt
|
||||
9007199254741092
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Transactions
|
||||
|
||||
Transactions are a mechanism for executing multiple queries in an _atomic_ way; that is, either all of the queries succeed or none of them do. Create a transaction with the `db.transaction()` method:
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={2}
|
||||
```ts
|
||||
const insertCat = db.prepare("INSERT INTO cats (name) VALUES ($name)");
|
||||
const insertCats = db.transaction(cats => {
|
||||
for (const cat of cats) insertCat.run(cat);
|
||||
@@ -525,32 +511,42 @@ At this stage, we haven't inserted any cats! The call to `db.transaction()` retu
|
||||
|
||||
To execute the transaction, call this function. All arguments will be passed through to the wrapped function; the return value of the wrapped function will be returned by the transaction function. The wrapped function also has access to the `this` context as defined where the transaction is executed.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={3}
|
||||
```ts
|
||||
const insert = db.prepare("INSERT INTO cats (name) VALUES ($name)");
|
||||
const insertCats = db.transaction(cats => {
|
||||
for (const cat of cats) insert.run(cat);
|
||||
return cats.length;
|
||||
});
|
||||
|
||||
const count = insertCats([{ $name: "Keanu" }, { $name: "Salem" }, { $name: "Crookshanks" }]);
|
||||
const count = insertCats([
|
||||
{ $name: "Keanu" },
|
||||
{ $name: "Salem" },
|
||||
{ $name: "Crookshanks" },
|
||||
]);
|
||||
|
||||
console.log(`Inserted ${count} cats`);
|
||||
```
|
||||
|
||||
The driver will automatically [`begin`](https://www.sqlite.org/lang_transaction.html) a transaction when `insertCats` is called and `commit` it when the wrapped function returns. If an exception is thrown, the transaction will be rolled back. The exception will propagate as usual; it is not caught.
|
||||
|
||||
<Note>
|
||||
{% callout %}
|
||||
**Nested transactions** — Transaction functions can be called from inside other transaction functions. When doing so, the inner transaction becomes a [savepoint](https://www.sqlite.org/lang_savepoint.html).
|
||||
|
||||
<Accordion title="View nested transaction example">
|
||||
{% details summary="View nested transaction example" %}
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// setup
|
||||
import { Database } from "bun:sqlite";
|
||||
const db = Database.open(":memory:");
|
||||
db.run("CREATE TABLE expenses (id INTEGER PRIMARY KEY AUTOINCREMENT, note TEXT, dollars INTEGER);");
|
||||
db.run("CREATE TABLE cats (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, age INTEGER)");
|
||||
const insertExpense = db.prepare("INSERT INTO expenses (note, dollars) VALUES (?, ?)");
|
||||
db.run(
|
||||
"CREATE TABLE expenses (id INTEGER PRIMARY KEY AUTOINCREMENT, note TEXT, dollars INTEGER);",
|
||||
);
|
||||
db.run(
|
||||
"CREATE TABLE cats (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, age INTEGER)",
|
||||
);
|
||||
const insertExpense = db.prepare(
|
||||
"INSERT INTO expenses (note, dollars) VALUES (?, ?)",
|
||||
);
|
||||
const insert = db.prepare("INSERT INTO cats (name, age) VALUES ($name, $age)");
|
||||
const insertCats = db.transaction(cats => {
|
||||
for (const cat of cats) insert.run(cat);
|
||||
@@ -568,8 +564,8 @@ adopt([
|
||||
]);
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</Note>
|
||||
{% /details %}
|
||||
{% /callout %}
|
||||
|
||||
Transactions also come with `deferred`, `immediate`, and `exclusive` versions.
|
||||
|
||||
@@ -584,24 +580,24 @@ insertCats.exclusive(cats); // uses "BEGIN EXCLUSIVE"
|
||||
|
||||
To load a [SQLite extension](https://www.sqlite.org/loadext.html), call `.loadExtension(name)` on your `Database` instance
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={4}
|
||||
```ts
|
||||
import { Database } from "bun:sqlite";
|
||||
|
||||
const db = new Database();
|
||||
db.loadExtension("myext");
|
||||
```
|
||||
|
||||
<Note>
|
||||
{% details summary="For macOS users" %}
|
||||
**MacOS users** By default, macOS ships with Apple's proprietary build of SQLite, which doesn't support extensions. To use extensions, you'll need to install a vanilla build of SQLite.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
brew install sqlite
|
||||
which sqlite # get path to binary
|
||||
```bash
|
||||
$ brew install sqlite
|
||||
$ which sqlite # get path to binary
|
||||
```
|
||||
|
||||
To point `bun:sqlite` to the new build, call `Database.setCustomSQLite(path)` before creating any `Database` instances. (On other operating systems, this is a no-op.) Pass a path to the SQLite `.dylib` file, _not_ the executable. With recent versions of Homebrew this is something like `/opt/homebrew/Cellar/sqlite/<version>/libsqlite3.dylib`.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={3}
|
||||
```ts
|
||||
import { Database } from "bun:sqlite";
|
||||
|
||||
Database.setCustomSQLite("/path/to/libsqlite.dylib");
|
||||
@@ -610,13 +606,13 @@ const db = new Database();
|
||||
db.loadExtension("myext");
|
||||
```
|
||||
|
||||
</Note>
|
||||
{% /details %}
|
||||
|
||||
### `.fileControl(cmd: number, value: any)`
|
||||
### .fileControl(cmd: number, value: any)
|
||||
|
||||
To use the advanced `sqlite3_file_control` API, call `.fileControl(cmd, value)` on your `Database` instance.
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg" highlight={6}
|
||||
```ts
|
||||
import { Database, constants } from "bun:sqlite";
|
||||
|
||||
const db = new Database();
|
||||
@@ -631,11 +627,9 @@ db.fileControl(constants.SQLITE_FCNTL_PERSIST_WAL, 0);
|
||||
- `TypedArray`
|
||||
- `undefined` or `null`
|
||||
|
||||
---
|
||||
|
||||
## Reference
|
||||
|
||||
```ts Type Reference icon="/icons/typescript.svg" expandable
|
||||
```ts
|
||||
class Database {
|
||||
constructor(
|
||||
filename: string,
|
||||
@@ -645,26 +639,18 @@ class Database {
|
||||
readonly?: boolean;
|
||||
create?: boolean;
|
||||
readwrite?: boolean;
|
||||
safeIntegers?: boolean;
|
||||
strict?: boolean;
|
||||
},
|
||||
);
|
||||
|
||||
prepare<ReturnType, Params>(sql: string): Statement<ReturnType, Params>;
|
||||
query<ReturnType, Params>(sql: string): Statement<ReturnType, Params>;
|
||||
run(sql: string, params?: SQLQueryBindings): { lastInsertRowid: number; changes: number };
|
||||
query<Params, ReturnType>(sql: string): Statement<Params, ReturnType>;
|
||||
run(
|
||||
sql: string,
|
||||
params?: SQLQueryBindings,
|
||||
): { lastInsertRowid: number; changes: number };
|
||||
exec = this.run;
|
||||
|
||||
transaction(insideTransaction: (...args: any) => void): CallableFunction & {
|
||||
deferred: (...args: any) => void;
|
||||
immediate: (...args: any) => void;
|
||||
exclusive: (...args: any) => void;
|
||||
};
|
||||
|
||||
close(throwOnError?: boolean): void;
|
||||
}
|
||||
|
||||
class Statement<ReturnType, Params> {
|
||||
class Statement<Params, ReturnType> {
|
||||
all(params: Params): ReturnType[];
|
||||
get(params: Params): ReturnType | undefined;
|
||||
run(params: Params): {
|
||||
@@ -1,19 +1,10 @@
|
||||
---
|
||||
title: Streams
|
||||
description: Use Bun's streams API to work with binary data without loading it all into memory at once
|
||||
---
|
||||
|
||||
Streams are an important abstraction for working with binary data without loading it all into memory at once. They are commonly used for reading and writing files, sending and receiving network requests, and processing large amounts of data.
|
||||
|
||||
Bun implements the Web APIs [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) and [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream).
|
||||
|
||||
<Note>
|
||||
Bun also implements the `node:stream` module, including
|
||||
[`Readable`](https://nodejs.org/api/stream.html#stream_readable_streams),
|
||||
[`Writable`](https://nodejs.org/api/stream.html#stream_writable_streams), and
|
||||
[`Duplex`](https://nodejs.org/api/stream.html#stream_duplex_and_transform_streams). For complete documentation, refer
|
||||
to the [Node.js docs](https://nodejs.org/api/stream.html).
|
||||
</Note>
|
||||
{% callout %}
|
||||
Bun also implements the `node:stream` module, including [`Readable`](https://nodejs.org/api/stream.html#stream_readable_streams), [`Writable`](https://nodejs.org/api/stream.html#stream_writable_streams), and [`Duplex`](https://nodejs.org/api/stream.html#stream_duplex_and_transform_streams). For complete documentation, refer to the [Node.js docs](https://nodejs.org/api/stream.html).
|
||||
{% /callout %}
|
||||
|
||||
To create a simple `ReadableStream`:
|
||||
|
||||
@@ -32,19 +23,28 @@ The contents of a `ReadableStream` can be read chunk-by-chunk with `for await` s
|
||||
```ts
|
||||
for await (const chunk of stream) {
|
||||
console.log(chunk);
|
||||
// => "hello"
|
||||
// => "world"
|
||||
}
|
||||
|
||||
// hello
|
||||
// world
|
||||
```
|
||||
|
||||
---
|
||||
`ReadableStream` also provides convenience methods for consuming the entire stream:
|
||||
|
||||
```ts
|
||||
const stream = new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue("hello world");
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
|
||||
const data = await stream.text(); // => "hello world"
|
||||
// Also available: .json(), .bytes(), .blob()
|
||||
```
|
||||
|
||||
## Direct `ReadableStream`
|
||||
|
||||
Bun implements an optimized version of `ReadableStream` that avoid unnecessary data copying & queue management logic.
|
||||
|
||||
With a traditional `ReadableStream`, chunks of data are _enqueued_. Each chunk is copied into a queue, where it sits until the stream is ready to send more data.
|
||||
Bun implements an optimized version of `ReadableStream` that avoid unnecessary data copying & queue management logic. With a traditional `ReadableStream`, chunks of data are _enqueued_. Each chunk is copied into a queue, where it sits until the stream is ready to send more data.
|
||||
|
||||
```ts
|
||||
const stream = new ReadableStream({
|
||||
@@ -60,7 +60,7 @@ With a direct `ReadableStream`, chunks of data are written directly to the strea
|
||||
|
||||
```ts
|
||||
const stream = new ReadableStream({
|
||||
type: "direct", // [!code ++]
|
||||
type: "direct",
|
||||
pull(controller) {
|
||||
controller.write("hello");
|
||||
controller.write("world");
|
||||
@@ -70,8 +70,6 @@ const stream = new ReadableStream({
|
||||
|
||||
When using a direct `ReadableStream`, all chunk queueing is handled by the destination. The consumer of the stream receives exactly what is passed to `controller.write()`, without any encoding or modification.
|
||||
|
||||
---
|
||||
|
||||
## Async generator streams
|
||||
|
||||
Bun also supports async generator functions as a source for `Response` and `Request`. This is an easy way to create a `ReadableStream` that fetches data from an asynchronous source.
|
||||
@@ -113,8 +111,6 @@ const response = new Response({
|
||||
await response.text(); // "hello"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `Bun.ArrayBufferSink`
|
||||
|
||||
The `Bun.ArrayBufferSink` class is a fast incremental writer for constructing an `ArrayBuffer` of unknown size.
|
||||
@@ -134,10 +130,10 @@ sink.end();
|
||||
|
||||
To instead retrieve the data as a `Uint8Array`, pass the `asUint8Array` option to the `start` method.
|
||||
|
||||
```ts
|
||||
```ts-diff
|
||||
const sink = new Bun.ArrayBufferSink();
|
||||
sink.start({
|
||||
asUint8Array: true, // [!code ++]
|
||||
+ asUint8Array: true
|
||||
});
|
||||
|
||||
sink.write("h");
|
||||
@@ -165,7 +161,7 @@ Once `.end()` is called, no more data can be written to the `ArrayBufferSink`. H
|
||||
```ts
|
||||
const sink = new Bun.ArrayBufferSink();
|
||||
sink.start({
|
||||
stream: true, // [!code ++]
|
||||
stream: true,
|
||||
});
|
||||
|
||||
sink.write("h");
|
||||
@@ -187,15 +183,13 @@ To manually set the size of the internal buffer in bytes, pass a value for `high
|
||||
```ts
|
||||
const sink = new Bun.ArrayBufferSink();
|
||||
sink.start({
|
||||
highWaterMark: 1024 * 1024, // 1 MB // [!code ++]
|
||||
highWaterMark: 1024 * 1024, // 1 MB
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
{% details summary="Reference" %}
|
||||
|
||||
## Reference
|
||||
|
||||
```ts See Typescript Definitions expandable
|
||||
```ts
|
||||
/**
|
||||
* Fast incremental writer that becomes an `ArrayBuffer` on end().
|
||||
*/
|
||||
@@ -216,7 +210,9 @@ export class ArrayBufferSink {
|
||||
stream?: boolean;
|
||||
}): void;
|
||||
|
||||
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
|
||||
write(
|
||||
chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer,
|
||||
): number;
|
||||
/**
|
||||
* Flush the internal buffer
|
||||
*
|
||||
@@ -230,3 +226,5 @@ export class ArrayBufferSink {
|
||||
end(): ArrayBuffer | Uint8Array<ArrayBuffer>;
|
||||
}
|
||||
```
|
||||
|
||||
{% /details %}
|
||||
@@ -1,15 +1,10 @@
|
||||
---
|
||||
title: TCP
|
||||
description: Use Bun's native TCP API to implement performance sensitive systems like database clients, game servers, or 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.
|
||||
Use Bun's native TCP API to implement performance sensitive systems like database clients, game servers, or 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.
|
||||
|
||||
## Start a server (`Bun.listen()`)
|
||||
|
||||
To start a TCP server with `Bun.listen`:
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
Bun.listen({
|
||||
hostname: "localhost",
|
||||
port: 8080,
|
||||
@@ -23,11 +18,11 @@ Bun.listen({
|
||||
});
|
||||
```
|
||||
|
||||
<Accordion title="An API designed for speed">
|
||||
{% details summary="An API designed for speed" %}
|
||||
|
||||
In Bun, a set of handlers are declared once per server instead of assigning callbacks to each socket, as with Node.js `EventEmitters` or the web-standard `WebSocket` API.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
Bun.listen({
|
||||
hostname: "localhost",
|
||||
port: 8080,
|
||||
@@ -43,11 +38,11 @@ Bun.listen({
|
||||
|
||||
For performance-sensitive servers, assigning listeners to each socket can cause significant garbage collector pressure and increase memory usage. By contrast, Bun only allocates one handler function for each event and shares it among all sockets. This is a small optimization, but it adds up.
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
|
||||
Contextual data can be attached to a socket in the `open` handler.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
type SocketData = { sessionId: string };
|
||||
|
||||
Bun.listen<SocketData>({
|
||||
@@ -55,10 +50,10 @@ Bun.listen<SocketData>({
|
||||
port: 8080,
|
||||
socket: {
|
||||
data(socket, data) {
|
||||
socket.write(`${socket.data.sessionId}: ack`); // [!code ++]
|
||||
socket.write(`${socket.data.sessionId}: ack`);
|
||||
},
|
||||
open(socket) {
|
||||
socket.data = { sessionId: "abcd" }; // [!code ++]
|
||||
socket.data = { sessionId: "abcd" };
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -66,7 +61,7 @@ Bun.listen<SocketData>({
|
||||
|
||||
To enable TLS, pass a `tls` object containing `key` and `cert` fields.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
Bun.listen({
|
||||
hostname: "localhost",
|
||||
port: 8080,
|
||||
@@ -75,29 +70,33 @@ Bun.listen({
|
||||
},
|
||||
tls: {
|
||||
// can be string, BunFile, TypedArray, Buffer, or array thereof
|
||||
key: Bun.file("./key.pem"), // [!code ++]
|
||||
cert: Bun.file("./cert.pem"), // [!code ++]
|
||||
key: Bun.file("./key.pem"),
|
||||
cert: Bun.file("./cert.pem"),
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
The `key` and `cert` fields expect the _contents_ of your TLS key and certificate. This can be a string, `BunFile`, `TypedArray`, or `Buffer`.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
Bun.listen({
|
||||
// ...
|
||||
tls: {
|
||||
key: Bun.file("./key.pem"), // BunFile
|
||||
key: fs.readFileSync("./key.pem"), // Buffer
|
||||
key: fs.readFileSync("./key.pem", "utf8"), // string
|
||||
key: [Bun.file("./key1.pem"), Bun.file("./key2.pem")], // array of above
|
||||
// BunFile
|
||||
key: Bun.file("./key.pem"),
|
||||
// Buffer
|
||||
key: fs.readFileSync("./key.pem"),
|
||||
// string
|
||||
key: fs.readFileSync("./key.pem", "utf8"),
|
||||
// array of above
|
||||
key: [Bun.file("./key1.pem"), Bun.file("./key2.pem")],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
The result of `Bun.listen` is a server that conforms to the `TCPSocket` interface.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const server = Bun.listen({
|
||||
/* config*/
|
||||
});
|
||||
@@ -110,13 +109,11 @@ server.stop(true);
|
||||
server.unref();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Create a connection (`Bun.connect()`)
|
||||
|
||||
Use `Bun.connect` to connect to a TCP server. Specify the server to connect to with `hostname` and `port`. TCP clients can define the same set of handlers as `Bun.listen`, plus a couple client-specific handlers.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// The client
|
||||
const socket = await Bun.connect({
|
||||
hostname: "localhost",
|
||||
@@ -143,48 +140,39 @@ To require TLS, specify `tls: true`.
|
||||
// The client
|
||||
const socket = await Bun.connect({
|
||||
// ... config
|
||||
tls: true, // [!code ++]
|
||||
tls: true,
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hot reloading
|
||||
|
||||
Both TCP servers and sockets can be hot reloaded with new handlers.
|
||||
|
||||
<CodeGroup>
|
||||
{% codetabs %}
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
const server = Bun.listen({
|
||||
/* config */
|
||||
});
|
||||
```ts#Server
|
||||
const server = Bun.listen({ /* config */ })
|
||||
|
||||
// reloads handlers for all active server-side sockets
|
||||
server.reload({
|
||||
socket: {
|
||||
data() {
|
||||
data(){
|
||||
// new 'data' handler
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
```ts client.ts icon="/icons/typescript.svg"
|
||||
const socket = await Bun.connect({
|
||||
/* config */
|
||||
});
|
||||
|
||||
```ts#Client
|
||||
const socket = await Bun.connect({ /* config */ })
|
||||
socket.reload({
|
||||
data() {
|
||||
data(){
|
||||
// new 'data' handler
|
||||
},
|
||||
});
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
---
|
||||
{% /codetabs %}
|
||||
|
||||
## Buffering
|
||||
|
||||
@@ -206,14 +194,11 @@ socket.write("hello");
|
||||
|
||||
To simplify this for now, consider using Bun's `ArrayBufferSink` with the `{stream: true}` option:
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { ArrayBufferSink } from "bun";
|
||||
|
||||
const sink = new ArrayBufferSink();
|
||||
sink.start({
|
||||
stream: true, // [!code ++]
|
||||
highWaterMark: 1024,
|
||||
});
|
||||
sink.start({ stream: true, highWaterMark: 1024 });
|
||||
|
||||
sink.write("h");
|
||||
sink.write("e");
|
||||
@@ -231,9 +216,6 @@ queueMicrotask(() => {
|
||||
});
|
||||
```
|
||||
|
||||
<Note>
|
||||
**Corking**
|
||||
|
||||
Support for corking is planned, but in the meantime backpressure must be managed manually with the `drain` handler.
|
||||
|
||||
</Note>
|
||||
{% callout %}
|
||||
**Corking** — Support for corking is planned, but in the meantime backpressure must be managed manually with the `drain` handler.
|
||||
{% /callout %}
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
title: Transpiler
|
||||
description: Use Bun's transpiler to transpile JavaScript and TypeScript code
|
||||
---
|
||||
|
||||
Bun exposes its internal transpiler via the `Bun.Transpiler` class. To create an instance of Bun's transpiler:
|
||||
|
||||
```ts
|
||||
@@ -11,14 +6,15 @@ const transpiler = new Bun.Transpiler({
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `.transformSync()`
|
||||
|
||||
Transpile code synchronously with the `.transformSync()` method. Modules are not resolved and the code is not executed. The result is a string of vanilla JavaScript code.
|
||||
|
||||
<CodeGroup>
|
||||
```ts transpile.ts icon="/icons/typescript.svg"
|
||||
<!-- It is synchronous and runs in the same thread as other JavaScript code. -->
|
||||
|
||||
{% codetabs %}
|
||||
|
||||
```js#Example
|
||||
const transpiler = new Bun.Transpiler({
|
||||
loader: 'tsx',
|
||||
});
|
||||
@@ -30,10 +26,9 @@ export function Home(props: {title: string}){
|
||||
}`;
|
||||
|
||||
const result = transpiler.transformSync(code);
|
||||
```
|
||||
|
||||
````
|
||||
|
||||
```ts output
|
||||
```js#Result
|
||||
import { __require as require } from "bun:wrap";
|
||||
import * as JSX from "react/jsx-dev-runtime";
|
||||
var jsx = require(JSX).jsxDEV;
|
||||
@@ -48,9 +43,9 @@ export default jsx(
|
||||
undefined,
|
||||
this,
|
||||
);
|
||||
````
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
{% /codetabs %}
|
||||
|
||||
To override the default loader specified in the `new Bun.Transpiler()` constructor, pass a second argument to `.transformSync()`.
|
||||
|
||||
@@ -58,15 +53,11 @@ To override the default loader specified in the `new Bun.Transpiler()` construct
|
||||
transpiler.transformSync("<div>hi!</div>", "tsx");
|
||||
```
|
||||
|
||||
<Accordion title="Nitty gritty">
|
||||
|
||||
{% details summary="Nitty gritty" %}
|
||||
When `.transformSync` is called, the transpiler is run in the same thread as the currently executed code.
|
||||
|
||||
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.
|
||||
|
||||
</Accordion>
|
||||
|
||||
---
|
||||
{% /details %}
|
||||
|
||||
## `.transform()`
|
||||
|
||||
@@ -84,23 +75,21 @@ Unless you're transpiling _many_ large files, you should probably use `Bun.Trans
|
||||
await transpiler.transform("<div>hi!</div>", "tsx");
|
||||
```
|
||||
|
||||
<Accordion title="Nitty gritty">
|
||||
|
||||
{% details summary="Nitty gritty" %}
|
||||
The `.transform()` method runs the transpiler in Bun's worker threadpool, so if you run it 100 times, it will run it across `Math.floor($cpu_count * 0.8)` threads, without blocking the main JavaScript thread.
|
||||
|
||||
If your code uses a macro, it will potentially spawn a new copy of Bun's JavaScript runtime environment in that new thread.
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
|
||||
## `.scan()`
|
||||
|
||||
The `Transpiler` instance can also scan some source code and return a list of its imports and exports, plus additional metadata about each one. [Type-only](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export) imports and exports are ignored.
|
||||
|
||||
<CodeGroup>
|
||||
{% codetabs %}
|
||||
|
||||
```ts example.ts icon="/icons/typescript.svg"
|
||||
```ts#Example
|
||||
const transpiler = new Bun.Transpiler({
|
||||
loader: "tsx",
|
||||
loader: 'tsx',
|
||||
});
|
||||
|
||||
const code = `
|
||||
@@ -115,9 +104,11 @@ export const name = "hello";
|
||||
const result = transpiler.scan(code);
|
||||
```
|
||||
|
||||
```json output
|
||||
```json#Output
|
||||
{
|
||||
"exports": ["name"],
|
||||
"exports": [
|
||||
"name"
|
||||
],
|
||||
"imports": [
|
||||
{
|
||||
"kind": "import-statement",
|
||||
@@ -135,7 +126,7 @@ const result = transpiler.scan(code);
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
{% /codetabs %}
|
||||
|
||||
Each import in the `imports` array has a `path` and `kind`. Bun categories imports into the following kinds:
|
||||
|
||||
@@ -145,18 +136,19 @@ Each import in the `imports` array has a `path` and `kind`. Bun categories impor
|
||||
- `dynamic-import`: `import('./loader')`
|
||||
- `import-rule`: `@import 'foo.css'`
|
||||
- `url-token`: `url('./foo.png')`
|
||||
|
||||
---
|
||||
<!-- - `internal`: `import {foo} from 'bun:internal'`
|
||||
- `entry-point-build`: `import {foo} from 'bun:entry'`
|
||||
- `entry-point-run`: `bun ./mymodule` -->
|
||||
|
||||
## `.scanImports()`
|
||||
|
||||
For performance-sensitive code, you can use the `.scanImports()` method to get a list of imports. It's faster than `.scan()` (especially for large files) but marginally less accurate due to some performance optimizations.
|
||||
|
||||
<CodeGroup>
|
||||
{% codetabs %}
|
||||
|
||||
```ts example.ts icon="/icons/typescript.svg"
|
||||
```ts#Example
|
||||
const transpiler = new Bun.Transpiler({
|
||||
loader: "tsx",
|
||||
loader: 'tsx',
|
||||
});
|
||||
|
||||
const code = `
|
||||
@@ -171,30 +163,26 @@ export const name = "hello";
|
||||
const result = transpiler.scanImports(code);
|
||||
```
|
||||
|
||||
```json results icon="file-json"
|
||||
```json#Results
|
||||
[
|
||||
{
|
||||
"kind": "import-statement",
|
||||
"path": "react"
|
||||
},
|
||||
{
|
||||
"kind": "require-call",
|
||||
"path": "./cjs.js"
|
||||
},
|
||||
{
|
||||
"kind": "dynamic-import",
|
||||
"path": "./loader"
|
||||
kind: "import-statement",
|
||||
path: "react"
|
||||
}, {
|
||||
kind: "require-call",
|
||||
path: "./cjs.js"
|
||||
}, {
|
||||
kind: "dynamic-import",
|
||||
path: "./loader"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
---
|
||||
{% /codetabs %}
|
||||
|
||||
## Reference
|
||||
|
||||
```ts See Typescript Definitions expandable
|
||||
```ts
|
||||
type Loader = "jsx" | "js" | "ts" | "tsx";
|
||||
|
||||
interface TranspilerOptions {
|
||||
@@ -278,7 +266,7 @@ type Import = {
|
||||
// url() in CSS
|
||||
| "url-token"
|
||||
// The import was injected by Bun
|
||||
| "internal"
|
||||
| "internal"
|
||||
// Entry point (not common)
|
||||
| "entry-point-build"
|
||||
| "entry-point-run"
|
||||
@@ -1,7 +1,4 @@
|
||||
---
|
||||
title: UDP
|
||||
description: Use Bun's UDP API to implement services with advanced real-time requirements, such as voice chat.
|
||||
---
|
||||
Use Bun's UDP API to implement services with advanced real-time requirements, such as voice chat.
|
||||
|
||||
## Bind a UDP socket (`Bun.udpSocket()`)
|
||||
|
||||
@@ -16,9 +13,8 @@ Specify a port:
|
||||
|
||||
```ts
|
||||
const socket = await Bun.udpSocket({
|
||||
port: 41234, // [!code ++]
|
||||
port: 41234,
|
||||
});
|
||||
|
||||
console.log(socket.port); // 41234
|
||||
```
|
||||
|
||||
@@ -37,7 +33,7 @@ DNS resolution, as it is intended for low-latency operations.
|
||||
|
||||
When creating your socket, add a callback to specify what should be done when packets are received:
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const server = await Bun.udpSocket({
|
||||
socket: {
|
||||
data(socket, buf, port, addr) {
|
||||
@@ -57,7 +53,7 @@ While UDP does not have a concept of a connection, many UDP communications (espe
|
||||
In such cases it can be beneficial to connect the socket to that peer, which specifies to which address all packets are sent
|
||||
and restricts incoming packets to that peer only.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const server = await Bun.udpSocket({
|
||||
socket: {
|
||||
data(socket, buf, port, addr) {
|
||||
@@ -66,7 +62,6 @@ const server = await Bun.udpSocket({
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const client = await Bun.udpSocket({
|
||||
connect: {
|
||||
port: server.port,
|
||||
@@ -87,23 +82,21 @@ of making a system call for each. This is made possible by the `sendMany()` API:
|
||||
For an unconnected socket, `sendMany` takes an array as its only argument. Each set of three array elements describes a packet:
|
||||
The first item is the data to be sent, the second is the target port, and the last is the target address.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const socket = await Bun.udpSocket({});
|
||||
|
||||
// sends 'Hello' to 127.0.0.1:41234, and 'foo' to 1.1.1.1:53 in a single operation
|
||||
socket.sendMany(["Hello", 41234, "127.0.0.1", "foo", 53, "1.1.1.1"]);
|
||||
```
|
||||
|
||||
With a connected socket, `sendMany` simply takes an array, where each element represents the data to be sent to the peer.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const socket = await Bun.udpSocket({
|
||||
connect: {
|
||||
port: 41234,
|
||||
hostname: "localhost",
|
||||
},
|
||||
});
|
||||
|
||||
socket.sendMany(["foo", "bar", "baz"]);
|
||||
```
|
||||
|
||||
@@ -116,7 +109,8 @@ It may happen that a packet that you're sending does not fit into the operating
|
||||
has happened when:
|
||||
|
||||
- `send` returns `false`
|
||||
- `sendMany` returns a number smaller than the number of packets you specified. In this case, the `drain` socket handler will be called once the socket becomes writable again:
|
||||
- `sendMany` returns a number smaller than the number of packets you specified
|
||||
In this case, the `drain` socket handler will be called once the socket becomes writable again:
|
||||
|
||||
```ts
|
||||
const socket = await Bun.udpSocket({
|
||||
@@ -1,22 +1,17 @@
|
||||
---
|
||||
title: Utils
|
||||
description: Use Bun's utility functions to work with the runtime
|
||||
---
|
||||
|
||||
## `Bun.version`
|
||||
|
||||
A `string` containing the version of the `bun` CLI that is currently running.
|
||||
|
||||
```ts terminal icon="terminal"
|
||||
```ts
|
||||
Bun.version;
|
||||
// => "1.3.3"
|
||||
// => "0.6.4"
|
||||
```
|
||||
|
||||
## `Bun.revision`
|
||||
|
||||
The git commit of [Bun](https://github.com/oven-sh/bun) that was compiled to create the current `bun` CLI.
|
||||
|
||||
```ts terminal icon="terminal"
|
||||
```ts
|
||||
Bun.revision;
|
||||
// => "f02561530fda1ee9396f51c8bc99b38716e38296"
|
||||
```
|
||||
@@ -29,7 +24,7 @@ An alias for `process.env`.
|
||||
|
||||
An absolute path to the entrypoint of the current program (the file that was executed with `bun run`).
|
||||
|
||||
```ts script.ts
|
||||
```ts#script.ts
|
||||
Bun.main;
|
||||
// /path/to/script.ts
|
||||
```
|
||||
@@ -132,11 +127,17 @@ The final 8 bytes of the UUID are a cryptographically secure random value. It us
|
||||
|
||||
```ts
|
||||
namespace Bun {
|
||||
function randomUUIDv7(encoding?: "hex" | "base64" | "base64url" = "hex", timestamp?: number = Date.now()): string;
|
||||
function randomUUIDv7(
|
||||
encoding?: "hex" | "base64" | "base64url" = "hex",
|
||||
timestamp?: number = Date.now(),
|
||||
): string;
|
||||
/**
|
||||
* If you pass "buffer", you get a 16-byte buffer instead of a string.
|
||||
*/
|
||||
function randomUUIDv7(encoding: "buffer", timestamp?: number = Date.now()): Buffer;
|
||||
function randomUUIDv7(
|
||||
encoding: "buffer",
|
||||
timestamp?: number = Date.now(),
|
||||
): Buffer;
|
||||
|
||||
// If you only pass a timestamp, you get a hex string
|
||||
function randomUUIDv7(timestamp?: number = Date.now()): string;
|
||||
@@ -145,13 +146,13 @@ namespace Bun {
|
||||
|
||||
You can optionally set encoding to `"buffer"` to get a 16-byte buffer instead of a string. This can sometimes avoid string conversion overhead.
|
||||
|
||||
```ts buffer.ts
|
||||
```ts#buffer.ts
|
||||
const buffer = Bun.randomUUIDv7("buffer");
|
||||
```
|
||||
|
||||
`base64` and `base64url` encodings are also supported when you want a slightly shorter string.
|
||||
|
||||
```ts base64.ts
|
||||
```ts#base64.ts
|
||||
const base64 = Bun.randomUUIDv7("base64");
|
||||
const base64url = Bun.randomUUIDv7("base64url");
|
||||
```
|
||||
@@ -199,7 +200,9 @@ test("peek", () => {
|
||||
// If we peek a rejected promise, it:
|
||||
// - returns the error
|
||||
// - does not mark the promise as handled
|
||||
const rejected = Promise.reject(new Error("Successfully tested promise rejection"));
|
||||
const rejected = Promise.reject(
|
||||
new Error("Successfully tested promise rejection"),
|
||||
);
|
||||
expect(peek(rejected).message).toBe("Successfully tested promise rejection");
|
||||
});
|
||||
```
|
||||
@@ -231,11 +234,11 @@ const currentFile = import.meta.url;
|
||||
Bun.openInEditor(currentFile);
|
||||
```
|
||||
|
||||
You can override this via the `debug.editor` setting in your [`bunfig.toml`](/runtime/bunfig).
|
||||
You can override this via the `debug.editor` setting in your [`bunfig.toml`](https://bun.com/docs/runtime/bunfig).
|
||||
|
||||
```toml bunfig.toml icon="settings"
|
||||
[debug] # [!code ++]
|
||||
editor = "code" # [!code ++]
|
||||
```toml-diff#bunfig.toml
|
||||
+ [debug]
|
||||
+ editor = "code"
|
||||
```
|
||||
|
||||
Or specify an editor with the `editor` param. You can also specify a line and column number.
|
||||
@@ -307,9 +310,7 @@ This function is optimized for large input. On an M1X, it processes 480 MB/s -
|
||||
20 GB/s, depending on how much data is being escaped and whether there is non-ascii
|
||||
text. Non-string types will be converted to a string before escaping.
|
||||
|
||||
## `Bun.stringWidth()`
|
||||
|
||||
<Note>~6,756x faster `string-width` alternative</Note>
|
||||
## `Bun.stringWidth()` ~6,756x faster `string-width` alternative
|
||||
|
||||
Get the column count of a string as it would be displayed in a terminal.
|
||||
Supports ANSI escape codes, emoji, and wide characters.
|
||||
@@ -351,7 +352,7 @@ npm/string-width 500 chars ascii 249,710 ns/iter (239,970 ns …
|
||||
|
||||
To make `Bun.stringWidth` fast, we've implemented it in Zig using optimized SIMD instructions, accounting for Latin1, UTF-16, and UTF-8 encodings. It passes `string-width`'s tests.
|
||||
|
||||
<Accordion title="View full benchmark">
|
||||
{% details summary="View full benchmark" %}
|
||||
|
||||
As a reminder, 1 nanosecond (ns) is 1 billionth of a second. Here's a quick reference for converting between units:
|
||||
|
||||
@@ -361,7 +362,7 @@ As a reminder, 1 nanosecond (ns) is 1 billionth of a second. Here's a quick refe
|
||||
| µs | 1,000 |
|
||||
| ms | 1 |
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```js
|
||||
❯ bun string-width.mjs
|
||||
cpu: 13th Gen Intel(R) Core(TM) i9-13900
|
||||
runtime: bun 1.0.29 (x64-linux)
|
||||
@@ -390,7 +391,7 @@ Bun.stringWidth 19,000 chars ansi+emoji+ascii 114.06 µs/iter (112.86 µs … 1
|
||||
Bun.stringWidth 95,000 chars ansi+emoji+ascii 572.69 µs/iter (565.52 µs … 607.22 µs) 572.45 µs 604.86 µs 605.21 µs
|
||||
```
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```ts
|
||||
❯ node string-width.mjs
|
||||
cpu: 13th Gen Intel(R) Core(TM) i9-13900
|
||||
runtime: node v21.4.0 (x64-linux)
|
||||
@@ -419,11 +420,11 @@ npm/string-width 19,000 chars ansi+emoji+ascii 27.19 ms/iter (26.89 ms … 2
|
||||
npm/string-width 95,000 chars ansi+emoji+ascii 3.68 s/iter (3.66 s … 3.7 s) 3.69 s 3.7 s 3.7 s
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
|
||||
TypeScript definition:
|
||||
|
||||
```ts expandable
|
||||
```ts
|
||||
namespace Bun {
|
||||
export function stringWidth(
|
||||
/**
|
||||
@@ -448,7 +449,7 @@ namespace Bun {
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
<!-- ## `Bun.enableANSIColors()` -->
|
||||
|
||||
## `Bun.fileURLToPath()`
|
||||
|
||||
@@ -459,8 +460,6 @@ const path = Bun.fileURLToPath(new URL("file:///foo/bar.txt"));
|
||||
console.log(path); // "/foo/bar.txt"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `Bun.pathToFileURL()`
|
||||
|
||||
Converts an absolute path to a `file://` URL.
|
||||
@@ -470,7 +469,7 @@ const url = Bun.pathToFileURL("/foo/bar.txt");
|
||||
console.log(url); // "file:///foo/bar.txt"
|
||||
```
|
||||
|
||||
---
|
||||
<!-- Bun.hash; -->
|
||||
|
||||
## `Bun.gzipSync()`
|
||||
|
||||
@@ -486,9 +485,9 @@ compressed; // => Uint8Array(30)
|
||||
|
||||
Optionally, pass a parameters object as the second argument:
|
||||
|
||||
<Accordion title="zlib compression options">
|
||||
{% details summary="zlib compression options"%}
|
||||
|
||||
```ts expandable
|
||||
```ts
|
||||
export type ZlibCompressionOptions = {
|
||||
/**
|
||||
* The compression level to use. Must be between `-1` and `9`.
|
||||
@@ -559,9 +558,7 @@ export type ZlibCompressionOptions = {
|
||||
};
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
---
|
||||
{% /details %}
|
||||
|
||||
## `Bun.gunzipSync()`
|
||||
|
||||
@@ -577,8 +574,6 @@ dec.decode(uncompressed);
|
||||
// => "hellohellohello..."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `Bun.deflateSync()`
|
||||
|
||||
Compresses a `Uint8Array` using zlib's DEFLATE algorithm.
|
||||
@@ -593,8 +588,6 @@ compressed; // => Uint8Array(12)
|
||||
|
||||
The second argument supports the same set of configuration options as [`Bun.gzipSync`](#bun-gzipsync).
|
||||
|
||||
---
|
||||
|
||||
## `Bun.inflateSync()`
|
||||
|
||||
Decompresses a `Uint8Array` using zlib's INFLATE algorithm.
|
||||
@@ -609,8 +602,6 @@ dec.decode(decompressed);
|
||||
// => "hellohellohello..."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `Bun.zstdCompress()` / `Bun.zstdCompressSync()`
|
||||
|
||||
Compresses a `Uint8Array` using the Zstandard algorithm.
|
||||
@@ -645,8 +636,6 @@ dec.decode(decompressedSync);
|
||||
// => "hellohellohello..."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `Bun.inspect()`
|
||||
|
||||
Serializes an object to a `string` exactly as it would be printed by `console.log`.
|
||||
@@ -661,7 +650,7 @@ const str = Bun.inspect(arr);
|
||||
// => "Uint8Array(3) [ 1, 2, 3 ]"
|
||||
```
|
||||
|
||||
### `Bun.inspect.custom`
|
||||
## `Bun.inspect.custom`
|
||||
|
||||
This is the symbol that Bun uses to implement `Bun.inspect`. You can override this to customize how your objects are printed. It is identical to `util.inspect.custom` in Node.js.
|
||||
|
||||
@@ -676,7 +665,7 @@ const foo = new Foo();
|
||||
console.log(foo); // => "foo"
|
||||
```
|
||||
|
||||
### `Bun.inspect.table(tabularData, properties, options)`
|
||||
## `Bun.inspect.table(tabularData, properties, options)`
|
||||
|
||||
Format tabular data into a string. Like [`console.table`](https://developer.mozilla.org/en-US/docs/Web/API/console/table_static), except it returns a string rather than printing to the console.
|
||||
|
||||
@@ -735,8 +724,6 @@ console.log(
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `Bun.nanoseconds()`
|
||||
|
||||
Returns the number of nanoseconds since the current `bun` process started, as a `number`. Useful for high-precision timing and benchmarking.
|
||||
@@ -746,8 +733,6 @@ Bun.nanoseconds();
|
||||
// => 7288958
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `Bun.readableStreamTo*()`
|
||||
|
||||
Bun implements a set of convenience functions for asynchronously consuming the body of a `ReadableStream` and converting it to various binary formats.
|
||||
@@ -782,8 +767,6 @@ await Bun.readableStreamToFormData(stream);
|
||||
await Bun.readableStreamToFormData(stream, multipartFormBoundary);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `Bun.resolveSync()`
|
||||
|
||||
Resolves a file path or module specifier using Bun's internal module resolution algorithm. The first argument is the path to resolve, and the second argument is the "root". If no match is found, an `Error` is thrown.
|
||||
@@ -809,11 +792,21 @@ To resolve relative to the directory containing the current file, pass `import.m
|
||||
Bun.resolveSync("./foo.ts", import.meta.dir);
|
||||
```
|
||||
|
||||
---
|
||||
## `serialize` & `deserialize` in `bun:jsc`
|
||||
|
||||
## `Bun.stripANSI()`
|
||||
To save a JavaScript value into an ArrayBuffer & back, use `serialize` and `deserialize` from the `"bun:jsc"` module.
|
||||
|
||||
<Note>~6-57x faster `strip-ansi` alternative</Note>
|
||||
```js
|
||||
import { serialize, deserialize } from "bun:jsc";
|
||||
|
||||
const buf = serialize({ foo: "bar" });
|
||||
const obj = deserialize(buf);
|
||||
console.log(obj); // => { foo: "bar" }
|
||||
```
|
||||
|
||||
Internally, [`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) and [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) serialize and deserialize the same way. This exposes the underlying [HTML Structured Clone Algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) to JavaScript as an ArrayBuffer.
|
||||
|
||||
## `Bun.stripANSI()` ~6-57x faster `strip-ansi` alternative
|
||||
|
||||
`Bun.stripANSI(text: string): string`
|
||||
|
||||
@@ -831,11 +824,8 @@ console.log(Bun.stripANSI(formatted)); // => "Bold and underlined"
|
||||
|
||||
`Bun.stripANSI` is significantly faster than the popular [`strip-ansi`](https://www.npmjs.com/package/strip-ansi) npm package:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun bench/snippets/strip-ansi.mjs
|
||||
```
|
||||
|
||||
```txt
|
||||
```js
|
||||
> bun bench/snippets/strip-ansi.mjs
|
||||
cpu: Apple M3 Max
|
||||
runtime: bun 1.2.21 (arm64-darwin)
|
||||
|
||||
@@ -854,11 +844,8 @@ Bun.stripANSI 212,992 chars long-ansi 227.65 µs/iter 234.50 µs
|
||||
(216.46 µs … 401.92 µs) 262.25 µs
|
||||
```
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
node bench/snippets/strip-ansi.mjs
|
||||
```
|
||||
|
||||
```txt
|
||||
```js
|
||||
> node bench/snippets/strip-ansi.mjs
|
||||
cpu: Apple M3 Max
|
||||
runtime: node 24.6.0 (arm64-darwin)
|
||||
|
||||
@@ -878,24 +865,6 @@ npm/strip-ansi 212,992 chars long-ansi 1.36 ms/iter 1.38 ms
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `serialize` & `deserialize` in `bun:jsc`
|
||||
|
||||
To save a JavaScript value into an ArrayBuffer & back, use `serialize` and `deserialize` from the `"bun:jsc"` module.
|
||||
|
||||
```js
|
||||
import { serialize, deserialize } from "bun:jsc";
|
||||
|
||||
const buf = serialize({ foo: "bar" });
|
||||
const obj = deserialize(buf);
|
||||
console.log(obj); // => { foo: "bar" }
|
||||
```
|
||||
|
||||
Internally, [`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) and [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) serialize and deserialize the same way. This exposes the underlying [HTML Structured Clone Algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) to JavaScript as an ArrayBuffer.
|
||||
|
||||
---
|
||||
|
||||
## `estimateShallowMemoryUsageOf` in `bun:jsc`
|
||||
|
||||
The `estimateShallowMemoryUsageOf` function returns a best-effort estimate of the memory usage of an object in bytes, excluding the memory usage of properties or other objects it references. For accurate per-object memory usage, use `Bun.generateHeapSnapshot`.
|
||||
@@ -1,32 +1,22 @@
|
||||
---
|
||||
title: WebSockets
|
||||
description: Server-side WebSockets in Bun
|
||||
---
|
||||
|
||||
`Bun.serve()` supports server-side WebSockets, with on-the-fly compression, TLS support, and a Bun-native publish-subscribe API.
|
||||
|
||||
<Info>
|
||||
{% callout %}
|
||||
|
||||
**⚡️ 7x more throughput**
|
||||
**⚡️ 7x more throughput** — Bun's WebSockets are fast. For a [simple chatroom](https://github.com/oven-sh/bun/tree/main/bench/websocket-server/README.md) on Linux x64, Bun can handle 7x more requests per second than Node.js + [`"ws"`](https://github.com/websockets/ws).
|
||||
|
||||
Bun's WebSockets are fast. For a [simple chatroom](https://github.com/oven-sh/bun/tree/main/bench/websocket-server/README.md) on Linux x64, Bun can handle 7x more requests per second than Node.js + [`"ws"`](https://github.com/websockets/ws).
|
||||
|
||||
| **Messages sent per second** | **Runtime** | **Clients** |
|
||||
| ---------------------------- | ------------------------------ | ----------- |
|
||||
| ~700,000 | (`Bun.serve`) Bun v0.2.1 (x64) | 16 |
|
||||
| ~100,000 | (`ws`) Node v18.10.0 (x64) | 16 |
|
||||
| Messages sent per second | Runtime | Clients |
|
||||
| ------------------------ | ------------------------------ | ------- |
|
||||
| ~700,000 | (`Bun.serve`) Bun v0.2.1 (x64) | 16 |
|
||||
| ~100,000 | (`ws`) Node v18.10.0 (x64) | 16 |
|
||||
|
||||
Internally Bun's WebSocket implementation is built on [uWebSockets](https://github.com/uNetworking/uWebSockets).
|
||||
|
||||
</Info>
|
||||
|
||||
---
|
||||
{% /callout %}
|
||||
|
||||
## Start a WebSocket server
|
||||
|
||||
Below is a simple WebSocket server built with `Bun.serve`, in which all incoming requests are [upgraded](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism) to WebSocket connections in the `fetch` handler. The socket handlers are declared in the `websocket` parameter.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
Bun.serve({
|
||||
fetch(req, server) {
|
||||
// upgrade the request to a WebSocket
|
||||
@@ -41,7 +31,7 @@ Bun.serve({
|
||||
|
||||
The following WebSocket event handlers are supported:
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
Bun.serve({
|
||||
fetch(req, server) {}, // upgrade logic
|
||||
websocket: {
|
||||
@@ -53,7 +43,7 @@ Bun.serve({
|
||||
});
|
||||
```
|
||||
|
||||
<Accordion title="An API designed for speed">
|
||||
{% details summary="An API designed for speed" %}
|
||||
|
||||
In Bun, handlers are declared once per server, instead of per socket.
|
||||
|
||||
@@ -70,12 +60,11 @@ But servers tend to have **many** socket connections open, which means:
|
||||
So, instead of using an event-based API, `ServerWebSocket` expects you to pass a single object with methods for each event in `Bun.serve()` and it is reused for each connection.
|
||||
|
||||
This leads to less memory usage and less time spent adding/removing event listeners.
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
|
||||
The first argument to each handler is the instance of `ServerWebSocket` handling the event. The `ServerWebSocket` class is a fast, Bun-native implementation of [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) with some additional features.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
Bun.serve({
|
||||
fetch(req, server) {}, // upgrade logic
|
||||
websocket: {
|
||||
@@ -90,32 +79,24 @@ Bun.serve({
|
||||
|
||||
Each `ServerWebSocket` instance has a `.send()` method for sending messages to the client. It supports a range of input types.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg" focus={4-6}
|
||||
Bun.serve({
|
||||
fetch(req, server) {}, // upgrade logic
|
||||
websocket: {
|
||||
message(ws, message) {
|
||||
ws.send("Hello world"); // string
|
||||
ws.send(response.arrayBuffer()); // ArrayBuffer
|
||||
ws.send(new Uint8Array([1, 2, 3])); // TypedArray | DataView
|
||||
},
|
||||
},
|
||||
});
|
||||
```ts
|
||||
ws.send("Hello world"); // string
|
||||
ws.send(response.arrayBuffer()); // ArrayBuffer
|
||||
ws.send(new Uint8Array([1, 2, 3])); // TypedArray | DataView
|
||||
```
|
||||
|
||||
### Headers
|
||||
|
||||
Once the upgrade succeeds, Bun will send a `101 Switching Protocols` response per the [spec](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism). Additional `headers` can be attached to this `Response` in the call to `server.upgrade()`.
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
Bun.serve({
|
||||
fetch(req, server) {
|
||||
const sessionId = await generateSessionId();
|
||||
server.upgrade(req, {
|
||||
headers: { // [!code ++]
|
||||
"Set-Cookie": `SessionId=${sessionId}`, // [!code ++]
|
||||
}, // [!code ++]
|
||||
headers: {
|
||||
"Set-Cookie": `SessionId=${sessionId}`,
|
||||
},
|
||||
});
|
||||
},
|
||||
websocket: {}, // handlers
|
||||
@@ -128,7 +109,7 @@ Contextual `data` can be attached to a new WebSocket in the `.upgrade()` call. T
|
||||
|
||||
To strongly type `ws.data`, add a `data` property to the `websocket` handler object. This types `ws.data` across all lifecycle hooks.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
type WebSocketData = {
|
||||
createdAt: number;
|
||||
channelId: string;
|
||||
@@ -153,6 +134,7 @@ Bun.serve({
|
||||
websocket: {
|
||||
// TypeScript: specify the type of ws.data like this
|
||||
data: {} as WebSocketData,
|
||||
|
||||
// handler called when a message is received
|
||||
async message(ws, message) {
|
||||
// ws.data is now properly typed as WebSocketData
|
||||
@@ -168,32 +150,29 @@ Bun.serve({
|
||||
});
|
||||
```
|
||||
|
||||
<Info>
|
||||
{% callout %}
|
||||
**Note:** Previously, you could specify the type of `ws.data` using a type parameter on `Bun.serve`, like `Bun.serve<MyData>({...})`. This pattern was removed due to [a limitation in TypeScript](https://github.com/microsoft/TypeScript/issues/26242) in favor of the `data` property shown above.
|
||||
</Info>
|
||||
{% /callout %}
|
||||
|
||||
To connect to this server from the browser, create a new `WebSocket`.
|
||||
|
||||
```js browser.js icon="file-code"
|
||||
```ts#browser.js
|
||||
const socket = new WebSocket("ws://localhost:3000/chat");
|
||||
|
||||
socket.addEventListener("message", event => {
|
||||
console.log(event.data);
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
<Info>
|
||||
**Identifying users**
|
||||
|
||||
The cookies that are currently set on the page will be sent with the WebSocket upgrade request and available on `req.headers` in the `fetch` handler. Parse these cookies to determine the identity of the connecting user and set the value of `data` accordingly.
|
||||
|
||||
</Info>
|
||||
{% callout %}
|
||||
**Identifying users** — The cookies that are currently set on the page will be sent with the WebSocket upgrade request and available on `req.headers` in the `fetch` handler. Parse these cookies to determine the identity of the connecting user and set the value of `data` accordingly.
|
||||
{% /callout %}
|
||||
|
||||
### Pub/Sub
|
||||
|
||||
Bun's `ServerWebSocket` implementation implements a native publish-subscribe API for topic-based broadcasting. Individual sockets can `.subscribe()` to a topic (specified with a string identifier) and `.publish()` messages to all other subscribers to that topic (excluding itself). This topic-based broadcast API is similar to [MQTT](https://en.wikipedia.org/wiki/MQTT) and [Redis Pub/Sub](https://redis.io/topics/pubsub).
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const server = Bun.serve({
|
||||
fetch(req, server) {
|
||||
const url = new URL(req.url);
|
||||
@@ -201,7 +180,9 @@ const server = Bun.serve({
|
||||
console.log(`upgrade!`);
|
||||
const username = getUsernameFromReq(req);
|
||||
const success = server.upgrade(req, { data: { username } });
|
||||
return success ? undefined : new Response("WebSocket upgrade error", { status: 400 });
|
||||
return success
|
||||
? undefined
|
||||
: new Response("WebSocket upgrade error", { status: 400 });
|
||||
}
|
||||
|
||||
return new Response("Hello world");
|
||||
@@ -209,6 +190,7 @@ const server = Bun.serve({
|
||||
websocket: {
|
||||
// TypeScript: specify the type of ws.data like this
|
||||
data: {} as { username: string },
|
||||
|
||||
open(ws) {
|
||||
const msg = `${ws.data.username} has entered the chat`;
|
||||
ws.subscribe("the-group-chat");
|
||||
@@ -218,9 +200,6 @@ const server = Bun.serve({
|
||||
// this is a group chat
|
||||
// so the server re-broadcasts incoming message to everyone
|
||||
server.publish("the-group-chat", `${ws.data.username}: ${message}`);
|
||||
|
||||
// inspect current subscriptions
|
||||
console.log(ws.subscriptions); // ["the-group-chat"]
|
||||
},
|
||||
close(ws) {
|
||||
const msg = `${ws.data.username} has left the chat`;
|
||||
@@ -250,10 +229,12 @@ server.publish("the-group-chat", "Hello world");
|
||||
|
||||
Per-message [compression](https://websockets.readthedocs.io/en/stable/topics/compression.html) can be enabled with the `perMessageDeflate` parameter.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
Bun.serve({
|
||||
fetch(req, server) {}, // upgrade logic
|
||||
websocket: {
|
||||
perMessageDeflate: true, // [!code ++]
|
||||
// enable compression and decompression
|
||||
perMessageDeflate: true,
|
||||
},
|
||||
});
|
||||
```
|
||||
@@ -284,7 +265,9 @@ By default, Bun will close a WebSocket connection if it is idle for 120 seconds.
|
||||
Bun.serve({
|
||||
fetch(req, server) {}, // upgrade logic
|
||||
websocket: {
|
||||
idleTimeout: 60, // 60 seconds // [!code ++]
|
||||
idleTimeout: 60, // 60 seconds
|
||||
|
||||
// ...
|
||||
},
|
||||
});
|
||||
```
|
||||
@@ -295,13 +278,13 @@ Bun will also close a WebSocket connection if it receives a message that is larg
|
||||
Bun.serve({
|
||||
fetch(req, server) {}, // upgrade logic
|
||||
websocket: {
|
||||
maxPayloadLength: 1024 * 1024, // 1 MB // [!code ++]
|
||||
maxPayloadLength: 1024 * 1024, // 1 MB
|
||||
|
||||
// ...
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Connect to a `Websocket` server
|
||||
|
||||
Bun implements the `WebSocket` class. To create a WebSocket client that connects to a `ws://` or `wss://` server, create an instance of `WebSocket`, as you would in the browser.
|
||||
@@ -320,8 +303,19 @@ For convenience, Bun lets you setting custom headers directly in the constructor
|
||||
```ts
|
||||
const socket = new WebSocket("ws://localhost:3000", {
|
||||
headers: {
|
||||
/* custom headers */
|
||||
}, // [!code ++]
|
||||
// custom headers
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Client compression
|
||||
|
||||
WebSocket clients support permessage-deflate compression. The `extensions` property shows negotiated compression:
|
||||
|
||||
```ts
|
||||
const socket = new WebSocket("wss://echo.websocket.org");
|
||||
socket.addEventListener("open", () => {
|
||||
console.log(socket.extensions); // => "permessage-deflate"
|
||||
});
|
||||
```
|
||||
|
||||
@@ -341,16 +335,17 @@ socket.addEventListener("close", event => {});
|
||||
socket.addEventListener("error", event => {});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Reference
|
||||
|
||||
```ts See Typescript Definitions expandable
|
||||
```ts
|
||||
namespace Bun {
|
||||
export function serve(params: {
|
||||
fetch: (req: Request, server: Server) => Response | Promise<Response>;
|
||||
websocket?: {
|
||||
message: (ws: ServerWebSocket, message: string | ArrayBuffer | Uint8Array) => void;
|
||||
message: (
|
||||
ws: ServerWebSocket,
|
||||
message: string | ArrayBuffer | Uint8Array,
|
||||
) => void;
|
||||
open?: (ws: ServerWebSocket) => void;
|
||||
close?: (ws: ServerWebSocket, code: number, reason: string) => void;
|
||||
error?: (ws: ServerWebSocket, error: Error) => void;
|
||||
@@ -388,7 +383,11 @@ type Compressor =
|
||||
|
||||
interface Server {
|
||||
pendingWebSockets: number;
|
||||
publish(topic: string, data: string | ArrayBufferView | ArrayBuffer, compress?: boolean): number;
|
||||
publish(
|
||||
topic: string,
|
||||
data: string | ArrayBufferView | ArrayBuffer,
|
||||
compress?: boolean,
|
||||
): number;
|
||||
upgrade(
|
||||
req: Request,
|
||||
options?: {
|
||||
@@ -402,7 +401,6 @@ interface ServerWebSocket {
|
||||
readonly data: any;
|
||||
readonly readyState: number;
|
||||
readonly remoteAddress: string;
|
||||
readonly subscriptions: string[];
|
||||
send(message: string | ArrayBuffer | Uint8Array, compress?: boolean): number;
|
||||
close(code?: number, reason?: string): void;
|
||||
subscribe(topic: string): void;
|
||||
@@ -412,3 +410,164 @@ interface ServerWebSocket {
|
||||
cork(cb: (ws: ServerWebSocket) => void): void;
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
### `Bun.serve(params)`
|
||||
|
||||
{% param name="params" %}
|
||||
Configuration object for WebSocket server
|
||||
{% /param %}
|
||||
|
||||
{% param name=" fetch" %}
|
||||
`(req: Request, server: Server) => Response | Promise<Response>`
|
||||
|
||||
Call `server.upgrade(req)` to upgrade the request to a WebSocket connection. This method returns `true` if the upgrade succeeds, or `false` if the upgrade fails.
|
||||
{% /param %}
|
||||
|
||||
{% param name=" websocket" %}
|
||||
Configuration object for WebSocket server
|
||||
{% /param %}
|
||||
|
||||
{% param name=" message" %}
|
||||
`(ws: ServerWebSocket, message: string | ArrayBuffer | Uint8Array) => void`
|
||||
|
||||
This handler is called when a `WebSocket` receives a message.
|
||||
{% /param %}
|
||||
|
||||
{% param name=" open" %}
|
||||
`(ws: ServerWebSocket) => void`
|
||||
|
||||
This handler is called when a `WebSocket` is opened.
|
||||
{% /param %}
|
||||
|
||||
{% param name=" close" %}
|
||||
`(ws: ServerWebSocket, code: number, message: string) => void`
|
||||
|
||||
This handler is called when a `WebSocket` is closed.
|
||||
{% /param %}
|
||||
|
||||
{% param name=" drain" %}
|
||||
`(ws: ServerWebSocket) => void`
|
||||
|
||||
This handler is called when a `WebSocket` is ready to receive more data.
|
||||
{% /param %}
|
||||
|
||||
{% param name=" perMessageDeflate" %}
|
||||
`boolean | {\n compress?: boolean | Compressor;\n decompress?: boolean | Compressor \n}`
|
||||
|
||||
Enable per-message compression and decompression. This is a boolean value or an object with `compress` and `decompress` properties. Each property can be a boolean value or one of the following `Compressor` types:
|
||||
|
||||
- `"disable"`
|
||||
- `"shared"`
|
||||
- `"dedicated"`
|
||||
- `"3KB"`
|
||||
- `"4KB"`
|
||||
- `"8KB"`
|
||||
- `"16KB"`
|
||||
- `"32KB"`
|
||||
- `"64KB"`
|
||||
- `"128KB"`
|
||||
- `"256KB"`
|
||||
|
||||
{% /param %}
|
||||
|
||||
### `ServerWebSocket`
|
||||
|
||||
{% param name="readyState" %}
|
||||
`number`
|
||||
|
||||
The current state of the `WebSocket` connection. This is one of the following values:
|
||||
|
||||
- `0` `CONNECTING`
|
||||
- `1` `OPEN`
|
||||
- `2` `CLOSING`
|
||||
- `3` `CLOSED`
|
||||
|
||||
{% /param %}
|
||||
|
||||
{% param name="remoteAddress" %}
|
||||
|
||||
`string`
|
||||
|
||||
The remote address of the `WebSocket` connection
|
||||
{% /param %}
|
||||
|
||||
{% param name="data" %}
|
||||
The data associated with the `WebSocket` connection. This is set in the `server.upgrade()` call.
|
||||
{% /param %}
|
||||
|
||||
{% param name=".send()" %}
|
||||
`send(message: string | ArrayBuffer | Uint8Array, compress?: boolean): number`
|
||||
|
||||
Send a message to the client. Returns a `number` indicating the result of the operation.
|
||||
|
||||
- `-1`: the message was enqueued but there is backpressure
|
||||
- `0`: the message was dropped due to a connection issue
|
||||
- `1+`: the number of bytes sent
|
||||
|
||||
The `compress` argument will enable compression for this message, even if the `perMessageDeflate` option is disabled.
|
||||
{% /param %}
|
||||
|
||||
{% param name=".subscribe()" %}
|
||||
`subscribe(topic: string): void`
|
||||
|
||||
Subscribe to a topic
|
||||
{% /param %}
|
||||
|
||||
{% param name=".unsubscribe()" %}
|
||||
`unsubscribe(topic: string): void`
|
||||
|
||||
Unsubscribe from a topic
|
||||
{% /param %}
|
||||
|
||||
{% param name=".publish()" %}
|
||||
`publish(topic: string, data: string | ArrayBufferView | ArrayBuffer, compress?: boolean): number;`
|
||||
|
||||
Send a message to all subscribers of a topic
|
||||
{% /param %}
|
||||
|
||||
{% param name=".isSubscribed()" %}
|
||||
`isSubscribed(topic: string): boolean`
|
||||
|
||||
Check if the `WebSocket` is subscribed to a topic
|
||||
{% /param %}
|
||||
{% param name=".cork()" %}
|
||||
`cork(cb: (ws: ServerWebSocket) => void): void;`
|
||||
|
||||
Batch a set of operations on a `WebSocket` connection. The `message`, `open`, and `drain` callbacks are automatically corked, so
|
||||
you only need to call this if you are sending messages outside of those
|
||||
callbacks or in async functions.
|
||||
|
||||
```ts
|
||||
ws.cork((ws) => {
|
||||
ws.send("first");
|
||||
ws.send("second");
|
||||
ws.send("third");
|
||||
});
|
||||
```
|
||||
|
||||
{% /param %}
|
||||
|
||||
{% param name=".close()" %}
|
||||
`close(code?: number, message?: string): void`
|
||||
|
||||
Close the `WebSocket` connection
|
||||
{% /param %}
|
||||
|
||||
### `Server`
|
||||
|
||||
{% param name="pendingWebsockets" %}
|
||||
Number of in-flight `WebSocket` messages
|
||||
{% /param %}
|
||||
|
||||
{% param name=".publish()" %}
|
||||
`publish(topic: string, data: string | ArrayBufferView | ArrayBuffer, compress?: boolean): number;`
|
||||
|
||||
Send a message to all subscribers of a topic
|
||||
{% /param %}
|
||||
|
||||
{% param name=".upgrade()" %}
|
||||
`upgrade(req: Request): boolean`
|
||||
|
||||
Upgrade a request to a `WebSocket` connection. Returns `true` if the upgrade succeeds, or `false` if the upgrade fails.
|
||||
{% /param %} -->
|
||||
@@ -1,12 +1,6 @@
|
||||
---
|
||||
title: Workers
|
||||
description: Use Bun's Workers API to create and communicate with a new JavaScript instance running on a separate thread while sharing I/O resources with the main thread
|
||||
---
|
||||
|
||||
<Warning>
|
||||
The `Worker` API is still experimental (particularly for terminating workers). We are actively working on improving
|
||||
this.
|
||||
</Warning>
|
||||
{% callout %}
|
||||
**🚧** — The `Worker` API is still experimental (particularly for terminating workers). We are actively working on improving this.
|
||||
{% /callout %}
|
||||
|
||||
[`Worker`](https://developer.mozilla.org/en-US/docs/Web/API/Worker) lets you start and communicate with a new JavaScript instance running on a separate thread while sharing I/O resources with the main thread.
|
||||
|
||||
@@ -18,7 +12,7 @@ Like in browsers, [`Worker`](https://developer.mozilla.org/en-US/docs/Web/API/Wo
|
||||
|
||||
### From the main thread
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```js#Main_thread
|
||||
const worker = new Worker("./worker.ts");
|
||||
|
||||
worker.postMessage("hello");
|
||||
@@ -29,7 +23,7 @@ worker.onmessage = event => {
|
||||
|
||||
### Worker thread
|
||||
|
||||
```ts worker.ts icon="file-code"
|
||||
```ts#worker.ts_(Worker_thread)
|
||||
// prevents TS errors
|
||||
declare var self: Worker;
|
||||
|
||||
@@ -60,7 +54,7 @@ The specifier passed to `Worker` is resolved relative to the project root (like
|
||||
|
||||
You can pass an array of module specifiers to the `preload` option to load modules before the worker starts. This is useful when you want to ensure some code is always loaded before the application starts, like loading OpenTelemetry, Sentry, DataDog, etc.
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```js
|
||||
const worker = new Worker("./worker.ts", {
|
||||
preload: ["./load-sentry.js"],
|
||||
});
|
||||
@@ -70,28 +64,42 @@ Like the `--preload` CLI argument, the `preload` option is processed before the
|
||||
|
||||
You can also pass a single string to the `preload` option:
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```js
|
||||
const worker = new Worker("./worker.ts", {
|
||||
preload: "./load-sentry.js",
|
||||
});
|
||||
```
|
||||
|
||||
This feature was added in Bun v1.1.35.
|
||||
|
||||
### `blob:` URLs
|
||||
|
||||
You can also pass a `blob:` URL to `Worker`. This is useful for creating workers from strings or other sources.
|
||||
As of Bun v1.1.13, you can also pass a `blob:` URL to `Worker`. This is useful for creating workers from strings or other sources.
|
||||
|
||||
```js
|
||||
const blob = new Blob([`self.onmessage = (event: MessageEvent) => postMessage(event.data)`], {
|
||||
type: "application/typescript",
|
||||
});
|
||||
const blob = new Blob(
|
||||
[
|
||||
`
|
||||
self.onmessage = (event: MessageEvent) => postMessage(event.data)`,
|
||||
],
|
||||
{
|
||||
type: "application/typescript",
|
||||
},
|
||||
);
|
||||
const url = URL.createObjectURL(blob);
|
||||
const worker = new Worker(url);
|
||||
```
|
||||
|
||||
Like the rest of Bun, workers created from `blob:` URLs support TypeScript, JSX, and other file types out of the box. You can communicate it should be loaded via typescript either via `type` or by passing a `filename` to the `File` constructor.
|
||||
|
||||
```ts
|
||||
const file = new File([`self.onmessage = (event: MessageEvent) => postMessage(event.data)`], "worker.ts");
|
||||
```js
|
||||
const file = new File(
|
||||
[
|
||||
`
|
||||
self.onmessage = (event: MessageEvent) => postMessage(event.data)`,
|
||||
],
|
||||
"worker.ts",
|
||||
);
|
||||
const url = URL.createObjectURL(file);
|
||||
const worker = new Worker(url);
|
||||
```
|
||||
@@ -100,7 +108,7 @@ const worker = new Worker(url);
|
||||
|
||||
The `"open"` event is emitted when a worker is created and ready to receive messages. This can be used to send an initial message to a worker once it's ready. (This event does not exist in browsers.)
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const worker = new Worker(new URL("worker.ts", import.meta.url).href);
|
||||
|
||||
worker.addEventListener("open", () => {
|
||||
@@ -133,7 +141,7 @@ With these fast paths, Bun's `postMessage` performs **2-241x faster** because th
|
||||
|
||||
**Bun (with fast paths):**
|
||||
|
||||
```ts
|
||||
```
|
||||
postMessage({ prop: 11 chars string, ...9 more props }) - 648ns
|
||||
postMessage({ prop: 14 KB string, ...9 more props }) - 719ns
|
||||
postMessage({ prop: 3 MB string, ...9 more props }) - 1.26µs
|
||||
@@ -141,7 +149,7 @@ postMessage({ prop: 3 MB string, ...9 more props }) - 1.26µs
|
||||
|
||||
**Node.js v24.6.0 (for comparison):**
|
||||
|
||||
```js
|
||||
```
|
||||
postMessage({ prop: 11 chars string, ...9 more props }) - 1.19µs
|
||||
postMessage({ prop: 14 KB string, ...9 more props }) - 2.69µs
|
||||
postMessage({ prop: 3 MB string, ...9 more props }) - 304µs
|
||||
@@ -197,7 +205,7 @@ worker.addEventListener("message", event => {
|
||||
|
||||
A `Worker` instance terminates automatically once it's event loop has no work left to do. Attaching a `"message"` listener on the global or any `MessagePort`s will keep the event loop alive. To forcefully terminate a `Worker`, call `worker.terminate()`.
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const worker = new Worker(new URL("worker.ts", import.meta.url).href);
|
||||
|
||||
// ...some time later
|
||||
@@ -214,7 +222,7 @@ A worker can terminate itself with `process.exit()`. This does not terminate the
|
||||
|
||||
The `"close"` event is emitted when a worker has been terminated. It can take some time for the worker to actually terminate, so this event is emitted when the worker has been marked as terminated. The `CloseEvent` will contain the exit code passed to `process.exit()`, or 0 if closed for other reasons.
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const worker = new Worker(new URL("worker.ts", import.meta.url).href);
|
||||
|
||||
worker.addEventListener("close", event => {
|
||||
@@ -232,7 +240,7 @@ By default, an active `Worker` will keep the main (spawning) process alive, so a
|
||||
|
||||
To stop a running worker from keeping the process alive, call `worker.unref()`. This decouples the lifetime of the worker to the lifetime of the main process, and is equivalent to what Node.js' `worker_threads` does.
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const worker = new Worker(new URL("worker.ts", import.meta.url).href);
|
||||
worker.unref();
|
||||
```
|
||||
@@ -243,7 +251,7 @@ Note: `worker.unref()` is not available in browsers.
|
||||
|
||||
To keep the process alive until the `Worker` terminates, call `worker.ref()`. A ref'd worker is the default behavior, and still needs something going on in the event loop (such as a `"message"` listener) for the worker to continue running.
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const worker = new Worker(new URL("worker.ts", import.meta.url).href);
|
||||
worker.unref();
|
||||
// later...
|
||||
@@ -252,7 +260,7 @@ worker.ref();
|
||||
|
||||
Alternatively, you can also pass an `options` object to `Worker`:
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const worker = new Worker(new URL("worker.ts", import.meta.url).href, {
|
||||
ref: false,
|
||||
});
|
||||
@@ -264,21 +272,21 @@ Note: `worker.ref()` is not available in browsers.
|
||||
|
||||
JavaScript instances can use a lot of memory. Bun's `Worker` supports a `smol` mode that reduces memory usage, at a cost of performance. To enable `smol` mode, pass `smol: true` to the `options` object in the `Worker` constructor.
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```js
|
||||
const worker = new Worker("./i-am-smol.ts", {
|
||||
smol: true,
|
||||
});
|
||||
```
|
||||
|
||||
<Accordion title="What does `smol` mode actually do?">
|
||||
Setting `smol: true` sets `JSC::HeapSize` to be `Small` instead of the default `Large`.
|
||||
</Accordion>
|
||||
{% details summary="What does `smol` mode actually do?" %}
|
||||
Setting `smol: true` sets `JSC::HeapSize` to be `Small` instead of the default `Large`.
|
||||
{% /details %}
|
||||
|
||||
## Environment Data
|
||||
|
||||
Share data between the main thread and workers using `setEnvironmentData()` and `getEnvironmentData()`.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
```js
|
||||
import { setEnvironmentData, getEnvironmentData } from "worker_threads";
|
||||
|
||||
// In main thread
|
||||
@@ -293,7 +301,7 @@ console.log(config); // => { apiUrl: "https://api.example.com" }
|
||||
|
||||
Listen for worker creation events using `process.emit()`:
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
```js
|
||||
process.on("worker", worker => {
|
||||
console.log("New worker created:", worker.threadId);
|
||||
});
|
||||
@@ -1,22 +1,16 @@
|
||||
---
|
||||
title: YAML
|
||||
description: Use Bun's built-in support for YAML files through both runtime APIs and bundler integration
|
||||
---
|
||||
In Bun, YAML is a first-class citizen alongside JSON and TOML.
|
||||
|
||||
In Bun, YAML is a first-class citizen alongside JSON and TOML. You can:
|
||||
Bun provides built-in support for YAML files through both runtime APIs and bundler integration. You can
|
||||
|
||||
- Parse YAML strings with `Bun.YAML.parse`
|
||||
- `import` & `require` YAML files as modules at runtime (including hot reloading & watch mode support)
|
||||
- `import` & `require` YAML files in frontend apps via bun's bundler
|
||||
|
||||
---
|
||||
- Stringify JavaScript objects to YAML with `Bun.YAML.stringify`
|
||||
- import & require YAML files as modules at runtime (including hot reloading & watch mode support)
|
||||
- import & require YAML files in frontend apps via bun's bundler
|
||||
|
||||
## Conformance
|
||||
|
||||
Bun's YAML parser currently passes over 90% of the official YAML test suite. While we're actively working on reaching 100% conformance, the current implementation covers the vast majority of real-world use cases. The parser is written in Zig for optimal performance and is continuously being improved.
|
||||
|
||||
---
|
||||
|
||||
## Runtime API
|
||||
|
||||
### `Bun.YAML.parse()`
|
||||
@@ -111,7 +105,7 @@ const data = Bun.YAML.parse(yaml);
|
||||
|
||||
#### Error Handling
|
||||
|
||||
`Bun.YAML.parse()` throws a `SyntaxError` if the YAML is invalid:
|
||||
`Bun.YAML.parse()` throws an error if the YAML is invalid:
|
||||
|
||||
```ts
|
||||
try {
|
||||
@@ -121,7 +115,174 @@ try {
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
### `Bun.YAML.stringify()`
|
||||
|
||||
Convert a JavaScript value into a YAML string. The API signature matches `JSON.stringify`:
|
||||
|
||||
```ts
|
||||
YAML.stringify(value, replacer?, space?)
|
||||
```
|
||||
|
||||
- `value`: The value to convert to YAML
|
||||
- `replacer`: Currently only `null` or `undefined` (function replacers not yet supported)
|
||||
- `space`: Number of spaces for indentation (e.g., `2`) or a string to use for indentation. **Without this parameter, outputs flow-style (single-line) YAML**
|
||||
|
||||
#### Basic Usage
|
||||
|
||||
```ts
|
||||
import { YAML } from "bun";
|
||||
|
||||
const data = {
|
||||
name: "John Doe",
|
||||
age: 30,
|
||||
hobbies: ["reading", "coding"],
|
||||
};
|
||||
|
||||
// Without space - outputs flow-style (single-line) YAML
|
||||
console.log(YAML.stringify(data));
|
||||
// {name: John Doe,age: 30,hobbies: [reading,coding]}
|
||||
|
||||
// With space=2 - outputs block-style (multi-line) YAML
|
||||
console.log(YAML.stringify(data, null, 2));
|
||||
// name: John Doe
|
||||
// age: 30
|
||||
// hobbies:
|
||||
// - reading
|
||||
// - coding
|
||||
```
|
||||
|
||||
#### Output Styles
|
||||
|
||||
```ts
|
||||
const arr = [1, 2, 3];
|
||||
|
||||
// Flow style (single-line) - default
|
||||
console.log(YAML.stringify(arr));
|
||||
// [1,2,3]
|
||||
|
||||
// Block style (multi-line) - with indentation
|
||||
console.log(YAML.stringify(arr, null, 2));
|
||||
// - 1
|
||||
// - 2
|
||||
// - 3
|
||||
```
|
||||
|
||||
#### String Quoting
|
||||
|
||||
`YAML.stringify()` automatically quotes strings when necessary:
|
||||
|
||||
- Strings that would be parsed as YAML keywords (`true`, `false`, `null`, `yes`, `no`, etc.)
|
||||
- Strings that would be parsed as numbers
|
||||
- Strings containing special characters or escape sequences
|
||||
|
||||
```ts
|
||||
const examples = {
|
||||
keyword: "true", // Will be quoted: "true"
|
||||
number: "123", // Will be quoted: "123"
|
||||
text: "hello world", // Won't be quoted: hello world
|
||||
empty: "", // Will be quoted: ""
|
||||
};
|
||||
|
||||
console.log(YAML.stringify(examples, null, 2));
|
||||
// keyword: "true"
|
||||
// number: "123"
|
||||
// text: hello world
|
||||
// empty: ""
|
||||
```
|
||||
|
||||
#### Cycles and References
|
||||
|
||||
`YAML.stringify()` automatically detects and handles circular references using YAML anchors and aliases:
|
||||
|
||||
```ts
|
||||
const obj = { name: "root" };
|
||||
obj.self = obj; // Circular reference
|
||||
|
||||
const yamlString = YAML.stringify(obj, null, 2);
|
||||
console.log(yamlString);
|
||||
// &root
|
||||
// name: root
|
||||
// self:
|
||||
// *root
|
||||
|
||||
// Objects with shared references
|
||||
const shared = { id: 1 };
|
||||
const data = {
|
||||
first: shared,
|
||||
second: shared,
|
||||
};
|
||||
|
||||
console.log(YAML.stringify(data, null, 2));
|
||||
// first:
|
||||
// &first
|
||||
// id: 1
|
||||
// second:
|
||||
// *first
|
||||
```
|
||||
|
||||
#### Special Values
|
||||
|
||||
```ts
|
||||
// Special numeric values
|
||||
console.log(YAML.stringify(Infinity)); // .inf
|
||||
console.log(YAML.stringify(-Infinity)); // -.inf
|
||||
console.log(YAML.stringify(NaN)); // .nan
|
||||
console.log(YAML.stringify(0)); // 0
|
||||
console.log(YAML.stringify(-0)); // -0
|
||||
|
||||
// null and undefined
|
||||
console.log(YAML.stringify(null)); // null
|
||||
console.log(YAML.stringify(undefined)); // undefined (returns undefined, not a string)
|
||||
|
||||
// Booleans
|
||||
console.log(YAML.stringify(true)); // true
|
||||
console.log(YAML.stringify(false)); // false
|
||||
```
|
||||
|
||||
#### Complex Objects
|
||||
|
||||
```ts
|
||||
const config = {
|
||||
server: {
|
||||
port: 3000,
|
||||
host: "localhost",
|
||||
ssl: {
|
||||
enabled: true,
|
||||
cert: "/path/to/cert.pem",
|
||||
key: "/path/to/key.pem",
|
||||
},
|
||||
},
|
||||
database: {
|
||||
connections: [
|
||||
{ name: "primary", host: "db1.example.com" },
|
||||
{ name: "replica", host: "db2.example.com" },
|
||||
],
|
||||
},
|
||||
features: {
|
||||
auth: true,
|
||||
"rate-limit": 100, // Keys with special characters are preserved
|
||||
},
|
||||
};
|
||||
|
||||
const yamlString = YAML.stringify(config, null, 2);
|
||||
console.log(yamlString);
|
||||
// server:
|
||||
// port: 3000
|
||||
// host: localhost
|
||||
// ssl:
|
||||
// enabled: true
|
||||
// cert: /path/to/cert.pem
|
||||
// key: /path/to/key.pem
|
||||
// database:
|
||||
// connections:
|
||||
// - name: primary
|
||||
// host: db1.example.com
|
||||
// - name: replica
|
||||
// host: db2.example.com
|
||||
// features:
|
||||
// auth: true
|
||||
// rate-limit: 100
|
||||
```
|
||||
|
||||
## Module Import
|
||||
|
||||
@@ -129,7 +290,7 @@ try {
|
||||
|
||||
You can import YAML files directly as ES modules. The YAML content is parsed and made available as both default and named exports:
|
||||
|
||||
```yaml config.yaml
|
||||
```yaml#config.yaml
|
||||
database:
|
||||
host: localhost
|
||||
port: 5432
|
||||
@@ -147,7 +308,7 @@ features:
|
||||
|
||||
#### Default Import
|
||||
|
||||
```ts app.ts icon="/icons/typescript.svg"
|
||||
```ts#app.ts
|
||||
import config from "./config.yaml";
|
||||
|
||||
console.log(config.database.host); // "localhost"
|
||||
@@ -158,7 +319,7 @@ console.log(config.redis.port); // 6379
|
||||
|
||||
You can destructure top-level YAML properties as named imports:
|
||||
|
||||
```ts app.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { database, redis, features } from "./config.yaml";
|
||||
|
||||
console.log(database.host); // "localhost"
|
||||
@@ -168,7 +329,7 @@ console.log(features.auth); // true
|
||||
|
||||
Or combine both:
|
||||
|
||||
```ts app.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import config, { database, features } from "./config.yaml";
|
||||
|
||||
// Use the full config object
|
||||
@@ -184,7 +345,7 @@ if (features.rateLimit) {
|
||||
|
||||
YAML files can also be required in CommonJS:
|
||||
|
||||
```ts app.ts icon="/icons/typescript.svg"
|
||||
```js
|
||||
const config = require("./config.yaml");
|
||||
console.log(config.database.name); // "myapp"
|
||||
|
||||
@@ -193,7 +354,44 @@ const { database, redis } = require("./config.yaml");
|
||||
console.log(database.port); // 5432
|
||||
```
|
||||
|
||||
---
|
||||
### TypeScript Support
|
||||
|
||||
While Bun can import YAML files directly, TypeScript doesn't know the types of your YAML files by default. To add TypeScript support for your YAML imports, create a declaration file with `.d.ts` appended to the YAML filename (e.g., `config.yaml` → `config.yaml.d.ts`):
|
||||
|
||||
```yaml#config.yaml
|
||||
features: "advanced"
|
||||
server:
|
||||
host: localhost
|
||||
port: 3000
|
||||
```
|
||||
|
||||
```ts#config.yaml.d.ts
|
||||
const contents: {
|
||||
features: string;
|
||||
server: {
|
||||
host: string;
|
||||
port: number;
|
||||
};
|
||||
};
|
||||
|
||||
export = contents;
|
||||
```
|
||||
|
||||
Now TypeScript will provide proper type checking and auto-completion:
|
||||
|
||||
```ts#app.ts
|
||||
import config from "./config.yaml";
|
||||
|
||||
// TypeScript knows the types!
|
||||
config.server.port; // number
|
||||
config.server.host; // string
|
||||
config.features; // string
|
||||
|
||||
// TypeScript will catch errors
|
||||
config.server.unknown; // Error: Property 'unknown' does not exist
|
||||
```
|
||||
|
||||
This approach works for both ES modules and CommonJS, giving you full type safety while Bun continues to handle the actual YAML parsing at runtime.
|
||||
|
||||
## Hot Reloading with YAML
|
||||
|
||||
@@ -201,7 +399,7 @@ One of the most powerful features of Bun's YAML support is hot reloading. When y
|
||||
|
||||
### Configuration Hot Reloading
|
||||
|
||||
```yaml config.yaml
|
||||
```yaml#config.yaml
|
||||
server:
|
||||
port: 3000
|
||||
host: localhost
|
||||
@@ -211,7 +409,7 @@ features:
|
||||
verbose: false
|
||||
```
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts#server.ts
|
||||
import { server, features } from "./config.yaml";
|
||||
|
||||
console.log(`Starting server on ${server.host}:${server.port}`);
|
||||
@@ -235,7 +433,7 @@ Bun.serve({
|
||||
|
||||
Run with hot reloading:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```bash
|
||||
bun --hot server.ts
|
||||
```
|
||||
|
||||
@@ -246,15 +444,13 @@ Now when you modify `config.yaml`, the changes are immediately reflected in your
|
||||
- Live debugging with configuration changes
|
||||
- Feature flag toggling
|
||||
|
||||
---
|
||||
|
||||
## Configuration Management
|
||||
|
||||
### Environment-Based Configuration
|
||||
|
||||
YAML excels at managing configuration across different environments:
|
||||
|
||||
```yaml config.yaml
|
||||
```yaml#config.yaml
|
||||
defaults: &defaults
|
||||
timeout: 5000
|
||||
retries: 3
|
||||
@@ -293,7 +489,7 @@ production:
|
||||
pretty: false
|
||||
```
|
||||
|
||||
```ts app.ts icon="/icons/typescript.svg"
|
||||
```ts#app.ts
|
||||
import configs from "./config.yaml";
|
||||
|
||||
const env = process.env.NODE_ENV || "development";
|
||||
@@ -317,7 +513,7 @@ export default interpolateEnvVars(config);
|
||||
|
||||
### Feature Flags Configuration
|
||||
|
||||
```yaml features.yaml
|
||||
```yaml#features.yaml
|
||||
features:
|
||||
newDashboard:
|
||||
enabled: true
|
||||
@@ -337,10 +533,13 @@ features:
|
||||
default: auto # auto, light, dark
|
||||
```
|
||||
|
||||
```ts feature-flags.ts icon="/icons/typescript.svg"
|
||||
```ts#feature-flags.ts
|
||||
import { features } from "./features.yaml";
|
||||
|
||||
export function isFeatureEnabled(featureName: string, userEmail?: string): boolean {
|
||||
export function isFeatureEnabled(
|
||||
featureName: string,
|
||||
userEmail?: string,
|
||||
): boolean {
|
||||
const feature = features[featureName];
|
||||
|
||||
if (!feature?.enabled) {
|
||||
@@ -373,7 +572,7 @@ if (isFeatureEnabled("newDashboard", user.email)) {
|
||||
|
||||
### Database Configuration
|
||||
|
||||
```yaml database.yaml icon="yaml"
|
||||
```yaml#database.yaml
|
||||
connections:
|
||||
primary:
|
||||
type: postgres
|
||||
@@ -409,7 +608,7 @@ seeds:
|
||||
directory: ./seeds
|
||||
```
|
||||
|
||||
```ts db.ts icon="/icons/typescript.svg"
|
||||
```ts#db.ts
|
||||
import { connections, migrations } from "./database.yaml";
|
||||
import { createConnection } from "./database-driver";
|
||||
|
||||
@@ -439,7 +638,7 @@ if (parseConfig(migrations).autoRun === "true") {
|
||||
|
||||
When you import YAML files in your application and bundle it with Bun, the YAML is parsed at build time and included as a JavaScript module:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```bash
|
||||
bun build app.ts --outdir=dist
|
||||
```
|
||||
|
||||
@@ -453,7 +652,7 @@ This means:
|
||||
|
||||
YAML files can be dynamically imported, useful for loading configuration on demand:
|
||||
|
||||
```ts Load configuration based on environment
|
||||
```ts#Load configuration based on environment
|
||||
const env = process.env.NODE_ENV || "development";
|
||||
const config = await import(`./configs/${env}.yaml`);
|
||||
|
||||
120
docs/benchmarks.md
Normal file
120
docs/benchmarks.md
Normal file
@@ -0,0 +1,120 @@
|
||||
Bun.js focuses on performance, developer experience, and compatibility with the JavaScript ecosystem.
|
||||
|
||||
## HTTP Requests
|
||||
|
||||
```ts
|
||||
// http.ts
|
||||
export default {
|
||||
port: 3000,
|
||||
fetch(request: Request) {
|
||||
return new Response("Hello World");
|
||||
},
|
||||
};
|
||||
|
||||
// bun ./http.ts
|
||||
```
|
||||
|
||||
| Requests per second | OS | CPU | Bun version |
|
||||
| ---------------------------------------------------------------------- | ----- | ------------------------------ | ----------- |
|
||||
| [260,000](https://twitter.com/jarredsumner/status/1512040623200616449) | macOS | Apple Silicon M1 Max | 0.0.76 |
|
||||
| [160,000](https://twitter.com/jarredsumner/status/1511988933587976192) | Linux | AMD Ryzen 5 3600 6-Core 2.2ghz | 0.0.76 |
|
||||
|
||||
{% details summary="See benchmark details" %}
|
||||
Measured with [`http_load_test`](https://github.com/uNetworking/uSockets/blob/master/examples/http_load_test.c) by running:
|
||||
|
||||
```bash
|
||||
$ ./http_load_test 20 127.0.0.1 3000
|
||||
```
|
||||
|
||||
{% /details %}
|
||||
|
||||
## File System
|
||||
|
||||
`cat` clone that runs [2x faster than GNU cat](https://twitter.com/jarredsumner/status/1511707890708586496) for large files on Linux
|
||||
|
||||
```js
|
||||
// cat.js
|
||||
import { resolve } from "path";
|
||||
import { write, stdout, file, argv } from "bun";
|
||||
|
||||
const path = resolve(argv.at(-1));
|
||||
|
||||
await write(
|
||||
// stdout is a Blob
|
||||
stdout,
|
||||
// file(path) returns a Blob - https://developer.mozilla.org/en-US/docs/Web/API/Blob
|
||||
file(path),
|
||||
);
|
||||
```
|
||||
|
||||
Run this with `bun cat.js /path/to/big/file`.
|
||||
|
||||
## Reading from standard input
|
||||
|
||||
```ts
|
||||
for await (const line of console) {
|
||||
// line of text from stdin
|
||||
console.log(line);
|
||||
}
|
||||
```
|
||||
|
||||
## React SSR
|
||||
|
||||
```js
|
||||
import { renderToReadableStream } from "react-dom/server";
|
||||
|
||||
const dt = new Intl.DateTimeFormat();
|
||||
|
||||
export default {
|
||||
port: 3000,
|
||||
async fetch(request: Request) {
|
||||
return new Response(
|
||||
await renderToReadableStream(
|
||||
<html>
|
||||
<head>
|
||||
<title>Hello World</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello from React!</h1>
|
||||
<p>The date is {dt.format(new Date())}</p>
|
||||
</body>
|
||||
</html>,
|
||||
),
|
||||
);
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
Write to stdout with `console.write`:
|
||||
|
||||
```js
|
||||
// no trailing newline
|
||||
// works with strings and typed arrays
|
||||
console.write("Hello World!");
|
||||
```
|
||||
|
||||
There are some more examples in the [examples](./examples) folder.
|
||||
|
||||
PRs adding more examples are very welcome!
|
||||
|
||||
## Fast paths for Web APIs
|
||||
|
||||
Bun.js has fast paths for common use cases that make Web APIs live up to the performance demands of servers and CLIs.
|
||||
|
||||
`Bun.file(path)` returns a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) that represents a lazily-loaded file.
|
||||
|
||||
When you pass a file blob to `Bun.write`, Bun automatically uses a faster system call:
|
||||
|
||||
```js
|
||||
const blob = Bun.file("input.txt");
|
||||
await Bun.write("output.txt", blob);
|
||||
```
|
||||
|
||||
On Linux, this uses the [`copy_file_range`](https://man7.org/linux/man-pages/man2/copy_file_range.2.html) syscall and on macOS, this becomes `clonefile` (or [`fcopyfile`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/copyfile.3.html)).
|
||||
|
||||
`Bun.write` also supports [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects. It automatically converts to a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob).
|
||||
|
||||
```js
|
||||
// Eventually, this will stream the response to disk but today it buffers
|
||||
await Bun.write("index.html", await fetch("https://example.com"));
|
||||
```
|
||||
42
docs/bun-flavored-toml.md
Normal file
42
docs/bun-flavored-toml.md
Normal file
@@ -0,0 +1,42 @@
|
||||
[TOML](https://toml.io/) is a minimal configuration file format designed to be easy for humans to read.
|
||||
|
||||
Bun implements a TOML parser with a few tweaks designed for better interoperability with INI files and with JavaScript.
|
||||
|
||||
### ; and # are comments
|
||||
|
||||
In Bun-flavored TOML, comments start with `#` or `;`
|
||||
|
||||
```ini
|
||||
# This is a comment
|
||||
; This is also a comment
|
||||
```
|
||||
|
||||
This matches the behavior of INI files.
|
||||
|
||||
In TOML, comments start with `#`
|
||||
|
||||
```toml
|
||||
# This is a comment
|
||||
```
|
||||
|
||||
### String escape characters
|
||||
|
||||
Bun-flavored adds a few more escape sequences to TOML to work better with JavaScript strings.
|
||||
|
||||
```
|
||||
# Bun-flavored TOML extras
|
||||
\x{XX} - ASCII (U+00XX)
|
||||
\u{x+} - unicode (U+0000000X) - (U+XXXXXXXX)
|
||||
\v - vertical tab
|
||||
|
||||
# Regular TOML
|
||||
\b - backspace (U+0008)
|
||||
\t - tab (U+0009)
|
||||
\n - linefeed (U+000A)
|
||||
\f - form feed (U+000C)
|
||||
\r - carriage return (U+000D)
|
||||
\" - quote (U+0022)
|
||||
\\ - backslash (U+005C)
|
||||
\uXXXX - unicode (U+XXXX)
|
||||
\UXXXXXXXX - unicode (U+XXXXXXXX)
|
||||
```
|
||||
@@ -1,465 +0,0 @@
|
||||
---
|
||||
title: Bytecode Caching
|
||||
description: Speed up JavaScript execution with bytecode caching in Bun's bundler
|
||||
---
|
||||
|
||||
Bytecode caching is a build-time optimization that dramatically improves application startup time by pre-compiling your JavaScript to bytecode. For example, when compiling TypeScript's `tsc` with bytecode enabled, startup time improves by **2x**.
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic usage
|
||||
|
||||
Enable bytecode caching with the `--bytecode` flag:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build ./index.ts --target=bun --bytecode --outdir=./dist
|
||||
```
|
||||
|
||||
This generates two files:
|
||||
|
||||
- `dist/index.js` - Your bundled JavaScript
|
||||
- `dist/index.jsc` - The bytecode cache file
|
||||
|
||||
At runtime, Bun automatically detects and uses the `.jsc` file:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun ./dist/index.js # Automatically uses index.jsc
|
||||
```
|
||||
|
||||
### With standalone executables
|
||||
|
||||
When creating executables with `--compile`, bytecode is embedded into the binary:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build ./cli.ts --compile --bytecode --outfile=mycli
|
||||
```
|
||||
|
||||
The resulting executable contains both the code and bytecode, giving you maximum performance in a single file.
|
||||
|
||||
### Combining with other optimizations
|
||||
|
||||
Bytecode works great with minification and source maps:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build --compile --bytecode --minify --sourcemap ./cli.ts --outfile=mycli
|
||||
```
|
||||
|
||||
- `--minify` reduces code size before generating bytecode (less code -> less bytecode)
|
||||
- `--sourcemap` preserves error reporting (errors still point to original source)
|
||||
- `--bytecode` eliminates parsing overhead
|
||||
|
||||
## Performance impact
|
||||
|
||||
The performance improvement scales with your codebase size:
|
||||
|
||||
| Application size | Typical startup improvement |
|
||||
| ------------------------- | --------------------------- |
|
||||
| Small CLI (< 100 KB) | 1.5-2x faster |
|
||||
| Medium-large app (> 5 MB) | 2.5x-4x faster |
|
||||
|
||||
Larger applications benefit more because they have more code to parse.
|
||||
|
||||
## When to use bytecode
|
||||
|
||||
### Great for:
|
||||
|
||||
#### CLI tools
|
||||
|
||||
- Invoked frequently (linters, formatters, git hooks)
|
||||
- Startup time is the entire user experience
|
||||
- Users notice the difference between 90ms and 45ms startup
|
||||
- Example: TypeScript compiler, Prettier, ESLint
|
||||
|
||||
#### Build tools and task runners
|
||||
|
||||
- Run hundreds or thousands of times during development
|
||||
- Milliseconds saved per run compound quickly
|
||||
- Developer experience improvement
|
||||
- Example: Build scripts, test runners, code generators
|
||||
|
||||
#### Standalone executables
|
||||
|
||||
- Distributed to users who care about snappy performance
|
||||
- Single-file distribution is convenient
|
||||
- File size less important than startup time
|
||||
- Example: CLIs distributed via npm or as binaries
|
||||
|
||||
### Skip it for:
|
||||
|
||||
- ❌ **Small scripts**
|
||||
- ❌ **Code that runs once**
|
||||
- ❌ **Development builds**
|
||||
- ❌ **Size-constrained environments**
|
||||
- ❌ **Code with top-level await** (not supported)
|
||||
|
||||
## Limitations
|
||||
|
||||
### CommonJS only
|
||||
|
||||
Bytecode caching currently works with CommonJS output format. Bun's bundler automatically converts most ESM code to CommonJS, but **top-level await** is the exception:
|
||||
|
||||
```js
|
||||
// This prevents bytecode caching
|
||||
const data = await fetch("https://api.example.com");
|
||||
export default data;
|
||||
```
|
||||
|
||||
**Why**: Top-level await requires async module evaluation, which can't be represented in CommonJS. The module graph becomes asynchronous, and the CommonJS wrapper function model breaks down.
|
||||
|
||||
**Workaround**: Move async initialization into a function:
|
||||
|
||||
```js
|
||||
async function init() {
|
||||
const data = await fetch("https://api.example.com");
|
||||
return data;
|
||||
}
|
||||
|
||||
export default init;
|
||||
```
|
||||
|
||||
Now the module exports a function that the consumer can await when needed.
|
||||
|
||||
### Version compatibility
|
||||
|
||||
Bytecode is **not portable across Bun versions**. The bytecode format is tied to JavaScriptCore's internal representation, which changes between versions.
|
||||
|
||||
When you update Bun, you must regenerate bytecode:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
# After updating Bun
|
||||
bun build --bytecode ./index.ts --outdir=./dist
|
||||
```
|
||||
|
||||
If bytecode doesn't match the current Bun version, it's automatically ignored and your code falls back to parsing the JavaScript source. Your app still runs - you just lose the performance optimization.
|
||||
|
||||
**Best practice**: Generate bytecode as part of your CI/CD build process. Don't commit `.jsc` files to git. Regenerate them whenever you update Bun.
|
||||
|
||||
### Source code still required
|
||||
|
||||
- The `.js` file (your bundled source code)
|
||||
- The `.jsc` file (the bytecode cache)
|
||||
|
||||
At runtime:
|
||||
|
||||
1. Bun loads the `.js` file, sees a `@bytecode` pragma, and checks the `.jsc` file
|
||||
2. Bun loads the `.jsc` file
|
||||
3. Bun validates the bytecode hash matches the source
|
||||
4. If valid, Bun uses the bytecode
|
||||
5. If invalid, Bun falls back to parsing the source
|
||||
|
||||
### Bytecode is not obfuscation
|
||||
|
||||
Bytecode **does not obscure your source code**. It's an optimization, not a security measure.
|
||||
|
||||
## Production deployment
|
||||
|
||||
### Docker
|
||||
|
||||
Include bytecode generation in your Dockerfile:
|
||||
|
||||
```dockerfile Dockerfile icon="docker"
|
||||
FROM oven/bun:1 AS builder
|
||||
WORKDIR /app
|
||||
COPY package.json bun.lock ./
|
||||
RUN bun install --frozen-lockfile
|
||||
|
||||
COPY . .
|
||||
RUN bun build --bytecode --minify --sourcemap \
|
||||
--target=bun \
|
||||
--outdir=./dist \
|
||||
--compile \
|
||||
./src/server.ts --outfile=./dist/server
|
||||
|
||||
FROM oven/bun:1 AS runner
|
||||
WORKDIR /app
|
||||
COPY --from=builder /dist/server /app/server
|
||||
CMD ["./server"]
|
||||
```
|
||||
|
||||
The bytecode is architecture-independent.
|
||||
|
||||
### CI/CD
|
||||
|
||||
Generate bytecode during your build pipeline:
|
||||
|
||||
```yaml workflow.yml icon="file-code"
|
||||
# GitHub Actions
|
||||
- name: Build with bytecode
|
||||
run: |
|
||||
bun install
|
||||
bun build --bytecode --minify \
|
||||
--outdir=./dist \
|
||||
--target=bun \
|
||||
./src/index.ts
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
### Verify bytecode is being used
|
||||
|
||||
Check that the `.jsc` file exists:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
ls -lh dist/
|
||||
```
|
||||
|
||||
```txt
|
||||
-rw-r--r-- 1 user staff 245K index.js
|
||||
-rw-r--r-- 1 user staff 1.1M index.jsc
|
||||
```
|
||||
|
||||
The `.jsc` file should be 2-8x larger than the `.js` file.
|
||||
|
||||
To log if bytecode is being used, set `BUN_JSC_verboseDiskCache=1` in your environment.
|
||||
|
||||
On success, it will log something like:
|
||||
|
||||
```txt
|
||||
[Disk cache] cache hit for sourceCode
|
||||
```
|
||||
|
||||
If you see a cache miss, it will log something like:
|
||||
|
||||
```txt
|
||||
[Disk cache] cache miss for sourceCode
|
||||
```
|
||||
|
||||
It's normal for it it to log a cache miss multiple times since Bun doesn't currently bytecode cache JavaScript code used in builtin modules.
|
||||
|
||||
### Common issues
|
||||
|
||||
**Bytecode silently ignored**: Usually caused by a Bun version update. The cache version doesn't match, so bytecode is rejected. Regenerate to fix.
|
||||
|
||||
**File size too large**: This is expected. Consider:
|
||||
|
||||
- Using `--minify` to reduce code size before bytecode generation
|
||||
- Compressing `.jsc` files for network transfer (gzip/brotli)
|
||||
- Evaluating if the startup performance gain is worth the size increase
|
||||
|
||||
**Top-level await**: Not supported. Refactor to use async initialization functions.
|
||||
|
||||
## What is bytecode?
|
||||
|
||||
When you run JavaScript, the JavaScript engine doesn't execute your source code directly. Instead, it goes through several steps:
|
||||
|
||||
1. **Parsing**: The engine reads your JavaScript source code and converts it into an Abstract Syntax Tree (AST)
|
||||
2. **Bytecode compilation**: The AST is compiled into bytecode - a lower-level representation that's faster to execute
|
||||
3. **Execution**: The bytecode is executed by the engine's interpreter or JIT compiler
|
||||
|
||||
Bytecode is an intermediate representation - it's lower-level than JavaScript source code, but higher-level than machine code. Think of it as assembly language for a virtual machine. Each bytecode instruction represents a single operation like "load this variable," "add two numbers," or "call this function."
|
||||
|
||||
This happens **every single time** you run your code. If you have a CLI tool that runs 100 times a day, your code gets parsed 100 times. If you have a serverless function with frequent cold starts, parsing happens on every cold start.
|
||||
|
||||
With bytecode caching, Bun moves steps 1 and 2 to the build step. At runtime, the engine loads the pre-compiled bytecode and jumps straight to execution.
|
||||
|
||||
### Why lazy parsing makes this even better
|
||||
|
||||
Modern JavaScript engines use a clever optimization called **lazy parsing**. They don't parse all your code upfront - instead, functions are only parsed when they're first called:
|
||||
|
||||
```js
|
||||
// Without bytecode caching:
|
||||
function rarely_used() {
|
||||
// This 500-line function is only parsed
|
||||
// when it's actually called
|
||||
}
|
||||
|
||||
function main() {
|
||||
console.log("Starting app");
|
||||
// rarely_used() is never called, so it's never parsed
|
||||
}
|
||||
```
|
||||
|
||||
This means parsing overhead isn't just a startup cost - it happens throughout your application's lifetime as different code paths execute. With bytecode caching, **all functions are pre-compiled**, even the ones that are lazily parsed. The parsing work happens once at build time instead of being distributed throughout your application's execution.
|
||||
|
||||
## The bytecode format
|
||||
|
||||
### Inside a .jsc file
|
||||
|
||||
A `.jsc` file contains a serialized bytecode structure. Understanding what's inside helps explain both the performance benefits and the file size tradeoff.
|
||||
|
||||
**Header section** (validated on every load):
|
||||
|
||||
- **Cache version**: A hash tied to the JavaScriptCore framework version. This ensures bytecode generated with one version of Bun only runs with that exact version.
|
||||
- **Code block type tag**: Identifies whether this is a Program, Module, Eval, or Function code block.
|
||||
|
||||
**SourceCodeKey** (validates bytecode matches source):
|
||||
|
||||
- **Source code hash**: A hash of the original JavaScript source code. Bun verifies this matches before using the bytecode.
|
||||
- **Source code length**: The exact length of the source, for additional validation.
|
||||
- **Compilation flags**: Critical compilation context like strict mode, whether it's a script vs module, eval context type, etc. The same source code compiled with different flags produces different bytecode.
|
||||
|
||||
**Bytecode instructions**:
|
||||
|
||||
- **Instruction stream**: The actual bytecode opcodes - the compiled representation of your JavaScript. This is a variable-length sequence of bytecode instructions.
|
||||
- **Metadata table**: Each opcode has associated metadata - things like profiling counters, type hints, and execution counts (even if not yet populated).
|
||||
- **Jump targets**: Pre-computed addresses for control flow (if/else, loops, switch statements).
|
||||
- **Switch tables**: Optimized lookup tables for switch statements.
|
||||
|
||||
**Constants and identifiers**:
|
||||
|
||||
- **Constant pool**: All literal values in your code - numbers, strings, booleans, null, undefined. These are stored as actual JavaScript values (JSValues) so they don't need to be parsed from source at runtime.
|
||||
- **Identifier table**: All variable and function names used in the code. Stored as deduplicated strings.
|
||||
- **Source code representation markers**: Flags indicating how constants should be represented (as integers, doubles, big ints, etc.).
|
||||
|
||||
**Function metadata** (for each function in your code):
|
||||
|
||||
- **Register allocation**: How many registers (local variables) the function needs - `thisRegister`, `scopeRegister`, `numVars`, `numCalleeLocals`, `numParameters`.
|
||||
- **Code features**: A bitmask of function characteristics: is it a constructor? an arrow function? does it use `super`? does it have tail calls? These affect how the function is executed.
|
||||
- **Lexically scoped features**: Strict mode and other lexical context.
|
||||
- **Parse mode**: The mode in which the function was parsed (normal, async, generator, async generator).
|
||||
|
||||
**Nested structures**:
|
||||
|
||||
- **Function declarations and expressions**: Each nested function gets its own bytecode block, recursively. A file with 100 functions has 100 separate bytecode blocks, all nested in the structure.
|
||||
- **Exception handlers**: Try/catch/finally blocks with their boundaries and handler addresses pre-computed.
|
||||
- **Expression info**: Maps bytecode positions back to source code locations for error reporting and debugging.
|
||||
|
||||
### What bytecode does NOT contain
|
||||
|
||||
Importantly, **bytecode does not embed your source code**. Instead:
|
||||
|
||||
- The JavaScript source is stored separately (in the `.js` file)
|
||||
- The bytecode only stores a hash and length of the source
|
||||
- At load time, Bun validates the bytecode matches the current source code
|
||||
|
||||
This is why you need to deploy both the `.js` and `.jsc` files. The `.jsc` file is useless without its corresponding `.js` file.
|
||||
|
||||
## The tradeoff: file size
|
||||
|
||||
Bytecode files are significantly larger than source code - typically 2-8x larger.
|
||||
|
||||
### Why is bytecode so much larger?
|
||||
|
||||
**Bytecode instructions are verbose**:
|
||||
A single line of minified JavaScript might compile to dozens of bytecode instructions. For example:
|
||||
|
||||
```js
|
||||
const sum = arr.reduce((a, b) => a + b, 0);
|
||||
```
|
||||
|
||||
Compiles to bytecode that:
|
||||
|
||||
- Loads the `arr` variable
|
||||
- Gets the `reduce` property
|
||||
- Creates the arrow function (which itself has bytecode)
|
||||
- Loads the initial value `0`
|
||||
- Sets up the call with the right number of arguments
|
||||
- Actually performs the call
|
||||
- Stores the result in `sum`
|
||||
|
||||
Each of these steps is a separate bytecode instruction with its own metadata.
|
||||
|
||||
**Constant pools store everything**:
|
||||
Every string literal, number, property name - everything gets stored in the constant pool. Even if your source code has `"hello"` a hundred times, the constant pool stores it once, but the identifier table and constant references add overhead.
|
||||
|
||||
**Per-function metadata**:
|
||||
Each function - even small one-line functions - gets its own complete metadata:
|
||||
|
||||
- Register allocation info
|
||||
- Code features bitmask
|
||||
- Parse mode
|
||||
- Exception handlers
|
||||
- Expression info for debugging
|
||||
|
||||
A file with 1,000 small functions has 1,000 sets of metadata.
|
||||
|
||||
**Profiling data structures**:
|
||||
Even though profiling data isn't populated yet, the _structures_ to hold profiling data are allocated. This includes:
|
||||
|
||||
- Value profile slots (tracking what types flow through each operation)
|
||||
- Array profile slots (tracking array access patterns)
|
||||
- Binary arithmetic profile slots (tracking number types in math operations)
|
||||
- Unary arithmetic profile slots
|
||||
|
||||
These take up space even when empty.
|
||||
|
||||
**Pre-computed control flow**:
|
||||
Jump targets, switch tables, and exception handler boundaries are all pre-computed and stored. This makes execution faster but increases file size.
|
||||
|
||||
### Mitigation strategies
|
||||
|
||||
**Compression**:
|
||||
Bytecode compresses extremely well with gzip/brotli (60-70% compression). The repetitive structure and metadata compress efficiently.
|
||||
|
||||
**Minification first**:
|
||||
Using `--minify` before bytecode generation helps:
|
||||
|
||||
- Shorter identifiers → smaller identifier table
|
||||
- Dead code elimination → less bytecode generated
|
||||
- Constant folding → fewer constants in the pool
|
||||
|
||||
**The tradeoff**:
|
||||
You're trading 2-4x larger files for 2-4x faster startup. For CLIs, this is usually worth it. For long-running servers where a few megabytes of disk space don't matter, it's even less of an issue.
|
||||
|
||||
## Versioning and portability
|
||||
|
||||
### Cross-architecture portability: ✅
|
||||
|
||||
Bytecode is **architecture-independent**. You can:
|
||||
|
||||
- Build on macOS ARM64, deploy to Linux x64
|
||||
- Build on Linux x64, deploy to AWS Lambda ARM64
|
||||
- Build on Windows x64, deploy to macOS ARM64
|
||||
|
||||
The bytecode contains abstract instructions that work on any architecture. Architecture-specific optimizations happen during JIT compilation at runtime, not in the cached bytecode.
|
||||
|
||||
### Cross-version portability: ❌
|
||||
|
||||
Bytecode is **not stable across Bun versions**. Here's why:
|
||||
|
||||
**Bytecode format changes**:
|
||||
JavaScriptCore's bytecode format evolves. New opcodes get added, old ones get removed or changed, metadata structures change. Each version of JavaScriptCore has a different bytecode format.
|
||||
|
||||
**Version validation**:
|
||||
The cache version in the `.jsc` file header is a hash of the JavaScriptCore framework. When Bun loads bytecode:
|
||||
|
||||
1. It extracts the cache version from the `.jsc` file
|
||||
2. It computes the current JavaScriptCore version
|
||||
3. If they don't match, the bytecode is **silently rejected**
|
||||
4. Bun falls back to parsing the `.js` source code
|
||||
|
||||
Your application still runs - you just lose the performance optimization.
|
||||
|
||||
**Graceful degradation**:
|
||||
This design means bytecode caching "fails open" - if anything goes wrong (version mismatch, corrupted file, missing file), your code still runs normally. You might see slower startup, but you won't see errors.
|
||||
|
||||
## Unlinked vs. linked bytecode
|
||||
|
||||
JavaScriptCore makes a crucial distinction between "unlinked" and "linked" bytecode. This separation is what makes bytecode caching possible:
|
||||
|
||||
### Unlinked bytecode (what's cached)
|
||||
|
||||
The bytecode saved in `.jsc` files is **unlinked bytecode**. It contains:
|
||||
|
||||
- The compiled bytecode instructions
|
||||
- Structural information about the code
|
||||
- Constants and identifiers
|
||||
- Control flow information
|
||||
|
||||
But it **doesn't** contain:
|
||||
|
||||
- Pointers to actual runtime objects
|
||||
- JIT-compiled machine code
|
||||
- Profiling data from previous runs
|
||||
- Call link information (which functions call which)
|
||||
|
||||
Unlinked bytecode is **immutable and shareable**. Multiple executions of the same code can all reference the same unlinked bytecode.
|
||||
|
||||
### Linked bytecode (runtime execution)
|
||||
|
||||
When Bun runs bytecode, it "links" it - creating a runtime wrapper that adds:
|
||||
|
||||
- **Call link information**: As your code runs, the engine learns which functions call which and optimizes those call sites.
|
||||
- **Profiling data**: The engine tracks how many times each instruction executes, what types of values flow through the code, array access patterns, etc.
|
||||
- **JIT compilation state**: References to baseline JIT or optimizing JIT (DFG/FTL) compiled versions of hot code.
|
||||
- **Runtime objects**: Pointers to actual JavaScript objects, prototypes, scopes, etc.
|
||||
|
||||
This linked representation is created fresh every time you run your code. This allows:
|
||||
|
||||
1. **Caching the expensive work** (parsing and compilation to unlinked bytecode)
|
||||
2. **Still collecting runtime profiling data** to guide optimizations
|
||||
3. **Still applying JIT optimizations** based on actual execution patterns
|
||||
|
||||
Bytecode caching moves expensive work (parsing and compiling to bytecode) from runtime to build time. For applications that start frequently, this can halve your startup time at the cost of larger files on disk.
|
||||
|
||||
For production CLIs and serverless deployments, the combination of `--bytecode --minify --sourcemap` gives you the best performance while maintaining debuggability.
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
title: CSS
|
||||
description: Bun's bundler has built-in support for CSS with modern features
|
||||
---
|
||||
|
||||
Bun's bundler has built-in support for CSS with the following features:
|
||||
|
||||
- Transpiling modern/future features to work on all browsers (including vendor prefixing)
|
||||
@@ -14,11 +9,11 @@ Bun's bundler has built-in support for CSS with the following features:
|
||||
|
||||
Bun's CSS bundler lets you use modern/future CSS features without having to worry about browser compatibility — all thanks to its transpiling and vendor prefixing features which are enabled by default.
|
||||
|
||||
Bun's CSS parser and bundler is a direct Rust → Zig port of LightningCSS, with a bundling approach inspired by esbuild. The transpiler converts modern CSS syntax into backwards-compatible equivalents that work across browsers.
|
||||
Bun's CSS parser and bundler is a direct Rust → Zig port of [LightningCSS](https://lightningcss.dev/), with a bundling approach inspired by esbuild. The transpiler converts modern CSS syntax into backwards-compatible equivalents that work across browsers.
|
||||
|
||||
<Note>A huge thanks goes to the amazing work from the authors of LightningCSS and esbuild.</Note>
|
||||
A huge thanks goes to the amazing work from the authors of [LightningCSS](https://lightningcss.dev/) and [esbuild](https://esbuild.github.io/).
|
||||
|
||||
## Browser Compatibility
|
||||
### Browser Compatibility
|
||||
|
||||
By default, Bun's CSS bundler targets the following browsers:
|
||||
|
||||
@@ -28,13 +23,13 @@ By default, Bun's CSS bundler targets the following browsers:
|
||||
- Chrome 87+
|
||||
- Safari 14+
|
||||
|
||||
## Syntax Lowering
|
||||
### Syntax Lowering
|
||||
|
||||
### Nesting
|
||||
#### Nesting
|
||||
|
||||
The CSS Nesting specification allows you to write more concise and intuitive stylesheets by nesting selectors inside one another. Instead of repeating parent selectors across your CSS file, you can write child styles directly within their parent blocks.
|
||||
|
||||
```scss title="styles.css" icon="file-code"
|
||||
```css
|
||||
/* With nesting */
|
||||
.card {
|
||||
background: white;
|
||||
@@ -53,7 +48,7 @@ The CSS Nesting specification allows you to write more concise and intuitive sty
|
||||
|
||||
Bun's CSS bundler automatically converts this nested syntax into traditional flat CSS that works in all browsers:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
/* Compiled output */
|
||||
.card {
|
||||
background: white;
|
||||
@@ -72,7 +67,7 @@ Bun's CSS bundler automatically converts this nested syntax into traditional fla
|
||||
|
||||
You can also nest media queries and other at-rules inside selectors, eliminating the need to repeat selector patterns:
|
||||
|
||||
```scss title="styles.css" icon="file-code"
|
||||
```css
|
||||
.responsive-element {
|
||||
display: block;
|
||||
|
||||
@@ -84,7 +79,7 @@ You can also nest media queries and other at-rules inside selectors, eliminating
|
||||
|
||||
This compiles to:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.responsive-element {
|
||||
display: block;
|
||||
}
|
||||
@@ -96,11 +91,11 @@ This compiles to:
|
||||
}
|
||||
```
|
||||
|
||||
### Color mix
|
||||
#### Color mix
|
||||
|
||||
The `color-mix()` function gives you an easy way to blend two colors together according to a specified ratio in a chosen color space. This powerful feature lets you create color variations without manually calculating the resulting values.
|
||||
|
||||
```scss title="styles.css" icon="file-code"
|
||||
```css
|
||||
.button {
|
||||
/* Mix blue and red in the RGB color space with a 30/70 proportion */
|
||||
background-color: color-mix(in srgb, blue 30%, red);
|
||||
@@ -114,7 +109,7 @@ The `color-mix()` function gives you an easy way to blend two colors together ac
|
||||
|
||||
Bun's CSS bundler evaluates these color mixes at build time when all color values are known (not CSS variables), generating static color values that work in all browsers:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.button {
|
||||
/* Computed to the exact resulting color */
|
||||
background-color: #b31a1a;
|
||||
@@ -127,11 +122,11 @@ Bun's CSS bundler evaluates these color mixes at build time when all color value
|
||||
|
||||
This feature is particularly useful for creating color systems with programmatically derived shades, tints, and accents without needing preprocessors or custom tooling.
|
||||
|
||||
### Relative colors
|
||||
#### Relative colors
|
||||
|
||||
CSS now allows you to modify individual components of a color using relative color syntax. This powerful feature lets you create color variations by adjusting specific attributes like lightness, saturation, or individual channels without having to recalculate the entire color.
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.theme-color {
|
||||
/* Start with a base color and increase lightness by 15% */
|
||||
--accent: lch(from purple calc(l + 15%) c h);
|
||||
@@ -152,23 +147,27 @@ Bun's CSS bundler computes these relative color modifications at build time (whe
|
||||
|
||||
This approach is extremely useful for theme generation, creating accessible color variants, or building color scales based on mathematical relationships instead of hard-coding each value.
|
||||
|
||||
### LAB colors
|
||||
#### LAB colors
|
||||
|
||||
Modern CSS supports perceptually uniform color spaces like LAB, LCH, OKLAB, and OKLCH that offer significant advantages over traditional RGB. These color spaces can represent colors outside the standard RGB gamut, resulting in more vibrant and visually consistent designs.
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.vibrant-element {
|
||||
/* A vibrant red that exceeds sRGB gamut boundaries */
|
||||
color: lab(55% 78 35);
|
||||
|
||||
/* A smooth gradient using perceptual color space */
|
||||
background: linear-gradient(to right, oklch(65% 0.25 10deg), oklch(65% 0.25 250deg));
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
oklch(65% 0.25 10deg),
|
||||
oklch(65% 0.25 250deg)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Bun's CSS bundler automatically converts these advanced color formats to backwards-compatible alternatives for browsers that don't yet support them:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.vibrant-element {
|
||||
/* Fallback to closest RGB approximation */
|
||||
color: #ff0f52;
|
||||
@@ -178,17 +177,21 @@ Bun's CSS bundler automatically converts these advanced color formats to backwar
|
||||
color: lab(55% 78 35);
|
||||
|
||||
background: linear-gradient(to right, #cd4e15, #3887ab);
|
||||
background: linear-gradient(to right, oklch(65% 0.25 10deg), oklch(65% 0.25 250deg));
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
oklch(65% 0.25 10deg),
|
||||
oklch(65% 0.25 250deg)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
This layered approach ensures optimal color rendering across all browsers while allowing you to use the latest color technologies in your designs.
|
||||
|
||||
### Color function
|
||||
#### Color function
|
||||
|
||||
The `color()` function provides a standardized way to specify colors in various predefined color spaces, expanding your design options beyond the traditional RGB space. This allows you to access wider color gamuts and create more vibrant designs.
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.vivid-element {
|
||||
/* Using the Display P3 color space for wider gamut colors */
|
||||
color: color(display-p3 1 0.1 0.3);
|
||||
@@ -200,7 +203,7 @@ The `color()` function provides a standardized way to specify colors in various
|
||||
|
||||
For browsers that don't support these advanced color functions yet, Bun's CSS bundler provides appropriate RGB fallbacks:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.vivid-element {
|
||||
/* RGB fallback first for maximum compatibility */
|
||||
color: #fa1a4c;
|
||||
@@ -214,11 +217,11 @@ For browsers that don't support these advanced color functions yet, Bun's CSS bu
|
||||
|
||||
This functionality lets you use modern color spaces immediately while ensuring your designs remain functional across all browsers, with optimal colors displayed in supporting browsers and reasonable approximations elsewhere.
|
||||
|
||||
### HWB colors
|
||||
#### HWB colors
|
||||
|
||||
The HWB (Hue, Whiteness, Blackness) color model provides an intuitive way to express colors based on how much white or black is mixed with a pure hue. Many designers find this approach more natural for creating color variations compared to manipulating RGB or HSL values.
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.easy-theming {
|
||||
/* Pure cyan with no white or black added */
|
||||
--primary: hwb(180 0% 0%);
|
||||
@@ -236,7 +239,7 @@ The HWB (Hue, Whiteness, Blackness) color model provides an intuitive way to exp
|
||||
|
||||
Bun's CSS bundler automatically converts HWB colors to RGB for compatibility with all browsers:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.easy-theming {
|
||||
--primary: #00ffff;
|
||||
--primary-light: #33ffff;
|
||||
@@ -247,11 +250,11 @@ Bun's CSS bundler automatically converts HWB colors to RGB for compatibility wit
|
||||
|
||||
The HWB model makes it particularly easy to create systematic color variations for design systems, providing a more intuitive approach to creating consistent tints and shades than working directly with RGB or HSL values.
|
||||
|
||||
### Color notation
|
||||
#### Color notation
|
||||
|
||||
Modern CSS has introduced more intuitive and concise ways to express colors. Space-separated color syntax eliminates the need for commas in RGB and HSL values, while hex colors with alpha channels provide a compact way to specify transparency.
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.modern-styling {
|
||||
/* Space-separated RGB notation (no commas) */
|
||||
color: rgb(50 100 200);
|
||||
@@ -269,7 +272,7 @@ Modern CSS has introduced more intuitive and concise ways to express colors. Spa
|
||||
|
||||
Bun's CSS bundler automatically converts these modern color formats to ensure compatibility with older browsers:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.modern-styling {
|
||||
/* Converted to comma format for older browsers */
|
||||
color: rgb(50, 100, 200);
|
||||
@@ -286,11 +289,11 @@ Bun's CSS bundler automatically converts these modern color formats to ensure co
|
||||
|
||||
This conversion process lets you write cleaner, more modern CSS while ensuring your styles work correctly across all browsers.
|
||||
|
||||
### light-dark() color function
|
||||
#### light-dark() color function
|
||||
|
||||
The `light-dark()` function provides an elegant solution for implementing color schemes that respect the user's system preference without requiring complex media queries. This function accepts two color values and automatically selects the appropriate one based on the current color scheme context.
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
:root {
|
||||
/* Define color scheme support */
|
||||
color-scheme: light dark;
|
||||
@@ -315,7 +318,7 @@ The `light-dark()` function provides an elegant solution for implementing color
|
||||
|
||||
For browsers that don't support this feature yet, Bun's CSS bundler converts it to use CSS variables with proper fallbacks:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
:root {
|
||||
--lightningcss-light: initial;
|
||||
--lightningcss-dark: ;
|
||||
@@ -342,19 +345,21 @@ For browsers that don't support this feature yet, Bun's CSS bundler converts it
|
||||
}
|
||||
|
||||
.themed-component {
|
||||
background-color: var(--lightningcss-light, #ffffff) var(--lightningcss-dark, #121212);
|
||||
background-color: var(--lightningcss-light, #ffffff)
|
||||
var(--lightningcss-dark, #121212);
|
||||
color: var(--lightningcss-light, #333333) var(--lightningcss-dark, #eeeeee);
|
||||
border-color: var(--lightningcss-light, #dddddd) var(--lightningcss-dark, #555555);
|
||||
border-color: var(--lightningcss-light, #dddddd)
|
||||
var(--lightningcss-dark, #555555);
|
||||
}
|
||||
```
|
||||
|
||||
This approach gives you a clean way to handle light and dark themes without duplicating styles or writing complex media queries, while maintaining compatibility with browsers that don't yet support the feature natively.
|
||||
|
||||
### Logical properties
|
||||
#### Logical properties
|
||||
|
||||
CSS logical properties let you define layout, spacing, and sizing relative to the document's writing mode and text direction rather than physical screen directions. This is crucial for creating truly international layouts that automatically adapt to different writing systems.
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.multilingual-component {
|
||||
/* Margin that adapts to writing direction */
|
||||
margin-inline-start: 1rem;
|
||||
@@ -373,7 +378,7 @@ CSS logical properties let you define layout, spacing, and sizing relative to th
|
||||
|
||||
For browsers that don't fully support logical properties, Bun's CSS bundler compiles them to physical properties with appropriate directional adjustments:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
/* For left-to-right languages */
|
||||
.multilingual-component:dir(ltr) {
|
||||
margin-left: 1rem;
|
||||
@@ -397,11 +402,11 @@ For browsers that don't fully support logical properties, Bun's CSS bundler comp
|
||||
|
||||
If the `:dir()` selector isn't supported, additional fallbacks are automatically generated to ensure your layouts work properly across all browsers and writing systems. This makes creating internationalized designs much simpler while maintaining compatibility with older browsers.
|
||||
|
||||
### :dir() selector
|
||||
#### :dir() selector
|
||||
|
||||
The `:dir()` pseudo-class selector allows you to style elements based on their text direction (RTL or LTR), providing a powerful way to create direction-aware designs without JavaScript. This selector matches elements based on their directionality as determined by the document or explicit direction attributes.
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
/* Apply different styles based on text direction */
|
||||
.nav-arrow:dir(ltr) {
|
||||
transform: rotate(0deg);
|
||||
@@ -423,7 +428,7 @@ The `:dir()` pseudo-class selector allows you to style elements based on their t
|
||||
|
||||
For browsers that don't support the `:dir()` selector yet, Bun's CSS bundler converts it to the more widely supported `:lang()` selector with appropriate language mappings:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
/* Converted to use language-based selectors as fallback */
|
||||
.nav-arrow:lang(en, fr, de, es, it, pt, nl) {
|
||||
transform: rotate(0deg);
|
||||
@@ -444,11 +449,11 @@ For browsers that don't support the `:dir()` selector yet, Bun's CSS bundler con
|
||||
|
||||
This conversion lets you write direction-aware CSS that works reliably across browsers, even those that don't yet support the `:dir()` selector natively. If multiple arguments to `:lang()` aren't supported, further fallbacks are automatically provided.
|
||||
|
||||
### :lang() selector
|
||||
#### :lang() selector
|
||||
|
||||
The `:lang()` pseudo-class selector allows you to target elements based on the language they're in, making it easy to apply language-specific styling. Modern CSS allows the `:lang()` selector to accept multiple language codes, letting you group language-specific rules more efficiently.
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
/* Typography adjustments for CJK languages */
|
||||
:lang(zh, ja, ko) {
|
||||
line-height: 1.8;
|
||||
@@ -467,7 +472,7 @@ blockquote:lang(de, nl, da, sv) {
|
||||
|
||||
For browsers that don't support multiple arguments in the `:lang()` selector, Bun's CSS bundler converts this syntax to use the `:is()` selector to maintain the same behavior:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
/* Multiple languages grouped with :is() for better browser support */
|
||||
:is(:lang(zh), :lang(ja), :lang(ko)) {
|
||||
line-height: 1.8;
|
||||
@@ -485,11 +490,11 @@ blockquote:is(:lang(de), :lang(nl), :lang(da), :lang(sv)) {
|
||||
|
||||
If needed, Bun can provide additional fallbacks for `:is()` as well, ensuring your language-specific styles work across all browsers. This approach simplifies creating internationalized designs with distinct typographic and styling rules for different language groups.
|
||||
|
||||
### :is() selector
|
||||
#### :is() selector
|
||||
|
||||
The `:is()` pseudo-class function (formerly `:matches()`) allows you to create more concise and readable selectors by grouping multiple selectors together. It accepts a selector list as its argument and matches if any of the selectors in that list match, significantly reducing repetition in your CSS.
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
/* Instead of writing these separately */
|
||||
/*
|
||||
.article h1,
|
||||
@@ -542,17 +547,13 @@ For browsers that don't support `:is()`, Bun's CSS bundler provides fallbacks us
|
||||
}
|
||||
```
|
||||
|
||||
<Warning>
|
||||
The vendor-prefixed versions have some limitations compared to the standardized `:is()` selector, particularly with
|
||||
complex selectors. Bun handles these limitations intelligently, only using prefixed versions when they'll work
|
||||
correctly.
|
||||
</Warning>
|
||||
It's worth noting that the vendor-prefixed versions have some limitations compared to the standardized `:is()` selector, particularly with complex selectors. Bun handles these limitations intelligently, only using prefixed versions when they'll work correctly.
|
||||
|
||||
### :not() selector
|
||||
#### :not() selector
|
||||
|
||||
The `:not()` pseudo-class allows you to exclude elements that match a specific selector. The modern version of this selector accepts multiple arguments, letting you exclude multiple patterns with a single, concise selector.
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
/* Select all buttons except primary and secondary variants */
|
||||
button:not(.primary, .secondary) {
|
||||
background-color: #f5f5f5;
|
||||
@@ -567,7 +568,7 @@ h2:not(.sidebar *, footer *) {
|
||||
|
||||
For browsers that don't support multiple arguments in `:not()`, Bun's CSS bundler converts this syntax to a more compatible form while preserving the same behavior:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
/* Converted to use :not with :is() for compatibility */
|
||||
button:not(:is(.primary, .secondary)) {
|
||||
background-color: #f5f5f5;
|
||||
@@ -581,7 +582,7 @@ h2:not(:is(.sidebar *, footer *)) {
|
||||
|
||||
And if `:is()` isn't supported, Bun can generate further fallbacks:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
/* Even more fallbacks for maximum compatibility */
|
||||
button:not(:-webkit-any(.primary, .secondary)) {
|
||||
background-color: #f5f5f5;
|
||||
@@ -601,11 +602,11 @@ button:not(:is(.primary, .secondary)) {
|
||||
|
||||
This conversion ensures your negative selectors work correctly across all browsers while maintaining the correct specificity and behavior of the original selector.
|
||||
|
||||
### Math functions
|
||||
#### Math functions
|
||||
|
||||
CSS now includes a rich set of mathematical functions that let you perform complex calculations directly in your stylesheets. These include standard math functions (`round()`, `mod()`, `rem()`, `abs()`, `sign()`), trigonometric functions (`sin()`, `cos()`, `tan()`, `asin()`, `acos()`, `atan()`, `atan2()`), and exponential functions (`pow()`, `sqrt()`, `exp()`, `log()`, `hypot()`).
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.dynamic-sizing {
|
||||
/* Clamp a value between minimum and maximum */
|
||||
width: clamp(200px, 50%, 800px);
|
||||
@@ -624,7 +625,7 @@ CSS now includes a rich set of mathematical functions that let you perform compl
|
||||
|
||||
Bun's CSS bundler evaluates these mathematical expressions at build time when all values are known constants (not variables), resulting in optimized output:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.dynamic-sizing {
|
||||
width: clamp(200px, 50%, 800px);
|
||||
padding: 15px;
|
||||
@@ -636,11 +637,11 @@ Bun's CSS bundler evaluates these mathematical expressions at build time when al
|
||||
|
||||
This approach lets you write more expressive and maintainable CSS with meaningful mathematical relationships, which then gets compiled to optimized values for maximum browser compatibility and performance.
|
||||
|
||||
### Media query ranges
|
||||
#### Media query ranges
|
||||
|
||||
Modern CSS supports intuitive range syntax for media queries, allowing you to specify breakpoints using comparison operators like `<`, `>`, `<=`, and `>=` instead of the more verbose `min-` and `max-` prefixes. This syntax is more readable and matches how we normally think about values and ranges.
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
/* Modern syntax with comparison operators */
|
||||
@media (width >= 768px) {
|
||||
.container {
|
||||
@@ -665,7 +666,7 @@ Modern CSS supports intuitive range syntax for media queries, allowing you to sp
|
||||
|
||||
Bun's CSS bundler converts these modern range queries to traditional media query syntax for compatibility with all browsers:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
/* Converted to traditional min/max syntax */
|
||||
@media (min-width: 768px) {
|
||||
.container {
|
||||
@@ -688,11 +689,11 @@ Bun's CSS bundler converts these modern range queries to traditional media query
|
||||
|
||||
This lets you write more intuitive and mathematical media queries while ensuring your stylesheets work correctly across all browsers, including those that don't support the modern range syntax.
|
||||
|
||||
### Shorthands
|
||||
#### Shorthands
|
||||
|
||||
CSS has introduced several modern shorthand properties that improve code readability and maintainability. Bun's CSS bundler ensures these convenient shorthands work on all browsers by converting them to their longhand equivalents when needed.
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
/* Alignment shorthands */
|
||||
.flex-container {
|
||||
/* Shorthand for align-items and justify-items */
|
||||
@@ -728,7 +729,7 @@ CSS has introduced several modern shorthand properties that improve code readabi
|
||||
|
||||
For browsers that don't support these modern shorthands, Bun converts them to their component longhand properties:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.flex-container {
|
||||
/* Expanded alignment properties */
|
||||
align-items: center;
|
||||
@@ -765,11 +766,11 @@ For browsers that don't support these modern shorthands, Bun converts them to th
|
||||
|
||||
This conversion ensures your stylesheets remain clean and maintainable while providing the broadest possible browser compatibility.
|
||||
|
||||
### Double position gradients
|
||||
#### Double position gradients
|
||||
|
||||
The double position gradient syntax is a modern CSS feature that allows you to create hard color stops in gradients by specifying the same color at two adjacent positions. This creates a sharp transition rather than a smooth fade, which is useful for creating stripes, color bands, and other multi-color designs.
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.striped-background {
|
||||
/* Creates a sharp transition from green to red at 30%-40% */
|
||||
background: linear-gradient(
|
||||
@@ -798,7 +799,7 @@ The double position gradient syntax is a modern CSS feature that allows you to c
|
||||
|
||||
For browsers that don't support this syntax, Bun's CSS bundler automatically converts it to the traditional format by duplicating color stops:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.striped-background {
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
@@ -829,11 +830,11 @@ For browsers that don't support this syntax, Bun's CSS bundler automatically con
|
||||
|
||||
This conversion lets you use the cleaner double position syntax in your source code while ensuring gradients display correctly in all browsers.
|
||||
|
||||
### system-ui font
|
||||
#### system-ui font
|
||||
|
||||
The `system-ui` generic font family lets you use the device's native UI font, creating interfaces that feel more integrated with the operating system. This provides a more native look and feel without having to specify different font stacks for each platform.
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.native-interface {
|
||||
/* Use the system's default UI font */
|
||||
font-family: system-ui;
|
||||
@@ -847,7 +848,7 @@ The `system-ui` generic font family lets you use the device's native UI font, cr
|
||||
|
||||
For browsers that don't support `system-ui`, Bun's CSS bundler automatically expands it to a comprehensive cross-platform font stack:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```css
|
||||
.native-interface {
|
||||
/* Expanded to support all major platforms */
|
||||
font-family:
|
||||
@@ -882,7 +883,7 @@ This approach gives you the simplicity of writing just `system-ui` in your sourc
|
||||
|
||||
## CSS Modules
|
||||
|
||||
Bun's bundler also supports bundling CSS modules in addition to regular CSS with support for the following features:
|
||||
Bun's bundler also supports bundling [CSS modules](https://css-tricks.com/css-modules-part-1-need/) in addition to [regular CSS](/docs/bundler/css) with support for the following features:
|
||||
|
||||
- Automatically detecting CSS module files (`.module.css`) with zero configuration
|
||||
- Composition (`composes` property)
|
||||
@@ -893,17 +894,17 @@ A CSS module is a CSS file (with the `.module.css` extension) where are all clas
|
||||
|
||||
Under the hood, Bun's bundler transforms locally scoped class names into unique identifiers.
|
||||
|
||||
### Getting started
|
||||
## Getting started
|
||||
|
||||
Create a CSS file with the `.module.css` extension:
|
||||
|
||||
```css title="styles.module.css" icon="file-code"
|
||||
```css
|
||||
/* styles.module.css */
|
||||
.button {
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
|
||||
```css title="other-styles.module.css" icon="file-code"
|
||||
/* other-styles.module.css */
|
||||
.button {
|
||||
color: blue;
|
||||
}
|
||||
@@ -911,7 +912,7 @@ Create a CSS file with the `.module.css` extension:
|
||||
|
||||
You can then import this file, for example into a TSX file:
|
||||
|
||||
```tsx title="app.tsx" icon="/icons/typescript.svg"
|
||||
```tsx
|
||||
import styles from "./styles.module.css";
|
||||
import otherStyles from "./other-styles.module.css";
|
||||
|
||||
@@ -925,9 +926,10 @@ export default function App() {
|
||||
}
|
||||
```
|
||||
|
||||
The styles object from importing the CSS module file will be an object with all class names as keys and their unique identifiers as values:
|
||||
The `styles` object from importing the CSS module file will be an object with all class names as keys and
|
||||
their unique identifiers as values:
|
||||
|
||||
```ts title="app.tsx" icon="/icons/typescript.svg"
|
||||
```tsx
|
||||
import styles from "./styles.module.css";
|
||||
import otherStyles from "./other-styles.module.css";
|
||||
|
||||
@@ -937,7 +939,7 @@ console.log(otherStyles);
|
||||
|
||||
This will output:
|
||||
|
||||
```ts title="app.tsx" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
{
|
||||
button: "button_123";
|
||||
}
|
||||
@@ -951,11 +953,12 @@ As you can see, the class names are unique to each file, avoiding any collisions
|
||||
|
||||
### Composition
|
||||
|
||||
CSS modules allow you to compose class selectors together. This lets you reuse style rules across multiple classes.
|
||||
CSS modules allow you to _compose_ class selectors together. This lets you reuse style rules across multiple classes.
|
||||
|
||||
For example:
|
||||
|
||||
```css title="styles.module.css" icon="file-code"
|
||||
```css
|
||||
/* styles.module.css */
|
||||
.button {
|
||||
composes: background;
|
||||
color: red;
|
||||
@@ -968,7 +971,7 @@ For example:
|
||||
|
||||
Would be the same as writing:
|
||||
|
||||
```css title="styles.module.css" icon="file-code"
|
||||
```css
|
||||
.button {
|
||||
background-color: blue;
|
||||
color: red;
|
||||
@@ -979,14 +982,13 @@ Would be the same as writing:
|
||||
}
|
||||
```
|
||||
|
||||
{% callout %}
|
||||
There are a couple rules to keep in mind when using `composes`:
|
||||
|
||||
<Info>
|
||||
**Composition Rules:** - A `composes` property must come before any regular CSS properties or declarations - You can
|
||||
only use `composes` on a simple selector with a single class name
|
||||
</Info>
|
||||
- A `composes` property must come before any regular CSS properties or declarations
|
||||
- You can only use `composes` on a **simple selector with a single class name**:
|
||||
|
||||
```css title="styles.module.css" icon="file-code"
|
||||
```css
|
||||
#button {
|
||||
/* Invalid! `#button` is not a class selector */
|
||||
composes: background;
|
||||
@@ -999,26 +1001,28 @@ There are a couple rules to keep in mind when using `composes`:
|
||||
}
|
||||
```
|
||||
|
||||
{% /callout %}
|
||||
|
||||
### Composing from a separate CSS module file
|
||||
|
||||
You can also compose from a separate CSS module file:
|
||||
|
||||
```css title="background.module.css" icon="file-code"
|
||||
```css
|
||||
/* background.module.css */
|
||||
.background {
|
||||
background-color: blue;
|
||||
}
|
||||
```
|
||||
|
||||
```css title="styles.module.css" icon="file-code"
|
||||
/* styles.module.css */
|
||||
.button {
|
||||
composes: background from "./background.module.css";
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
|
||||
<Warning>
|
||||
{% callout %}
|
||||
When composing classes from separate files, be sure that they do not contain the same properties.
|
||||
|
||||
The CSS module spec says that composing classes from separate files with conflicting properties is undefined behavior, meaning that the output may differ and be unreliable.
|
||||
|
||||
</Warning>
|
||||
The CSS module spec says that composing classes from separate files with conflicting properties is
|
||||
undefined behavior, meaning that the output may differ and be unreliable.
|
||||
{% /callout %}
|
||||
145
docs/bundler/css_modules.md
Normal file
145
docs/bundler/css_modules.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# CSS Modules
|
||||
|
||||
Bun's bundler also supports bundling [CSS modules](https://css-tricks.com/css-modules-part-1-need/) in addition to [regular CSS](/docs/bundler/css) with support for the following features:
|
||||
|
||||
- Automatically detecting CSS module files (`.module.css`) with zero configuration
|
||||
- Composition (`composes` property)
|
||||
- Importing CSS modules into JSX/TSX
|
||||
- Warnings/errors for invalid usages of CSS modules
|
||||
|
||||
A CSS module is a CSS file (with the `.module.css` extension) where are all class names and animations are scoped to the file. This helps you avoid class name collisions as CSS declarations are globally scoped by default.
|
||||
|
||||
Under the hood, Bun's bundler transforms locally scoped class names into unique identifiers.
|
||||
|
||||
## Getting started
|
||||
|
||||
Create a CSS file with the `.module.css` extension:
|
||||
|
||||
```css
|
||||
/* styles.module.css */
|
||||
.button {
|
||||
color: red;
|
||||
}
|
||||
|
||||
/* other-styles.module.css */
|
||||
.button {
|
||||
color: blue;
|
||||
}
|
||||
```
|
||||
|
||||
You can then import this file, for example into a TSX file:
|
||||
|
||||
```tsx
|
||||
import styles from "./styles.module.css";
|
||||
import otherStyles from "./other-styles.module.css";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<>
|
||||
<button className={styles.button}>Red button!</button>
|
||||
<button className={otherStyles.button}>Blue button!</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
The `styles` object from importing the CSS module file will be an object with all class names as keys and
|
||||
their unique identifiers as values:
|
||||
|
||||
```tsx
|
||||
import styles from "./styles.module.css";
|
||||
import otherStyles from "./other-styles.module.css";
|
||||
|
||||
console.log(styles);
|
||||
console.log(otherStyles);
|
||||
```
|
||||
|
||||
This will output:
|
||||
|
||||
```ts
|
||||
{
|
||||
button: "button_123";
|
||||
}
|
||||
|
||||
{
|
||||
button: "button_456";
|
||||
}
|
||||
```
|
||||
|
||||
As you can see, the class names are unique to each file, avoiding any collisions!
|
||||
|
||||
### Composition
|
||||
|
||||
CSS modules allow you to _compose_ class selectors together. This lets you reuse style rules across multiple classes.
|
||||
|
||||
For example:
|
||||
|
||||
```css
|
||||
/* styles.module.css */
|
||||
.button {
|
||||
composes: background;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.background {
|
||||
background-color: blue;
|
||||
}
|
||||
```
|
||||
|
||||
Would be the same as writing:
|
||||
|
||||
```css
|
||||
.button {
|
||||
background-color: blue;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.background {
|
||||
background-color: blue;
|
||||
}
|
||||
```
|
||||
|
||||
{% callout %}
|
||||
There are a couple rules to keep in mind when using `composes`:
|
||||
|
||||
- A `composes` property must come before any regular CSS properties or declarations
|
||||
- You can only use `composes` on a **simple selector with a single class name**:
|
||||
|
||||
```css
|
||||
#button {
|
||||
/* Invalid! `#button` is not a class selector */
|
||||
composes: background;
|
||||
}
|
||||
|
||||
.button,
|
||||
.button-secondary {
|
||||
/* Invalid! `.button, .button-secondary` is not a simple selector */
|
||||
composes: background;
|
||||
}
|
||||
```
|
||||
|
||||
{% /callout %}
|
||||
|
||||
### Composing from a separate CSS module file
|
||||
|
||||
You can also compose from a separate CSS module file:
|
||||
|
||||
```css
|
||||
/* background.module.css */
|
||||
.background {
|
||||
background-color: blue;
|
||||
}
|
||||
|
||||
/* styles.module.css */
|
||||
.button {
|
||||
composes: background from "./background.module.css";
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
|
||||
{% callout %}
|
||||
When composing classes from separate files, be sure that they do not contain the same properties.
|
||||
|
||||
The CSS module spec says that composing classes from separate files with conflicting properties is
|
||||
undefined behavior, meaning that the output may differ and be unreliable.
|
||||
{% /callout %}
|
||||
@@ -1,297 +0,0 @@
|
||||
---
|
||||
title: esbuild
|
||||
description: Migration guide from esbuild to Bun's bundler
|
||||
---
|
||||
|
||||
Bun's bundler API is inspired heavily by esbuild. Migrating to Bun's bundler from esbuild should be relatively painless. This guide will briefly explain why you might consider migrating to Bun's bundler and provide a side-by-side API comparison reference for those who are already familiar with esbuild's API.
|
||||
|
||||
There are a few behavioral differences to note.
|
||||
|
||||
<Note>
|
||||
**Bundling by default.** Unlike esbuild, Bun always bundles by default. This is why the `--bundle` flag isn't
|
||||
necessary in the Bun example. To transpile each file individually, use `Bun.Transpiler`.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
**It's just a bundler.** Unlike esbuild, Bun's bundler does not include a built-in development server or file watcher.
|
||||
It's just a bundler. The bundler is intended for use in conjunction with `Bun.serve` and other runtime APIs to achieve
|
||||
the same effect. As such, all options relating to HTTP/file watching are not applicable.
|
||||
</Note>
|
||||
|
||||
## Performance
|
||||
|
||||
With a performance-minded API coupled with the extensively optimized Zig-based JS/TS parser, Bun's bundler is 1.75x faster than esbuild on esbuild's three.js benchmark.
|
||||
|
||||
<Info>Bundling 10 copies of three.js from scratch, with sourcemaps and minification</Info>
|
||||
|
||||
## CLI API
|
||||
|
||||
Bun and esbuild both provide a command-line interface.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
# esbuild
|
||||
esbuild <entrypoint> --outdir=out --bundle
|
||||
|
||||
# bun
|
||||
bun build <entrypoint> --outdir=out
|
||||
```
|
||||
|
||||
In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Other flags like `--outdir <path>` do accept an argument; these flags can be written as `--outdir out` or `--outdir=out`. Some flags like `--define` can be specified several times: `--define foo=bar --define bar=baz`.
|
||||
|
||||
| esbuild | bun build | Notes |
|
||||
| ---------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `--bundle` | n/a | Bun always bundles, use `--no-bundle` to disable this behavior. |
|
||||
| `--define:K=V` | `--define K=V` | Small syntax difference; no colon.<br/>`esbuild --define:foo=bar`<br/>`bun build --define foo=bar` |
|
||||
| `--external:<pkg>` | `--external <pkg>` | Small syntax difference; no colon.<br/>`esbuild --external:react`<br/>`bun build --external react` |
|
||||
| `--format` | `--format` | Bun supports `"esm"` and `"cjs"` currently, but more module formats are planned. esbuild defaults to `"iife"`. |
|
||||
| `--loader:.ext=loader` | `--loader .ext:loader` | Bun supports a different set of built-in loaders than esbuild; see Bundler > Loaders for a complete reference. The esbuild loaders `dataurl`, `binary`, `base64`, `copy`, and `empty` are not yet implemented.<br/><br/>The syntax for `--loader` is slightly different.<br/>`esbuild app.ts --bundle --loader:.svg=text`<br/>`bun build app.ts --loader .svg:text` |
|
||||
| `--minify` | `--minify` | No differences |
|
||||
| `--outdir` | `--outdir` | No differences |
|
||||
| `--outfile` | `--outfile` | No differences |
|
||||
| `--packages` | `--packages` | No differences |
|
||||
| `--platform` | `--target` | Renamed to `--target` for consistency with tsconfig. Does not support `neutral`. |
|
||||
| `--serve` | n/a | Not applicable |
|
||||
| `--sourcemap` | `--sourcemap` | No differences |
|
||||
| `--splitting` | `--splitting` | No differences |
|
||||
| `--target` | n/a | Not supported. Bun's bundler performs no syntactic down-leveling at this time. |
|
||||
| `--watch` | `--watch` | No differences |
|
||||
| `--allow-overwrite` | n/a | Overwriting is never allowed |
|
||||
| `--analyze` | n/a | Not supported |
|
||||
| `--asset-names` | `--asset-naming` | Renamed for consistency with naming in JS API |
|
||||
| `--banner` | `--banner` | Only applies to js bundles |
|
||||
| `--footer` | `--footer` | Only applies to js bundles |
|
||||
| `--certfile` | n/a | Not applicable |
|
||||
| `--charset=utf8` | n/a | Not supported |
|
||||
| `--chunk-names` | `--chunk-naming` | Renamed for consistency with naming in JS API |
|
||||
| `--color` | n/a | Always enabled |
|
||||
| `--drop` | `--drop` | |
|
||||
| `--entry-names` | `--entry-naming` | Renamed for consistency with naming in JS API |
|
||||
| `--global-name` | n/a | Not applicable, Bun does not support `iife` output at this time |
|
||||
| `--ignore-annotations` | `--ignore-dce-annotations` | |
|
||||
| `--inject` | n/a | Not supported |
|
||||
| `--jsx` | `--jsx-runtime <runtime>` | Supports `"automatic"` (uses jsx transform) and `"classic"` (uses `React.createElement`) |
|
||||
| `--jsx-dev` | n/a | Bun reads `compilerOptions.jsx` from `tsconfig.json` to determine a default. If `compilerOptions.jsx` is `"react-jsx"`, or if `NODE_ENV=production`, Bun will use the jsx transform. Otherwise, it uses `jsxDEV`. The bundler does not support `preserve`. |
|
||||
| `--jsx-factory` | `--jsx-factory` | |
|
||||
| `--jsx-fragment` | `--jsx-fragment` | |
|
||||
| `--jsx-import-source` | `--jsx-import-source` | |
|
||||
| `--jsx-side-effects` | n/a | JSX is always assumed to be side-effect-free |
|
||||
| `--keep-names` | n/a | Not supported |
|
||||
| `--keyfile` | n/a | Not applicable |
|
||||
| `--legal-comments` | n/a | Not supported |
|
||||
| `--log-level` | n/a | Not supported. This can be set in `bunfig.toml` as `logLevel`. |
|
||||
| `--log-limit` | n/a | Not supported |
|
||||
| `--log-override:X=Y` | n/a | Not supported |
|
||||
| `--main-fields` | n/a | Not supported |
|
||||
| `--mangle-cache` | n/a | Not supported |
|
||||
| `--mangle-props` | n/a | Not supported |
|
||||
| `--mangle-quoted` | n/a | Not supported |
|
||||
| `--metafile` | n/a | Not supported |
|
||||
| `--minify-whitespace` | `--minify-whitespace` | |
|
||||
| `--minify-identifiers` | `--minify-identifiers` | |
|
||||
| `--minify-syntax` | `--minify-syntax` | |
|
||||
| `--out-extension` | n/a | Not supported |
|
||||
| `--outbase` | `--root` | |
|
||||
| `--preserve-symlinks` | n/a | Not supported |
|
||||
| `--public-path` | `--public-path` | |
|
||||
| `--pure` | n/a | Not supported |
|
||||
| `--reserve-props` | n/a | Not supported |
|
||||
| `--resolve-extensions` | n/a | Not supported |
|
||||
| `--servedir` | n/a | Not applicable |
|
||||
| `--source-root` | n/a | Not supported |
|
||||
| `--sourcefile` | n/a | Not supported. Bun does not support stdin input yet. |
|
||||
| `--sourcemap` | `--sourcemap` | No differences |
|
||||
| `--sources-content` | n/a | Not supported |
|
||||
| `--supported` | n/a | Not supported |
|
||||
| `--tree-shaking` | n/a | Always true |
|
||||
| `--tsconfig` | `--tsconfig-override` | |
|
||||
| `--version` | n/a | Run `bun --version` to see the version of Bun. |
|
||||
|
||||
## JavaScript API
|
||||
|
||||
| esbuild.build() | Bun.build() | Notes |
|
||||
| ------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `absWorkingDir` | n/a | Always set to `process.cwd()` |
|
||||
| `alias` | n/a | Not supported |
|
||||
| `allowOverwrite` | n/a | Always false |
|
||||
| `assetNames` | `naming.asset` | Uses same templating syntax as esbuild, but `[ext]` must be included explicitly.<br/><br/>`ts<br/>Bun.build({<br/> entrypoints: ["./index.tsx"],<br/> naming: {<br/> asset: "[name].[ext]",<br/> },<br/>});<br/>` |
|
||||
| `banner` | n/a | Not supported |
|
||||
| `bundle` | n/a | Always true. Use `Bun.Transpiler` to transpile without bundling. |
|
||||
| `charset` | n/a | Not supported |
|
||||
| `chunkNames` | `naming.chunk` | Uses same templating syntax as esbuild, but `[ext]` must be included explicitly.<br/><br/>`ts<br/>Bun.build({<br/> entrypoints: ["./index.tsx"],<br/> naming: {<br/> chunk: "[name].[ext]",<br/> },<br/>});<br/>` |
|
||||
| `color` | n/a | Bun returns logs in the `logs` property of the build result. |
|
||||
| `conditions` | n/a | Not supported. Export conditions priority is determined by `target`. |
|
||||
| `define` | `define` | |
|
||||
| `drop` | n/a | Not supported |
|
||||
| `entryNames` | `naming` or `naming.entry` | Bun supports a `naming` key that can either be a string or an object. Uses same templating syntax as esbuild, but `[ext]` must be included explicitly.<br/><br/>`ts<br/>Bun.build({<br/> entrypoints: ["./index.tsx"],<br/> // when string, this is equivalent to entryNames<br/> naming: "[name].[ext]",<br/><br/> // granular naming options<br/> naming: {<br/> entry: "[name].[ext]",<br/> asset: "[name].[ext]",<br/> chunk: "[name].[ext]",<br/> },<br/>});<br/>` |
|
||||
| `entryPoints` | `entrypoints` | Capitalization difference |
|
||||
| `external` | `external` | No differences |
|
||||
| `footer` | n/a | Not supported |
|
||||
| `format` | `format` | Only supports `"esm"` currently. Support for `"cjs"` and `"iife"` is planned. |
|
||||
| `globalName` | n/a | Not supported |
|
||||
| `ignoreAnnotations` | n/a | Not supported |
|
||||
| `inject` | n/a | Not supported |
|
||||
| `jsx` | `jsx` | Not supported in JS API, configure in `tsconfig.json` |
|
||||
| `jsxDev` | `jsxDev` | Not supported in JS API, configure in `tsconfig.json` |
|
||||
| `jsxFactory` | `jsxFactory` | Not supported in JS API, configure in `tsconfig.json` |
|
||||
| `jsxFragment` | `jsxFragment` | Not supported in JS API, configure in `tsconfig.json` |
|
||||
| `jsxImportSource` | `jsxImportSource` | Not supported in JS API, configure in `tsconfig.json` |
|
||||
| `jsxSideEffects` | `jsxSideEffects` | Not supported in JS API, configure in `tsconfig.json` |
|
||||
| `keepNames` | n/a | Not supported |
|
||||
| `legalComments` | n/a | Not supported |
|
||||
| `loader` | `loader` | Bun supports a different set of built-in loaders than esbuild; see Bundler > Loaders for a complete reference. The esbuild loaders `dataurl`, `binary`, `base64`, `copy`, and `empty` are not yet implemented. |
|
||||
| `logLevel` | n/a | Not supported |
|
||||
| `logLimit` | n/a | Not supported |
|
||||
| `logOverride` | n/a | Not supported |
|
||||
| `mainFields` | n/a | Not supported |
|
||||
| `mangleCache` | n/a | Not supported |
|
||||
| `mangleProps` | n/a | Not supported |
|
||||
| `mangleQuoted` | n/a | Not supported |
|
||||
| `metafile` | n/a | Not supported |
|
||||
| `minify` | `minify` | In Bun, `minify` can be a boolean or an object.<br/><br/>`ts<br/>await Bun.build({<br/> entrypoints: ['./index.tsx'],<br/> // enable all minification<br/> minify: true<br/><br/> // granular options<br/> minify: {<br/> identifiers: true,<br/> syntax: true,<br/> whitespace: true<br/> }<br/>})<br/>` |
|
||||
| `minifyIdentifiers` | `minify.identifiers` | See `minify` |
|
||||
| `minifySyntax` | `minify.syntax` | See `minify` |
|
||||
| `minifyWhitespace` | `minify.whitespace` | See `minify` |
|
||||
| `nodePaths` | n/a | Not supported |
|
||||
| `outExtension` | n/a | Not supported |
|
||||
| `outbase` | `root` | Different name |
|
||||
| `outdir` | `outdir` | No differences |
|
||||
| `outfile` | `outfile` | No differences |
|
||||
| `packages` | n/a | Not supported, use `external` |
|
||||
| `platform` | `target` | Supports `"bun"`, `"node"` and `"browser"` (the default). Does not support `"neutral"`. |
|
||||
| `plugins` | `plugins` | Bun's plugin API is a subset of esbuild's. Some esbuild plugins will work out of the box with Bun. |
|
||||
| `preserveSymlinks` | n/a | Not supported |
|
||||
| `publicPath` | `publicPath` | No differences |
|
||||
| `pure` | n/a | Not supported |
|
||||
| `reserveProps` | n/a | Not supported |
|
||||
| `resolveExtensions` | n/a | Not supported |
|
||||
| `sourceRoot` | n/a | Not supported |
|
||||
| `sourcemap` | `sourcemap` | Supports `"inline"`, `"external"`, and `"none"` |
|
||||
| `sourcesContent` | n/a | Not supported |
|
||||
| `splitting` | `splitting` | No differences |
|
||||
| `stdin` | n/a | Not supported |
|
||||
| `supported` | n/a | Not supported |
|
||||
| `target` | n/a | No support for syntax downleveling |
|
||||
| `treeShaking` | n/a | Always true |
|
||||
| `tsconfig` | n/a | Not supported |
|
||||
| `write` | n/a | Set to true if `outdir`/`outfile` is set, otherwise false |
|
||||
|
||||
## Plugin API
|
||||
|
||||
Bun's plugin API is designed to be esbuild compatible. Bun doesn't support esbuild's entire plugin API surface, but the core functionality is implemented. Many third-party esbuild plugins will work out of the box with Bun.
|
||||
|
||||
<Note>
|
||||
Long term, we aim for feature parity with esbuild's API, so if something doesn't work please file an issue to help us
|
||||
prioritize.
|
||||
</Note>
|
||||
|
||||
Plugins in Bun and esbuild are defined with a builder object.
|
||||
|
||||
```ts title="myPlugin.ts" icon="/icons/typescript.svg"
|
||||
import type { BunPlugin } from "bun";
|
||||
|
||||
const myPlugin: BunPlugin = {
|
||||
name: "my-plugin",
|
||||
setup(builder) {
|
||||
// define plugin
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
The builder object provides some methods for hooking into parts of the bundling process. Bun implements `onResolve` and `onLoad`; it does not yet implement the esbuild hooks `onStart`, `onEnd`, and `onDispose`, and `resolve` utilities. `initialOptions` is partially implemented, being read-only and only having a subset of esbuild's options; use `config` (same thing but with Bun's `BuildConfig` format) instead.
|
||||
|
||||
```ts title="myPlugin.ts" icon="/icons/typescript.svg"
|
||||
import type { BunPlugin } from "bun";
|
||||
const myPlugin: BunPlugin = {
|
||||
name: "my-plugin",
|
||||
setup(builder) {
|
||||
builder.onResolve(
|
||||
{
|
||||
/* onResolve.options */
|
||||
},
|
||||
args => {
|
||||
return {
|
||||
/* onResolve.results */
|
||||
};
|
||||
},
|
||||
);
|
||||
builder.onLoad(
|
||||
{
|
||||
/* onLoad.options */
|
||||
},
|
||||
args => {
|
||||
return {
|
||||
/* onLoad.results */
|
||||
};
|
||||
},
|
||||
);
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### onResolve
|
||||
|
||||
<Tabs>
|
||||
<Tab title="options">
|
||||
|
||||
- 🟢 `filter`
|
||||
- 🟢 `namespace`
|
||||
|
||||
</Tab>
|
||||
<Tab title="arguments">
|
||||
|
||||
- 🟢 `path`
|
||||
- 🟢 `importer`
|
||||
- 🔴 `namespace`
|
||||
- 🔴 `resolveDir`
|
||||
- 🔴 `kind`
|
||||
- 🔴 `pluginData`
|
||||
|
||||
</Tab>
|
||||
<Tab title="results">
|
||||
|
||||
- 🟢 `namespace`
|
||||
- 🟢 `path`
|
||||
- 🔴 `errors`
|
||||
- 🔴 `external`
|
||||
- 🔴 `pluginData`
|
||||
- 🔴 `pluginName`
|
||||
- 🔴 `sideEffects`
|
||||
- 🔴 `suffix`
|
||||
- 🔴 `warnings`
|
||||
- 🔴 `watchDirs`
|
||||
- 🔴 `watchFiles`
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### onLoad
|
||||
|
||||
<Tabs>
|
||||
<Tab title="options">
|
||||
|
||||
- 🟢 `filter`
|
||||
- 🟢 `namespace`
|
||||
|
||||
</Tab>
|
||||
<Tab title="arguments">
|
||||
|
||||
- 🟢 `path`
|
||||
- 🔴 `namespace`
|
||||
- 🔴 `suffix`
|
||||
- 🔴 `pluginData`
|
||||
|
||||
</Tab>
|
||||
<Tab title="results">
|
||||
|
||||
- 🟢 `contents`
|
||||
- 🟢 `loader`
|
||||
- 🔴 `errors`
|
||||
- 🔴 `pluginData`
|
||||
- 🔴 `pluginName`
|
||||
- 🔴 `resolveDir`
|
||||
- 🔴 `warnings`
|
||||
- 🔴 `watchDirs`
|
||||
- 🔴 `watchFiles`
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
@@ -1,43 +1,33 @@
|
||||
---
|
||||
title: "Single-file executable"
|
||||
description: "Generate standalone executables from TypeScript or JavaScript files with Bun"
|
||||
---
|
||||
|
||||
Bun's bundler implements a `--compile` flag for generating a standalone binary from a TypeScript or JavaScript file.
|
||||
|
||||
<CodeGroup>
|
||||
{% codetabs %}
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build ./cli.ts --compile --outfile mycli
|
||||
```bash
|
||||
$ bun build ./cli.ts --compile --outfile mycli
|
||||
```
|
||||
|
||||
```ts cli.ts icon="/icons/typescript.svg"
|
||||
```ts#cli.ts
|
||||
console.log("Hello world!");
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
{% /codetabs %}
|
||||
|
||||
This bundles `cli.ts` into an executable that can be executed directly:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
./mycli
|
||||
```
|
||||
|
||||
```txt
|
||||
$ ./mycli
|
||||
Hello world!
|
||||
```
|
||||
|
||||
All imported files and packages are bundled into the executable, along with a copy of the Bun runtime. All built-in Bun and Node.js APIs are supported.
|
||||
|
||||
---
|
||||
|
||||
## Cross-compile to other platforms
|
||||
|
||||
The `--target` flag lets you compile your standalone executable for a different operating system, architecture, or version of Bun than the machine you're running `bun build` on.
|
||||
|
||||
To build for Linux x64 (most servers):
|
||||
|
||||
```bash icon="terminal" terminal
|
||||
```sh
|
||||
bun build --compile --target=bun-linux-x64 ./index.ts --outfile myapp
|
||||
|
||||
# To support CPUs from before 2013, use the baseline version (nehalem)
|
||||
@@ -50,14 +40,14 @@ bun build --compile --target=bun-linux-x64-modern ./index.ts --outfile myapp
|
||||
|
||||
To build for Linux ARM64 (e.g. Graviton or Raspberry Pi):
|
||||
|
||||
```bash icon="terminal" terminal
|
||||
```sh
|
||||
# Note: the default architecture is x64 if no architecture is specified.
|
||||
bun build --compile --target=bun-linux-arm64 ./index.ts --outfile myapp
|
||||
```
|
||||
|
||||
To build for Windows x64:
|
||||
|
||||
```bash icon="terminal" terminal
|
||||
```sh
|
||||
bun build --compile --target=bun-windows-x64 ./path/to/my/app.ts --outfile myapp
|
||||
|
||||
# To support CPUs from before 2013, use the baseline version (nehalem)
|
||||
@@ -71,17 +61,17 @@ bun build --compile --target=bun-windows-x64-modern ./path/to/my/app.ts --outfil
|
||||
|
||||
To build for macOS arm64:
|
||||
|
||||
```bash icon="terminal" terminal
|
||||
```sh
|
||||
bun build --compile --target=bun-darwin-arm64 ./path/to/my/app.ts --outfile myapp
|
||||
```
|
||||
|
||||
To build for macOS x64:
|
||||
|
||||
```bash icon="terminal" terminal
|
||||
```sh
|
||||
bun build --compile --target=bun-darwin-x64 ./path/to/my/app.ts --outfile myapp
|
||||
```
|
||||
|
||||
### Supported targets
|
||||
#### Supported targets
|
||||
|
||||
The order of the `--target` flag does not matter, as long as they're delimited by a `-`.
|
||||
|
||||
@@ -90,38 +80,27 @@ The order of the `--target` flag does not matter, as long as they're delimited b
|
||||
| bun-linux-x64 | Linux | x64 | ✅ | ✅ | glibc |
|
||||
| bun-linux-arm64 | Linux | arm64 | ✅ | N/A | glibc |
|
||||
| bun-windows-x64 | Windows | x64 | ✅ | ✅ | - |
|
||||
| ~~bun-windows-arm64~~ | ~~Windows~~ | ~~arm64~~ | ❌ | ❌ | - |
|
||||
| ~~bun-windows-arm64~~ | Windows | arm64 | ❌ | ❌ | - |
|
||||
| bun-darwin-x64 | macOS | x64 | ✅ | ✅ | - |
|
||||
| bun-darwin-arm64 | macOS | arm64 | ✅ | N/A | - |
|
||||
| bun-linux-x64-musl | Linux | x64 | ✅ | ✅ | musl |
|
||||
| bun-linux-arm64-musl | Linux | arm64 | ✅ | N/A | musl |
|
||||
|
||||
<Warning>
|
||||
On x64 platforms, Bun uses SIMD optimizations which require a modern CPU supporting AVX2 instructions. The `-baseline`
|
||||
build of Bun is for older CPUs that don't support these optimizations. Normally, when you install Bun we automatically
|
||||
detect which version to use but this can be harder to do when cross-compiling since you might not know the target CPU.
|
||||
You usually don't need to worry about it on Darwin x64, but it is relevant for Windows x64 and Linux x64. If you or
|
||||
your users see `"Illegal instruction"` errors, you might need to use the baseline version.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
On x64 platforms, Bun uses SIMD optimizations which require a modern CPU supporting AVX2 instructions. The `-baseline` build of Bun is for older CPUs that don't support these optimizations. Normally, when you install Bun we automatically detect which version to use but this can be harder to do when cross-compiling since you might not know the target CPU. You usually don't need to worry about it on Darwin x64, but it is relevant for Windows x64 and Linux x64. If you or your users see `"Illegal instruction"` errors, you might need to use the baseline version.
|
||||
|
||||
## Build-time constants
|
||||
|
||||
Use the `--define` flag to inject build-time constants into your executable, such as version numbers, build timestamps, or configuration values:
|
||||
|
||||
```bash icon="terminal" terminal
|
||||
bun build --compile --define BUILD_VERSION='"1.2.3"' --define BUILD_TIME='"2024-01-15T10:30:00Z"' src/cli.ts --outfile mycli
|
||||
```bash
|
||||
$ bun build --compile --define BUILD_VERSION='"1.2.3"' --define BUILD_TIME='"2024-01-15T10:30:00Z"' src/cli.ts --outfile mycli
|
||||
```
|
||||
|
||||
These constants are embedded directly into your compiled binary at build time, providing zero runtime overhead and enabling dead code elimination optimizations.
|
||||
|
||||
<Note>
|
||||
For comprehensive examples and advanced patterns, see the [Build-time constants
|
||||
guide](/guides/runtime/build-time-constants).
|
||||
</Note>
|
||||
|
||||
---
|
||||
{% callout type="info" %}
|
||||
For comprehensive examples and advanced patterns, see the [Build-time constants guide](/guides/runtime/build-time-constants).
|
||||
{% /callout %}
|
||||
|
||||
## Deploying to production
|
||||
|
||||
@@ -133,7 +112,7 @@ With compiled executables, you can move that cost from runtime to build-time.
|
||||
|
||||
When deploying to production, we recommend the following:
|
||||
|
||||
```bash icon="terminal" terminal
|
||||
```sh
|
||||
bun build --compile --minify --sourcemap ./path/to/my/app.ts --outfile myapp
|
||||
```
|
||||
|
||||
@@ -141,22 +120,17 @@ bun build --compile --minify --sourcemap ./path/to/my/app.ts --outfile myapp
|
||||
|
||||
To improve startup time, enable bytecode compilation:
|
||||
|
||||
```bash icon="terminal" terminal
|
||||
```sh
|
||||
bun build --compile --minify --sourcemap --bytecode ./path/to/my/app.ts --outfile myapp
|
||||
```
|
||||
|
||||
Using bytecode compilation, `tsc` starts 2x faster:
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
{% image src="https://github.com/user-attachments/assets/dc8913db-01d2-48f8-a8ef-ac4e984f9763" width="689" /%}
|
||||
|
||||
Bytecode compilation moves parsing overhead for large input files from runtime to bundle time. Your app starts faster, in exchange for making the `bun build` command a little slower. It doesn't obscure source code.
|
||||
|
||||
<Warning>
|
||||
**Experimental:** Bytecode compilation is an experimental feature. Only `cjs` format is supported (which means no
|
||||
top-level-await). Let us know if you run into any issues!
|
||||
</Warning>
|
||||
**Experimental:** Bytecode compilation is an experimental feature introduced in Bun v1.1.30. Only `cjs` format is supported (which means no top-level-await). Let us know if you run into any issues!
|
||||
|
||||
### What do these flags do?
|
||||
|
||||
@@ -166,106 +140,68 @@ The `--sourcemap` argument embeds a sourcemap compressed with zstd, so that erro
|
||||
|
||||
The `--bytecode` argument enables bytecode compilation. Every time you run JavaScript code in Bun, JavaScriptCore (the engine) will compile your source code into bytecode. We can move this parsing work from runtime to bundle time, saving you startup time.
|
||||
|
||||
---
|
||||
|
||||
## Embedding runtime arguments
|
||||
|
||||
**`--compile-exec-argv="args"`** - Embed runtime arguments that are available via `process.execArgv`:
|
||||
|
||||
```bash icon="terminal" terminal
|
||||
```bash
|
||||
bun build --compile --compile-exec-argv="--smol --user-agent=MyBot" ./app.ts --outfile myapp
|
||||
```
|
||||
|
||||
```ts app.ts icon="/icons/typescript.svg"
|
||||
```js
|
||||
// In the compiled app
|
||||
console.log(process.execArgv); // ["--smol", "--user-agent=MyBot"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Disabling automatic config loading
|
||||
|
||||
By default, standalone executables look for `.env` and `bunfig.toml` files in the directory where the executable is run. You can disable this behavior at build time for deterministic execution regardless of the user's working directory.
|
||||
|
||||
```bash icon="terminal" terminal
|
||||
# Disable .env loading
|
||||
bun build --compile --no-compile-autoload-dotenv ./app.ts --outfile myapp
|
||||
|
||||
# Disable bunfig.toml loading
|
||||
bun build --compile --no-compile-autoload-bunfig ./app.ts --outfile myapp
|
||||
|
||||
# Disable both
|
||||
bun build --compile --no-compile-autoload-dotenv --no-compile-autoload-bunfig ./app.ts --outfile myapp
|
||||
```
|
||||
|
||||
You can also configure this via the JavaScript API:
|
||||
|
||||
```ts
|
||||
await Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
compile: {
|
||||
autoloadDotenv: false, // Disable .env loading
|
||||
autoloadBunfig: false, // Disable bunfig.toml loading
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Act as the Bun CLI
|
||||
|
||||
<Note>New in Bun v1.2.16</Note>
|
||||
{% note %}
|
||||
|
||||
New in Bun v1.2.16
|
||||
|
||||
{% /note %}
|
||||
|
||||
You can run a standalone executable as if it were the `bun` CLI itself by setting the `BUN_BE_BUN=1` environment variable. When this variable is set, the executable will ignore its bundled entrypoint and instead expose all the features of Bun's CLI.
|
||||
|
||||
For example, consider an executable compiled from a simple script:
|
||||
|
||||
```bash icon="terminal" terminal
|
||||
echo "console.log(\"you shouldn't see this\");" > such-bun.js
|
||||
bun build --compile ./such-bun.js
|
||||
```
|
||||
```sh
|
||||
$ cat such-bun.js
|
||||
console.log("you shouldn't see this");
|
||||
|
||||
```txt
|
||||
[3ms] bundle 1 modules
|
||||
$ bun build --compile ./such-bun.js
|
||||
[3ms] bundle 1 modules
|
||||
[89ms] compile such-bun
|
||||
```
|
||||
|
||||
Normally, running `./such-bun` with arguments would execute the script.
|
||||
Normally, running `./such-bun` with arguments would execute the script. However, with the `BUN_BE_BUN=1` environment variable, it acts just like the `bun` binary:
|
||||
|
||||
```bash icon="terminal" terminal
|
||||
```sh
|
||||
# Executable runs its own entrypoint by default
|
||||
./such-bun install
|
||||
```
|
||||
|
||||
```txt
|
||||
$ ./such-bun install
|
||||
you shouldn't see this
|
||||
```
|
||||
|
||||
However, with the `BUN_BE_BUN=1` environment variable, it acts just like the `bun` binary:
|
||||
|
||||
```bash icon="terminal" terminal
|
||||
# With the env var, the executable acts like the `bun` CLI
|
||||
BUN_BE_BUN=1 ./such-bun install
|
||||
```
|
||||
|
||||
```txt
|
||||
$ BUN_BE_BUN=1 ./such-bun install
|
||||
bun install v1.2.16-canary.1 (1d1db811)
|
||||
Checked 63 installs across 64 packages (no changes) [5.00ms]
|
||||
```
|
||||
|
||||
This is useful for building CLI tools on top of Bun that may need to install packages, bundle dependencies, run different or local files and more without needing to download a separate binary or install bun.
|
||||
|
||||
---
|
||||
|
||||
## Full-stack executables
|
||||
|
||||
<Note>New in Bun v1.2.17</Note>
|
||||
{% note %}
|
||||
|
||||
New in Bun v1.2.17
|
||||
|
||||
{% /note %}
|
||||
|
||||
Bun's `--compile` flag can create standalone executables that contain both server and client code, making it ideal for full-stack applications. When you import an HTML file in your server code, Bun automatically bundles all frontend assets (JavaScript, CSS, etc.) and embeds them into the executable. When Bun sees the HTML import on the server, it kicks off a frontend build process to bundle JavaScript, CSS, and other assets.
|
||||
|
||||
<CodeGroup>
|
||||
{% codetabs %}
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
```ts#server.ts
|
||||
import { serve } from "bun";
|
||||
import index from "./index.html";
|
||||
|
||||
@@ -279,35 +215,35 @@ const server = serve({
|
||||
console.log(`Server running at http://localhost:${server.port}`);
|
||||
```
|
||||
|
||||
```html index.html icon="file-code"
|
||||
```html#index.html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>My App</title>
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<link rel="stylesheet" href="./styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello World</h1>
|
||||
<script src="./app.ts"></script>
|
||||
<script src="./app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```ts app.ts icon="file-code"
|
||||
```js#app.js
|
||||
console.log("Hello from the client!");
|
||||
```
|
||||
|
||||
```css styles.css icon="file-code"
|
||||
```css#styles.css
|
||||
body {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
{% /codetabs %}
|
||||
|
||||
To build this into a single executable:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```sh
|
||||
bun build --compile ./server.ts --outfile myapp
|
||||
```
|
||||
|
||||
@@ -320,27 +256,25 @@ This creates a self-contained binary that includes:
|
||||
|
||||
The result is a single file that can be deployed anywhere without needing Node.js, Bun, or any dependencies installed. Just run:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```sh
|
||||
./myapp
|
||||
```
|
||||
|
||||
Bun automatically handles serving the frontend assets with proper MIME types and cache headers. The HTML import is replaced with a manifest object that `Bun.serve` uses to efficiently serve pre-bundled assets.
|
||||
|
||||
For more details on building full-stack applications with Bun, see the [full-stack guide](/bundler/fullstack).
|
||||
|
||||
---
|
||||
For more details on building full-stack applications with Bun, see the [full-stack guide](/docs/bundler/fullstack).
|
||||
|
||||
## Worker
|
||||
|
||||
To use workers in a standalone executable, add the worker's entrypoint to the CLI arguments:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build --compile ./index.ts ./my-worker.ts --outfile myapp
|
||||
```sh
|
||||
$ bun build --compile ./index.ts ./my-worker.ts --outfile myapp
|
||||
```
|
||||
|
||||
Then, reference the worker in your code:
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
console.log("Hello from Bun!");
|
||||
|
||||
// Any of these will work:
|
||||
@@ -349,21 +283,19 @@ new Worker(new URL("./my-worker.ts", import.meta.url));
|
||||
new Worker(new URL("./my-worker.ts", import.meta.url).href);
|
||||
```
|
||||
|
||||
When you add multiple entrypoints to a standalone executable, they will be bundled separately into the executable.
|
||||
As of Bun v1.1.25, when you add multiple entrypoints to a standalone executable, they will be bundled separately into the executable.
|
||||
|
||||
In the future, we may automatically detect usages of statically-known paths in `new Worker(path)` and then bundle those into the executable, but for now, you'll need to add it to the shell command manually like the above example.
|
||||
|
||||
If you use a relative path to a file not included in the standalone executable, it will attempt to load that path from disk relative to the current working directory of the process (and then error if it doesn't exist).
|
||||
|
||||
---
|
||||
|
||||
## SQLite
|
||||
|
||||
You can use `bun:sqlite` imports with `bun build --compile`.
|
||||
|
||||
By default, the database is resolved relative to the current working directory of the process.
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```js
|
||||
import db from "./my.db" with { type: "sqlite" };
|
||||
|
||||
console.log(db.query("select * from users LIMIT 1").get());
|
||||
@@ -371,12 +303,10 @@ console.log(db.query("select * from users LIMIT 1").get());
|
||||
|
||||
That means if the executable is located at `/usr/bin/hello`, the user's terminal is located at `/home/me/Desktop`, it will look for `/home/me/Desktop/my.db`.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
cd /home/me/Desktop
|
||||
./hello
|
||||
```
|
||||
|
||||
---
|
||||
$ cd /home/me/Desktop
|
||||
$ ./hello
|
||||
```
|
||||
|
||||
## Embed assets & files
|
||||
|
||||
@@ -384,7 +314,7 @@ Standalone executables support embedding files.
|
||||
|
||||
To embed files into an executable with `bun build --compile`, import the file in your code.
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
// this becomes an internal file path
|
||||
import icon from "./icon.png" with { type: "file" };
|
||||
import { file } from "bun";
|
||||
@@ -401,7 +331,7 @@ Embedded files can be read using `Bun.file`'s functions or the Node.js `fs.readF
|
||||
|
||||
For example, to read the contents of the embedded file:
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```js
|
||||
import icon from "./icon.png" with { type: "file" };
|
||||
import { file } from "bun";
|
||||
|
||||
@@ -414,7 +344,7 @@ const bytes = await file(icon).arrayBuffer();
|
||||
|
||||
If your application wants to embed a SQLite database, set `type: "sqlite"` in the import attribute and the `embed` attribute to `"true"`.
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```js
|
||||
import myEmbeddedDb from "./my.db" with { type: "sqlite", embed: "true" };
|
||||
|
||||
console.log(myEmbeddedDb.query("select * from users LIMIT 1").get());
|
||||
@@ -424,9 +354,9 @@ This database is read-write, but all changes are lost when the executable exits
|
||||
|
||||
### Embed N-API Addons
|
||||
|
||||
You can embed `.node` files into executables.
|
||||
As of Bun v1.0.23, you can embed `.node` files into executables.
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```js
|
||||
const addon = require("./addon.node");
|
||||
|
||||
console.log(addon.hello());
|
||||
@@ -438,13 +368,13 @@ Unfortunately, if you're using `@mapbox/node-pre-gyp` or other similar tools, yo
|
||||
|
||||
To embed a directory with `bun build --compile`, use a shell glob in your `bun build` command:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build --compile ./index.ts ./public/**/*.png
|
||||
```sh
|
||||
$ bun build --compile ./index.ts ./public/**/*.png
|
||||
```
|
||||
|
||||
Then, you can reference the files in your code:
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import icon from "./public/assets/icon.png" with { type: "file" };
|
||||
import { file } from "bun";
|
||||
|
||||
@@ -462,7 +392,7 @@ This is honestly a workaround, and we expect to improve this in the future with
|
||||
|
||||
To get a list of all embedded files, use `Bun.embeddedFiles`:
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```js
|
||||
import "./icon.png" with { type: "file" };
|
||||
import { embeddedFiles } from "bun";
|
||||
|
||||
@@ -483,40 +413,141 @@ By default, embedded files have a content hash appended to their name. This is u
|
||||
|
||||
To disable the content hash, pass `--asset-naming` to `bun build --compile` like this:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build --compile --asset-naming="[name].[ext]" ./index.ts
|
||||
```sh
|
||||
$ bun build --compile --asset-naming="[name].[ext]" ./index.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Minification
|
||||
|
||||
To trim down the size of the executable a little, pass `--minify` to `bun build --compile`. This uses Bun's minifier to reduce the code size. Overall though, Bun's binary is still way too big and we need to make it smaller.
|
||||
|
||||
---
|
||||
## Using Bun.build() API
|
||||
|
||||
You can also generate standalone executables using the `Bun.build()` JavaScript API. This is useful when you need programmatic control over the build process.
|
||||
|
||||
### Basic usage
|
||||
|
||||
```js
|
||||
await Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
outdir: "./dist",
|
||||
compile: {
|
||||
target: "bun-windows-x64",
|
||||
outfile: "myapp.exe",
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Windows metadata with Bun.build()
|
||||
|
||||
When targeting Windows, you can specify metadata through the `windows` object:
|
||||
|
||||
```js
|
||||
await Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
outdir: "./dist",
|
||||
compile: {
|
||||
target: "bun-windows-x64",
|
||||
outfile: "myapp.exe",
|
||||
windows: {
|
||||
title: "My Application",
|
||||
publisher: "My Company Inc",
|
||||
version: "1.2.3.4",
|
||||
description: "A powerful application built with Bun",
|
||||
copyright: "© 2024 My Company Inc",
|
||||
hideConsole: false, // Set to true for GUI applications
|
||||
icon: "./icon.ico", // Path to icon file
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Cross-compilation with Bun.build()
|
||||
|
||||
You can cross-compile for different platforms:
|
||||
|
||||
```js
|
||||
// Build for multiple platforms
|
||||
const platforms = [
|
||||
{ target: "bun-windows-x64", outfile: "app-windows.exe" },
|
||||
{ target: "bun-linux-x64", outfile: "app-linux" },
|
||||
{ target: "bun-darwin-arm64", outfile: "app-macos" },
|
||||
];
|
||||
|
||||
for (const platform of platforms) {
|
||||
await Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
outdir: "./dist",
|
||||
compile: platform,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Windows-specific flags
|
||||
|
||||
When compiling a standalone executable on Windows, there are two platform-specific options that can be used to customize metadata on the generated `.exe` file:
|
||||
When compiling a standalone executable for Windows, there are several platform-specific options that can be used to customize the generated `.exe` file:
|
||||
|
||||
- `--windows-icon=path/to/icon.ico` to customize the executable file icon.
|
||||
- `--windows-hide-console` to disable the background terminal, which can be used for applications that do not need a TTY.
|
||||
### Visual customization
|
||||
|
||||
<Warning>These flags currently cannot be used when cross-compiling because they depend on Windows APIs.</Warning>
|
||||
- `--windows-icon=path/to/icon.ico` - Set the executable file icon
|
||||
- `--windows-hide-console` - Disable the background terminal window (useful for GUI applications)
|
||||
|
||||
---
|
||||
### Metadata customization
|
||||
|
||||
You can embed version information and other metadata into your Windows executable:
|
||||
|
||||
- `--windows-title <STR>` - Set the product name (appears in file properties)
|
||||
- `--windows-publisher <STR>` - Set the company name
|
||||
- `--windows-version <STR>` - Set the version number (e.g. "1.2.3.4")
|
||||
- `--windows-description <STR>` - Set the file description
|
||||
- `--windows-copyright <STR>` - Set the copyright information
|
||||
|
||||
#### Example with all metadata flags:
|
||||
|
||||
```sh
|
||||
bun build --compile ./app.ts \
|
||||
--outfile myapp.exe \
|
||||
--windows-title "My Application" \
|
||||
--windows-publisher "My Company Inc" \
|
||||
--windows-version "1.2.3.4" \
|
||||
--windows-description "A powerful application built with Bun" \
|
||||
--windows-copyright "© 2024 My Company Inc"
|
||||
```
|
||||
|
||||
This metadata will be visible in Windows Explorer when viewing the file properties:
|
||||
|
||||
1. Right-click the executable in Windows Explorer
|
||||
2. Select "Properties"
|
||||
3. Go to the "Details" tab
|
||||
|
||||
#### Version string format
|
||||
|
||||
The `--windows-version` flag accepts version strings in the following formats:
|
||||
|
||||
- `"1"` - Will be normalized to "1.0.0.0"
|
||||
- `"1.2"` - Will be normalized to "1.2.0.0"
|
||||
- `"1.2.3"` - Will be normalized to "1.2.3.0"
|
||||
- `"1.2.3.4"` - Full version format
|
||||
|
||||
Each version component must be a number between 0 and 65535.
|
||||
|
||||
{% callout %}
|
||||
|
||||
These flags currently cannot be used when cross-compiling because they depend on Windows APIs. They are only available when building on Windows itself.
|
||||
|
||||
{% /callout %}
|
||||
|
||||
## Code signing on macOS
|
||||
|
||||
To codesign a standalone executable on macOS (which fixes Gatekeeper warnings), use the `codesign` command.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
codesign --deep --force -vvvv --sign "XXXXXXXXXX" ./myapp
|
||||
```sh
|
||||
$ codesign --deep --force -vvvv --sign "XXXXXXXXXX" ./myapp
|
||||
```
|
||||
|
||||
We recommend including an `entitlements.plist` file with JIT permissions.
|
||||
|
||||
```xml icon="xml" title="info.plist"
|
||||
```xml#entitlements.plist
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
@@ -537,57 +568,54 @@ We recommend including an `entitlements.plist` file with JIT permissions.
|
||||
|
||||
To codesign with JIT support, pass the `--entitlements` flag to `codesign`.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
codesign --deep --force -vvvv --sign "XXXXXXXXXX" --entitlements entitlements.plist ./myapp
|
||||
```sh
|
||||
$ codesign --deep --force -vvvv --sign "XXXXXXXXXX" --entitlements entitlements.plist ./myapp
|
||||
```
|
||||
|
||||
After codesigning, verify the executable:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
codesign -vvv --verify ./myapp
|
||||
```sh
|
||||
$ codesign -vvv --verify ./myapp
|
||||
./myapp: valid on disk
|
||||
./myapp: satisfies its Designated Requirement
|
||||
```
|
||||
|
||||
<Warning>Codesign support requires Bun v1.2.4 or newer.</Warning>
|
||||
{% callout %}
|
||||
|
||||
---
|
||||
Codesign support requires Bun v1.2.4 or newer.
|
||||
|
||||
{% /callout %}
|
||||
|
||||
## Code splitting
|
||||
|
||||
Standalone executables support code splitting. Use `--compile` with `--splitting` to create an executable that loads code-split chunks at runtime.
|
||||
|
||||
```bash
|
||||
bun build --compile --splitting ./src/entry.ts --outdir ./build
|
||||
$ bun build --compile --splitting ./src/entry.ts --outdir ./build
|
||||
```
|
||||
|
||||
<CodeGroup>
|
||||
{% codetabs %}
|
||||
|
||||
```ts src/entry.ts icon="/icons/typescript.svg"
|
||||
```ts#src/entry.ts
|
||||
console.log("Entrypoint loaded");
|
||||
const lazy = await import("./lazy.ts");
|
||||
lazy.hello();
|
||||
```
|
||||
|
||||
```ts src/lazy.ts icon="/icons/typescript.svg"
|
||||
```ts#src/lazy.ts
|
||||
export function hello() {
|
||||
console.log("Lazy module loaded");
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
{% /codetabs %}
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
./build/entry
|
||||
```
|
||||
|
||||
```txt
|
||||
```bash
|
||||
$ ./build/entry
|
||||
Entrypoint loaded
|
||||
Lazy module loaded
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Unsupported CLI arguments
|
||||
|
||||
Currently, the `--compile` flag can only accept a single entrypoint at a time and does not support the following flags:
|
||||
418
docs/bundler/fullstack.md
Normal file
418
docs/bundler/fullstack.md
Normal file
@@ -0,0 +1,418 @@
|
||||
To get started, import HTML files and pass them to the `routes` option in `Bun.serve()`.
|
||||
|
||||
```ts
|
||||
import { sql, serve } from "bun";
|
||||
import dashboard from "./dashboard.html";
|
||||
import homepage from "./index.html";
|
||||
|
||||
const server = serve({
|
||||
routes: {
|
||||
// ** HTML imports **
|
||||
// Bundle & route index.html to "/". This uses HTMLRewriter to scan the HTML for `<script>` and `<link>` tags, run's Bun's JavaScript & CSS bundler on them, transpiles any TypeScript, JSX, and TSX, downlevels CSS with Bun's CSS parser and serves the result.
|
||||
"/": homepage,
|
||||
// Bundle & route dashboard.html to "/dashboard"
|
||||
"/dashboard": dashboard,
|
||||
|
||||
// ** API endpoints ** (Bun v1.2.3+ required)
|
||||
"/api/users": {
|
||||
async GET(req) {
|
||||
const users = await sql`SELECT * FROM users`;
|
||||
return Response.json(users);
|
||||
},
|
||||
async POST(req) {
|
||||
const { name, email } = await req.json();
|
||||
const [user] =
|
||||
await sql`INSERT INTO users (name, email) VALUES (${name}, ${email})`;
|
||||
return Response.json(user);
|
||||
},
|
||||
},
|
||||
"/api/users/:id": async req => {
|
||||
const { id } = req.params;
|
||||
const [user] = await sql`SELECT * FROM users WHERE id = ${id}`;
|
||||
return Response.json(user);
|
||||
},
|
||||
},
|
||||
|
||||
// Enable development mode for:
|
||||
// - Detailed error messages
|
||||
// - Hot reloading (Bun v1.2.3+ required)
|
||||
development: true,
|
||||
|
||||
// Prior to v1.2.3, the `fetch` option was used to handle all API requests. It is now optional.
|
||||
// async fetch(req) {
|
||||
// // Return 404 for unmatched routes
|
||||
// return new Response("Not Found", { status: 404 });
|
||||
// },
|
||||
});
|
||||
|
||||
console.log(`Listening on ${server.url}`);
|
||||
```
|
||||
|
||||
```bash
|
||||
$ bun run app.ts
|
||||
```
|
||||
|
||||
## HTML imports are routes
|
||||
|
||||
The web starts with HTML, and so does Bun's fullstack dev server.
|
||||
|
||||
To specify entrypoints to your frontend, import HTML files into your JavaScript/TypeScript/TSX/JSX files.
|
||||
|
||||
```ts
|
||||
import dashboard from "./dashboard.html";
|
||||
import homepage from "./index.html";
|
||||
```
|
||||
|
||||
These HTML files are used as routes in Bun's dev server you can pass to `Bun.serve()`.
|
||||
|
||||
```ts
|
||||
Bun.serve({
|
||||
routes: {
|
||||
"/": homepage,
|
||||
"/dashboard": dashboard,
|
||||
}
|
||||
|
||||
fetch(req) {
|
||||
// ... api requests
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
When you make a request to `/dashboard` or `/`, Bun automatically bundles the `<script>` and `<link>` tags in the HTML files, exposes them as static routes, and serves the result.
|
||||
|
||||
An index.html file like this:
|
||||
|
||||
```html#index.html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Home</title>
|
||||
<link rel="stylesheet" href="./reset.css" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="./sentry-and-preloads.ts"></script>
|
||||
<script type="module" src="./my-app.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Becomes something like this:
|
||||
|
||||
```html#index.html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Home</title>
|
||||
<link rel="stylesheet" href="/index-[hash].css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/index-[hash].js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### How to use with React
|
||||
|
||||
To use React in your client-side code, import `react-dom/client` and render your app.
|
||||
|
||||
{% codetabs %}
|
||||
|
||||
```ts#src/backend.ts
|
||||
import dashboard from "../public/dashboard.html";
|
||||
import { serve } from "bun";
|
||||
|
||||
serve({
|
||||
routes: {
|
||||
"/": dashboard,
|
||||
},
|
||||
|
||||
async fetch(req) {
|
||||
// ...api requests
|
||||
return new Response("hello world");
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```ts#src/frontend.tsx
|
||||
import "./styles.css";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { App } from "./app.tsx";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const root = createRoot(document.getElementById("root"));
|
||||
root.render(<App />);
|
||||
});
|
||||
```
|
||||
|
||||
```html#public/dashboard.html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Dashboard</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="../src/frontend.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css#src/styles.css
|
||||
body {
|
||||
background-color: red;
|
||||
}
|
||||
```
|
||||
|
||||
```tsx#src/app.tsx
|
||||
export function App() {
|
||||
return <div>Hello World</div>;
|
||||
}
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
|
||||
### Development mode
|
||||
|
||||
When building locally, enable development mode by setting `development: true` in `Bun.serve()`.
|
||||
|
||||
```js-diff
|
||||
import homepage from "./index.html";
|
||||
import dashboard from "./dashboard.html";
|
||||
|
||||
Bun.serve({
|
||||
routes: {
|
||||
"/": homepage,
|
||||
"/dashboard": dashboard,
|
||||
}
|
||||
|
||||
+ development: true,
|
||||
|
||||
fetch(req) {
|
||||
// ... api requests
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
When `development` is `true`, Bun will:
|
||||
|
||||
- Include the `SourceMap` header in the response so that devtools can show the original source code
|
||||
- Disable minification
|
||||
- Re-bundle assets on each request to a .html file
|
||||
- Enable hot module reloading (unless `hmr: false` is set)
|
||||
|
||||
#### Echo console logs from browser to terminal
|
||||
|
||||
Bun.serve() supports echoing console logs from the browser to the terminal.
|
||||
|
||||
To enable this, pass `console: true` in the `development` object in `Bun.serve()`.
|
||||
|
||||
```ts
|
||||
import homepage from "./index.html";
|
||||
|
||||
Bun.serve({
|
||||
// development can also be an object.
|
||||
development: {
|
||||
// Enable Hot Module Reloading
|
||||
hmr: true,
|
||||
|
||||
// Echo console logs from the browser to the terminal
|
||||
console: true,
|
||||
},
|
||||
|
||||
routes: {
|
||||
"/": homepage,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
When `console: true` is set, Bun will stream console logs from the browser to the terminal. This reuses the existing WebSocket connection from HMR to send the logs.
|
||||
|
||||
#### Production mode
|
||||
|
||||
Hot reloading and `development: true` helps you iterate quickly, but in production, your server should be as fast as possible and have as few external dependencies as possible.
|
||||
|
||||
##### Ahead of time bundling (recommended)
|
||||
|
||||
As of Bun v1.2.17, you can use `Bun.build` or `bun build` to bundle your full-stack application ahead of time.
|
||||
|
||||
```sh
|
||||
$ bun build --target=bun --production --outdir=dist ./src/index.ts
|
||||
```
|
||||
|
||||
When Bun's bundler sees an HTML import from server-side code, it will bundle the referenced JavaScript/TypeScript/TSX/JSX and CSS files into a manifest object that Bun.serve() can use to serve the assets.
|
||||
|
||||
```ts
|
||||
import { serve } from "bun";
|
||||
import index from "./index.html";
|
||||
|
||||
serve({
|
||||
routes: { "/": index },
|
||||
});
|
||||
```
|
||||
|
||||
{% details summary="Internally, the `index` variable is a manifest object that looks something like this" %}
|
||||
|
||||
```json
|
||||
{
|
||||
"index": "./index.html",
|
||||
"files": [
|
||||
{
|
||||
"input": "index.html",
|
||||
"path": "./index-f2me3qnf.js",
|
||||
"loader": "js",
|
||||
"isEntry": true,
|
||||
"headers": {
|
||||
"etag": "eet6gn75",
|
||||
"content-type": "text/javascript;charset=utf-8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": "index.html",
|
||||
"path": "./index.html",
|
||||
"loader": "html",
|
||||
"isEntry": true,
|
||||
"headers": {
|
||||
"etag": "r9njjakd",
|
||||
"content-type": "text/html;charset=utf-8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": "index.html",
|
||||
"path": "./index-gysa5fmk.css",
|
||||
"loader": "css",
|
||||
"isEntry": true,
|
||||
"headers": {
|
||||
"etag": "50zb7x61",
|
||||
"content-type": "text/css;charset=utf-8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": "logo.svg",
|
||||
"path": "./logo-kygw735p.svg",
|
||||
"loader": "file",
|
||||
"isEntry": false,
|
||||
"headers": {
|
||||
"etag": "kygw735p",
|
||||
"content-type": "application/octet-stream"
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": "react.svg",
|
||||
"path": "./react-ck11dneg.svg",
|
||||
"loader": "file",
|
||||
"isEntry": false,
|
||||
"headers": {
|
||||
"etag": "ck11dneg",
|
||||
"content-type": "application/octet-stream"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
{% /details %}
|
||||
|
||||
##### Runtime bundling
|
||||
|
||||
When adding a build step is too complicated, you can set `development: false` in `Bun.serve()`.
|
||||
|
||||
- Enable in-memory caching of bundled assets. Bun will bundle assets lazily on the first request to an `.html` file, and cache the result in memory until the server restarts.
|
||||
- Enables `Cache-Control` headers and `ETag` headers
|
||||
- Minifies JavaScript/TypeScript/TSX/JSX files
|
||||
|
||||
## Plugins
|
||||
|
||||
Bun's [bundler plugins](https://bun.com/docs/bundler/plugins) are also supported when bundling static routes.
|
||||
|
||||
To configure plugins for `Bun.serve`, add a `plugins` array in the `[serve.static]` section of your `bunfig.toml`.
|
||||
|
||||
### Using TailwindCSS in HTML routes
|
||||
|
||||
For example, enable TailwindCSS on your routes by installing and adding the `bun-plugin-tailwind` plugin:
|
||||
|
||||
```sh
|
||||
$ bun add bun-plugin-tailwind
|
||||
```
|
||||
|
||||
```toml#bunfig.toml
|
||||
[serve.static]
|
||||
plugins = ["bun-plugin-tailwind"]
|
||||
```
|
||||
|
||||
This will allow you to use TailwindCSS utility classes in your HTML and CSS files. All you need to do is import `tailwindcss` somewhere:
|
||||
|
||||
```html#index.html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Home</title>
|
||||
<link rel="stylesheet" href="tailwindcss" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- the rest of your HTML... -->
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Or in your CSS:
|
||||
|
||||
```css#style.css
|
||||
@import "tailwindcss";
|
||||
```
|
||||
|
||||
### Custom plugins
|
||||
|
||||
Any JS file or module which exports a [valid bundler plugin object](https://bun.com/docs/bundler/plugins#usage) (essentially an object with a `name` and `setup` field) can be placed inside the `plugins` array:
|
||||
|
||||
```toml#bunfig.toml
|
||||
[serve.static]
|
||||
plugins = ["./my-plugin-implementation.ts"]
|
||||
```
|
||||
|
||||
Bun will lazily resolve and load each plugin and use them to bundle your routes.
|
||||
|
||||
Note: this is currently in `bunfig.toml` to make it possible to know statically which plugins are in use when we eventually integrate this with the `bun build` CLI. These plugins work in `Bun.build()`'s JS API, but are not yet supported in the CLI.
|
||||
|
||||
## How this works
|
||||
|
||||
Bun uses [`HTMLRewriter`](/docs/api/html-rewriter) to scan for `<script>` and `<link>` tags in HTML files, uses them as entrypoints for [Bun's bundler](/docs/bundler), generates an optimized bundle for the JavaScript/TypeScript/TSX/JSX and CSS files, and serves the result.
|
||||
|
||||
1. **`<script>` processing**
|
||||
- Transpiles TypeScript, JSX, and TSX in `<script>` tags
|
||||
- Bundles imported dependencies
|
||||
- Generates sourcemaps for debugging
|
||||
- Minifies when `development` is not `true` in `Bun.serve()`
|
||||
|
||||
```html
|
||||
<script type="module" src="./counter.tsx"></script>
|
||||
```
|
||||
|
||||
2. **`<link>` processing**
|
||||
- Processes CSS imports and `<link>` tags
|
||||
- Concatenates CSS files
|
||||
- Rewrites `url` and asset paths to include content-addressable hashes in URLs
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
```
|
||||
|
||||
3. **`<img>` & asset processing**
|
||||
- Links to assets are rewritten to include content-addressable hashes in URLs
|
||||
- Small assets in CSS files are inlined into `data:` URLs, reducing the total number of HTTP requests sent over the wire
|
||||
|
||||
4. **Rewrite HTML**
|
||||
- Combines all `<script>` tags into a single `<script>` tag with a content-addressable hash in the URL
|
||||
- Combines all `<link>` tags into a single `<link>` tag with a content-addressable hash in the URL
|
||||
- Outputs a new HTML file
|
||||
|
||||
5. **Serve**
|
||||
- All the output files from the bundler are exposed as static routes, using the same mechanism internally as when you pass a `Response` object to [`static` in `Bun.serve()`](/docs/api/http#static-routes).
|
||||
|
||||
This works similarly to how [`Bun.build` processes HTML files](/docs/bundler/html).
|
||||
|
||||
## This is a work in progress
|
||||
|
||||
- This doesn't support `bun build` yet. It also will in the future.
|
||||
File diff suppressed because it is too large
Load Diff
234
docs/bundler/hmr.md
Normal file
234
docs/bundler/hmr.md
Normal file
@@ -0,0 +1,234 @@
|
||||
Hot Module Replacement (HMR) allows you to update modules in a running
|
||||
application without needing a full page reload. This preserves the application
|
||||
state and improves the development experience.
|
||||
|
||||
HMR is enabled by default when using Bun's full-stack development server.
|
||||
|
||||
## `import.meta.hot` API Reference
|
||||
|
||||
Bun implements a client-side HMR API modeled after [Vite's `import.meta.hot` API](https://vitejs.dev/guide/api-hmr.html). It can be checked for with `if (import.meta.hot)`, tree-shaking it in production
|
||||
|
||||
```ts
|
||||
if (import.meta.hot) {
|
||||
// HMR APIs are available.
|
||||
}
|
||||
```
|
||||
|
||||
However, **this check is often not needed** as Bun will dead-code-eliminate
|
||||
calls to all of the HMR APIs in production builds.
|
||||
|
||||
```ts
|
||||
// This entire function call will be removed in production!
|
||||
import.meta.hot.dispose(() => {
|
||||
console.log("dispose");
|
||||
});
|
||||
```
|
||||
|
||||
For this to work, Bun forces these APIs to be called without indirection. That means the following do not work:
|
||||
|
||||
```ts#invalid-hmr-usage.ts
|
||||
// INVALID: Assigning `hot` to a variable
|
||||
const hot = import.meta.hot;
|
||||
hot.accept();
|
||||
|
||||
// INVALID: Assigning `import.meta` to a variable
|
||||
const meta = import.meta;
|
||||
meta.hot.accept();
|
||||
console.log(meta.hot.data);
|
||||
|
||||
// INVALID: Passing to a function
|
||||
doSomething(import.meta.hot.dispose);
|
||||
|
||||
// OK: The full phrase "import.meta.hot.<API>" must be called directly:
|
||||
import.meta.hot.accept();
|
||||
|
||||
// OK: `data` can be passed to functions:
|
||||
doSomething(import.meta.hot.data);
|
||||
```
|
||||
|
||||
{% callout %}
|
||||
|
||||
**Note** — The HMR API is still a work in progress. Some features are missing. HMR can be disabled in `Bun.serve` by setting the `development` option to `{ hmr: false }`.
|
||||
|
||||
{% endcallout %}
|
||||
|
||||
| | Method | Notes |
|
||||
| --- | ------------------ | --------------------------------------------------------------------- |
|
||||
| ✅ | `hot.accept()` | Indicate that a hot update can be replaced gracefully. |
|
||||
| ✅ | `hot.data` | Persist data between module evaluations. |
|
||||
| ✅ | `hot.dispose()` | Add a callback function to run when a module is about to be replaced. |
|
||||
| ❌ | `hot.invalidate()` | |
|
||||
| ✅ | `hot.on()` | Attach an event listener |
|
||||
| ✅ | `hot.off()` | Remove an event listener from `on`. |
|
||||
| ❌ | `hot.send()` | |
|
||||
| 🚧 | `hot.prune()` | **NOTE**: Callback is currently never called. |
|
||||
| ✅ | `hot.decline()` | No-op to match Vite's `import.meta.hot` |
|
||||
|
||||
### `import.meta.hot.accept()`
|
||||
|
||||
The `accept()` method indicates that a module can be hot-replaced. When called
|
||||
without arguments, it indicates that this module can be replaced simply by
|
||||
re-evaluating the file. After a hot update, importers of this module will be
|
||||
automatically patched.
|
||||
|
||||
```ts#index.ts
|
||||
import { getCount } from "./foo.ts";
|
||||
|
||||
console.log("count is ", getCount());
|
||||
|
||||
import.meta.hot.accept();
|
||||
|
||||
export function getNegativeCount() {
|
||||
return -getCount();
|
||||
}
|
||||
```
|
||||
|
||||
This creates a hot-reloading boundary for all of the files that `index.ts`
|
||||
imports. That means whenever `foo.ts` or any of its dependencies are saved, the
|
||||
update will bubble up to `index.ts` will re-evaluate. Files that import
|
||||
`index.ts` will then be patched to import the new version of
|
||||
`getNegativeCount()`. If only `index.ts` is updated, only the one file will be
|
||||
re-evaluated, and the counter in `foo.ts` is reused.
|
||||
|
||||
This may be used in combination with `import.meta.hot.data` to transfer state
|
||||
from the previous module to the new one.
|
||||
|
||||
When no modules call `import.meta.hot.accept()` (and there isn't React Fast
|
||||
Refresh or a plugin calling it for you), the page will reload when the file
|
||||
updates, and a console warning shows which files were invalidated. This warning
|
||||
is safe to ignore if it makes more sense to rely on full page reloads.
|
||||
|
||||
#### With callback
|
||||
|
||||
When provided one callback, `import.meta.hot.accept` will function how it does
|
||||
in Vite. Instead of patching the importers of this module, it will call the
|
||||
callback with the new module.
|
||||
|
||||
```ts
|
||||
export const count = 0;
|
||||
|
||||
import.meta.hot.accept(newModule => {
|
||||
if (newModule) {
|
||||
// newModule is undefined when SyntaxError happened
|
||||
console.log("updated: count is now ", newModule.count);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Prefer using `import.meta.hot.accept()` without an argument as it usually makes your code easier to understand.
|
||||
|
||||
#### Accepting other modules
|
||||
|
||||
```ts
|
||||
import { count } from "./foo";
|
||||
|
||||
import.meta.hot.accept("./foo", () => {
|
||||
if (!newModule) return;
|
||||
|
||||
console.log("updated: count is now ", count);
|
||||
});
|
||||
```
|
||||
|
||||
Indicates that a dependency's module can be accepted. When the dependency is updated, the callback will be called with the new module.
|
||||
|
||||
#### With multiple dependencies
|
||||
|
||||
```ts
|
||||
import.meta.hot.accept(["./foo", "./bar"], newModules => {
|
||||
// newModules is an array where each item corresponds to the updated module
|
||||
// or undefined if that module had a syntax error
|
||||
});
|
||||
```
|
||||
|
||||
Indicates that multiple dependencies' modules can be accepted. This variant accepts an array of dependencies, where the callback will receive the updated modules, and `undefined` for any that had errors.
|
||||
|
||||
### `import.meta.hot.data`
|
||||
|
||||
`import.meta.hot.data` maintains state between module instances during hot
|
||||
replacement, enabling data transfer from previous to new versions. When
|
||||
`import.meta.hot.data` is written into, Bun will also mark this module as
|
||||
capable of self-accepting (equivalent of calling `import.meta.hot.accept()`).
|
||||
|
||||
```ts
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { App } from "./app";
|
||||
|
||||
const root = import.meta.hot.data.root ??= createRoot(elem);
|
||||
root.render(<App />); // re-use an existing root
|
||||
```
|
||||
|
||||
In production, `data` is inlined to be `{}`, meaning it cannot be used as a state holder.
|
||||
|
||||
The above pattern is recommended for stateful modules because Bun knows it can minify `{}.prop ??= value` into `value` in production.
|
||||
|
||||
### `import.meta.hot.dispose()`
|
||||
|
||||
Attaches an on-dispose callback. This is called:
|
||||
|
||||
- Just before the module is replaced with another copy (before the next is loaded)
|
||||
- After the module is detached (removing all imports to this module, see `import.meta.hot.prune()`)
|
||||
|
||||
```ts
|
||||
const sideEffect = setupSideEffect();
|
||||
|
||||
import.meta.hot.dispose(() => {
|
||||
sideEffect.cleanup();
|
||||
});
|
||||
```
|
||||
|
||||
This callback is not called on route navigation or when the browser tab closes.
|
||||
|
||||
Returning a promise will delay module replacement until the module is disposed.
|
||||
All dispose callbacks are called in parallel.
|
||||
|
||||
### `import.meta.hot.prune()`
|
||||
|
||||
Attaches an on-prune callback. This is called when all imports to this module
|
||||
are removed, but the module was previously loaded.
|
||||
|
||||
This can be used to clean up resources that were created when the module was
|
||||
loaded. Unlike `import.meta.hot.dispose()`, this pairs much better with `accept`
|
||||
and `data` to manage stateful resources. A full example managing a `WebSocket`:
|
||||
|
||||
```ts
|
||||
import { something } from "./something";
|
||||
|
||||
// Initialize or re-use a WebSocket connection
|
||||
export const ws = (import.meta.hot.data.ws ??= new WebSocket(location.origin));
|
||||
|
||||
// If the module's import is removed, clean up the WebSocket connection.
|
||||
import.meta.hot.prune(() => {
|
||||
ws.close();
|
||||
});
|
||||
```
|
||||
|
||||
If `dispose` was used instead, the WebSocket would close and re-open on every
|
||||
hot update. Both versions of the code will prevent page reloads when imported
|
||||
files are updated.
|
||||
|
||||
### `import.meta.hot.on()` and `off()`
|
||||
|
||||
`on()` and `off()` are used to listen for events from the HMR runtime. Event names are prefixed with a prefix so that plugins do not conflict with each other.
|
||||
|
||||
```ts
|
||||
import.meta.hot.on("bun:beforeUpdate", () => {
|
||||
console.log("before a hot update");
|
||||
});
|
||||
```
|
||||
|
||||
When a file is replaced, all of its event listeners are automatically removed.
|
||||
|
||||
A list of all built-in events:
|
||||
|
||||
| Event | Emitted when |
|
||||
| ---------------------- | ----------------------------------------------------------------------------------------------- |
|
||||
| `bun:beforeUpdate` | before a hot update is applied. |
|
||||
| `bun:afterUpdate` | after a hot update is applied. |
|
||||
| `bun:beforeFullReload` | before a full page reload happens. |
|
||||
| `bun:beforePrune` | before prune callbacks are called. |
|
||||
| `bun:invalidate` | when a module is invalidated with `import.meta.hot.invalidate()` |
|
||||
| `bun:error` | when a build or runtime error occurs |
|
||||
| `bun:ws:disconnect` | when the HMR WebSocket connection is lost. This can indicate the development server is offline. |
|
||||
| `bun:ws:connect` | when the HMR WebSocket connects or re-connects. |
|
||||
|
||||
For compatibility with Vite, the above events are also available via `vite:*` prefix instead of `bun:*`.
|
||||
@@ -1,229 +0,0 @@
|
||||
---
|
||||
title: Hot reloading
|
||||
description: Hot Module Replacement (HMR) for Bun's development server
|
||||
---
|
||||
|
||||
Hot Module Replacement (HMR) allows you to update modules in a running application without needing a full page reload. This preserves the application state and improves the development experience.
|
||||
|
||||
<Note>HMR is enabled by default when using Bun's full-stack development server.</Note>
|
||||
|
||||
## `import.meta.hot` API Reference
|
||||
|
||||
Bun implements a client-side HMR API modeled after [Vite's `import.meta.hot` API](https://vite.dev/guide/api-hmr). It can be checked for with `if (import.meta.hot)`, tree-shaking it in production.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
if (import.meta.hot) {
|
||||
// HMR APIs are available.
|
||||
}
|
||||
```
|
||||
|
||||
However, this check is often not needed as Bun will dead-code-eliminate calls to all of the HMR APIs in production builds.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
// This entire function call will be removed in production!
|
||||
import.meta.hot.dispose(() => {
|
||||
console.log("dispose");
|
||||
});
|
||||
```
|
||||
|
||||
<Warning>
|
||||
For this to work, Bun forces these APIs to be called without indirection. That means the following do not work:
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
// INVALID: Assigning `hot` to a variable
|
||||
const hot = import.meta.hot;
|
||||
hot.accept();
|
||||
|
||||
// INVALID: Assigning `import.meta` to a variable
|
||||
const meta = import.meta;
|
||||
meta.hot.accept();
|
||||
console.log(meta.hot.data);
|
||||
|
||||
// INVALID: Passing to a function
|
||||
doSomething(import.meta.hot.dispose);
|
||||
|
||||
// OK: The full phrase "import.meta.hot.<API>" must be called directly:
|
||||
import.meta.hot.accept();
|
||||
|
||||
// OK: `data` can be passed to functions:
|
||||
doSomething(import.meta.hot.data);
|
||||
```
|
||||
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
The HMR API is still a work in progress. Some features are missing. HMR can be disabled in `Bun.serve` by setting the development option to `{ hmr: false }`.
|
||||
</Note>
|
||||
|
||||
## API Methods
|
||||
|
||||
| Method | Status | Notes |
|
||||
| ------------------ | ------ | --------------------------------------------------------------------- |
|
||||
| `hot.accept()` | ✅ | Indicate that a hot update can be replaced gracefully. |
|
||||
| `hot.data` | ✅ | Persist data between module evaluations. |
|
||||
| `hot.dispose()` | ✅ | Add a callback function to run when a module is about to be replaced. |
|
||||
| `hot.invalidate()` | ❌ | |
|
||||
| `hot.on()` | ✅ | Attach an event listener |
|
||||
| `hot.off()` | ✅ | Remove an event listener from `on`. |
|
||||
| `hot.send()` | ❌ | |
|
||||
| `hot.prune()` | 🚧 | NOTE: Callback is currently never called. |
|
||||
| `hot.decline()` | ✅ | No-op to match Vite's `import.meta.hot` |
|
||||
|
||||
## import.meta.hot.accept()
|
||||
|
||||
The `accept()` method indicates that a module can be hot-replaced. When called without arguments, it indicates that this module can be replaced simply by re-evaluating the file. After a hot update, importers of this module will be automatically patched.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
// index.ts
|
||||
import { getCount } from "./foo.ts";
|
||||
|
||||
console.log("count is ", getCount());
|
||||
|
||||
import.meta.hot.accept();
|
||||
|
||||
export function getNegativeCount() {
|
||||
return -getCount();
|
||||
}
|
||||
```
|
||||
|
||||
This creates a hot-reloading boundary for all of the files that `index.ts` imports. That means whenever `foo.ts` or any of its dependencies are saved, the update will bubble up to `index.ts` will re-evaluate. Files that import `index.ts` will then be patched to import the new version of `getNegativeCount()`. If only `index.ts` is updated, only the one file will be re-evaluated, and the counter in `foo.ts` is reused.
|
||||
|
||||
This may be used in combination with `import.meta.hot.data` to transfer state from the previous module to the new one.
|
||||
|
||||
<Info>
|
||||
When no modules call `import.meta.hot.accept()` (and there isn't React Fast Refresh or a plugin calling it for you),
|
||||
the page will reload when the file updates, and a console warning shows which files were invalidated. This warning is
|
||||
safe to ignore if it makes more sense to rely on full page reloads.
|
||||
</Info>
|
||||
|
||||
### With callback
|
||||
|
||||
When provided one callback, `import.meta.hot.accept` will function how it does in Vite. Instead of patching the importers of this module, it will call the callback with the new module.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
export const count = 0;
|
||||
|
||||
import.meta.hot.accept(newModule => {
|
||||
if (newModule) {
|
||||
// newModule is undefined when SyntaxError happened
|
||||
console.log("updated: count is now ", newModule.count);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
<Tip>
|
||||
Prefer using `import.meta.hot.accept()` without an argument as it usually makes your code easier to understand.
|
||||
</Tip>
|
||||
|
||||
### Accepting other modules
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import { count } from "./foo";
|
||||
|
||||
import.meta.hot.accept("./foo", () => {
|
||||
if (!newModule) return;
|
||||
|
||||
console.log("updated: count is now ", count);
|
||||
});
|
||||
```
|
||||
|
||||
Indicates that a dependency's module can be accepted. When the dependency is updated, the callback will be called with the new module.
|
||||
|
||||
### With multiple dependencies
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import.meta.hot.accept(["./foo", "./bar"], newModules => {
|
||||
// newModules is an array where each item corresponds to the updated module
|
||||
// or undefined if that module had a syntax error
|
||||
});
|
||||
```
|
||||
|
||||
Indicates that multiple dependencies' modules can be accepted. This variant accepts an array of dependencies, where the callback will receive the updated modules, and `undefined` for any that had errors.
|
||||
|
||||
## import.meta.hot.data
|
||||
|
||||
`import.meta.hot.data` maintains state between module instances during hot replacement, enabling data transfer from previous to new versions. When `import.meta.hot.data` is written into, Bun will also mark this module as capable of self-accepting (equivalent of calling `import.meta.hot.accept()`).
|
||||
|
||||
```tsx title="index.tsx" icon="/icons/typescript.svg"
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { App } from "./app";
|
||||
|
||||
const root = (import.meta.hot.data.root ??= createRoot(elem));
|
||||
root.render(<App />); // re-use an existing root
|
||||
```
|
||||
|
||||
In production, `data` is inlined to be `{}`, meaning it cannot be used as a state holder.
|
||||
|
||||
<Tip>
|
||||
The above pattern is recommended for stateful modules because Bun knows it can minify `{}.prop ??= value` into `value`
|
||||
in production.
|
||||
</Tip>
|
||||
|
||||
## import.meta.hot.dispose()
|
||||
|
||||
Attaches an on-dispose callback. This is called:
|
||||
|
||||
- Just before the module is replaced with another copy (before the next is loaded)
|
||||
- After the module is detached (removing all imports to this module, see `import.meta.hot.prune()`)
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
const sideEffect = setupSideEffect();
|
||||
|
||||
import.meta.hot.dispose(() => {
|
||||
sideEffect.cleanup();
|
||||
});
|
||||
```
|
||||
|
||||
<Warning>This callback is not called on route navigation or when the browser tab closes.</Warning>
|
||||
|
||||
Returning a promise will delay module replacement until the module is disposed. All dispose callbacks are called in parallel.
|
||||
|
||||
## import.meta.hot.prune()
|
||||
|
||||
Attaches an on-prune callback. This is called when all imports to this module are removed, but the module was previously loaded.
|
||||
|
||||
This can be used to clean up resources that were created when the module was loaded. Unlike `import.meta.hot.dispose()`, this pairs much better with `accept` and `data` to manage stateful resources. A full example managing a WebSocket:
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import { something } from "./something";
|
||||
|
||||
// Initialize or re-use a WebSocket connection
|
||||
export const ws = (import.meta.hot.data.ws ??= new WebSocket(location.origin));
|
||||
|
||||
// If the module's import is removed, clean up the WebSocket connection.
|
||||
import.meta.hot.prune(() => {
|
||||
ws.close();
|
||||
});
|
||||
```
|
||||
|
||||
<Info>
|
||||
If `dispose` was used instead, the WebSocket would close and re-open on every hot update. Both versions of the code
|
||||
will prevent page reloads when imported files are updated.
|
||||
</Info>
|
||||
|
||||
## import.meta.hot.on() and off()
|
||||
|
||||
`on()` and `off()` are used to listen for events from the HMR runtime. Event names are prefixed with a prefix so that plugins do not conflict with each other.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import.meta.hot.on("bun:beforeUpdate", () => {
|
||||
console.log("before a hot update");
|
||||
});
|
||||
```
|
||||
|
||||
When a file is replaced, all of its event listeners are automatically removed.
|
||||
|
||||
### Built-in events
|
||||
|
||||
| Event | Emitted when |
|
||||
| ---------------------- | ----------------------------------------------------------------------------------------------- |
|
||||
| `bun:beforeUpdate` | before a hot update is applied. |
|
||||
| `bun:afterUpdate` | after a hot update is applied. |
|
||||
| `bun:beforeFullReload` | before a full page reload happens. |
|
||||
| `bun:beforePrune` | before prune callbacks are called. |
|
||||
| `bun:invalidate` | when a module is invalidated with `import.meta.hot.invalidate()` |
|
||||
| `bun:error` | when a build or runtime error occurs |
|
||||
| `bun:ws:disconnect` | when the HMR WebSocket connection is lost. This can indicate the development server is offline. |
|
||||
| `bun:ws:connect` | when the HMR WebSocket connects or re-connects. |
|
||||
|
||||
<Note>For compatibility with Vite, the above events are also available via `vite:*` prefix instead of `bun:*`.</Note>
|
||||
@@ -1,11 +1,6 @@
|
||||
---
|
||||
title: HTML & static sites
|
||||
description: Build static sites, landing pages, and web applications with Bun's bundler
|
||||
---
|
||||
|
||||
Bun's bundler has first-class support for HTML. Build static sites, landing pages, and web applications with zero configuration. Just point Bun at your HTML file and it handles everything else.
|
||||
|
||||
```html title="index.html" icon="file-code"
|
||||
```html#index.html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
@@ -20,16 +15,7 @@ Bun's bundler has first-class support for HTML. Build static sites, landing page
|
||||
|
||||
To get started, pass HTML files to `bun`.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun ./index.html
|
||||
```
|
||||
|
||||
```
|
||||
Bun v1.3.3
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Press h + Enter to show shortcuts
|
||||
```
|
||||
{% bunDevServerTerminal alt="bun ./index.html" path="./index.html" routes="" /%}
|
||||
|
||||
Bun's development server provides powerful features with zero configuration:
|
||||
|
||||
@@ -40,26 +26,19 @@ Bun's development server provides powerful features with zero configuration:
|
||||
- **Plugins** - Plugins for TailwindCSS and more
|
||||
- **ESM & CommonJS** - Use ESM and CommonJS in your JavaScript, TypeScript, and JSX files
|
||||
- **CSS Bundling & Minification** - Bundles CSS from `<link>` tags and `@import` statements
|
||||
- **Asset Management** - Automatic copying & hashing of images and assets; Rewrites asset paths in JavaScript, CSS, and HTML
|
||||
- **Asset Management**
|
||||
- Automatic copying & hashing of images and assets
|
||||
- Rewrites asset paths in JavaScript, CSS, and HTML
|
||||
|
||||
## Single Page Apps (SPA)
|
||||
|
||||
When you pass a single `.html` file to Bun, Bun will use it as a fallback route for all paths. This makes it perfect for single page apps that use client-side routing:
|
||||
When you pass a single .html file to Bun, Bun will use it as a fallback route for all paths. This makes it perfect for single page apps that use client-side routing:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun index.html
|
||||
```
|
||||
|
||||
```
|
||||
Bun v1.3.3
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Press h + Enter to show shortcuts
|
||||
```
|
||||
{% bunDevServerTerminal alt="bun index.html" path="index.html" routes="" /%}
|
||||
|
||||
Your React or other SPA will work out of the box — no configuration needed. All routes like `/about`, `/users/123`, etc. will serve the same HTML file, letting your client-side router handle the navigation.
|
||||
|
||||
```html title="index.html" icon="file-code"
|
||||
```html#index.html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
@@ -74,21 +53,9 @@ Your React or other SPA will work out of the box — no configuration needed. Al
|
||||
|
||||
## Multi-page apps (MPA)
|
||||
|
||||
Some projects have several separate routes or HTML files as entry points. To support multiple entry points, pass them all to `bun`:
|
||||
Some projects have several separate routes or HTML files as entry points. To support multiple entry points, pass them all to `bun`
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun ./index.html ./about.html
|
||||
```
|
||||
|
||||
```txt
|
||||
Bun v1.3.3
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Routes:
|
||||
/ ./index.html
|
||||
/about ./about.html
|
||||
Press h + Enter to show shortcuts
|
||||
```
|
||||
{% bunDevServerTerminal alt="bun ./index.html ./about.html" path="./index.html ./about.html" routes="[{\"path\": \"/\", \"file\": \"./index.html\"}, {\"path\": \"/about\", \"file\": \"./about.html\"}]" /%}
|
||||
|
||||
This will serve:
|
||||
|
||||
@@ -99,44 +66,19 @@ This will serve:
|
||||
|
||||
To specify multiple files, you can use glob patterns that end in `.html`:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun ./**/*.html
|
||||
```
|
||||
|
||||
```
|
||||
Bun v1.3.3
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Routes:
|
||||
/ ./index.html
|
||||
/about ./about.html
|
||||
Press h + Enter to show shortcuts
|
||||
```
|
||||
{% bunDevServerTerminal alt="bun ./**/*.html" path="./**/*.html" routes="[{\"path\": \"/\", \"file\": \"./index.html\"}, {\"path\": \"/about\", \"file\": \"./about.html\"}]" /%}
|
||||
|
||||
### Path normalization
|
||||
|
||||
The base path is chosen from the longest common prefix among all the files.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun ./index.html ./about/index.html ./about/foo/index.html
|
||||
```
|
||||
|
||||
```
|
||||
Bun v1.3.3
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Routes:
|
||||
/ ./index.html
|
||||
/about ./about/index.html
|
||||
/about/foo ./about/foo/index.html
|
||||
Press h + Enter to show shortcuts
|
||||
```
|
||||
{% bunDevServerTerminal alt="bun ./index.html ./about/index.html ./about/foo/index.html" path="./index.html ./about/index.html ./about/foo/index.html" routes="[{\"path\": \"/\", \"file\": \"./index.html\"}, {\"path\": \"/about\", \"file\": \"./about/index.html\"}, {\"path\": \"/about/foo\", \"file\": \"./about/foo/index.html\"}]" /%}
|
||||
|
||||
## JavaScript, TypeScript, and JSX
|
||||
|
||||
Bun's transpiler natively implements JavaScript, TypeScript, and JSX support. Learn more about loaders in Bun.
|
||||
Bun's transpiler natively implements JavaScript, TypeScript, and JSX support. [Learn more about loaders in Bun](/docs/bundler/loaders).
|
||||
|
||||
<Note>Bun's transpiler is also used at runtime.</Note>
|
||||
Bun's transpiler is also used at runtime.
|
||||
|
||||
### ES Modules & CommonJS
|
||||
|
||||
@@ -144,7 +86,7 @@ You can use ESM and CJS in your JavaScript, TypeScript, and JSX files. Bun will
|
||||
|
||||
There is no pre-build or separate optimization step. It's all done at the same time.
|
||||
|
||||
Learn more about module resolution in Bun.
|
||||
Learn more about [module resolution in Bun](/docs/runtime/modules).
|
||||
|
||||
## CSS
|
||||
|
||||
@@ -154,9 +96,7 @@ It's also a CSS bundler. You can use `@import` in your CSS files to import other
|
||||
|
||||
For example:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```css styles.css icon="file-code"
|
||||
```css#styles.css
|
||||
@import "./abc.css";
|
||||
|
||||
.container {
|
||||
@@ -164,17 +104,15 @@ For example:
|
||||
}
|
||||
```
|
||||
|
||||
```css abc.css icon="file-code"
|
||||
```css#abc.css
|
||||
body {
|
||||
background-color: red;
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
This outputs:
|
||||
|
||||
```css styles.css icon="file-code"
|
||||
```css#styles.css
|
||||
body {
|
||||
background-color: red;
|
||||
}
|
||||
@@ -188,7 +126,7 @@ body {
|
||||
|
||||
You can reference local assets in your CSS files.
|
||||
|
||||
```css styles.css icon="file-code"
|
||||
```css#styles.css
|
||||
body {
|
||||
background-image: url("./logo.png");
|
||||
}
|
||||
@@ -196,7 +134,7 @@ body {
|
||||
|
||||
This will copy `./logo.png` to the output directory and rewrite the path in the CSS file to include a content hash.
|
||||
|
||||
```css styles.css icon="file-code"
|
||||
```css#styles.css
|
||||
body {
|
||||
background-image: url("./logo-[ABC123].png");
|
||||
}
|
||||
@@ -206,7 +144,7 @@ body {
|
||||
|
||||
To associate a CSS file with a JavaScript file, you can import it in your JavaScript file.
|
||||
|
||||
```ts app.ts icon="/icons/typescript.svg"
|
||||
```ts#app.ts
|
||||
import "./styles.css";
|
||||
import "./more-styles.css";
|
||||
```
|
||||
@@ -221,71 +159,84 @@ The dev server supports plugins.
|
||||
|
||||
To use TailwindCSS, install the `bun-plugin-tailwind` plugin:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```bash
|
||||
# Or any npm client
|
||||
bun install --dev bun-plugin-tailwind
|
||||
$ bun install --dev bun-plugin-tailwind
|
||||
```
|
||||
|
||||
Then, add the plugin to your `bunfig.toml`:
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
```toml
|
||||
[serve.static]
|
||||
plugins = ["bun-plugin-tailwind"]
|
||||
```
|
||||
|
||||
Then, reference TailwindCSS in your HTML via `<link>` tag, `@import` in CSS, or import in JavaScript.
|
||||
Then, reference TailwindCSS in your HTML via `<link>` tag, `@import` in CSS, or `import` in JavaScript.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="index.html">
|
||||
{% codetabs %}
|
||||
|
||||
```html title="index.html" icon="file-code"
|
||||
<!-- Reference TailwindCSS in your HTML -->
|
||||
<link rel="stylesheet" href="tailwindcss" />
|
||||
```
|
||||
```html#index.html
|
||||
<!-- Reference TailwindCSS in your HTML -->
|
||||
<link rel="stylesheet" href="tailwindcss" />
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="styles.css">
|
||||
```css#styles.css
|
||||
/* Import TailwindCSS in your CSS */
|
||||
@import "tailwindcss";
|
||||
```
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
@import "tailwindcss";
|
||||
```
|
||||
```ts#app.ts
|
||||
/* Import TailwindCSS in your JavaScript */
|
||||
import "tailwindcss";
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="app.ts">
|
||||
{% /codetabs %}
|
||||
|
||||
```ts title="app.ts" icon="/icons/typescript.svg"
|
||||
import "tailwindcss";
|
||||
```
|
||||
Only one of those are necessary, not all three.
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Info>Only one of those are necessary, not all three.</Info>
|
||||
|
||||
## Echo console logs from browser to terminal
|
||||
### Echo console logs from browser to terminal
|
||||
|
||||
Bun's dev server supports streaming console logs from the browser to the terminal.
|
||||
|
||||
To enable, pass the `--console` CLI flag.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun ./index.html --console
|
||||
```
|
||||
|
||||
```
|
||||
Bun v1.3.3
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Press h + Enter to show shortcuts
|
||||
```
|
||||
{% bunDevServerTerminal alt="bun ./index.html --console" path="./index.html --console" routes="" /%}
|
||||
|
||||
Each call to `console.log` or `console.error` will be broadcast to the terminal that started the server. This is useful to see errors from the browser in the same place you run your server. This is also useful for AI agents that watch terminal output.
|
||||
|
||||
Internally, this reuses the existing WebSocket connection from hot module reloading to send the logs.
|
||||
|
||||
## Edit files in the browser
|
||||
### Edit files in the browser
|
||||
|
||||
Bun's frontend dev server has support for Automatic Workspace Folders in Chrome DevTools, which lets you save edits to files in the browser.
|
||||
Bun's frontend dev server has support for [Automatic Workspace Folders](https://chromium.googlesource.com/devtools/devtools-frontend/+/main/docs/ecosystem/automatic_workspace_folders.md) in Chrome DevTools, which lets you save edits to files in the browser.
|
||||
|
||||
{% image src="/images/bun-chromedevtools.gif" alt="Bun's frontend dev server has support for Automatic Workspace Folders in Chrome DevTools, which lets you save edits to files in the browser." /%}
|
||||
|
||||
{% details summary="How it works" %}
|
||||
|
||||
Bun's dev server automatically adds a `/.well-known/appspecific/com.chrome.devtools.json` route to the server.
|
||||
|
||||
This route returns a JSON object with the following shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"workspace": {
|
||||
"root": "/path/to/your/project",
|
||||
"uuid": "a-unique-identifier-for-this-workspace"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For security reasons, this is only enabled when:
|
||||
|
||||
1. The request is coming from localhost, 127.0.0.1, or ::1.
|
||||
2. Hot Module Reloading is enabled.
|
||||
3. The `chromeDevToolsAutomaticWorkspaceFolders` flag is set to `true` or `undefined`.
|
||||
4. There are no other routes that match the request.
|
||||
|
||||
You can disable this by passing `development: { chromeDevToolsAutomaticWorkspaceFolders: false }` in `Bun.serve`'s options.
|
||||
|
||||
{% /details %}
|
||||
|
||||
## Keyboard Shortcuts
|
||||
|
||||
@@ -293,45 +244,45 @@ While the server is running:
|
||||
|
||||
- `o + Enter` - Open in browser
|
||||
- `c + Enter` - Clear console
|
||||
- `q + Enter` (or `Ctrl+C`) - Quit server
|
||||
- `q + Enter` (or Ctrl+C) - Quit server
|
||||
|
||||
## Build for Production
|
||||
|
||||
When you're ready to deploy, use `bun build` to create optimized production bundles:
|
||||
|
||||
<Tabs>
|
||||
<Tab title="CLI">
|
||||
```bash terminal icon="terminal"
|
||||
bun build ./index.html --minify --outdir=dist
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="API">
|
||||
```ts title="build.ts" icon="/icons/typescript.svg"
|
||||
await Bun.build({
|
||||
entrypoints: ["./index.html"],
|
||||
outdir: "./dist",
|
||||
minify: true,
|
||||
});
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
{% codetabs %}
|
||||
|
||||
<Warning>
|
||||
Currently, plugins are only supported through `Bun.build`'s API or through `bunfig.toml` with the frontend dev server
|
||||
- not yet supported in `bun build`'s CLI.
|
||||
</Warning>
|
||||
```bash#CLI
|
||||
$ bun build ./index.html --minify --outdir=dist
|
||||
```
|
||||
|
||||
```ts#API
|
||||
Bun.build({
|
||||
entrypoints: ["./index.html"],
|
||||
outdir: "./dist",
|
||||
minify: {
|
||||
whitespace: true,
|
||||
identifiers: true,
|
||||
syntax: true,
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
|
||||
Currently, plugins are only supported through `Bun.build`'s API or through `bunfig.toml` with the frontend dev server - not yet supported in `bun build`'s CLI.
|
||||
|
||||
### Watch Mode
|
||||
|
||||
You can run `bun build --watch` to watch for changes and rebuild automatically. This works nicely for library development.
|
||||
|
||||
<Info>You've never seen a watch mode this fast.</Info>
|
||||
You've never seen a watch mode this fast.
|
||||
|
||||
## Plugin API
|
||||
### Plugin API
|
||||
|
||||
Need more control? Configure the bundler through the JavaScript API and use Bun's builtin `HTMLRewriter` to preprocess HTML.
|
||||
|
||||
```ts title="build.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
await Bun.build({
|
||||
entrypoints: ["./index.html"],
|
||||
outdir: "./dist",
|
||||
@@ -371,31 +322,28 @@ await Bun.build({
|
||||
|
||||
Bun automatically handles all common web assets:
|
||||
|
||||
- **Scripts** (`<script src>`) are run through Bun's JavaScript/TypeScript/JSX bundler
|
||||
- **Stylesheets** (`<link rel="stylesheet">`) are run through Bun's CSS parser & bundler
|
||||
- **Images** (`<img>`, `<picture>`) are copied and hashed
|
||||
- **Media** (`<video>`, `<audio>`, `<source>`) are copied and hashed
|
||||
- Scripts (`<script src>`) are run through Bun's JavaScript/TypeScript/JSX bundler
|
||||
- Stylesheets (`<link rel="stylesheet">`) are run through Bun's CSS parser & bundler
|
||||
- Images (`<img>`, `<picture>`) are copied and hashed
|
||||
- Media (`<video>`, `<audio>`, `<source>`) are copied and hashed
|
||||
- Any `<link>` tag with an `href` attribute pointing to a local file is rewritten to the new path, and hashed
|
||||
|
||||
All paths are resolved relative to your HTML file, making it easy to organize your project however you want.
|
||||
|
||||
<Warning>
|
||||
**This is a work in progress**
|
||||
## This is a work in progress
|
||||
|
||||
- Need more plugins
|
||||
- Need more configuration options for things like asset handling
|
||||
- Need a way to configure CORS, headers, etc.
|
||||
|
||||
{/* todo: find the correct link to link to as this 404's and there isn't any similar files */}
|
||||
{/* If you want to submit a PR, most of the code is [here](https://github.com/oven-sh/bun/blob/main/src/bun.js/api/bun/html-rewriter.ts). You could even copy paste that file into your project and use it as a starting point. */}
|
||||
|
||||
</Warning>
|
||||
If you want to submit a PR, most of the [code is here](https://github.com/oven-sh/bun/blob/main/src/js/internal/html.ts). You could even copy paste that file into your project and use it as a starting point.
|
||||
|
||||
## How this works
|
||||
|
||||
This is a small wrapper around Bun's support for HTML imports in JavaScript.
|
||||
|
||||
## Adding a backend to your frontend
|
||||
### Adding a backend to your frontend
|
||||
|
||||
To add a backend to your frontend, you can use the "routes" option in `Bun.serve`.
|
||||
To add a backend to your frontend, you can use the `"routes"` option in `Bun.serve`.
|
||||
|
||||
Learn more in the full-stack docs.
|
||||
Learn more in [the full-stack docs](/docs/bundler/fullstack).
|
||||
1735
docs/bundler/index.md
Normal file
1735
docs/bundler/index.md
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
75
docs/bundler/intro.md
Normal file
75
docs/bundler/intro.md
Normal file
@@ -0,0 +1,75 @@
|
||||
<!-- This document is a work in progress. It's not currently included in the actual docs. -->
|
||||
|
||||
The goal of this document is to break down why bundling is necessary, how it works, and how the bundler became such a key part of modern JavaScript development. The content is not specific to Bun's bundler, but is rather aimed at anyone looking for a greater understanding of how bundlers work and, by extension, how most modern frameworks are implemented.
|
||||
|
||||
## What is bundling
|
||||
|
||||
With the adoption of ECMAScript modules (ESM), browsers can now resolve `import`/`export` statements in JavaScript files loaded via `<script>` tags.
|
||||
|
||||
{% codetabs %}
|
||||
|
||||
```html#index.html
|
||||
<html>
|
||||
<head>
|
||||
<script type="module" src="/index.js" ></script>
|
||||
</head>
|
||||
</html>
|
||||
```
|
||||
|
||||
```js#index.js
|
||||
import {sayHello} from "./hello.js";
|
||||
|
||||
sayHello();
|
||||
```
|
||||
|
||||
```js#hello.js
|
||||
export function sayHello() {
|
||||
console.log("Hello, world!");
|
||||
}
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
|
||||
When a user visits this website, the files are loaded in the following order:
|
||||
|
||||
{% image src="/images/module_loading_unbundled.png" /%}
|
||||
|
||||
{% callout %}
|
||||
**Relative imports** — Relative imports are resolved relative to the URL of the importing file. Because we're importing `./hello.js` from `/index.js`, the browser resolves it to `/hello.js`. If instead we'd imported `./hello.js` from `/src/index.js`, the browser would have resolved it to `/src/hello.js`.
|
||||
{% /callout %}
|
||||
|
||||
This approach works, it requires three round-trip HTTP requests before the browser is ready to render the page. On slow internet connections, this may add up to a non-trivial delay.
|
||||
|
||||
This example is extremely simplistic. A modern app may be loading dozens of modules from `node_modules`, each consisting of hundred of files. Loading each of these files with a separate HTTP request becomes untenable very quickly. While most of these requests will be running in parallel, the number of round-trip requests can still be very high; plus, there are limits on how many simultaneous requests a browser can make.
|
||||
|
||||
{% callout %}
|
||||
Some recent advances like modulepreload and HTTP/3 are intended to solve some of these problems, but at the moment bundling is still the most performant approach.
|
||||
{% /callout %}
|
||||
|
||||
The answer: bundling.
|
||||
|
||||
## Entrypoints
|
||||
|
||||
A bundler accepts an "entrypoint" to your source code (in this case, `/index.js`) and outputs a single file containing all of the code needed to run your app. If does so by parsing your source code, reading the `import`/`export` statements, and building a "module graph" of your app's dependencies.
|
||||
|
||||
{% image src="/images/bundling.png" /%}
|
||||
|
||||
We can now load `/bundle.js` from our `index.html` file and eliminate a round trip request, decreasing load times for our app.
|
||||
|
||||
{% image src="/images/module_loading_bundled.png" /%}
|
||||
|
||||
## Loaders
|
||||
|
||||
Bundlers typically have some set of built-in "loaders".
|
||||
|
||||
## Transpilation
|
||||
|
||||
The JavaScript files above are just that: plain JavaScript. They can be directly executed by any modern browser.
|
||||
|
||||
But modern tooling goes far beyond HTML, JavaScript, and CSS. JSX, TypeScript, and PostCSS/CSS-in-JS are all popular technologies that involve non-standard syntax that must be converted into vanilla JavaScript and CSS before if can be consumed by a browser.
|
||||
|
||||
## Chunking
|
||||
|
||||
## Module resolution
|
||||
|
||||
## Plugins
|
||||
@@ -1,24 +1,15 @@
|
||||
---
|
||||
title: "File Types"
|
||||
description: "File types and loaders supported by Bun's bundler and runtime"
|
||||
---
|
||||
|
||||
The Bun bundler implements a set of default loaders out of the box. As a rule of thumb, the bundler and the runtime both support the same set of file types out of the box.
|
||||
|
||||
`.js` `.cjs` `.mjs` `.mts` `.cts` `.ts` `.tsx` `.jsx` `.css` `.json` `.jsonc` `.toml` `.yaml` `.yml` `.txt` `.wasm` `.node` `.html` `.sh`
|
||||
`.js` `.cjs` `.mjs` `.mts` `.cts` `.ts` `.tsx` `.jsx` `.toml` `.json` `.yaml` `.yml` `.txt` `.wasm` `.node` `.html`
|
||||
|
||||
Bun uses the file extension to determine which built-in _loader_ should be used to parse the file. Every loader has a name, such as `js`, `tsx`, or `json`. These names are used when building [plugins](/bundler/plugins) that extend Bun with custom loaders.
|
||||
Bun uses the file extension to determine which built-in _loader_ should be used to parse the file. Every loader has a name, such as `js`, `tsx`, or `json`. These names are used when building [plugins](https://bun.com/docs/bundler/plugins) that extend Bun with custom loaders.
|
||||
|
||||
You can explicitly specify which loader to use using the `'type'` import attribute.
|
||||
You can explicitly specify which loader to use using the 'loader' import attribute.
|
||||
|
||||
```ts
|
||||
import my_toml from "./my_file" with { type: "toml" };
|
||||
// or with dynamic imports
|
||||
const { default: my_toml } = await import("./my_file", { with: { type: "toml" } });
|
||||
import my_toml from "./my_file" with { loader: "toml" };
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Built-in loaders
|
||||
|
||||
### `js`
|
||||
@@ -66,9 +57,9 @@ pkg.name;
|
||||
|
||||
If a `.json` file is passed as an entrypoint to the bundler, it will be converted to a `.js` module that `export default`s the parsed object.
|
||||
|
||||
<CodeGroup>
|
||||
{% codetabs %}
|
||||
|
||||
```json Input
|
||||
```json#Input
|
||||
{
|
||||
"name": "John Doe",
|
||||
"age": 35,
|
||||
@@ -76,38 +67,15 @@ If a `.json` file is passed as an entrypoint to the bundler, it will be converte
|
||||
}
|
||||
```
|
||||
|
||||
```ts Output
|
||||
```js#Output
|
||||
export default {
|
||||
name: "John Doe",
|
||||
age: 35,
|
||||
email: "johndoe@example.com",
|
||||
};
|
||||
email: "johndoe@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
### `jsonc`
|
||||
|
||||
**JSON with Comments loader**. Default for `.jsonc`.
|
||||
|
||||
JSONC (JSON with Comments) files can be directly imported. Bun will parse them, stripping out comments and trailing commas.
|
||||
|
||||
```ts
|
||||
import config from "./config.jsonc";
|
||||
console.log(config);
|
||||
```
|
||||
|
||||
During bundling, the parsed JSONC is inlined into the bundle as a JavaScript object, identical to the `json` loader.
|
||||
|
||||
```ts
|
||||
var config = {
|
||||
option: "value",
|
||||
};
|
||||
```
|
||||
|
||||
<Note>
|
||||
Bun automatically uses the `jsonc` loader for `tsconfig.json`, `jsconfig.json`, `package.json`, and `bun.lock` files.
|
||||
</Note>
|
||||
{% /codetabs %}
|
||||
|
||||
### `toml`
|
||||
|
||||
@@ -135,23 +103,23 @@ config.logLevel;
|
||||
|
||||
If a `.toml` file is passed as an entrypoint, it will be converted to a `.js` module that `export default`s the parsed object.
|
||||
|
||||
<CodeGroup>
|
||||
{% codetabs %}
|
||||
|
||||
```toml Input
|
||||
```toml#Input
|
||||
name = "John Doe"
|
||||
age = 35
|
||||
email = "johndoe@example.com"
|
||||
```
|
||||
|
||||
```ts Output
|
||||
```js#Output
|
||||
export default {
|
||||
name: "John Doe",
|
||||
age: 35,
|
||||
email: "johndoe@example.com",
|
||||
};
|
||||
email: "johndoe@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
{% /codetabs %}
|
||||
|
||||
### `yaml`
|
||||
|
||||
@@ -161,41 +129,46 @@ YAML files can be directly imported. Bun will parse them with its fast native YA
|
||||
|
||||
```ts
|
||||
import config from "./config.yaml";
|
||||
console.log(config);
|
||||
config.database.host; // => "localhost"
|
||||
|
||||
// via import attribute:
|
||||
import data from "./data.txt" with { type: "yaml" };
|
||||
// import myCustomYAML from './my.config' with {type: "yaml"};
|
||||
```
|
||||
|
||||
During bundling, the parsed YAML is inlined into the bundle as a JavaScript object.
|
||||
|
||||
```ts
|
||||
var config = {
|
||||
name: "my-app",
|
||||
version: "1.0.0",
|
||||
database: {
|
||||
host: "localhost",
|
||||
port: 5432,
|
||||
},
|
||||
// ...other fields
|
||||
};
|
||||
config.database.host;
|
||||
```
|
||||
|
||||
If a `.yaml` or `.yml` file is passed as an entrypoint, it will be converted to a `.js` module that `export default`s the parsed object.
|
||||
|
||||
<CodeGroup>
|
||||
{% codetabs %}
|
||||
|
||||
```yaml Input
|
||||
```yaml#Input
|
||||
name: John Doe
|
||||
age: 35
|
||||
email: johndoe@example.com
|
||||
```
|
||||
|
||||
```ts Output
|
||||
```js#Output
|
||||
export default {
|
||||
name: "John Doe",
|
||||
age: 35,
|
||||
email: "johndoe@example.com",
|
||||
};
|
||||
email: "johndoe@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
{% /codetabs %}
|
||||
|
||||
For more details on YAML support including the runtime API `Bun.YAML.parse()`, see the [YAML API documentation](/docs/api/yaml).
|
||||
|
||||
### `text`
|
||||
|
||||
@@ -222,17 +195,17 @@ console.log(contents);
|
||||
|
||||
If a `.txt` file is passed as an entrypoint, it will be converted to a `.js` module that `export default`s the file contents.
|
||||
|
||||
<CodeGroup>
|
||||
{% codetabs %}
|
||||
|
||||
```txt Input
|
||||
```txt#Input
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
```ts Output
|
||||
```js#Output
|
||||
export default "Hello, world!";
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
{% /codetabs %}
|
||||
|
||||
### `napi`
|
||||
|
||||
@@ -251,7 +224,7 @@ In the bundler, `.node` files are handled using the [`file`](#file) loader.
|
||||
|
||||
**SQLite loader**. `with { "type": "sqlite" }` import attribute
|
||||
|
||||
In the runtime and bundler, SQLite databases can be directly imported. This will load the database using [`bun:sqlite`](/runtime/sqlite).
|
||||
In the runtime and bundler, SQLite databases can be directly imported. This will load the database using [`bun:sqlite`](https://bun.com/docs/api/sqlite).
|
||||
|
||||
```ts
|
||||
import db from "./my.db" with { type: "sqlite" };
|
||||
@@ -268,7 +241,7 @@ You can change this behavior with the `"embed"` attribute:
|
||||
import db from "./my.db" with { type: "sqlite", embed: "true" };
|
||||
```
|
||||
|
||||
When using a [standalone executable](/bundler/executables), the database is embedded into the single-file executable.
|
||||
When using a [standalone executable](https://bun.com/docs/bundler/executables), the database is embedded into the single-file executable.
|
||||
|
||||
Otherwise, the database to embed is copied into the `outdir` with a hashed filename.
|
||||
|
||||
@@ -283,37 +256,37 @@ The html loader processes HTML files and bundles any referenced assets. It will:
|
||||
|
||||
For example, given this HTML file:
|
||||
|
||||
<CodeGroup>
|
||||
{% codetabs %}
|
||||
|
||||
```html src/index.html
|
||||
```html#src/index.html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<img src="./image.jpg" alt="Local image" />
|
||||
<img src="https://example.com/image.jpg" alt="External image" />
|
||||
<img src="./image.jpg" alt="Local image">
|
||||
<img src="https://example.com/image.jpg" alt="External image">
|
||||
<script type="module" src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
{% /codetabs %}
|
||||
|
||||
It will output a new HTML file with the bundled assets:
|
||||
|
||||
<CodeGroup>
|
||||
{% codetabs %}
|
||||
|
||||
```html dist/output.html
|
||||
```html#dist/output.html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<img src="./image-HASHED.jpg" alt="Local image" />
|
||||
<img src="https://example.com/image.jpg" alt="External image" />
|
||||
<img src="./image-HASHED.jpg" alt="Local image">
|
||||
<img src="https://example.com/image.jpg" alt="External image">
|
||||
<script type="module" src="./output-ALSO-HASHED.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
{% /codetabs %}
|
||||
|
||||
Under the hood, it uses [`lol-html`](https://github.com/cloudflare/lol-html) to extract script and link tags as entrypoints, and other assets as external.
|
||||
|
||||
@@ -338,7 +311,7 @@ Currently, the list of selectors is:
|
||||
- `video[poster]`
|
||||
- `video[src]`
|
||||
|
||||
<Note>
|
||||
{% callout %}
|
||||
|
||||
**HTML Loader Behavior in Different Contexts**
|
||||
|
||||
@@ -350,28 +323,16 @@ The `html` loader behaves differently depending on how it's used:
|
||||
|
||||
3. **Full-stack Build:** When you run `bun build --target=bun server.ts` (where `server.ts` imports an HTML file), the import resolves to a manifest object that `Bun.serve` uses to efficiently serve pre-bundled assets in production.
|
||||
|
||||
</Note>
|
||||
|
||||
### `css`
|
||||
|
||||
**CSS loader**. Default for `.css`.
|
||||
|
||||
CSS files can be directly imported. This is primarily useful for [full-stack applications](/bundler/html-static) where CSS is bundled alongside HTML.
|
||||
|
||||
```ts
|
||||
import "./styles.css";
|
||||
```
|
||||
|
||||
There isn't any value returned from the import, it's only used for side effects.
|
||||
{% /callout %}
|
||||
|
||||
### `sh` loader
|
||||
|
||||
**Bun Shell loader**. Default for `.sh` files
|
||||
|
||||
This loader is used to parse [Bun Shell](/runtime/shell) scripts. It's only supported when starting Bun itself, so it's not available in the bundler or in the runtime.
|
||||
This loader is used to parse [Bun Shell](https://bun.com/docs/runtime/shell) scripts. It's only supported when starting Bun itself, so it's not available in the bundler or in the runtime.
|
||||
|
||||
```sh
|
||||
bun run ./script.sh
|
||||
$ bun run ./script.sh
|
||||
```
|
||||
|
||||
### `file`
|
||||
@@ -380,7 +341,7 @@ bun run ./script.sh
|
||||
|
||||
The file loader resolves the import as a _path/URL_ to the imported file. It's commonly used for referencing media or font assets.
|
||||
|
||||
```ts logo.ts
|
||||
```ts#logo.ts
|
||||
import logo from "./logo.svg";
|
||||
console.log(logo);
|
||||
```
|
||||
@@ -388,32 +349,47 @@ console.log(logo);
|
||||
_In the runtime_, Bun checks that the `logo.svg` file exists and converts it to an absolute path to the location of `logo.svg` on disk.
|
||||
|
||||
```bash
|
||||
bun run logo.ts
|
||||
$ bun run logo.ts
|
||||
/path/to/project/logo.svg
|
||||
```
|
||||
|
||||
_In the bundler_, things are slightly different. The file is copied into `outdir` as-is, and the import is resolved as a relative path pointing to the copied file.
|
||||
|
||||
```ts Output
|
||||
```ts#Output
|
||||
var logo = "./logo.svg";
|
||||
console.log(logo);
|
||||
```
|
||||
|
||||
If a value is specified for `publicPath`, the import will use value as a prefix to construct an absolute path/URL.
|
||||
|
||||
| Public path | Resolved import |
|
||||
| ---------------------------- | ---------------------------------- |
|
||||
| `""` (default) | `/logo.svg` |
|
||||
| `"/assets"` | `/assets/logo.svg` |
|
||||
| `"https://cdn.example.com/"` | `https://cdn.example.com/logo.svg` |
|
||||
{% table %}
|
||||
|
||||
<Note>
|
||||
The location and file name of the copied file is determined by the value of [`naming.asset`](/bundler#naming).
|
||||
</Note>
|
||||
This loader is copied into the `outdir` as-is. The name of the copied file is determined using the value of
|
||||
`naming.asset`.
|
||||
- Public path
|
||||
- Resolved import
|
||||
|
||||
<Accordion title="Fixing TypeScript import errors">
|
||||
---
|
||||
|
||||
- `""` (default)
|
||||
- `/logo.svg`
|
||||
|
||||
---
|
||||
|
||||
- `"/assets"`
|
||||
- `/assets/logo.svg`
|
||||
|
||||
---
|
||||
|
||||
- `"https://cdn.example.com/"`
|
||||
- `https://cdn.example.com/logo.svg`
|
||||
|
||||
{% /table %}
|
||||
|
||||
{% callout %}
|
||||
The location and file name of the copied file is determined by the value of [`naming.asset`](https://bun.com/docs/bundler#naming).
|
||||
{% /callout %}
|
||||
This loader is copied into the `outdir` as-is. The name of the copied file is determined using the value of `naming.asset`.
|
||||
|
||||
{% details summary="Fixing TypeScript import errors" %}
|
||||
If you're using TypeScript, you may get an error like this:
|
||||
|
||||
```ts
|
||||
@@ -431,5 +407,4 @@ declare module "*.svg" {
|
||||
```
|
||||
|
||||
This tells TypeScript that any default imports from `.svg` should be treated as a string.
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
@@ -1,451 +0,0 @@
|
||||
---
|
||||
title: Loaders
|
||||
description: Built-in loaders for the Bun bundler and runtime
|
||||
---
|
||||
|
||||
The Bun bundler implements a set of default loaders out of the box.
|
||||
|
||||
> As a rule of thumb: **the bundler and the runtime both support the same set of file types out of the box.**
|
||||
|
||||
`.js` `.cjs` `.mjs` `.mts` `.cts` `.ts` `.tsx` `.jsx` `.css` `.json` `.jsonc` `.toml` `.yaml` `.yml` `.txt` `.wasm` `.node` `.html` `.sh`
|
||||
|
||||
Bun uses the file extension to determine which built-in loader should be used to parse the file. Every loader has a name, such as `js`, `tsx`, or `json`. These names are used when building plugins that extend Bun with custom loaders.
|
||||
|
||||
You can explicitly specify which loader to use using the `'type'` import attribute.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import my_toml from "./my_file" with { type: "toml" };
|
||||
// or with dynamic imports
|
||||
const { default: my_toml } = await import("./my_file", { with: { type: "toml" } });
|
||||
```
|
||||
|
||||
## Built-in loaders
|
||||
|
||||
### `js`
|
||||
|
||||
**JavaScript loader.** Default for `.cjs` and `.mjs`.
|
||||
|
||||
Parses the code and applies a set of default transforms like dead-code elimination and tree shaking. Note that Bun does not attempt to down-convert syntax at the moment.
|
||||
|
||||
---
|
||||
|
||||
### `jsx`
|
||||
|
||||
**JavaScript + JSX loader.** Default for `.js` and `.jsx`.
|
||||
|
||||
Same as the `js` loader, but JSX syntax is supported. By default, JSX is down-converted to plain JavaScript; the details of how this is done depends on the `jsx*` compiler options in your `tsconfig.json`. Refer to the [TypeScript documentation on JSX](https://www.typescriptlang.org/tsconfig#jsx) for more information.
|
||||
|
||||
---
|
||||
|
||||
### `ts`
|
||||
|
||||
**TypeScript loader.** Default for `.ts`, `.mts`, and `.cts`.
|
||||
|
||||
Strips out all TypeScript syntax, then behaves identically to the `js` loader. Bun does not perform typechecking.
|
||||
|
||||
---
|
||||
|
||||
### `tsx`
|
||||
|
||||
**TypeScript + JSX loader.** Default for `.tsx`.
|
||||
|
||||
Transpiles both TypeScript and JSX to vanilla JavaScript.
|
||||
|
||||
---
|
||||
|
||||
### `json`
|
||||
|
||||
**JSON loader.** Default for `.json`.
|
||||
|
||||
JSON files can be directly imported.
|
||||
|
||||
```js
|
||||
import pkg from "./package.json";
|
||||
pkg.name; // => "my-package"
|
||||
```
|
||||
|
||||
During bundling, the parsed JSON is inlined into the bundle as a JavaScript object.
|
||||
|
||||
```js
|
||||
const pkg = {
|
||||
name: "my-package",
|
||||
// ... other fields
|
||||
};
|
||||
|
||||
pkg.name;
|
||||
```
|
||||
|
||||
If a `.json` file is passed as an entrypoint to the bundler, it will be converted to a `.js` module that `export default`s the parsed object.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```json Input
|
||||
{
|
||||
"name": "John Doe",
|
||||
"age": 35,
|
||||
"email": "johndoe@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
```js Output
|
||||
export default {
|
||||
name: "John Doe",
|
||||
age: 35,
|
||||
email: "johndoe@example.com",
|
||||
};
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
---
|
||||
|
||||
### `jsonc`
|
||||
|
||||
**JSON with Comments loader.** Default for `.jsonc`.
|
||||
|
||||
JSONC (JSON with Comments) files can be directly imported. Bun will parse them, stripping out comments and trailing commas.
|
||||
|
||||
```js
|
||||
import config from "./config.jsonc";
|
||||
console.log(config);
|
||||
```
|
||||
|
||||
During bundling, the parsed JSONC is inlined into the bundle as a JavaScript object, identical to the `json` loader.
|
||||
|
||||
```js
|
||||
var config = {
|
||||
option: "value",
|
||||
};
|
||||
```
|
||||
|
||||
<Note>
|
||||
Bun automatically uses the `jsonc` loader for `tsconfig.json`, `jsconfig.json`, `package.json`, and `bun.lock` files.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
### `toml`
|
||||
|
||||
**TOML loader.** Default for `.toml`.
|
||||
|
||||
TOML files can be directly imported. Bun will parse them with its fast native TOML parser.
|
||||
|
||||
```js
|
||||
import config from "./bunfig.toml";
|
||||
config.logLevel; // => "debug"
|
||||
|
||||
// via import attribute:
|
||||
// import myCustomTOML from './my.config' with {type: "toml"};
|
||||
```
|
||||
|
||||
During bundling, the parsed TOML is inlined into the bundle as a JavaScript object.
|
||||
|
||||
```js
|
||||
var config = {
|
||||
logLevel: "debug",
|
||||
// ...other fields
|
||||
};
|
||||
config.logLevel;
|
||||
```
|
||||
|
||||
If a `.toml` file is passed as an entrypoint, it will be converted to a `.js` module that `export default`s the parsed object.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```toml Input
|
||||
name = "John Doe"
|
||||
age = 35
|
||||
email = "johndoe@example.com"
|
||||
```
|
||||
|
||||
```js Output
|
||||
export default {
|
||||
name: "John Doe",
|
||||
age: 35,
|
||||
email: "johndoe@example.com",
|
||||
};
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
---
|
||||
|
||||
### `yaml`
|
||||
|
||||
**YAML loader.** Default for `.yaml` and `.yml`.
|
||||
|
||||
YAML files can be directly imported. Bun will parse them with its fast native YAML parser.
|
||||
|
||||
```js
|
||||
import config from "./config.yaml";
|
||||
console.log(config);
|
||||
|
||||
// via import attribute:
|
||||
import data from "./data.txt" with { type: "yaml" };
|
||||
```
|
||||
|
||||
During bundling, the parsed YAML is inlined into the bundle as a JavaScript object.
|
||||
|
||||
```js
|
||||
var config = {
|
||||
name: "my-app",
|
||||
version: "1.0.0",
|
||||
// ...other fields
|
||||
};
|
||||
```
|
||||
|
||||
If a `.yaml` or `.yml` file is passed as an entrypoint, it will be converted to a `.js` module that `export default`s the parsed object.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```yaml Input
|
||||
name: John Doe
|
||||
age: 35
|
||||
email: johndoe@example.com
|
||||
```
|
||||
|
||||
```js Output
|
||||
export default {
|
||||
name: "John Doe",
|
||||
age: 35,
|
||||
email: "johndoe@example.com",
|
||||
};
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
---
|
||||
|
||||
### `text`
|
||||
|
||||
**Text loader.** Default for `.txt`.
|
||||
|
||||
The contents of the text file are read and inlined into the bundle as a string. Text files can be directly imported. The file is read and returned as a string.
|
||||
|
||||
```js
|
||||
import contents from "./file.txt";
|
||||
console.log(contents); // => "Hello, world!"
|
||||
|
||||
// To import an html file as text
|
||||
// The "type" attribute can be used to override the default loader.
|
||||
import html from "./index.html" with { type: "text" };
|
||||
```
|
||||
|
||||
When referenced during a build, the contents are inlined into the bundle as a string.
|
||||
|
||||
```js
|
||||
var contents = `Hello, world!`;
|
||||
console.log(contents);
|
||||
```
|
||||
|
||||
If a `.txt` file is passed as an entrypoint, it will be converted to a `.js` module that `export default`s the file contents.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```txt Input
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
```js Output
|
||||
export default "Hello, world!";
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
---
|
||||
|
||||
### `napi`
|
||||
|
||||
**Native addon loader.** Default for `.node`.
|
||||
|
||||
In the runtime, native addons can be directly imported.
|
||||
|
||||
```js
|
||||
import addon from "./addon.node";
|
||||
console.log(addon);
|
||||
```
|
||||
|
||||
<Note>In the bundler, `.node` files are handled using the file loader.</Note>
|
||||
|
||||
---
|
||||
|
||||
### `sqlite`
|
||||
|
||||
**SQLite loader.** Requires `with { "type": "sqlite" }` import attribute.
|
||||
|
||||
In the runtime and bundler, SQLite databases can be directly imported. This will load the database using `bun:sqlite`.
|
||||
|
||||
```js
|
||||
import db from "./my.db" with { type: "sqlite" };
|
||||
```
|
||||
|
||||
<Warning>This is only supported when the target is `bun`.</Warning>
|
||||
|
||||
By default, the database is external to the bundle (so that you can potentially use a database loaded elsewhere), so the database file on-disk won't be bundled into the final output.
|
||||
|
||||
You can change this behavior with the `"embed"` attribute:
|
||||
|
||||
```js
|
||||
// embed the database into the bundle
|
||||
import db from "./my.db" with { type: "sqlite", embed: "true" };
|
||||
```
|
||||
|
||||
<Info>
|
||||
When using a standalone executable, the database is embedded into the single-file executable.
|
||||
|
||||
Otherwise, the database to embed is copied into the `outdir` with a hashed filename.
|
||||
|
||||
</Info>
|
||||
|
||||
---
|
||||
|
||||
### `html`
|
||||
|
||||
**HTML loader.** Default for `.html`.
|
||||
|
||||
The `html` loader processes HTML files and bundles any referenced assets. It will:
|
||||
|
||||
- Bundle and hash referenced JavaScript files (`<script src="...">`)
|
||||
- Bundle and hash referenced CSS files (`<link rel="stylesheet" href="...">`)
|
||||
- Hash referenced images (`<img src="...">`)
|
||||
- Preserve external URLs (by default, anything starting with `http://` or `https://`)
|
||||
|
||||
For example, given this HTML file:
|
||||
|
||||
```html title="src/index.html" icon="file-code"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<img src="./image.jpg" alt="Local image" />
|
||||
<img src="https://example.com/image.jpg" alt="External image" />
|
||||
<script type="module" src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
It will output a new HTML file with the bundled assets:
|
||||
|
||||
```html title="dist/index.html" icon="file-code"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<img src="./image-HASHED.jpg" alt="Local image" />
|
||||
<img src="https://example.com/image.jpg" alt="External image" />
|
||||
<script type="module" src="./output-ALSO-HASHED.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Under the hood, it uses [`lol-html`](https://github.com/cloudflare/lol-html) to extract script and link tags as entrypoints, and other assets as external.
|
||||
|
||||
<Accordion title="List of supported HTML selectors">
|
||||
Currently, the list of selectors is:
|
||||
|
||||
- `audio[src]`
|
||||
- `iframe[src]`
|
||||
- `img[src]`
|
||||
- `img[srcset]`
|
||||
- `link:not([rel~='stylesheet']):not([rel~='modulepreload']):not([rel~='manifest']):not([rel~='icon']):not([rel~='apple-touch-icon'])[href]`
|
||||
- `link[as='font'][href], link[type^='font/'][href]`
|
||||
- `link[as='image'][href]`
|
||||
- `link[as='style'][href]`
|
||||
- `link[as='video'][href], link[as='audio'][href]`
|
||||
- `link[as='worker'][href]`
|
||||
- `link[rel='icon'][href], link[rel='apple-touch-icon'][href]`
|
||||
- `link[rel='manifest'][href]`
|
||||
- `link[rel='stylesheet'][href]`
|
||||
- `script[src]`
|
||||
- `source[src]`
|
||||
- `source[srcset]`
|
||||
- `video[poster]`
|
||||
- `video[src]`
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Note>
|
||||
|
||||
**HTML Loader Behavior in Different Contexts**
|
||||
|
||||
The `html` loader behaves differently depending on how it's used:
|
||||
|
||||
- Static Build: When you run `bun build ./index.html`, Bun produces a static site with all assets bundled and hashed.
|
||||
- Runtime: When you run `bun run server.ts` (where `server.ts` imports an HTML file), Bun bundles assets on-the-fly during development, enabling features like hot module replacement.
|
||||
- Full-stack Build: When you run `bun build --target=bun server.ts` (where `server.ts` imports an HTML file), the import resolves to a manifest object that `Bun.serve` uses to efficiently serve pre-bundled assets in production.
|
||||
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
### `css`
|
||||
|
||||
**CSS loader.** Default for `.css`.
|
||||
|
||||
CSS files can be directly imported. The bundler will parse and bundle CSS files, handling `@import` statements and `url()` references.
|
||||
|
||||
```js
|
||||
import "./styles.css";
|
||||
```
|
||||
|
||||
During bundling, all imported CSS files are bundled together into a single `.css` file in the output directory.
|
||||
|
||||
```css
|
||||
.my-class {
|
||||
background: url("./image.png");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `sh`
|
||||
|
||||
**Bun Shell loader.** Default for `.sh` files.
|
||||
|
||||
This loader is used to parse Bun Shell scripts. It's only supported when starting Bun itself, so it's not available in the bundler or in the runtime.
|
||||
|
||||
```bash
|
||||
bun run ./script.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `file`
|
||||
|
||||
**File loader.** Default for all unrecognized file types.
|
||||
|
||||
The file loader resolves the import as a path/URL to the imported file. It's commonly used for referencing media or font assets.
|
||||
|
||||
```js
|
||||
// logo.ts
|
||||
import logo from "./logo.svg";
|
||||
console.log(logo);
|
||||
```
|
||||
|
||||
In the runtime, Bun checks that the `logo.svg` file exists and converts it to an absolute path to the location of `logo.svg` on disk.
|
||||
|
||||
```bash
|
||||
bun run logo.ts
|
||||
# Output: /path/to/project/logo.svg
|
||||
```
|
||||
|
||||
In the bundler, things are slightly different. The file is copied into `outdir` as-is, and the import is resolved as a relative path pointing to the copied file.
|
||||
|
||||
```js
|
||||
// Output
|
||||
var logo = "./logo.svg";
|
||||
console.log(logo);
|
||||
```
|
||||
|
||||
If a value is specified for `publicPath`, the import will use value as a prefix to construct an absolute path/URL.
|
||||
|
||||
| Public path | Resolved import |
|
||||
| ---------------------------- | ---------------------------------- |
|
||||
| `""` (default) | `/logo.svg` |
|
||||
| `"/assets"` | `/assets/logo.svg` |
|
||||
| `"https://cdn.example.com/"` | `https://cdn.example.com/logo.svg` |
|
||||
|
||||
<Note>
|
||||
The location and file name of the copied file is determined by the value of `naming.asset`.
|
||||
|
||||
This loader is copied into the `outdir` as-is. The name of the copied file is determined using the value of `naming.asset`.
|
||||
|
||||
</Note>
|
||||
@@ -1,13 +1,10 @@
|
||||
---
|
||||
title: Macros
|
||||
description: Run JavaScript functions at bundle-time with Bun macros
|
||||
---
|
||||
Macros are a mechanism for running JavaScript functions _at bundle-time_. The value returned from these functions are directly inlined into your bundle.
|
||||
|
||||
Macros are a mechanism for running JavaScript functions at bundle-time. The value returned from these functions are directly inlined into your bundle.
|
||||
<!-- embed the result in your (browser) bundle. This is useful for things like embedding the current Git commit hash in your code, making fetch requests to your API at build-time, dead code elimination, and more. -->
|
||||
|
||||
As a toy example, consider this simple function that returns a random number.
|
||||
|
||||
```ts title="random.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
export function random() {
|
||||
return Math.random();
|
||||
}
|
||||
@@ -15,28 +12,24 @@ export function random() {
|
||||
|
||||
This is just a regular function in a regular file, but we can use it as a macro like so:
|
||||
|
||||
```tsx title="cli.tsx" icon="/icons/typescript.svg"
|
||||
import { random } from "./random.ts" with { type: "macro" };
|
||||
```ts#cli.tsx
|
||||
import { random } from './random.ts' with { type: 'macro' };
|
||||
|
||||
console.log(`Your random number is ${random()}`);
|
||||
```
|
||||
|
||||
<Note>
|
||||
Macros are indicated using import attribute syntax. If you haven't seen this syntax before, it's a Stage 3 TC39
|
||||
proposal that lets you attach additional metadata to import statements.
|
||||
</Note>
|
||||
{% callout %}
|
||||
**Note** — Macros are indicated using [_import attribute_](https://github.com/tc39/proposal-import-attributes) syntax. If you haven't seen this syntax before, it's a Stage 3 TC39 proposal that lets you attach additional metadata to `import` statements.
|
||||
{% /callout %}
|
||||
|
||||
Now we'll bundle this file with `bun build`. The bundled file will be printed to stdout.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build ./cli.tsx
|
||||
```
|
||||
|
||||
```js
|
||||
```bash
|
||||
$ bun build ./cli.tsx
|
||||
console.log(`Your random number is ${0.6805550949689833}`);
|
||||
```
|
||||
|
||||
As you can see, the source code of the `random` function occurs nowhere in the bundle. Instead, it is executed during bundling and function call (`random()`) is replaced with the result of the function. Since the source code will never be included in the bundle, macros can safely perform privileged operations like reading from a database.
|
||||
As you can see, the source code of the `random` function occurs nowhere in the bundle. Instead, it is executed _during bundling_ and function call (`random()`) is replaced with the result of the function. Since the source code will never be included in the bundle, macros can safely perform privileged operations like reading from a database.
|
||||
|
||||
## When to use macros
|
||||
|
||||
@@ -48,8 +41,8 @@ If you find yourself running a lot of code at bundle-time though, consider runni
|
||||
|
||||
Bun Macros are import statements annotated using either:
|
||||
|
||||
- `with { type: 'macro' }` — an import attribute, a Stage 3 ECMA Script proposal
|
||||
- `assert { type: 'macro' }` — an import assertion, an earlier incarnation of import attributes that has now been abandoned (but is already supported by a number of browsers and runtimes)
|
||||
- `with { type: 'macro' }` — an [import attribute](https://github.com/tc39/proposal-import-attributes), a Stage 3 ECMA Scrd
|
||||
- `assert { type: 'macro' }` — an import assertion, an earlier incarnation of import attributes that has now been abandoned (but is [already supported](https://caniuse.com/mdn-javascript_statements_import_import_assertions) by a number of browsers and runtimes)
|
||||
|
||||
## Security considerations
|
||||
|
||||
@@ -57,7 +50,7 @@ Macros must explicitly be imported with `{ type: "macro" }` in order to be execu
|
||||
|
||||
You can disable macros entirely by passing the `--no-macros` flag to Bun. It produces a build error like this:
|
||||
|
||||
```
|
||||
```js
|
||||
error: Macros are disabled
|
||||
|
||||
foo();
|
||||
@@ -65,9 +58,9 @@ foo();
|
||||
./hello.js:3:1 53
|
||||
```
|
||||
|
||||
To reduce the potential attack surface for malicious packages, macros cannot be invoked from inside `node_modules/**/*`. If a package attempts to invoke a macro, you'll see an error like this:
|
||||
To reduce the potential attack surface for malicious packages, macros cannot be _invoked_ from inside `node_modules/**/*`. If a package attempts to invoke a macro, you'll see an error like this:
|
||||
|
||||
```
|
||||
```js
|
||||
error: For security reasons, macros cannot be run from node_modules.
|
||||
|
||||
beEvil();
|
||||
@@ -77,17 +70,17 @@ node_modules/evil/index.js:3:1 50
|
||||
|
||||
Your application code can still import macros from `node_modules` and invoke them.
|
||||
|
||||
```ts title="cli.tsx" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { macro } from "some-package" with { type: "macro" };
|
||||
|
||||
macro();
|
||||
```
|
||||
|
||||
## Export condition "macro"
|
||||
## Export condition `"macro"`
|
||||
|
||||
When shipping a library containing a macro to npm or another package registry, use the `"macro"` export condition to provide a special version of your package exclusively for the macro environment.
|
||||
When shipping a library containing a macro to `npm` or another package registry, use the `"macro"` [export condition](https://nodejs.org/api/packages.html#conditional-exports) to provide a special version of your package exclusively for the macro environment.
|
||||
|
||||
```json title="package.json" icon="file-json"
|
||||
```jsonc#package.json
|
||||
{
|
||||
"name": "my-package",
|
||||
"exports": {
|
||||
@@ -101,7 +94,7 @@ When shipping a library containing a macro to npm or another package registry, u
|
||||
|
||||
With this configuration, users can consume your package at runtime or at bundle-time using the same import specifier:
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import pkg from "my-package"; // runtime import
|
||||
import { macro } from "my-package" with { type: "macro" }; // macro import
|
||||
```
|
||||
@@ -112,15 +105,15 @@ The first import will resolve to `./node_modules/my-package/index.js`, while the
|
||||
|
||||
When Bun's transpiler sees a macro import, it calls the function inside the transpiler using Bun's JavaScript runtime and converts the return value from JavaScript into an AST node. These JavaScript functions are called at bundle-time, not runtime.
|
||||
|
||||
Macros are executed synchronously in the transpiler during the visiting phase—before plugins and before the transpiler generates the AST. They are executed in the order they are imported. The transpiler will wait for the macro to finish executing before continuing. The transpiler will also await any Promise returned by a macro.
|
||||
Macros are executed synchronously in the transpiler during the visiting phase—before plugins and before the transpiler generates the AST. They are executed in the order they are imported. The transpiler will wait for the macro to finish executing before continuing. The transpiler will also `await` any `Promise` returned by a macro.
|
||||
|
||||
Bun's bundler is multi-threaded. As such, macros execute in parallel inside of multiple spawned JavaScript "workers".
|
||||
|
||||
## Dead code elimination
|
||||
|
||||
The bundler performs dead code elimination after running and inlining macros. So given the following macro:
|
||||
The bundler performs dead code elimination _after_ running and inlining macros. So given the following macro:
|
||||
|
||||
```ts title="returnFalse.ts" icon="/icons/typescript.svg"
|
||||
```ts#returnFalse.ts
|
||||
export function returnFalse() {
|
||||
return false;
|
||||
}
|
||||
@@ -128,7 +121,7 @@ export function returnFalse() {
|
||||
|
||||
...then bundling the following file will produce an empty bundle, provided that the minify syntax option is enabled.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { returnFalse } from "./returnFalse.ts" with { type: "macro" };
|
||||
|
||||
if (returnFalse()) {
|
||||
@@ -140,19 +133,19 @@ if (returnFalse()) {
|
||||
|
||||
Bun's transpiler needs to be able to serialize the result of the macro so it can be inlined into the AST. All JSON-compatible data structures are supported:
|
||||
|
||||
```ts title="macro.ts" icon="/icons/typescript.svg"
|
||||
```ts#macro.ts
|
||||
export function getObject() {
|
||||
return {
|
||||
foo: "bar",
|
||||
baz: 123,
|
||||
array: [1, 2, { nested: "value" }],
|
||||
array: [ 1, 2, { nested: "value" }],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Macros can be async, or return Promise instances. Bun's transpiler will automatically await the Promise and inline the result.
|
||||
Macros can be async, or return `Promise` instances. Bun's transpiler will automatically `await` the `Promise` and inline the result.
|
||||
|
||||
```ts title="macro.ts" icon="/icons/typescript.svg"
|
||||
```ts#macro.ts
|
||||
export async function getText() {
|
||||
return "async value";
|
||||
}
|
||||
@@ -160,21 +153,21 @@ export async function getText() {
|
||||
|
||||
The transpiler implements special logic for serializing common data formats like `Response`, `Blob`, `TypedArray`.
|
||||
|
||||
- **TypedArray**: Resolves to a base64-encoded string.
|
||||
- **Response**: Bun will read the `Content-Type` and serialize accordingly; for instance, a Response with type `application/json` will be automatically parsed into an object and `text/plain` will be inlined as a string. Responses with an unrecognized or undefined type will be base-64 encoded.
|
||||
- **Blob**: As with Response, the serialization depends on the `type` property.
|
||||
- `TypedArray`: Resolves to a base64-encoded string.
|
||||
- `Response`: Bun will read the `Content-Type` and serialize accordingly; for instance, a `Response` with type `application/json` will be automatically parsed into an object and `text/plain` will be inlined as a string. Responses with an unrecognized or `undefined` `type` will be base-64 encoded.
|
||||
- `Blob`: As with `Response`, the serialization depends on the `type` property.
|
||||
|
||||
The result of `fetch` is `Promise<Response>`, so it can be directly returned.
|
||||
|
||||
```ts title="macro.ts" icon="/icons/typescript.svg"
|
||||
```ts#macro.ts
|
||||
export function getObject() {
|
||||
return fetch("https://bun.com");
|
||||
return fetch("https://bun.com")
|
||||
}
|
||||
```
|
||||
|
||||
Functions and instances of most classes (except those mentioned above) are not serializable.
|
||||
|
||||
```ts title="macro.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
export function getText(url: string) {
|
||||
// this doesn't work!
|
||||
return () => {};
|
||||
@@ -185,7 +178,7 @@ export function getText(url: string) {
|
||||
|
||||
Macros can accept inputs, but only in limited cases. The value must be statically known. For example, the following is not allowed:
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { getText } from "./getText.ts" with { type: "macro" };
|
||||
|
||||
export function howLong() {
|
||||
@@ -199,7 +192,7 @@ export function howLong() {
|
||||
|
||||
However, if the value of `foo` is known at bundle-time (say, if it's a constant or the result of another macro) then it's allowed:
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { getText } from "./getText.ts" with { type: "macro" };
|
||||
import { getFoo } from "./getFoo.ts" with { type: "macro" };
|
||||
|
||||
@@ -213,7 +206,7 @@ export function howLong() {
|
||||
|
||||
This outputs:
|
||||
|
||||
```js
|
||||
```ts
|
||||
function howLong() {
|
||||
console.log("The page is", 1322, "characters long");
|
||||
}
|
||||
@@ -224,9 +217,11 @@ export { howLong };
|
||||
|
||||
### Embed latest git commit hash
|
||||
|
||||
```ts title="getGitCommitHash.ts" icon="/icons/typescript.svg"
|
||||
{% codetabs %}
|
||||
|
||||
```ts#getGitCommitHash.ts
|
||||
export function getGitCommitHash() {
|
||||
const { stdout } = Bun.spawnSync({
|
||||
const {stdout} = Bun.spawnSync({
|
||||
cmd: ["git", "rev-parse", "HEAD"],
|
||||
stdout: "pipe",
|
||||
});
|
||||
@@ -235,32 +230,33 @@ export function getGitCommitHash() {
|
||||
}
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
|
||||
<!-- --target=browser so they can clearly see it's for browsers -->
|
||||
|
||||
When we build it, the `getGitCommitHash` is replaced with the result of calling the function:
|
||||
|
||||
<CodeGroup>
|
||||
{% codetabs %}
|
||||
|
||||
```ts input
|
||||
import { getGitCommitHash } from "./getGitCommitHash.ts" with { type: "macro" };
|
||||
```ts#input
|
||||
import { getGitCommitHash } from './getGitCommitHash.ts' with { type: 'macro' };
|
||||
|
||||
console.log(`The current Git commit hash is ${getGitCommitHash()}`);
|
||||
```
|
||||
|
||||
```ts output
|
||||
console.log(`The current Git commit hash is 3ee3259104e4507cf62c160f0ff5357ec4c7a7f8`);
|
||||
```bash#output
|
||||
console.log(`The current Git commit hash is 3ee3259104f`);
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
{% /codetabs %}
|
||||
|
||||
<Info>
|
||||
You're probably thinking "Why not just use `process.env.GIT_COMMIT_HASH`?" Well, you can do that too. But can you do
|
||||
this with an environment variable?
|
||||
</Info>
|
||||
You're probably thinking "Why not just use `process.env.GIT_COMMIT_HASH`?" Well, you can do that too. But can you do this with an environment variable?
|
||||
|
||||
### Make fetch() requests at bundle-time
|
||||
### Make `fetch()` requests at bundle-time
|
||||
|
||||
In this example, we make an outgoing HTTP request using `fetch()`, parse the HTML response using `HTMLRewriter`, and return an object containing the title and meta tags–all at bundle-time.
|
||||
|
||||
```ts title="meta.ts" icon="/icons/typescript.svg"
|
||||
```ts
|
||||
export async function extractMetaTags(url: string) {
|
||||
const response = await fetch(url);
|
||||
const meta = {
|
||||
@@ -275,7 +271,9 @@ export async function extractMetaTags(url: string) {
|
||||
.on("meta", {
|
||||
element(element) {
|
||||
const name =
|
||||
element.getAttribute("name") || element.getAttribute("property") || element.getAttribute("itemprop");
|
||||
element.getAttribute("name") ||
|
||||
element.getAttribute("property") ||
|
||||
element.getAttribute("itemprop");
|
||||
|
||||
if (name) meta[name] = element.getAttribute("content");
|
||||
},
|
||||
@@ -286,12 +284,14 @@ export async function extractMetaTags(url: string) {
|
||||
}
|
||||
```
|
||||
|
||||
The `extractMetaTags` function is erased at bundle-time and replaced with the result of the function call. This means that the fetch request happens at bundle-time, and the result is embedded in the bundle. Also, the branch throwing the error is eliminated since it's unreachable.
|
||||
<!-- --target=browser so they can clearly see it's for browsers -->
|
||||
|
||||
<CodeGroup>
|
||||
The `extractMetaTags` function is erased at bundle-time and replaced with the result of the function call. This means that the `fetch` request happens at bundle-time, and the result is embedded in the bundle. Also, the branch throwing the error is eliminated since it's unreachable.
|
||||
|
||||
```jsx input
|
||||
import { extractMetaTags } from "./meta.ts" with { type: "macro" };
|
||||
{% codetabs %}
|
||||
|
||||
```ts#input
|
||||
import { extractMetaTags } from './meta.ts' with { type: 'macro' };
|
||||
|
||||
export const Head = () => {
|
||||
const headTags = extractMetaTags("https://example.com");
|
||||
@@ -300,29 +300,30 @@ export const Head = () => {
|
||||
throw new Error("Expected title to be 'Example Domain'");
|
||||
}
|
||||
|
||||
return (
|
||||
<head>
|
||||
<title>{headTags.title}</title>
|
||||
<meta name="viewport" content={headTags.viewport} />
|
||||
</head>
|
||||
);
|
||||
return <head>
|
||||
<title>{headTags.title}</title>
|
||||
<meta name="viewport" content={headTags.viewport} />
|
||||
</head>;
|
||||
};
|
||||
```
|
||||
|
||||
```jsx output
|
||||
```ts#output
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
export const Head = () => {
|
||||
const headTags = {
|
||||
title: "Example Domain",
|
||||
viewport: "width=device-width, initial-scale=1",
|
||||
};
|
||||
|
||||
return (
|
||||
<head>
|
||||
<title>{headTags.title}</title>
|
||||
<meta name="viewport" content={headTags.viewport} />
|
||||
</head>
|
||||
);
|
||||
jsxs("head", {
|
||||
children: [
|
||||
jsx("title", {
|
||||
children: "Example Domain",
|
||||
}),
|
||||
jsx("meta", {
|
||||
name: "viewport",
|
||||
content: "width=device-width, initial-scale=1",
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
export { Head };
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
{% /codetabs %}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,3 @@
|
||||
---
|
||||
title: "Plugins"
|
||||
description: "Universal plugin API for extending Bun's runtime and bundler"
|
||||
---
|
||||
|
||||
Bun provides a universal plugin API that can be used to extend both the _runtime_ and _bundler_.
|
||||
|
||||
Plugins intercept imports and perform custom loading logic: reading files, transpiling code, etc. They can be used to add support for additional file types, like `.scss` or `.yaml`. In the context of Bun's bundler, plugins can be used to implement framework-level features like CSS extraction, macros, and client-server code co-location.
|
||||
@@ -14,15 +9,17 @@ Plugins can register callbacks to be run at various points in the lifecycle of a
|
||||
- [`onStart()`](#onstart): Run once the bundler has started a bundle
|
||||
- [`onResolve()`](#onresolve): Run before a module is resolved
|
||||
- [`onLoad()`](#onload): Run before a module is loaded.
|
||||
- [`onEnd()`](#onend): Run after the bundle has completed
|
||||
- [`onBeforeParse()`](#onbeforeparse): Run zero-copy native addons in the parser thread before a file is parsed.
|
||||
|
||||
### Reference
|
||||
|
||||
A rough overview of the types (please refer to Bun's `bun.d.ts` for the full type definitions):
|
||||
|
||||
```ts Plugin Types icon="/icons/typescript.svg"
|
||||
```ts
|
||||
type PluginBuilder = {
|
||||
onStart(callback: () => void): void;
|
||||
onEnd(callback: (result: BuildOutput) => void | Promise<void>): void;
|
||||
onResolve: (
|
||||
args: { filter: RegExp; namespace?: string },
|
||||
callback: (args: { path: string; importer: string }) => {
|
||||
@@ -42,28 +39,14 @@ type PluginBuilder = {
|
||||
config: BuildConfig;
|
||||
};
|
||||
|
||||
type Loader =
|
||||
| "js"
|
||||
| "jsx"
|
||||
| "ts"
|
||||
| "tsx"
|
||||
| "json"
|
||||
| "jsonc"
|
||||
| "toml"
|
||||
| "yaml"
|
||||
| "file"
|
||||
| "napi"
|
||||
| "wasm"
|
||||
| "text"
|
||||
| "css"
|
||||
| "html";
|
||||
type Loader = "js" | "jsx" | "ts" | "tsx" | "css" | "json" | "toml";
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
A plugin is defined as simple JavaScript object containing a `name` property and a `setup` function.
|
||||
|
||||
```tsx myPlugin.ts icon="/icons/typescript.svg"
|
||||
```tsx#myPlugin.ts
|
||||
import type { BunPlugin } from "bun";
|
||||
|
||||
const myPlugin: BunPlugin = {
|
||||
@@ -76,7 +59,7 @@ const myPlugin: BunPlugin = {
|
||||
|
||||
This plugin can be passed into the `plugins` array when calling `Bun.build`.
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
await Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
outdir: "./out",
|
||||
@@ -107,7 +90,7 @@ onStart(callback: () => void): Promise<void> | void;
|
||||
|
||||
Registers a callback to be run when the bundler starts a new bundle.
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { plugin } from "bun";
|
||||
|
||||
plugin({
|
||||
@@ -125,7 +108,7 @@ The callback can return a `Promise`. After the bundle process has initialized, t
|
||||
|
||||
For example:
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
const result = await Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
outdir: "./dist",
|
||||
@@ -180,7 +163,7 @@ The callback receives as input the _path_ to the matching module. The callback c
|
||||
|
||||
For example, redirecting all imports to `images/` to `./public/images/`:
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { plugin } from "bun";
|
||||
|
||||
plugin({
|
||||
@@ -225,7 +208,7 @@ The callback can return a new `contents` string for the module as well as a new
|
||||
|
||||
For example:
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { plugin } from "bun";
|
||||
|
||||
const envPlugin: BunPlugin = {
|
||||
@@ -262,7 +245,7 @@ This is useful for returning contents of a module that depends on other modules.
|
||||
|
||||
##### Example: tracking and reporting unused exports
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
```ts
|
||||
import { plugin } from "bun";
|
||||
|
||||
plugin({
|
||||
@@ -304,13 +287,60 @@ plugin({
|
||||
|
||||
Note that the `.defer()` function currently has the limitation that it can only be called once per `onLoad` callback.
|
||||
|
||||
### `onEnd`
|
||||
|
||||
```ts
|
||||
onEnd(callback: (result: BuildOutput) => void | Promise<void>): void;
|
||||
```
|
||||
|
||||
Registers a callback to be run when the bundler completes a bundle (whether successful or not).
|
||||
|
||||
The callback receives the `BuildOutput` object containing:
|
||||
|
||||
- `success`: boolean indicating if the build succeeded
|
||||
- `outputs`: array of generated build artifacts
|
||||
- `logs`: array of build messages (warnings, errors, etc.)
|
||||
|
||||
This is useful for post-processing, cleanup, notifications, or custom error handling.
|
||||
|
||||
```ts
|
||||
await Bun.build({
|
||||
entrypoints: ["./index.ts"],
|
||||
outdir: "./out",
|
||||
plugins: [
|
||||
{
|
||||
name: "onEnd example",
|
||||
setup(build) {
|
||||
build.onEnd(result => {
|
||||
if (result.success) {
|
||||
console.log(
|
||||
`✅ Build succeeded with ${result.outputs.length} outputs`,
|
||||
);
|
||||
} else {
|
||||
console.error(`❌ Build failed with ${result.logs.length} errors`);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
The `onEnd` callbacks are called:
|
||||
|
||||
- **Before** the build promise resolves or rejects
|
||||
- **After** all bundling is complete
|
||||
- **In the order** they were registered
|
||||
|
||||
Multiple plugins can register `onEnd` callbacks, and they will all be called sequentially. If an `onEnd` callback returns a promise, the build will wait for it to resolve before continuing.
|
||||
|
||||
## Native plugins
|
||||
|
||||
One of the reasons why Bun's bundler is so fast is that it is written in native code and leverages multi-threading to load and parse modules in parallel.
|
||||
|
||||
However, one limitation of plugins written in JavaScript is that JavaScript itself is single-threaded.
|
||||
|
||||
Native plugins are written as [NAPI](/runtime/node-api) modules and can be run on multiple threads. This allows native plugins to run much faster than JavaScript plugins.
|
||||
Native plugins are written as [NAPI](https://bun.com/docs/api/node-api) modules and can be run on multiple threads. This allows native plugins to run much faster than JavaScript plugins.
|
||||
|
||||
In addition, native plugins can skip unnecessary work such as the UTF-8 -> UTF-16 conversion needed to pass strings to JavaScript.
|
||||
|
||||
@@ -328,14 +358,14 @@ Native plugins are NAPI modules which expose lifecycle hooks as C ABI functions.
|
||||
|
||||
To create a native plugin, you must export a C ABI function which matches the signature of the native lifecycle hook you want to implement.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```bash
|
||||
bun add -g @napi-rs/cli
|
||||
napi new
|
||||
```
|
||||
|
||||
Then install this crate:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```bash
|
||||
cargo add bun-native-plugin
|
||||
```
|
||||
|
||||
@@ -344,7 +374,7 @@ will implement our native plugin.
|
||||
|
||||
Here's an example implementing the `onBeforeParse` hook:
|
||||
|
||||
```rs lib.rs icon="/icons/rust.svg"
|
||||
```rs
|
||||
use bun_native_plugin::{define_bun_plugin, OnBeforeParse, bun, Result, anyhow, BunLoader};
|
||||
use napi_derive::napi;
|
||||
|
||||
@@ -1,425 +0,0 @@
|
||||
---
|
||||
title: Plugins
|
||||
description: Universal plugin API for extending Bun's runtime and bundler
|
||||
---
|
||||
|
||||
Bun provides a universal plugin API that can be used to extend both the runtime and bundler.
|
||||
|
||||
Plugins intercept imports and perform custom loading logic: reading files, transpiling code, etc. They can be used to add support for additional file types, like `.scss` or `.yaml`. In the context of Bun's bundler, plugins can be used to implement framework-level features like CSS extraction, macros, and client-server code co-location.
|
||||
|
||||
## Lifecycle hooks
|
||||
|
||||
Plugins can register callbacks to be run at various points in the lifecycle of a bundle:
|
||||
|
||||
- `onStart()`: Run once the bundler has started a bundle
|
||||
- `onResolve()`: Run before a module is resolved
|
||||
- `onLoad()`: Run before a module is loaded
|
||||
- `onBeforeParse()`: Run zero-copy native addons in the parser thread before a file is parsed
|
||||
|
||||
## Reference
|
||||
|
||||
A rough overview of the types (please refer to Bun's `bun.d.ts` for the full type definitions):
|
||||
|
||||
```ts title="bun.d.ts" icon="/icons/typescript.svg"
|
||||
type PluginBuilder = {
|
||||
onStart(callback: () => void): void;
|
||||
onResolve: (
|
||||
args: { filter: RegExp; namespace?: string },
|
||||
callback: (args: { path: string; importer: string }) => {
|
||||
path: string;
|
||||
namespace?: string;
|
||||
} | void,
|
||||
) => void;
|
||||
onLoad: (
|
||||
args: { filter: RegExp; namespace?: string },
|
||||
defer: () => Promise<void>,
|
||||
callback: (args: { path: string }) => {
|
||||
loader?: Loader;
|
||||
contents?: string;
|
||||
exports?: Record<string, any>;
|
||||
},
|
||||
) => void;
|
||||
config: BuildConfig;
|
||||
};
|
||||
|
||||
type Loader =
|
||||
| "js"
|
||||
| "jsx"
|
||||
| "ts"
|
||||
| "tsx"
|
||||
| "json"
|
||||
| "jsonc"
|
||||
| "toml"
|
||||
| "yaml"
|
||||
| "file"
|
||||
| "napi"
|
||||
| "wasm"
|
||||
| "text"
|
||||
| "css"
|
||||
| "html";
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
A plugin is defined as simple JavaScript object containing a `name` property and a `setup` function.
|
||||
|
||||
```ts title="myPlugin.ts" icon="/icons/typescript.svg"
|
||||
import type { BunPlugin } from "bun";
|
||||
|
||||
const myPlugin: BunPlugin = {
|
||||
name: "Custom loader",
|
||||
setup(build) {
|
||||
// implementation
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
This plugin can be passed into the `plugins` array when calling `Bun.build`.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
await Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
outdir: "./out",
|
||||
plugins: [myPlugin],
|
||||
});
|
||||
```
|
||||
|
||||
## Plugin lifecycle
|
||||
|
||||
### Namespaces
|
||||
|
||||
`onLoad` and `onResolve` accept an optional `namespace` string. What is a namespace?
|
||||
|
||||
Every module has a namespace. Namespaces are used to prefix the import in transpiled code; for instance, a loader with a `filter: /\.yaml$/` and `namespace: "yaml:"` will transform an import from `./myfile.yaml` into `yaml:./myfile.yaml`.
|
||||
|
||||
The default namespace is `"file"` and it is not necessary to specify it, for instance: `import myModule from "./my-module.ts"` is the same as `import myModule from "file:./my-module.ts"`.
|
||||
|
||||
Other common namespaces are:
|
||||
|
||||
- `"bun"`: for Bun-specific modules (e.g. `"bun:test"`, `"bun:sqlite"`)
|
||||
- `"node"`: for Node.js modules (e.g. `"node:fs"`, `"node:path"`)
|
||||
|
||||
### onStart
|
||||
|
||||
```ts
|
||||
onStart(callback: () => void): Promise<void> | void;
|
||||
```
|
||||
|
||||
Registers a callback to be run when the bundler starts a new bundle.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import { plugin } from "bun";
|
||||
|
||||
plugin({
|
||||
name: "onStart example",
|
||||
|
||||
setup(build) {
|
||||
build.onStart(() => {
|
||||
console.log("Bundle started!");
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
The callback can return a Promise. After the bundle process has initialized, the bundler waits until all `onStart()` callbacks have completed before continuing.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
const result = await Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
outdir: "./dist",
|
||||
sourcemap: "external",
|
||||
plugins: [
|
||||
{
|
||||
name: "Sleep for 10 seconds",
|
||||
setup(build) {
|
||||
build.onStart(async () => {
|
||||
await Bun.sleep(10_000);
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Log bundle time to a file",
|
||||
setup(build) {
|
||||
build.onStart(async () => {
|
||||
const now = Date.now();
|
||||
await Bun.$`echo ${now} > bundle-time.txt`;
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
In the above example, Bun will wait until the first `onStart()` (sleeping for 10 seconds) has completed, as well as the second `onStart()` (writing the bundle time to a file).
|
||||
|
||||
<Note>
|
||||
`onStart()` callbacks (like every other lifecycle callback) do not have the ability to modify the `build.config`
|
||||
object. If you want to mutate `build.config`, you must do so directly in the `setup()` function.
|
||||
</Note>
|
||||
|
||||
### onResolve
|
||||
|
||||
```ts
|
||||
onResolve(
|
||||
args: { filter: RegExp; namespace?: string },
|
||||
callback: (args: { path: string; importer: string }) => {
|
||||
path: string;
|
||||
namespace?: string;
|
||||
} | void,
|
||||
): void;
|
||||
```
|
||||
|
||||
To bundle your project, Bun walks down the dependency tree of all modules in your project. For each imported module, Bun actually has to find and read that module. The "finding" part is known as "resolving" a module.
|
||||
|
||||
The `onResolve()` plugin lifecycle callback allows you to configure how a module is resolved.
|
||||
|
||||
The first argument to `onResolve()` is an object with a `filter` and `namespace` property. The `filter` is a regular expression which is run on the import string. Effectively, these allow you to filter which modules your custom resolution logic will apply to.
|
||||
|
||||
The second argument to `onResolve()` is a callback which is run for each module import Bun finds that matches the filter and namespace defined in the first argument.
|
||||
|
||||
The callback receives as input the path to the matching module. The callback can return a new path for the module. Bun will read the contents of the new path and parse it as a module.
|
||||
|
||||
For example, redirecting all imports to `images/` to `./public/images/`:
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import { plugin } from "bun";
|
||||
|
||||
plugin({
|
||||
name: "onResolve example",
|
||||
setup(build) {
|
||||
build.onResolve({ filter: /.*/, namespace: "file" }, args => {
|
||||
if (args.path.startsWith("images/")) {
|
||||
return {
|
||||
path: args.path.replace("images/", "./public/images/"),
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### onLoad
|
||||
|
||||
```ts
|
||||
onLoad(
|
||||
args: { filter: RegExp; namespace?: string },
|
||||
defer: () => Promise<void>,
|
||||
callback: (args: { path: string, importer: string, namespace: string, kind: ImportKind }) => {
|
||||
loader?: Loader;
|
||||
contents?: string;
|
||||
exports?: Record<string, any>;
|
||||
},
|
||||
): void;
|
||||
```
|
||||
|
||||
After Bun's bundler has resolved a module, it needs to read the contents of the module and parse it.
|
||||
|
||||
The `onLoad()` plugin lifecycle callback allows you to modify the contents of a module before it is read and parsed by Bun.
|
||||
|
||||
Like `onResolve()`, the first argument to `onLoad()` allows you to filter which modules this invocation of `onLoad()` will apply to.
|
||||
|
||||
The second argument to `onLoad()` is a callback which is run for each matching module before Bun loads the contents of the module into memory.
|
||||
|
||||
This callback receives as input the path to the matching module, the importer of the module (the module that imported the module), the namespace of the module, and the kind of the module.
|
||||
|
||||
The callback can return a new `contents` string for the module as well as a new `loader`.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import { plugin } from "bun";
|
||||
|
||||
const envPlugin: BunPlugin = {
|
||||
name: "env plugin",
|
||||
setup(build) {
|
||||
build.onLoad({ filter: /env/, namespace: "file" }, args => {
|
||||
return {
|
||||
contents: `export default ${JSON.stringify(process.env)}`,
|
||||
loader: "js",
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
outdir: "./dist",
|
||||
plugins: [envPlugin],
|
||||
});
|
||||
|
||||
// import env from "env"
|
||||
// env.FOO === "bar"
|
||||
```
|
||||
|
||||
This plugin will transform all imports of the form `import env from "env"` into a JavaScript module that exports the current environment variables.
|
||||
|
||||
#### .defer()
|
||||
|
||||
One of the arguments passed to the `onLoad` callback is a `defer` function. This function returns a Promise that is resolved when all other modules have been loaded.
|
||||
|
||||
This allows you to delay execution of the `onLoad` callback until all other modules have been loaded.
|
||||
|
||||
This is useful for returning contents of a module that depends on other modules.
|
||||
|
||||
<Accordion title="Example: tracking and reporting unused exports">
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import { plugin } from "bun";
|
||||
|
||||
plugin({
|
||||
name: "track imports",
|
||||
setup(build) {
|
||||
const transpiler = new Bun.Transpiler();
|
||||
|
||||
let trackedImports: Record<string, number> = {};
|
||||
|
||||
// Each module that goes through this onLoad callback
|
||||
// will record its imports in `trackedImports`
|
||||
build.onLoad({ filter: /\.ts/ }, async ({ path }) => {
|
||||
const contents = await Bun.file(path).arrayBuffer();
|
||||
|
||||
const imports = transpiler.scanImports(contents);
|
||||
|
||||
for (const i of imports) {
|
||||
trackedImports[i.path] = (trackedImports[i.path] || 0) + 1;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
|
||||
build.onLoad({ filter: /stats\.json/ }, async ({ defer }) => {
|
||||
// Wait for all files to be loaded, ensuring
|
||||
// that every file goes through the above `onLoad()` function
|
||||
// and their imports tracked
|
||||
await defer();
|
||||
|
||||
// Emit JSON containing the stats of each import
|
||||
return {
|
||||
contents: `export default ${JSON.stringify(trackedImports)}`,
|
||||
loader: "json",
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Warning>
|
||||
The `.defer()` function currently has the limitation that it can only be called once per `onLoad` callback.
|
||||
</Warning>
|
||||
|
||||
## Native plugins
|
||||
|
||||
One of the reasons why Bun's bundler is so fast is that it is written in native code and leverages multi-threading to load and parse modules in parallel.
|
||||
|
||||
However, one limitation of plugins written in JavaScript is that JavaScript itself is single-threaded.
|
||||
|
||||
Native plugins are written as NAPI modules and can be run on multiple threads. This allows native plugins to run much faster than JavaScript plugins.
|
||||
|
||||
In addition, native plugins can skip unnecessary work such as the UTF-8 -> UTF-16 conversion needed to pass strings to JavaScript.
|
||||
|
||||
These are the following lifecycle hooks which are available to native plugins:
|
||||
|
||||
- `onBeforeParse()`: Called on any thread before a file is parsed by Bun's bundler.
|
||||
|
||||
Native plugins are NAPI modules which expose lifecycle hooks as C ABI functions.
|
||||
|
||||
To create a native plugin, you must export a C ABI function which matches the signature of the native lifecycle hook you want to implement.
|
||||
|
||||
### Creating a native plugin in Rust
|
||||
|
||||
Native plugins are NAPI modules which expose lifecycle hooks as C ABI functions.
|
||||
|
||||
To create a native plugin, you must export a C ABI function which matches the signature of the native lifecycle hook you want to implement.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun add -g @napi-rs/cli
|
||||
napi new
|
||||
```
|
||||
|
||||
Then install this crate:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
cargo add bun-native-plugin
|
||||
```
|
||||
|
||||
Now, inside the `lib.rs` file, we'll use the `bun_native_plugin::bun` proc macro to define a function which will implement our native plugin.
|
||||
|
||||
Here's an example implementing the `onBeforeParse` hook:
|
||||
|
||||
```rust title="lib.rs" icon="/icons/rust.svg"
|
||||
use bun_native_plugin::{define_bun_plugin, OnBeforeParse, bun, Result, anyhow, BunLoader};
|
||||
use napi_derive::napi;
|
||||
|
||||
/// Define the plugin and its name
|
||||
define_bun_plugin!("replace-foo-with-bar");
|
||||
|
||||
/// Here we'll implement `onBeforeParse` with code that replaces all occurrences of
|
||||
/// `foo` with `bar`.
|
||||
///
|
||||
/// We use the #[bun] macro to generate some of the boilerplate code.
|
||||
///
|
||||
/// The argument of the function (`handle: &mut OnBeforeParse`) tells
|
||||
/// the macro that this function implements the `onBeforeParse` hook.
|
||||
#[bun]
|
||||
pub fn replace_foo_with_bar(handle: &mut OnBeforeParse) -> Result<()> {
|
||||
// Fetch the input source code.
|
||||
let input_source_code = handle.input_source_code()?;
|
||||
|
||||
// Get the Loader for the file
|
||||
let loader = handle.output_loader();
|
||||
|
||||
let output_source_code = input_source_code.replace("foo", "bar");
|
||||
|
||||
handle.set_output_source_code(output_source_code, BunLoader::BUN_LOADER_JSX);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
And to use it in `Bun.build()`:
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import myNativeAddon from "./my-native-addon";
|
||||
|
||||
Bun.build({
|
||||
entrypoints: ["./app.tsx"],
|
||||
plugins: [
|
||||
{
|
||||
name: "my-plugin",
|
||||
|
||||
setup(build) {
|
||||
build.onBeforeParse(
|
||||
{
|
||||
namespace: "file",
|
||||
filter: "**/*.tsx",
|
||||
},
|
||||
{
|
||||
napiModule: myNativeAddon,
|
||||
symbol: "replace_foo_with_bar",
|
||||
// external: myNativeAddon.getSharedState()
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### onBeforeParse
|
||||
|
||||
```ts
|
||||
onBeforeParse(
|
||||
args: { filter: RegExp; namespace?: string },
|
||||
callback: { napiModule: NapiModule; symbol: string; external?: unknown },
|
||||
): void;
|
||||
```
|
||||
|
||||
This lifecycle callback is run immediately before a file is parsed by Bun's bundler.
|
||||
|
||||
As input, it receives the file's contents and can optionally return new source code.
|
||||
|
||||
<Info>This callback can be called from any thread and so the napi module implementation must be thread-safe.</Info>
|
||||
1127
docs/bundler/vs-esbuild.md
Normal file
1127
docs/bundler/vs-esbuild.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,97 +1,91 @@
|
||||
---
|
||||
title: "bun add"
|
||||
description: "Add packages to your project with Bun's fast package manager"
|
||||
---
|
||||
|
||||
import Add from "/snippets/cli/add.mdx";
|
||||
|
||||
To add a particular package:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun add preact
|
||||
```bash
|
||||
$ bun add preact
|
||||
```
|
||||
|
||||
To specify a version, version range, or tag:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun add zod@3.20.0
|
||||
bun add zod@^3.0.0
|
||||
bun add zod@latest
|
||||
```bash
|
||||
$ bun add zod@3.20.0
|
||||
$ bun add zod@^3.0.0
|
||||
$ bun add zod@latest
|
||||
```
|
||||
|
||||
## `--dev`
|
||||
|
||||
<Note>**Alias** — `--development`, `-d`, `-D`</Note>
|
||||
{% callout %}
|
||||
**Alias** — `--development`, `-d`, `-D`
|
||||
{% /callout %}
|
||||
|
||||
To add a package as a dev dependency (`"devDependencies"`):
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun add --dev @types/react
|
||||
bun add -d @types/react
|
||||
```bash
|
||||
$ bun add --dev @types/react
|
||||
$ bun add -d @types/react
|
||||
```
|
||||
|
||||
## `--optional`
|
||||
|
||||
To add a package as an optional dependency (`"optionalDependencies"`):
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun add --optional lodash
|
||||
```bash
|
||||
$ bun add --optional lodash
|
||||
```
|
||||
|
||||
## `--peer`
|
||||
|
||||
To add a package as a peer dependency (`"peerDependencies"`):
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun add --peer @types/bun
|
||||
```bash
|
||||
$ bun add --peer @types/bun
|
||||
```
|
||||
|
||||
## `--exact`
|
||||
|
||||
<Note>**Alias** — `-E`</Note>
|
||||
{% callout %}
|
||||
**Alias** — `-E`
|
||||
{% /callout %}
|
||||
|
||||
To add a package and pin to the resolved version, use `--exact`. This will resolve the version of the package and add it to your `package.json` with an exact version number instead of a version range.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun add react --exact
|
||||
bun add react -E
|
||||
```bash
|
||||
$ bun add react --exact
|
||||
$ bun add react -E
|
||||
```
|
||||
|
||||
This will add the following to your `package.json`:
|
||||
|
||||
```json package.json icon="file-json"
|
||||
```jsonc
|
||||
{
|
||||
"dependencies": {
|
||||
// without --exact
|
||||
"react": "^18.2.0", // this matches >= 18.2.0 < 19.0.0
|
||||
|
||||
// with --exact
|
||||
"react": "18.2.0" // this matches only 18.2.0 exactly
|
||||
}
|
||||
"react": "18.2.0", // this matches only 18.2.0 exactly
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
To view a complete list of options for this command:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun add --help
|
||||
```bash
|
||||
$ bun add --help
|
||||
```
|
||||
|
||||
## `--global`
|
||||
|
||||
<Note>
|
||||
**Note** — This would not modify package.json of your current project folder. **Alias** - `bun add --global`, `bun add
|
||||
-g`, `bun install --global` and `bun install -g`
|
||||
</Note>
|
||||
{% callout %}
|
||||
**Note** — This would not modify package.json of your current project folder.
|
||||
**Alias** - `bun add --global`, `bun add -g`, `bun install --global` and `bun install -g`
|
||||
{% /callout %}
|
||||
|
||||
To install a package globally, use the `-g`/`--global` flag. This will not modify the `package.json` of your current project. Typically this is used for installing command-line tools.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun add --global cowsay # or `bun add -g cowsay`
|
||||
cowsay "Bun!"
|
||||
```
|
||||
|
||||
```txt
|
||||
```bash
|
||||
$ bun add --global cowsay # or `bun add -g cowsay`
|
||||
$ cowsay "Bun!"
|
||||
______
|
||||
< Bun! >
|
||||
------
|
||||
@@ -102,9 +96,9 @@ cowsay "Bun!"
|
||||
|| ||
|
||||
```
|
||||
|
||||
<Accordion title="Configuring global installation behavior">
|
||||
{% details summary="Configuring global installation behavior" %}
|
||||
|
||||
```toml bunfig.toml icon="settings"
|
||||
```toml
|
||||
[install]
|
||||
# where `bun add --global` installs packages
|
||||
globalDir = "~/.bun/install/global"
|
||||
@@ -113,7 +107,7 @@ globalDir = "~/.bun/install/global"
|
||||
globalBinDir = "~/.bun/bin"
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
|
||||
## Trusted dependencies
|
||||
|
||||
@@ -121,31 +115,33 @@ Unlike other npm clients, Bun does not execute arbitrary lifecycle scripts for i
|
||||
|
||||
To tell Bun to allow lifecycle scripts for a particular package, add the package to `trustedDependencies` in your package.json.
|
||||
|
||||
```json package.json icon="file-json"
|
||||
{
|
||||
"name": "my-app",
|
||||
"version": "1.0.0",
|
||||
"trustedDependencies": ["my-trusted-package"] // [!code ++]
|
||||
}
|
||||
```json-diff
|
||||
{
|
||||
"name": "my-app",
|
||||
"version": "1.0.0",
|
||||
+ "trustedDependencies": ["my-trusted-package"]
|
||||
}
|
||||
```
|
||||
|
||||
Bun reads this field and will run lifecycle scripts for `my-trusted-package`.
|
||||
|
||||
<!-- Bun maintains an allow-list of popular packages containing `postinstall` scripts that are known to be safe. To run lifecycle scripts for packages that aren't on this list, add the package to `trustedDependencies` in your package.json. -->
|
||||
|
||||
## Git dependencies
|
||||
|
||||
To add a dependency from a public or private git repository:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun add git@github.com:moment/moment.git
|
||||
```bash
|
||||
$ bun add git@github.com:moment/moment.git
|
||||
```
|
||||
|
||||
<Note>
|
||||
To install private repositories, your system needs the appropriate SSH credentials to access the repository.
|
||||
</Note>
|
||||
{% callout %}
|
||||
**Note** — To install private repositories, your system needs the appropriate SSH credentials to access the repository.
|
||||
{% /callout %}
|
||||
|
||||
Bun supports a variety of protocols, including [`github`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#github-urls), [`git`](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#git-urls-as-dependencies), `git+ssh`, `git+https`, and many more.
|
||||
|
||||
```json package.json icon="file-json"
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"dayjs": "git+https://github.com/iamkun/dayjs.git",
|
||||
@@ -160,13 +156,13 @@ Bun supports a variety of protocols, including [`github`](https://docs.npmjs.com
|
||||
|
||||
A package name can correspond to a publicly hosted `.tgz` file. During installation, Bun will download and install the package from the specified tarball URL, rather than from the package registry.
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
bun add zod@https://registry.npmjs.org/zod/-/zod-3.21.4.tgz
|
||||
```sh
|
||||
$ bun add zod@https://registry.npmjs.org/zod/-/zod-3.21.4.tgz
|
||||
```
|
||||
|
||||
This will add the following line to your `package.json`:
|
||||
|
||||
```json package.json icon="file-json"
|
||||
```json#package.json
|
||||
{
|
||||
"dependencies": {
|
||||
"zod": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz"
|
||||
@@ -174,6 +170,4 @@ This will add the following line to your `package.json`:
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<Add />
|
||||
{% bunCLIUsage command="add" /%}
|
||||
3
docs/cli/bun-completions.md
Normal file
3
docs/cli/bun-completions.md
Normal file
@@ -0,0 +1,3 @@
|
||||
This command installs completions for `zsh` and/or `fish`. It runs automatically on every `bun upgrade` and on install. It reads from `$SHELL` to determine which shell to install for. It tries several common shell completion directories for your shell and OS.
|
||||
|
||||
If you want to copy the completions manually, run `bun completions > path-to-file`. If you know the completions directory to install them to, run `bun completions /path/to/directory`.
|
||||
@@ -1,18 +1,10 @@
|
||||
---
|
||||
title: bun create
|
||||
description: Create a new Bun project from a React component, a `create-<template>` npm package, a GitHub repo, or a local template
|
||||
---
|
||||
|
||||
<Note>
|
||||
You don't need `bun create` to use Bun. You don't need any configuration at all. This command exists to make getting
|
||||
started a bit quicker and easier.
|
||||
</Note>
|
||||
|
||||
---
|
||||
{% callout %}
|
||||
**Note** — You don’t need `bun create` to use Bun. You don’t need any configuration at all. This command exists to make getting started a bit quicker and easier.
|
||||
{% /callout %}
|
||||
|
||||
Template a new Bun project with `bun create`. This is a flexible command that can be used to create a new project from a React component, a `create-<template>` npm package, a GitHub repo, or a local template.
|
||||
|
||||
If you're looking to create a brand new empty project, use [`bun init`](/runtime/templating/init).
|
||||
If you're looking to create a brand new empty project, use [`bun init`](https://bun.com/docs/cli/init).
|
||||
|
||||
## From a React component
|
||||
|
||||
@@ -22,35 +14,27 @@ If you're looking to create a brand new empty project, use [`bun init`](/runtime
|
||||
$ bun create ./MyComponent.jsx # .tsx also supported
|
||||
```
|
||||
|
||||
<Frame>
|
||||
<video
|
||||
style={{ aspectRatio: "2062 / 1344", width: "100%", height: "100%", objectFit: "contain" }}
|
||||
loop
|
||||
autoPlay
|
||||
muted
|
||||
playsInline
|
||||
>
|
||||
<source
|
||||
src="/images/bun-create-shadcn.mp4"
|
||||
style={{ width: "100%", height: "100%", objectFit: "contain" }}
|
||||
type="video/mp4"
|
||||
/>
|
||||
</video>
|
||||
</Frame>
|
||||
{% raw %}
|
||||
|
||||
<Note>
|
||||
<video style="aspect-ratio: 2062 / 1344; width: 100%; height: 100%; object-fit: contain;" loop autoplay muted playsinline>
|
||||
<source src="/bun-create-shadcn.mp4" style="width: 100%; height: 100%; object-fit: contain;" type="video/mp4">
|
||||
</video>
|
||||
|
||||
{% /raw %}
|
||||
|
||||
{% callout %}
|
||||
🚀 **Create React App Successor** — `bun create <component>` provides everything developers loved about Create React App, but with modern tooling, faster builds, and backend support.
|
||||
</Note>
|
||||
{% /callout %}
|
||||
|
||||
#### How this works
|
||||
|
||||
When you run `bun create <component>`, Bun:
|
||||
|
||||
1. Uses [Bun's JavaScript bundler](/bundler) to analyze your module graph.
|
||||
1. Uses [Bun's JavaScript bundler](https://bun.com/docs/bundler) to analyze your module graph.
|
||||
2. Collects all the dependencies needed to run the component.
|
||||
3. Scans the exports of the entry point for a React component.
|
||||
4. Generates a `package.json` file with the dependencies and scripts needed to run the component.
|
||||
5. Installs any missing dependencies using [`bun install --only-missing`](/pm/cli/install).
|
||||
5. Installs any missing dependencies using [`bun install --only-missing`](https://bun.com/docs/cli/install).
|
||||
6. Generates the following files:
|
||||
- `${component}.html`
|
||||
- `${component}.client.tsx` (entry point for the frontend)
|
||||
@@ -63,7 +47,7 @@ When you run `bun create <component>`, Bun:
|
||||
|
||||
When you run `bun create <component>`, Bun scans your JSX/TSX file for TailwindCSS class names (and any files it imports). If it detects TailwindCSS class names, it will add the following dependencies to your `package.json`:
|
||||
|
||||
```json package.json icon="file-json"
|
||||
```json#package.json
|
||||
{
|
||||
"dependencies": {
|
||||
"tailwindcss": "^4",
|
||||
@@ -74,14 +58,14 @@ When you run `bun create <component>`, Bun scans your JSX/TSX file for TailwindC
|
||||
|
||||
We also configure `bunfig.toml` to use Bun's TailwindCSS plugin with `Bun.serve()`
|
||||
|
||||
```toml bunfig.toml icon="settings"
|
||||
```toml#bunfig.toml
|
||||
[serve.static]
|
||||
plugins = ["bun-plugin-tailwind"]
|
||||
```
|
||||
|
||||
And a `${component}.css` file with `@import "tailwindcss";` at the top:
|
||||
|
||||
```css MyComponent.css icon="file-code"
|
||||
```css#MyComponent.css
|
||||
@import "tailwindcss";
|
||||
```
|
||||
|
||||
@@ -93,9 +77,9 @@ And a `${component}.css` file with `@import "tailwindcss";` at the top:
|
||||
|
||||
If it finds any, it runs:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```bash
|
||||
# Assuming bun detected imports to @/components/ui/accordion and @/components/ui/button
|
||||
bunx shadcn@canary add accordion button # and any other components
|
||||
$ bunx shadcn@canary add accordion button # and any other components
|
||||
```
|
||||
|
||||
Since `shadcn/ui` itself uses TailwindCSS, `bun create` also adds the necessary TailwindCSS dependencies to your `package.json` and configures `bunfig.toml` to use Bun's TailwindCSS plugin with `Bun.serve()` as described above.
|
||||
@@ -111,15 +95,15 @@ Additionally, we setup the following:
|
||||
|
||||
## From `npm`
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
bun create <template> [<destination>]
|
||||
```sh
|
||||
$ bun create <template> [<destination>]
|
||||
```
|
||||
|
||||
Assuming you don't have a [local template](#from-a-local-template) with the same name, this command will download and execute the `create-<template>` package from npm. The following two commands will behave identically:
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
bun create remix
|
||||
bunx create-remix
|
||||
```sh
|
||||
$ bun create remix
|
||||
$ bunx create-remix
|
||||
```
|
||||
|
||||
Refer to the documentation of the associated `create-<template>` package for complete documentation and usage instructions.
|
||||
@@ -128,16 +112,16 @@ Refer to the documentation of the associated `create-<template>` package for com
|
||||
|
||||
This will download the contents of the GitHub repo to disk.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun create <user>/<repo>
|
||||
bun create github.com/<user>/<repo>
|
||||
```bash
|
||||
$ bun create <user>/<repo>
|
||||
$ bun create github.com/<user>/<repo>
|
||||
```
|
||||
|
||||
Optionally specify a name for the destination folder. If no destination is specified, the repo name will be used.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun create <user>/<repo> mydir
|
||||
bun create github.com/<user>/<repo> mydir
|
||||
```bash
|
||||
$ bun create <user>/<repo> mydir
|
||||
$ bun create github.com/<user>/<repo> mydir
|
||||
```
|
||||
|
||||
Bun will perform the following steps:
|
||||
@@ -148,33 +132,77 @@ Bun will perform the following steps:
|
||||
- Initialize a fresh Git repo. Opt out with the `--no-git` flag.
|
||||
- Run the template's configured `start` script, if defined.
|
||||
|
||||
<Note>By default Bun will _not overwrite_ any existing files. Use the `--force` flag to overwrite existing files.</Note>
|
||||
{% callout %}
|
||||
By default Bun will _not overwrite_ any existing files. Use the `--force` flag to overwrite existing files.
|
||||
{% /callout %}
|
||||
|
||||
<!-- ### Official templates
|
||||
|
||||
The following official templates are available.
|
||||
|
||||
```bash
|
||||
bun create next ./myapp
|
||||
bun create react ./myapp
|
||||
bun create svelte-kit ./myapp
|
||||
bun create elysia ./myapp
|
||||
bun create hono ./myapp
|
||||
bun create kingworld ./myapp
|
||||
```
|
||||
|
||||
Each of these corresponds to a directory in the [bun-community/create-templates](https://github.com/bun-community/create-templates) repo. If you think a major framework is missing, please open a PR there. This list will change over time as additional examples are added. To see an up-to-date list, run `bun create` with no arguments.
|
||||
|
||||
```bash
|
||||
$ bun create
|
||||
Welcome to bun! Create a new project by pasting any of the following:
|
||||
<list of templates>
|
||||
```
|
||||
|
||||
{% callout %}
|
||||
⚡️ **Speed** — At the time of writing, `bun create react app` runs ~11x faster on a M1 Macbook Pro than `yarn create react-app app`.
|
||||
{% /callout %} -->
|
||||
|
||||
<!-- ### GitHub repos
|
||||
|
||||
A template of the form `<username>/<repo>` will be downloaded from GitHub.
|
||||
|
||||
```bash
|
||||
$ bun create ahfarmer/calculator ./myapp
|
||||
```
|
||||
|
||||
Complete GitHub URLs will also work:
|
||||
|
||||
```bash
|
||||
$ bun create github.com/ahfarmer/calculator ./myapp
|
||||
$ bun create https://github.com/ahfarmer/calculator ./myapp
|
||||
```
|
||||
|
||||
Bun installs the files as they currently exist current default branch (usually `main` or `master`). Unlike `git clone` it doesn't download the commit history or configure a remote. -->
|
||||
|
||||
## From a local template
|
||||
|
||||
<Warning>
|
||||
Unlike remote templates, running `bun create` with a local template will delete the entire destination folder if it
|
||||
already exists! Be careful.
|
||||
</Warning>
|
||||
|
||||
{% callout %}
|
||||
**⚠️ Warning** — Unlike remote templates, running `bun create` with a local template will delete the entire destination folder if it already exists! Be careful.
|
||||
{% /callout %}
|
||||
Bun's templater can be extended to support custom templates defined on your local file system. These templates should live in one of the following directories:
|
||||
|
||||
- `$HOME/.bun-create/<name>`: global templates
|
||||
- `<project root>/.bun-create/<name>`: project-specific templates
|
||||
|
||||
<Note>You can customize the global template path by setting the `BUN_CREATE_DIR` environment variable.</Note>
|
||||
{% callout %}
|
||||
**Note** — You can customize the global template path by setting the `BUN_CREATE_DIR` environment variable.
|
||||
{% /callout %}
|
||||
|
||||
To create a local template, navigate to `$HOME/.bun-create` and create a new directory with the desired name of your template.
|
||||
|
||||
```bash
|
||||
cd $HOME/.bun-create
|
||||
mkdir foo
|
||||
cd foo
|
||||
$ cd $HOME/.bun-create
|
||||
$ mkdir foo
|
||||
$ cd foo
|
||||
```
|
||||
|
||||
Then, create a `package.json` file in that directory with the following contents:
|
||||
|
||||
```json package.json icon="file-json"
|
||||
```json
|
||||
{
|
||||
"name": "foo"
|
||||
}
|
||||
@@ -186,7 +214,7 @@ You can run `bun create foo` elsewhere on your file system to verify that Bun is
|
||||
|
||||
You can specify pre- and post-install setup scripts in the `"bun-create"` section of your local template's `package.json`.
|
||||
|
||||
```json package.json icon="file-json"
|
||||
```json
|
||||
{
|
||||
"name": "@bun-examples/simplereact",
|
||||
"version": "0.0.1",
|
||||
@@ -205,10 +233,19 @@ You can specify pre- and post-install setup scripts in the `"bun-create"` sectio
|
||||
|
||||
The following fields are supported. Each of these can correspond to a string or array of strings. An array of commands will be executed in order.
|
||||
|
||||
| Field | Description |
|
||||
| ------------- | ----------------------------------- |
|
||||
| `postinstall` | runs after installing dependencies |
|
||||
| `preinstall` | runs before installing dependencies |
|
||||
{% table %}
|
||||
|
||||
---
|
||||
|
||||
- `postinstall`
|
||||
- runs after installing dependencies
|
||||
|
||||
---
|
||||
|
||||
- `preinstall`
|
||||
- runs before installing dependencies
|
||||
|
||||
{% /table %}
|
||||
|
||||
After cloning a template, `bun create` will automatically remove the `"bun-create"` section from `package.json` before writing it to the destination folder.
|
||||
|
||||
@@ -216,21 +253,53 @@ After cloning a template, `bun create` will automatically remove the `"bun-creat
|
||||
|
||||
### CLI flags
|
||||
|
||||
| Flag | Description |
|
||||
| -------------- | -------------------------------------- |
|
||||
| `--force` | Overwrite existing files |
|
||||
| `--no-install` | Skip installing `node_modules` & tasks |
|
||||
| `--no-git` | Don't initialize a git repository |
|
||||
| `--open` | Start & open in-browser after finish |
|
||||
{% table %}
|
||||
|
||||
- Flag
|
||||
- Description
|
||||
|
||||
---
|
||||
|
||||
- `--force`
|
||||
- Overwrite existing files
|
||||
|
||||
---
|
||||
|
||||
- `--no-install`
|
||||
- Skip installing `node_modules` & tasks
|
||||
|
||||
---
|
||||
|
||||
- `--no-git`
|
||||
- Don’t initialize a git repository
|
||||
|
||||
---
|
||||
|
||||
- `--open`
|
||||
- Start & open in-browser after finish
|
||||
|
||||
{% /table %}
|
||||
|
||||
### Environment variables
|
||||
|
||||
| Name | Description |
|
||||
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `GITHUB_API_DOMAIN` | If you're using a GitHub enterprise or a proxy, you can customize the GitHub domain Bun pings for downloads |
|
||||
| `GITHUB_TOKEN` (or `GITHUB_ACCESS_TOKEN`) | This lets `bun create` work with private repositories or if you get rate-limited. `GITHUB_TOKEN` is chosen over `GITHUB_ACCESS_TOKEN` if both exist. |
|
||||
{% table %}
|
||||
|
||||
<Accordion title={<span>How <code>bun create</code> works</span>}>
|
||||
- Name
|
||||
- Description
|
||||
|
||||
---
|
||||
|
||||
- `GITHUB_API_DOMAIN`
|
||||
- If you’re using a GitHub enterprise or a proxy, you can customize the GitHub domain Bun pings for downloads
|
||||
|
||||
---
|
||||
|
||||
- `GITHUB_TOKEN` (or `GITHUB_ACCESS_TOKEN`)
|
||||
- This lets `bun create` work with private repositories or if you get rate-limited. `GITHUB_TOKEN` is chosen over `GITHUB_ACCESS_TOKEN` if both exist.
|
||||
|
||||
{% /table %}
|
||||
|
||||
{% details summary="How `bun create` works" %}
|
||||
|
||||
When you run `bun create ${template} ${destination}`, here’s what happens:
|
||||
|
||||
@@ -255,7 +324,7 @@ ELSE IF local template
|
||||
|
||||
4. Parse the `package.json` (again!), update `name` to be `${basename(destination)}`, remove the `bun-create` section from the `package.json` and save the updated `package.json` to disk.
|
||||
- IF Next.js is detected, add `bun-framework-next` to the list of dependencies
|
||||
- IF Create React App is detected, add the entry point in `/src/index.{js,jsx,ts,tsx}` to `public/index.html`
|
||||
- IF Create React App is detected, add the entry point in /src/index.{js,jsx,ts,tsx} to `public/index.html`
|
||||
- IF Relay is detected, add `bun-macro-relay` so that Relay works
|
||||
5. Auto-detect the npm client, preferring `pnpm`, `yarn` (v1), and lastly `npm`
|
||||
6. Run any tasks defined in `"bun-create": { "preinstall" }` with the npm client
|
||||
@@ -266,4 +335,4 @@ ELSE IF local template
|
||||
- If there are dependencies, this runs in a separate thread concurrently while node_modules are being installed
|
||||
- Using libgit2 if available was tested and performed 3x slower in microbenchmarks
|
||||
|
||||
</Accordion>
|
||||
{% /details %}
|
||||
349
docs/cli/bun-install.md
Normal file
349
docs/cli/bun-install.md
Normal file
@@ -0,0 +1,349 @@
|
||||
### `bun install`
|
||||
|
||||
bun install is a fast package manager & npm client.
|
||||
|
||||
bun install can be configured via `bunfig.toml`, environment variables, and CLI flags.
|
||||
|
||||
#### Configuring `bun install` with `bunfig.toml`
|
||||
|
||||
`bunfig.toml` is searched for in the following paths on `bun install`, `bun remove`, and `bun add`:
|
||||
|
||||
1. `$XDG_CONFIG_HOME/.bunfig.toml` or `$HOME/.bunfig.toml`
|
||||
2. `./bunfig.toml`
|
||||
|
||||
If both are found, the results are merged together.
|
||||
|
||||
Configuring with `bunfig.toml` is optional. Bun tries to be zero configuration in general, but that's not always possible.
|
||||
|
||||
```toml
|
||||
# Using scoped packages with bun install
|
||||
[install.scopes]
|
||||
|
||||
# Scope name The value can be a URL string or an object
|
||||
"@mybigcompany" = { token = "123456", url = "https://registry.mybigcompany.com" }
|
||||
# URL is optional and falls back to the default registry
|
||||
|
||||
# The "@" in the scope is optional
|
||||
mybigcompany2 = { token = "123456" }
|
||||
|
||||
# Environment variables can be referenced as a string that starts with $ and it will be replaced
|
||||
mybigcompany3 = { token = "$npm_config_token" }
|
||||
|
||||
# Setting username and password turns it into a Basic Auth header by taking base64("username:password")
|
||||
mybigcompany4 = { username = "myusername", password = "$npm_config_password", url = "https://registry.yarnpkg.com/" }
|
||||
# You can set username and password in the registry URL. This is the same as above.
|
||||
mybigcompany5 = "https://username:password@registry.yarnpkg.com/"
|
||||
|
||||
# You can set a token for a registry URL:
|
||||
mybigcompany6 = "https://:$NPM_CONFIG_TOKEN@registry.yarnpkg.com/"
|
||||
|
||||
[install]
|
||||
# Default registry
|
||||
# can be a URL string or an object
|
||||
registry = "https://registry.yarnpkg.com/"
|
||||
# as an object
|
||||
#registry = { url = "https://registry.yarnpkg.com/", token = "123456" }
|
||||
|
||||
# Install for production? This is the equivalent to the "--production" CLI argument
|
||||
production = false
|
||||
|
||||
# Save a text-based lockfile? This is equivalent to the "--save-text-lockfile" CLI argument
|
||||
saveTextLockfile = false
|
||||
|
||||
# Disallow changes to lockfile? This is the equivalent to the "--frozen-lockfile" CLI argument
|
||||
frozenLockfile = false
|
||||
|
||||
# Don't actually install
|
||||
dryRun = true
|
||||
|
||||
# Install optionalDependencies (default: true)
|
||||
# Setting this to false is equivalent to the `--omit=optional` CLI argument
|
||||
optional = true
|
||||
|
||||
# Install local devDependencies (default: true)
|
||||
# Setting this to false is equivalent to the `--omit=dev` CLI argument
|
||||
dev = true
|
||||
|
||||
# Install peerDependencies (default: true)
|
||||
# Setting this to false is equivalent to the `--omit=peer` CLI argument
|
||||
peer = true
|
||||
|
||||
# Max number of concurrent lifecycle scripts (default: (cpu count or GOMAXPROCS) x2)
|
||||
concurrentScripts = 16
|
||||
|
||||
# When using `bun install -g`, install packages here
|
||||
globalDir = "~/.bun/install/global"
|
||||
|
||||
# When using `bun install -g`, link package bins here
|
||||
globalBinDir = "~/.bun/bin"
|
||||
|
||||
# cache-related configuration
|
||||
[install.cache]
|
||||
# The directory to use for the cache
|
||||
dir = "~/.bun/install/cache"
|
||||
|
||||
# Don't load from the global cache.
|
||||
# Note: Bun may still write to node_modules/.cache
|
||||
disable = false
|
||||
|
||||
|
||||
# Always resolve the latest versions from the registry
|
||||
disableManifest = false
|
||||
|
||||
|
||||
# Lockfile-related configuration
|
||||
[install.lockfile]
|
||||
|
||||
# Print a yarn v1 lockfile
|
||||
# Note: it does not load the lockfile, it just converts bun.lock into a yarn.lock
|
||||
print = "yarn"
|
||||
|
||||
# Save the lockfile to disk
|
||||
save = true
|
||||
|
||||
```
|
||||
|
||||
If it's easier to read as TypeScript types:
|
||||
|
||||
```ts
|
||||
export interface Root {
|
||||
install: Install;
|
||||
}
|
||||
|
||||
export interface Install {
|
||||
scopes: Scopes;
|
||||
registry: Registry;
|
||||
production: boolean;
|
||||
saveTextLockfile: boolean;
|
||||
frozenLockfile: boolean;
|
||||
dryRun: boolean;
|
||||
optional: boolean;
|
||||
dev: boolean;
|
||||
peer: boolean;
|
||||
globalDir: string;
|
||||
globalBinDir: string;
|
||||
cache: Cache;
|
||||
lockfile: Lockfile;
|
||||
logLevel: "debug" | "error" | "warn";
|
||||
}
|
||||
|
||||
type Registry =
|
||||
| string
|
||||
| {
|
||||
url?: string;
|
||||
token?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
};
|
||||
|
||||
type Scopes = Record<string, Registry>;
|
||||
|
||||
export interface Cache {
|
||||
dir: string;
|
||||
disable: boolean;
|
||||
disableManifest: boolean;
|
||||
}
|
||||
|
||||
export interface Lockfile {
|
||||
print?: "yarn";
|
||||
save: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
## Configuring with environment variables
|
||||
|
||||
Environment variables have a higher priority than `bunfig.toml`.
|
||||
|
||||
| Name | Description |
|
||||
| -------------------------------- | ------------------------------------------------------------- |
|
||||
| BUN_CONFIG_REGISTRY | Set an npm registry (default: <https://registry.npmjs.org>) |
|
||||
| BUN_CONFIG_TOKEN | Set an auth token (currently does nothing) |
|
||||
| BUN_CONFIG_YARN_LOCKFILE | Save a Yarn v1-style yarn.lock |
|
||||
| BUN_CONFIG_LINK_NATIVE_BINS | Point `bin` in package.json to a platform-specific dependency |
|
||||
| BUN_CONFIG_SKIP_SAVE_LOCKFILE | Don’t save a lockfile |
|
||||
| BUN_CONFIG_SKIP_LOAD_LOCKFILE | Don’t load a lockfile |
|
||||
| BUN_CONFIG_SKIP_INSTALL_PACKAGES | Don’t install any packages |
|
||||
|
||||
Bun always tries to use the fastest available installation method for the target platform. On macOS, that’s `clonefile` and on Linux, that’s `hardlink`. You can change which installation method is used with the `--backend` flag. When unavailable or on error, `clonefile` and `hardlink` fallsback to a platform-specific implementation of copying files.
|
||||
|
||||
Bun stores installed packages from npm in `~/.bun/install/cache/${name}@${version}`. Note that if the semver version has a `build` or a `pre` tag, it is replaced with a hash of that value instead. This is to reduce the chances of errors from long file paths, but unfortunately complicates figuring out where a package was installed on disk.
|
||||
|
||||
When the `node_modules` folder exists, before installing, Bun checks if the `"name"` and `"version"` in `package/package.json` in the expected node_modules folder matches the expected `name` and `version`. This is how it determines whether it should install. It uses a custom JSON parser which stops parsing as soon as it finds `"name"` and `"version"`.
|
||||
|
||||
When a `bun.lock` doesn’t exist or `package.json` has changed dependencies, tarballs are downloaded & extracted eagerly while resolving.
|
||||
|
||||
When a `bun.lock` exists and `package.json` hasn’t changed, Bun downloads missing dependencies lazily. If the package with a matching `name` & `version` already exists in the expected location within `node_modules`, Bun won’t attempt to download the tarball.
|
||||
|
||||
## Platform-specific dependencies?
|
||||
|
||||
bun stores normalized `cpu` and `os` values from npm in the lockfile, along with the resolved packages. It skips downloading, extracting, and installing packages disabled for the current target at runtime. This means the lockfile won't change between platforms/architectures even if the packages ultimately installed do change.
|
||||
|
||||
### `--cpu` and `--os` flags
|
||||
|
||||
You can override the target platform for package selection:
|
||||
|
||||
```bash
|
||||
bun install --cpu=x64 --os=linux
|
||||
```
|
||||
|
||||
This installs packages for the specified platform instead of the current system. Useful for cross-platform builds or when preparing deployments for different environments.
|
||||
|
||||
**Accepted values for `--cpu`**: `arm64`, `x64`, `ia32`, `ppc64`, `s390x`
|
||||
|
||||
**Accepted values for `--os`**: `linux`, `darwin`, `win32`, `freebsd`, `openbsd`, `sunos`, `aix`
|
||||
|
||||
## Peer dependencies?
|
||||
|
||||
Peer dependencies are handled similarly to yarn. `bun install` will automatically install peer dependencies. If the dependency is marked optional in `peerDependenciesMeta`, an existing dependency will be chosen if possible.
|
||||
|
||||
## Lockfile
|
||||
|
||||
`bun.lock` is Bun’s lockfile format. See [our blogpost about the text lockfile](https://bun.com/blog/bun-lock-text-lockfile).
|
||||
|
||||
Prior to Bun 1.2, the lockfile was binary and called `bun.lockb`. Old lockfiles can be upgraded to the new format by running `bun install --save-text-lockfile --frozen-lockfile --lockfile-only`, and then deleting `bun.lockb`.
|
||||
|
||||
## Cache
|
||||
|
||||
To delete the cache:
|
||||
|
||||
```bash
|
||||
$ rm -rf ~/.bun/install/cache
|
||||
```
|
||||
|
||||
## Platform-specific backends
|
||||
|
||||
`bun install` uses different system calls to install dependencies depending on the platform. This is a performance optimization. You can force a specific backend with the `--backend` flag.
|
||||
|
||||
**`hardlink`** is the default backend on Linux. Benchmarking showed it to be the fastest on Linux.
|
||||
|
||||
```bash
|
||||
$ rm -rf node_modules
|
||||
$ bun install --backend hardlink
|
||||
```
|
||||
|
||||
**`clonefile`** is the default backend on macOS. Benchmarking showed it to be the fastest on macOS. It is only available on macOS.
|
||||
|
||||
```bash
|
||||
$ rm -rf node_modules
|
||||
$ bun install --backend clonefile
|
||||
```
|
||||
|
||||
**`clonefile_each_dir`** is similar to `clonefile`, except it clones each file individually per directory. It is only available on macOS and tends to perform slower than `clonefile`. Unlike `clonefile`, this does not recursively clone subdirectories in one system call.
|
||||
|
||||
```bash
|
||||
$ rm -rf node_modules
|
||||
$ bun install --backend clonefile_each_dir
|
||||
```
|
||||
|
||||
**`copyfile`** is the fallback used when any of the above fail, and is the slowest. on macOS, it uses `fcopyfile()` and on linux it uses `copy_file_range()`.
|
||||
|
||||
```bash
|
||||
$ rm -rf node_modules
|
||||
$ bun install --backend copyfile
|
||||
```
|
||||
|
||||
**`symlink`** is typically only used for `file:` dependencies (and eventually `link:`) internally. To prevent infinite loops, it skips symlinking the `node_modules` folder.
|
||||
|
||||
If you install with `--backend=symlink`, Node.js won't resolve node_modules of dependencies unless each dependency has its own node_modules folder or you pass `--preserve-symlinks` to `node` or `bun`. See [Node.js documentation on `--preserve-symlinks`](https://nodejs.org/api/cli.html#--preserve-symlinks).
|
||||
|
||||
```bash
|
||||
$ rm -rf node_modules
|
||||
$ bun install --backend symlink
|
||||
$ bun --preserve-symlinks ./my-file.js
|
||||
$ node --preserve-symlinks ./my-file.js # https://nodejs.org/api/cli.html#--preserve-symlinks
|
||||
```
|
||||
|
||||
## npm registry metadata
|
||||
|
||||
bun uses a binary format for caching NPM registry responses. This loads much faster than JSON and tends to be smaller on disk.
|
||||
You will see these files in `~/.bun/install/cache/*.npm`. The filename pattern is `${hash(packageName)}.npm`. It’s a hash so that extra directories don’t need to be created for scoped packages.
|
||||
|
||||
Bun's usage of `Cache-Control` ignores `Age`. This improves performance, but means bun may be about 5 minutes out of date to receive the latest package version metadata from npm.
|
||||
|
||||
## pnpm migration
|
||||
|
||||
Bun automatically migrates projects from pnpm to bun. When a `pnpm-lock.yaml` file is detected and no `bun.lock` file exists, Bun will automatically migrate the lockfile to `bun.lock` during installation. The original `pnpm-lock.yaml` file remains unmodified.
|
||||
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
**Note**: Migration only runs when `bun.lock` is absent. There is currently no opt-out flag for pnpm migration.
|
||||
|
||||
The migration process handles:
|
||||
|
||||
### Lockfile Migration
|
||||
|
||||
- Converts `pnpm-lock.yaml` to `bun.lock` format
|
||||
- Preserves package versions and resolution information
|
||||
- Maintains dependency relationships and peer dependencies
|
||||
- Handles patched dependencies with integrity hashes
|
||||
|
||||
### Workspace Configuration
|
||||
|
||||
When a `pnpm-workspace.yaml` file exists, Bun migrates workspace settings to your root `package.json`:
|
||||
|
||||
```yaml
|
||||
# pnpm-workspace.yaml
|
||||
packages:
|
||||
- "apps/*"
|
||||
- "packages/*"
|
||||
|
||||
catalog:
|
||||
react: ^18.0.0
|
||||
typescript: ^5.0.0
|
||||
|
||||
catalogs:
|
||||
build:
|
||||
webpack: ^5.0.0
|
||||
babel: ^7.0.0
|
||||
```
|
||||
|
||||
The workspace packages list and catalogs are moved to the `workspaces` field in `package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"workspaces": {
|
||||
"packages": ["apps/*", "packages/*"],
|
||||
"catalog": {
|
||||
"react": "^18.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"catalogs": {
|
||||
"build": {
|
||||
"webpack": "^5.0.0",
|
||||
"babel": "^7.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Catalog Dependencies
|
||||
|
||||
Dependencies using pnpm's `catalog:` protocol are preserved:
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"react": "catalog:",
|
||||
"webpack": "catalog:build"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration Migration
|
||||
|
||||
The following pnpm configuration is migrated from both `pnpm-lock.yaml` and `pnpm-workspace.yaml`:
|
||||
|
||||
- **Overrides**: Moved from `pnpm.overrides` to root-level `overrides` in `package.json`
|
||||
- **Patched Dependencies**: Moved from `pnpm.patchedDependencies` to root-level `patchedDependencies` in `package.json`
|
||||
- **Workspace Overrides**: Applied from `pnpm-workspace.yaml` to root `package.json`
|
||||
|
||||
### Requirements
|
||||
|
||||
- Requires pnpm lockfile version 7 or higher
|
||||
- Workspace packages must have a `name` field in their `package.json`
|
||||
- All catalog entries referenced by dependencies must exist in the catalogs definition
|
||||
|
||||
After migration, you can safely remove `pnpm-lock.yaml` and `pnpm-workspace.yaml` files.
|
||||
39
docs/cli/bun-upgrade.md
Normal file
39
docs/cli/bun-upgrade.md
Normal file
@@ -0,0 +1,39 @@
|
||||
To upgrade Bun, run `bun upgrade`.
|
||||
|
||||
It automatically downloads the latest version of Bun and overwrites the currently-running version.
|
||||
|
||||
This works by checking the latest version of Bun in [bun-releases-for-updater](https://github.com/Jarred-Sumner/bun-releases-for-updater/releases) and unzipping it using the system-provided `unzip` library (so that Gatekeeper works on macOS)
|
||||
|
||||
If for any reason you run into issues, you can also use the curl install script:
|
||||
|
||||
```bash
|
||||
$ curl https://bun.com/install | bash
|
||||
```
|
||||
|
||||
It will still work when Bun is already installed.
|
||||
|
||||
Bun is distributed as a single binary file, so you can also do this manually:
|
||||
|
||||
- Download the latest version of Bun for your platform in [bun-releases-for-updater](https://github.com/Jarred-Sumner/bun-releases-for-updater/releases/latest) (`darwin` == macOS)
|
||||
- Unzip the folder
|
||||
- Move the `bun` binary to `~/.bun/bin` (or anywhere)
|
||||
|
||||
## `--canary`
|
||||
|
||||
[Canary](https://github.com/oven-sh/bun/releases/tag/canary) builds are generated on every commit.
|
||||
|
||||
To install a [canary](https://github.com/oven-sh/bun/releases/tag/canary) build of Bun, run:
|
||||
|
||||
```bash
|
||||
$ bun upgrade --canary
|
||||
```
|
||||
|
||||
This flag is not persistent (though that might change in the future). If you want to always run the canary build of Bun, set the `BUN_CANARY` environment variable to `1` in your shell's startup script.
|
||||
|
||||
This will download the release zip from https://github.com/oven-sh/bun/releases/tag/canary.
|
||||
|
||||
To revert to the latest published version of Bun, run:
|
||||
|
||||
```bash
|
||||
$ bun upgrade
|
||||
```
|
||||
@@ -1,24 +1,20 @@
|
||||
---
|
||||
title: "bunx"
|
||||
description: "Run packages from npm"
|
||||
---
|
||||
|
||||
<Note>`bunx` is an alias for `bun x`. The `bunx` CLI will be auto-installed when you install `bun`.</Note>
|
||||
{% callout %}
|
||||
**Note** — `bunx` is an alias for `bun x`. The `bunx` CLI will be auto-installed when you install `bun`.
|
||||
{% /callout %}
|
||||
|
||||
Use `bunx` to auto-install and run packages from `npm`. It's Bun's equivalent of `npx` or `yarn dlx`.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bunx cowsay "Hello world!"
|
||||
```bash
|
||||
$ bunx cowsay "Hello world!"
|
||||
```
|
||||
|
||||
<Note>
|
||||
⚡️ **Speed** — With Bun's fast startup times, `bunx` is [roughly 100x
|
||||
faster](https://twitter.com/jarredsumner/status/1606163655527059458) than `npx` for locally installed packages.
|
||||
</Note>
|
||||
{% callout %}
|
||||
⚡️ **Speed** — With Bun's fast startup times, `bunx` is [roughly 100x faster](https://twitter.com/jarredsumner/status/1606163655527059458) than `npx` for locally installed packages.
|
||||
{% /callout %}
|
||||
|
||||
Packages can declare executables in the `"bin"` field of their `package.json`. These are known as _package executables_ or _package binaries_.
|
||||
|
||||
```json package.json icon="file-json"
|
||||
```jsonc#package.json
|
||||
{
|
||||
// ... other fields
|
||||
"name": "my-cli",
|
||||
@@ -30,7 +26,7 @@ Packages can declare executables in the `"bin"` field of their `package.json`. T
|
||||
|
||||
These executables are commonly plain JavaScript files marked with a [shebang line](<https://en.wikipedia.org/wiki/Shebang_(Unix)>) to indicate which program should be used to execute them. The following file indicates that it should be executed with `node`.
|
||||
|
||||
```js dist/index.js icon="/icons/javascript.svg"
|
||||
```js#dist/index.js
|
||||
#!/usr/bin/env node
|
||||
|
||||
console.log("Hello world!");
|
||||
@@ -38,8 +34,8 @@ console.log("Hello world!");
|
||||
|
||||
These executables can be run with `bunx`,
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bunx my-cli
|
||||
```bash
|
||||
$ bunx my-cli
|
||||
```
|
||||
|
||||
As with `npx`, `bunx` will check for a locally installed package first, then fall back to auto-installing the package from `npm`. Installed packages will be stored in Bun's global cache for future use.
|
||||
@@ -48,36 +44,46 @@ As with `npx`, `bunx` will check for a locally installed package first, then fal
|
||||
|
||||
To pass additional command-line flags and arguments through to the executable, place them after the executable name.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bunx my-cli --foo bar
|
||||
```bash
|
||||
$ bunx my-cli --foo bar
|
||||
```
|
||||
|
||||
## Shebangs
|
||||
|
||||
By default, Bun respects shebangs. If an executable is marked with `#!/usr/bin/env node`, Bun will spin up a `node` process to execute the file. However, in some cases it may be desirable to run executables using Bun's runtime, even if the executable indicates otherwise. To do so, include the `--bun` flag.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bunx --bun my-cli
|
||||
```bash
|
||||
$ bunx --bun my-cli
|
||||
```
|
||||
|
||||
The `--bun` flag must occur _before_ the executable name. Flags that appear _after_ the name are passed through to the executable.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bunx --bun my-cli # good
|
||||
bunx my-cli --bun # bad
|
||||
```bash
|
||||
$ bunx --bun my-cli # good
|
||||
$ bunx my-cli --bun # bad
|
||||
```
|
||||
|
||||
## Package flag
|
||||
|
||||
**`--package <pkg>` or `-p <pkg>`** - Run binary from specific package. Useful when binary name differs from package name:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```bash
|
||||
bunx -p renovate renovate-config-validator
|
||||
bunx --package @angular/cli ng
|
||||
```
|
||||
|
||||
To force bun to always be used with a script, use a shebang.
|
||||
|
||||
```js dist/index.js icon="/icons/javascript.svg"
|
||||
```
|
||||
#!/usr/bin/env bun
|
||||
```
|
||||
|
||||
<!-- ## Environment variables
|
||||
|
||||
Bun automatically loads environment variables from `.env` files before running a file, script, or executable. The following files are checked, in order:
|
||||
|
||||
1. `.env.local` (first)
|
||||
2. `NODE_ENV` === `"production"` ? `.env.production` : `.env.development`
|
||||
3. `.env`
|
||||
|
||||
To debug environment variables, run `bun --print process.env` to view a list of resolved environment variables. -->
|
||||
@@ -1,14 +1,7 @@
|
||||
---
|
||||
title: "bun --filter"
|
||||
description: "Select packages by pattern in a monorepo using the --filter flag"
|
||||
---
|
||||
|
||||
The `--filter` (or `-F`) flag is used for selecting packages by pattern in a monorepo. Patterns can be used to match package names or package paths, with full glob syntax support.
|
||||
|
||||
Currently `--filter` is supported by `bun install` and `bun outdated`, and can also be used to run scripts for multiple packages at once.
|
||||
|
||||
---
|
||||
|
||||
## Matching
|
||||
|
||||
### Package Name `--filter <pattern>`
|
||||
@@ -19,8 +12,6 @@ Name patterns select packages based on the package name, as specified in `packag
|
||||
|
||||
Path patterns are specified by starting the pattern with `./`, and will select all packages in directories that match the pattern. For example, to match all packages in subdirectories of `packages`, you can use `--filter './packages/**'`. To match a package located in `packages/foo`, use `--filter ./packages/foo`.
|
||||
|
||||
---
|
||||
|
||||
## `bun install` and `bun outdated`
|
||||
|
||||
Both `bun install` and `bun outdated` support the `--filter` flag.
|
||||
@@ -29,42 +20,40 @@ Both `bun install` and `bun outdated` support the `--filter` flag.
|
||||
|
||||
Given a monorepo with workspaces `pkg-a`, `pkg-b`, and `pkg-c` under `./packages`:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```bash
|
||||
# Install dependencies for all workspaces except `pkg-c`
|
||||
bun install --filter '!pkg-c'
|
||||
$ bun install --filter '!pkg-c'
|
||||
|
||||
# Install dependencies for packages in `./packages` (`pkg-a`, `pkg-b`, `pkg-c`)
|
||||
bun install --filter './packages/*'
|
||||
$ bun install --filter './packages/*'
|
||||
|
||||
# Save as above, but exclude the root package.json
|
||||
bun install --filter '!./' --filter './packages/*'
|
||||
$ bun install --filter '!./' --filter './packages/*'
|
||||
```
|
||||
|
||||
Similarly, `bun outdated` will display outdated dependencies for all packages in the monorepo, and `--filter` can be used to restrict the command to a subset of the packages:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```bash
|
||||
# Display outdated dependencies for workspaces starting with `pkg-`
|
||||
bun outdated --filter 'pkg-*'
|
||||
$ bun outdated --filter 'pkg-*'
|
||||
|
||||
# Display outdated dependencies for only the root package.json
|
||||
bun outdated --filter './'
|
||||
$ bun outdated --filter './'
|
||||
```
|
||||
|
||||
For more information on both these commands, see [`bun install`](/pm/cli/install) and [`bun outdated`](/pm/cli/outdated).
|
||||
|
||||
---
|
||||
For more information on both these commands, see [`bun install`](https://bun.com/docs/cli/install) and [`bun outdated`](https://bun.com/docs/cli/outdated).
|
||||
|
||||
## Running scripts with `--filter`
|
||||
|
||||
Use the `--filter` flag to execute scripts in multiple packages at once:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```bash
|
||||
bun --filter <pattern> <script>
|
||||
```
|
||||
|
||||
Say you have a monorepo with two packages: `packages/api` and `packages/frontend`, both with a `dev` script that will start a local development server. Normally, you would have to open two separate terminal tabs, cd into each package directory, and run `bun dev`:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```bash
|
||||
cd packages/api
|
||||
bun dev
|
||||
|
||||
@@ -75,20 +64,19 @@ bun dev
|
||||
|
||||
Using `--filter`, you can run the `dev` script in both packages at once:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```bash
|
||||
bun --filter '*' dev
|
||||
```
|
||||
|
||||
Both commands will be run in parallel, and you will see a nice terminal UI showing their respective outputs:
|
||||
|
||||
<Frame></Frame>
|
||||

|
||||
|
||||
### Running scripts in workspaces
|
||||
|
||||
Filters respect your [workspace configuration](/pm/workspaces): If you have a `package.json` file that specifies which packages are part of the workspace,
|
||||
Filters respect your [workspace configuration](https://bun.com/docs/install/workspaces): If you have a `package.json` file that specifies which packages are part of the workspace,
|
||||
`--filter` will be restricted to only these packages. Also, in a workspace you can use `--filter` to run scripts in packages that are located anywhere in the workspace:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```bash
|
||||
# Packages
|
||||
# src/foo
|
||||
# src/bar
|
||||
@@ -1,14 +1,9 @@
|
||||
---
|
||||
title: "bun info"
|
||||
description: "Display package metadata from the npm registry"
|
||||
---
|
||||
|
||||
`bun info` displays package metadata from the npm registry.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun info react
|
||||
```bash
|
||||
$ bun info react
|
||||
```
|
||||
|
||||
This will display information about the `react` package, including its latest version, description, homepage, dependencies, and more.
|
||||
@@ -17,54 +12,54 @@ This will display information about the `react` package, including its latest ve
|
||||
|
||||
To view information about a specific version:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun info react@18.0.0
|
||||
```bash
|
||||
$ bun info react@18.0.0
|
||||
```
|
||||
|
||||
## Viewing specific properties
|
||||
|
||||
You can also query specific properties from the package metadata:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun info react version
|
||||
bun info react dependencies
|
||||
bun info react repository.url
|
||||
```bash
|
||||
$ bun info react version
|
||||
$ bun info react dependencies
|
||||
$ bun info react repository.url
|
||||
```
|
||||
|
||||
## JSON output
|
||||
|
||||
To get the output in JSON format, use the `--json` flag:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun info react --json
|
||||
```bash
|
||||
$ bun info react --json
|
||||
```
|
||||
|
||||
## Alias
|
||||
|
||||
`bun pm view` is an alias for `bun info`:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun pm view react # equivalent to: bun info react
|
||||
```bash
|
||||
$ bun pm view react # equivalent to: bun info react
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
```bash
|
||||
# View basic package information
|
||||
bun info is-number
|
||||
$ bun info is-number
|
||||
|
||||
# View a specific version
|
||||
bun info is-number@7.0.0
|
||||
$ bun info is-number@7.0.0
|
||||
|
||||
# View all available versions
|
||||
bun info is-number versions
|
||||
$ bun info is-number versions
|
||||
|
||||
# View package dependencies
|
||||
bun info express dependencies
|
||||
$ bun info express dependencies
|
||||
|
||||
# View package homepage
|
||||
bun info lodash homepage
|
||||
$ bun info lodash homepage
|
||||
|
||||
# Get JSON output
|
||||
bun info react --json
|
||||
$ bun info react --json
|
||||
```
|
||||
@@ -1,17 +1,8 @@
|
||||
---
|
||||
title: "bun init"
|
||||
description: "Scaffold an empty Bun project with the interactive `bun init` command"
|
||||
---
|
||||
Scaffold an empty Bun project with the interactive `bun init` command.
|
||||
|
||||
import Init from "/snippets/cli/init.mdx";
|
||||
```bash
|
||||
$ bun init
|
||||
|
||||
Get started with Bun by scaffolding a new project with `bun init`.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun init my-app
|
||||
```
|
||||
|
||||
```txt
|
||||
? Select a project template - Press return to submit.
|
||||
❯ Blank
|
||||
React
|
||||
@@ -19,23 +10,30 @@ bun init my-app
|
||||
|
||||
✓ Select a project template: Blank
|
||||
|
||||
+ .gitignore
|
||||
+ CLAUDE.md
|
||||
+ .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc -> CLAUDE.md
|
||||
+ index.ts
|
||||
+ tsconfig.json (for editor autocomplete)
|
||||
+ README.md
|
||||
+ .gitignore
|
||||
+ index.ts
|
||||
+ tsconfig.json (for editor autocomplete)
|
||||
+ README.md
|
||||
|
||||
To get started, run:
|
||||
|
||||
bun run index.ts
|
||||
|
||||
bun install v$BUN_LATEST_VERSION
|
||||
|
||||
+ @types/bun@$BUN_LATEST_VERSION
|
||||
+ typescript@5.9.2
|
||||
|
||||
7 packages installed
|
||||
```
|
||||
|
||||
Press `enter` to accept the default answer for each prompt, or pass the `-y` flag to auto-accept the defaults.
|
||||
|
||||
---
|
||||
{% details summary="How `bun init` works" %}
|
||||
|
||||
`bun init` is a quick way to start a blank project with Bun. It guesses with sane defaults and is non-destructive when run multiple times.
|
||||
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||

|
||||
|
||||
It creates:
|
||||
|
||||
@@ -53,6 +51,35 @@ If you pass `-y` or `--yes`, it will assume you want to continue without asking
|
||||
|
||||
At the end, it runs `bun install` to install `@types/bun`.
|
||||
|
||||
---
|
||||
{% /details %}
|
||||
|
||||
<Init />
|
||||
{% bunCLIUsage command="init" /%}
|
||||
|
||||
## React
|
||||
|
||||
The `--react` flag will scaffold a React project:
|
||||
|
||||
```bash
|
||||
$ bun init --react
|
||||
```
|
||||
|
||||
The `--react` flag accepts the following values:
|
||||
|
||||
- `tailwind` - Scaffold a React project with Tailwind CSS
|
||||
- `shadcn` - Scaffold a React project with Shadcn/UI and Tailwind CSS
|
||||
|
||||
### React + TailwindCSS
|
||||
|
||||
This will create a React project with Tailwind CSS configured with Bun's bundler and dev server.
|
||||
|
||||
```bash
|
||||
$ bun init --react=tailwind
|
||||
```
|
||||
|
||||
### React + @shadcn/ui
|
||||
|
||||
This will create a React project with shadcn/ui and Tailwind CSS configured with Bun's bundler and dev server.
|
||||
|
||||
```bash
|
||||
$ bun init --react=shadcn
|
||||
```
|
||||
344
docs/cli/install.md
Normal file
344
docs/cli/install.md
Normal file
@@ -0,0 +1,344 @@
|
||||
The `bun` CLI contains a Node.js-compatible package manager designed to be a dramatically faster replacement for `npm`, `yarn`, and `pnpm`. It's a standalone tool that will work in pre-existing Node.js projects; if your project has a `package.json`, `bun install` can help you speed up your workflow.
|
||||
|
||||
{% callout %}
|
||||
|
||||
**⚡️ 25x faster** — Switch from `npm install` to `bun install` in any Node.js project to make your installations up to 25x faster.
|
||||
|
||||
{% image src="https://user-images.githubusercontent.com/709451/147004342-571b6123-17a9-49a2-8bfd-dcfc5204047e.png" height="200" /%}
|
||||
|
||||
{% /callout %}
|
||||
|
||||
{% callout %}
|
||||
|
||||
**💾 Disk efficient** — Bun install stores all packages in a global cache (`~/.bun/install/cache/`) and creates hardlinks (Linux) or copy-on-write clones (macOS) to `node_modules`. This means duplicate packages across projects point to the same underlying data, taking up virtually no extra disk space.
|
||||
|
||||
For more details, see [Package manager > Global cache](https://bun.com/docs/install/cache).
|
||||
|
||||
{% /callout %}
|
||||
|
||||
{% details summary="For Linux users" %}
|
||||
The recommended minimum Linux Kernel version is 5.6. If you're on Linux kernel 5.1 - 5.5, `bun install` will work, but HTTP requests will be slow due to a lack of support for io_uring's `connect()` operation.
|
||||
|
||||
If you're using Ubuntu 20.04, here's how to install a [newer kernel](https://wiki.ubuntu.com/Kernel/LTSEnablementStack):
|
||||
|
||||
```bash
|
||||
# If this returns a version >= 5.6, you don't need to do anything
|
||||
$ uname -r
|
||||
|
||||
# Install the official Ubuntu hardware enablement kernel
|
||||
$ sudo apt install --install-recommends linux-generic-hwe-20.04
|
||||
```
|
||||
|
||||
{% /details %}
|
||||
|
||||
To install all dependencies of a project:
|
||||
|
||||
```bash
|
||||
$ bun install
|
||||
```
|
||||
|
||||
Running `bun install` will:
|
||||
|
||||
- **Install** all `dependencies`, `devDependencies`, and `optionalDependencies`. Bun will install `peerDependencies` by default.
|
||||
- **Run** your project's `{pre|post}install` and `{pre|post}prepare` scripts at the appropriate time. For security reasons Bun _does not execute_ lifecycle scripts of installed dependencies.
|
||||
- **Write** a `bun.lock` lockfile to the project root.
|
||||
|
||||
## Logging
|
||||
|
||||
To modify logging verbosity:
|
||||
|
||||
```bash
|
||||
$ bun install --verbose # debug logging
|
||||
$ bun install --silent # no logging
|
||||
```
|
||||
|
||||
## Lifecycle scripts
|
||||
|
||||
Unlike other npm clients, Bun does not execute arbitrary lifecycle scripts like `postinstall` for installed dependencies. Executing arbitrary scripts represents a potential security risk.
|
||||
|
||||
To tell Bun to allow lifecycle scripts for a particular package, add the package to `trustedDependencies` in your package.json.
|
||||
|
||||
```json-diff
|
||||
{
|
||||
"name": "my-app",
|
||||
"version": "1.0.0",
|
||||
+ "trustedDependencies": ["my-trusted-package"]
|
||||
}
|
||||
```
|
||||
|
||||
Then re-install the package. Bun will read this field and run lifecycle scripts for `my-trusted-package`.
|
||||
|
||||
Lifecycle scripts will run in parallel during installation. To adjust the maximum number of concurrent scripts, use the `--concurrent-scripts` flag. The default is two times the reported cpu count or GOMAXPROCS.
|
||||
|
||||
```bash
|
||||
$ bun install --concurrent-scripts 5
|
||||
```
|
||||
|
||||
## Workspaces
|
||||
|
||||
Bun supports `"workspaces"` in package.json. For complete documentation refer to [Package manager > Workspaces](https://bun.com/docs/install/workspaces).
|
||||
|
||||
```json#package.json
|
||||
{
|
||||
"name": "my-app",
|
||||
"version": "1.0.0",
|
||||
"workspaces": ["packages/*"],
|
||||
"dependencies": {
|
||||
"preact": "^10.5.13"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Installing dependencies for specific packages
|
||||
|
||||
In a monorepo, you can install the dependencies for a subset of packages using the `--filter` flag.
|
||||
|
||||
```bash
|
||||
# Install dependencies for all workspaces except `pkg-c`
|
||||
$ bun install --filter '!pkg-c'
|
||||
|
||||
# Install dependencies for only `pkg-a` in `./packages/pkg-a`
|
||||
$ bun install --filter './packages/pkg-a'
|
||||
```
|
||||
|
||||
For more information on filtering with `bun install`, refer to [Package Manager > Filtering](https://bun.com/docs/cli/filter#bun-install-and-bun-outdated)
|
||||
|
||||
## Overrides and resolutions
|
||||
|
||||
Bun supports npm's `"overrides"` and Yarn's `"resolutions"` in `package.json`. These are mechanisms for specifying a version range for _metadependencies_—the dependencies of your dependencies. Refer to [Package manager > Overrides and resolutions](https://bun.com/docs/install/overrides) for complete documentation.
|
||||
|
||||
```json-diff#package.json
|
||||
{
|
||||
"name": "my-app",
|
||||
"dependencies": {
|
||||
"foo": "^2.0.0"
|
||||
},
|
||||
+ "overrides": {
|
||||
+ "bar": "~4.4.0"
|
||||
+ }
|
||||
}
|
||||
```
|
||||
|
||||
## Global packages
|
||||
|
||||
To install a package globally, use the `-g`/`--global` flag. Typically this is used for installing command-line tools.
|
||||
|
||||
```bash
|
||||
$ bun install --global cowsay # or `bun install -g cowsay`
|
||||
$ cowsay "Bun!"
|
||||
______
|
||||
< Bun! >
|
||||
------
|
||||
\ ^__^
|
||||
\ (oo)\_______
|
||||
(__)\ )\/\
|
||||
||----w |
|
||||
|| ||
|
||||
```
|
||||
|
||||
## Production mode
|
||||
|
||||
To install in production mode (i.e. without `devDependencies` or `optionalDependencies`):
|
||||
|
||||
```bash
|
||||
$ bun install --production
|
||||
```
|
||||
|
||||
For reproducible installs, use `--frozen-lockfile`. This will install the exact versions of each package specified in the lockfile. If your `package.json` disagrees with `bun.lock`, Bun will exit with an error. The lockfile will not be updated.
|
||||
|
||||
```bash
|
||||
$ bun install --frozen-lockfile
|
||||
```
|
||||
|
||||
For more information on Bun's lockfile `bun.lock`, refer to [Package manager > Lockfile](https://bun.com/docs/install/lockfile).
|
||||
|
||||
## Omitting dependencies
|
||||
|
||||
To omit dev, peer, or optional dependencies use the `--omit` flag.
|
||||
|
||||
```bash
|
||||
# Exclude "devDependencies" from the installation. This will apply to the
|
||||
# root package and workspaces if they exist. Transitive dependencies will
|
||||
# not have "devDependencies".
|
||||
$ bun install --omit dev
|
||||
|
||||
# Install only dependencies from "dependencies"
|
||||
$ bun install --omit=dev --omit=peer --omit=optional
|
||||
```
|
||||
|
||||
## Dry run
|
||||
|
||||
To perform a dry run (i.e. don't actually install anything):
|
||||
|
||||
```bash
|
||||
$ bun install --dry-run
|
||||
```
|
||||
|
||||
## Non-npm dependencies
|
||||
|
||||
Bun supports installing dependencies from Git, GitHub, and local or remotely-hosted tarballs. For complete documentation refer to [Package manager > Git, GitHub, and tarball dependencies](https://bun.com/docs/cli/add).
|
||||
|
||||
```json#package.json
|
||||
{
|
||||
"dependencies": {
|
||||
"dayjs": "git+https://github.com/iamkun/dayjs.git",
|
||||
"lodash": "git+ssh://github.com/lodash/lodash.git#4.17.21",
|
||||
"moment": "git@github.com:moment/moment.git",
|
||||
"zod": "github:colinhacks/zod",
|
||||
"react": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||
"bun-types": "npm:@types/bun"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Installation strategies
|
||||
|
||||
Bun supports two package installation strategies that determine how dependencies are organized in `node_modules`:
|
||||
|
||||
### Hoisted installs (default for single projects)
|
||||
|
||||
The traditional npm/Yarn approach that flattens dependencies into a shared `node_modules` directory:
|
||||
|
||||
```bash
|
||||
$ bun install --linker hoisted
|
||||
```
|
||||
|
||||
### Isolated installs
|
||||
|
||||
A pnpm-like approach that creates strict dependency isolation to prevent phantom dependencies:
|
||||
|
||||
```bash
|
||||
$ bun install --linker isolated
|
||||
```
|
||||
|
||||
Isolated installs create a central package store in `node_modules/.bun/` with symlinks in the top-level `node_modules`. This ensures packages can only access their declared dependencies.
|
||||
|
||||
For complete documentation on isolated installs, refer to [Package manager > Isolated installs](https://bun.com/docs/install/isolated).
|
||||
|
||||
## Disk efficiency
|
||||
|
||||
Bun uses a global cache at `~/.bun/install/cache/` to minimize disk usage. Packages are stored once and linked to `node_modules` using hardlinks (Linux/Windows) or copy-on-write (macOS), so duplicate packages across projects don't consume additional disk space.
|
||||
|
||||
For complete documentation refer to [Package manager > Global cache](https://bun.com/docs/install/cache).
|
||||
|
||||
## Minimum release age
|
||||
|
||||
To protect against supply chain attacks where malicious packages are quickly published, you can configure a minimum age requirement for npm packages. Package versions published more recently than the specified threshold (in seconds) will be filtered out during installation.
|
||||
|
||||
```bash
|
||||
# Only install package versions published at least 3 days ago
|
||||
$ bun add @types/bun --minimum-release-age 259200 # seconds
|
||||
```
|
||||
|
||||
You can also configure this in `bunfig.toml`:
|
||||
|
||||
```toml
|
||||
[install]
|
||||
# Only install package versions published at least 3 days ago
|
||||
minimumReleaseAge = 259200 # seconds
|
||||
|
||||
# Exclude trusted packages from the age gate
|
||||
minimumReleaseAgeExcludes = ["@types/node", "typescript"]
|
||||
```
|
||||
|
||||
When the minimum age filter is active:
|
||||
|
||||
- Only affects new package resolution - existing packages in `bun.lock` remain unchanged
|
||||
- All dependencies (direct and transitive) are filtered to meet the age requirement when being resolved
|
||||
- When versions are blocked by the age gate, a stability check detects rapid bugfix patterns
|
||||
- If multiple versions were published close together just outside your age gate, it extends the filter to skip those potentially unstable versions and selects an older, more mature version
|
||||
- Searches up to 7 days after the age gate, however if still finding rapid releases it ignores stability check
|
||||
- Exact version requests (like `package@1.1.1`) still respect the age gate but bypass the stability check
|
||||
- Versions without a `time` field are treated as passing the age check (npm registry should always provide timestamps)
|
||||
|
||||
For more advanced security scanning, including integration with services & custom filtering, see [Package manager > Security Scanner API](https://bun.com/docs/install/security-scanner-api).
|
||||
|
||||
## Configuration
|
||||
|
||||
The default behavior of `bun install` can be configured in `bunfig.toml`. The default values are shown below.
|
||||
|
||||
```toml
|
||||
[install]
|
||||
|
||||
# whether to install optionalDependencies
|
||||
optional = true
|
||||
|
||||
# whether to install devDependencies
|
||||
dev = true
|
||||
|
||||
# whether to install peerDependencies
|
||||
peer = true
|
||||
|
||||
# equivalent to `--production` flag
|
||||
production = false
|
||||
|
||||
# equivalent to `--save-text-lockfile` flag
|
||||
saveTextLockfile = false
|
||||
|
||||
# equivalent to `--frozen-lockfile` flag
|
||||
frozenLockfile = false
|
||||
|
||||
# equivalent to `--dry-run` flag
|
||||
dryRun = false
|
||||
|
||||
# equivalent to `--concurrent-scripts` flag
|
||||
concurrentScripts = 16 # (cpu count or GOMAXPROCS) x2
|
||||
|
||||
# installation strategy: "hoisted" or "isolated"
|
||||
# default: "hoisted"
|
||||
linker = "hoisted"
|
||||
|
||||
# minimum age config
|
||||
minimumReleaseAge = 259200 # seconds
|
||||
minimumReleaseAgeExcludes = ["@types/node", "typescript"]
|
||||
```
|
||||
|
||||
## CI/CD
|
||||
|
||||
Use the official [`oven-sh/setup-bun`](https://github.com/oven-sh/setup-bun) action to install `bun` in a GitHub Actions pipeline:
|
||||
|
||||
```yaml#.github/workflows/release.yml
|
||||
name: bun-types
|
||||
jobs:
|
||||
build:
|
||||
name: build-app
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
- name: Build app
|
||||
run: bun run build
|
||||
```
|
||||
|
||||
For CI/CD environments that want to enforce reproducible builds, use `bun ci` to fail the build if the package.json is out of sync with the lockfile:
|
||||
|
||||
```bash
|
||||
$ bun ci
|
||||
```
|
||||
|
||||
This is equivalent to `bun install --frozen-lockfile`. It installs exact versions from `bun.lock` and fails if `package.json` doesn't match the lockfile. To use `bun ci` or `bun install --frozen-lockfile`, you must commit `bun.lock` to version control.
|
||||
|
||||
And instead of running `bun install`, run `bun ci`.
|
||||
|
||||
```yaml#.github/workflows/release.yml
|
||||
name: bun-types
|
||||
jobs:
|
||||
build:
|
||||
name: build-app
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
- name: Install dependencies
|
||||
run: bun ci
|
||||
- name: Build app
|
||||
run: bun run build
|
||||
```
|
||||
|
||||
{% bunCLIUsage command="install" /%}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user