mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 23:18:47 +00:00
Compare commits
265 Commits
claude/fix
...
claude/fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8612285e3d | ||
|
|
0a307ed880 | ||
|
|
b4f85c8866 | ||
|
|
614e8292e3 | ||
|
|
3829b6d0aa | ||
|
|
f30e3951a7 | ||
|
|
b131639cc5 | ||
|
|
b9b07172aa | ||
|
|
02b474415d | ||
|
|
de9a38bd11 | ||
|
|
2e57c6bf95 | ||
|
|
35a57ff008 | ||
|
|
7a931d5b26 | ||
|
|
2b42be9dcc | ||
|
|
e6be28b8d4 | ||
|
|
d0a1984a20 | ||
|
|
1896c75d78 | ||
|
|
3a810da66c | ||
|
|
0db90b2526 | ||
|
|
6f8138b6e4 | ||
|
|
23a2b2129c | ||
|
|
8ec856124c | ||
|
|
94bc68f72c | ||
|
|
75f271a306 | ||
|
|
267be9a54a | ||
|
|
44402ad27a | ||
|
|
e01f454635 | ||
|
|
f56232a810 | ||
|
|
cf0ae19c2a | ||
|
|
4ac293bf01 | ||
|
|
314088ab37 | ||
|
|
86a0ff442a | ||
|
|
f4404a55db | ||
|
|
1d4e3c0ab2 | ||
|
|
782f684b2e | ||
|
|
995d988c73 | ||
|
|
c7b9e0dc92 | ||
|
|
df5d0fcfa1 | ||
|
|
4a326979f4 | ||
|
|
b3f8930c4a | ||
|
|
126f4686af | ||
|
|
1606a9f24e | ||
|
|
550522e99b | ||
|
|
46d4ed3c33 | ||
|
|
fa219a2f8e | ||
|
|
4250ce6157 | ||
|
|
f8dce87f24 | ||
|
|
359f04d81f | ||
|
|
9ce2504554 | ||
|
|
5d76a0b2f8 | ||
|
|
8a9249c216 | ||
|
|
aad4d800ff | ||
|
|
528620e9ae | ||
|
|
7197fb1f04 | ||
|
|
946470dcd7 | ||
|
|
d76fad3618 | ||
|
|
bdaab89253 | ||
|
|
797847639a | ||
|
|
39c43170e6 | ||
|
|
f770b1b1c7 | ||
|
|
42543fb544 | ||
|
|
60c0fd7786 | ||
|
|
219b9c6cfc | ||
|
|
a912eca96a | ||
|
|
8058d78b6a | ||
|
|
8b98746808 | ||
|
|
f50b44e35b | ||
|
|
b02d46498e | ||
|
|
28be8a9915 | ||
|
|
5aeef40479 | ||
|
|
9953d78a66 | ||
|
|
0564b81e64 | ||
|
|
7c9e8a2b10 | ||
|
|
c5def80191 | ||
|
|
fb6384160d | ||
|
|
759018caf9 | ||
|
|
b3b465937f | ||
|
|
570b0a03a4 | ||
|
|
358596afbd | ||
|
|
5b5b02dee6 | ||
|
|
8f0f06ba52 | ||
|
|
90ce355ef0 | ||
|
|
476e1cfe69 | ||
|
|
646aede0d4 | ||
|
|
1d728bb778 | ||
|
|
a7fc6eb354 | ||
|
|
ddd4018bda | ||
|
|
1de4448425 | ||
|
|
9d4a04cff9 | ||
|
|
9d0ef94557 | ||
|
|
a5f8b0e8dd | ||
|
|
fe1bc56637 | ||
|
|
98c04e37ec | ||
|
|
4f1b90ad1d | ||
|
|
51431b6e65 | ||
|
|
eb77bdd286 | ||
|
|
523fc14d76 | ||
|
|
a0a69ee146 | ||
|
|
668eba0eb8 | ||
|
|
6580b563b0 | ||
|
|
f3ed784a6b | ||
|
|
64bfd8b938 | ||
|
|
2afafbfa23 | ||
|
|
1e849b905a | ||
|
|
b280e8d326 | ||
|
|
b7ae21d0bc | ||
|
|
a75cef5079 | ||
|
|
4c00d8f016 | ||
|
|
f58a066236 | ||
|
|
3367fa6ae3 | ||
|
|
a2b262ed69 | ||
|
|
fb1fbe62e6 | ||
|
|
d2c2842420 | ||
|
|
0fba69d50c | ||
|
|
f4b6396eac | ||
|
|
cfe561a083 | ||
|
|
5a7b824091 | ||
|
|
a3f18b9e0e | ||
|
|
afd125fc12 | ||
|
|
0dd6aa47ea | ||
|
|
ab1395d38e | ||
|
|
e76570f452 | ||
|
|
d648547942 | ||
|
|
787a46d110 | ||
|
|
29028bbabe | ||
|
|
5a82e85876 | ||
|
|
7bf67e78d7 | ||
|
|
fb75e077a2 | ||
|
|
24d9d642de | ||
|
|
b278c85753 | ||
|
|
066f706a99 | ||
|
|
0ad4e6af2d | ||
|
|
b90abdda08 | ||
|
|
89fa0f3439 | ||
|
|
72f1ffdaf7 | ||
|
|
bb5f0f5d69 | ||
|
|
a3c43dc8b9 | ||
|
|
45841d6630 | ||
|
|
d846e9a1e7 | ||
|
|
06eea5213a | ||
|
|
1aaabcf4de | ||
|
|
3bc78598c6 | ||
|
|
12e22af382 | ||
|
|
88fa296dcd | ||
|
|
cd8043b76e | ||
|
|
840c6ca471 | ||
|
|
150338faab | ||
|
|
7662de9632 | ||
|
|
789a5f4078 | ||
|
|
965051fd1f | ||
|
|
7750afa29b | ||
|
|
2c86fdb818 | ||
|
|
07317193fe | ||
|
|
3e53ada574 | ||
|
|
25a8dea38b | ||
|
|
3520393b25 | ||
|
|
8b8e98d0fb | ||
|
|
b1f83d0bb2 | ||
|
|
32a28385dd | ||
|
|
881514a18a | ||
|
|
6dffd32d52 | ||
|
|
5971bf67ef | ||
|
|
686998ed3d | ||
|
|
b3c69e5a4e | ||
|
|
1e3e693f4a | ||
|
|
2557b1cc2a | ||
|
|
ebc0cfeacd | ||
|
|
abb85018df | ||
|
|
1c4d8b1c1c | ||
|
|
3921f76ff8 | ||
|
|
74fa49963c | ||
|
|
fb2bf3fe83 | ||
|
|
4539d241a1 | ||
|
|
6f3dfa79bb | ||
|
|
767c61d355 | ||
|
|
e63a897c66 | ||
|
|
576b21f2ff | ||
|
|
8eab0fc9fc | ||
|
|
de4a5a07b1 | ||
|
|
f912355587 | ||
|
|
acefbe2421 | ||
|
|
4a06991d3b | ||
|
|
74faec2cc9 | ||
|
|
abb82a6905 | ||
|
|
47af6e92d9 | ||
|
|
0cb41b1de8 | ||
|
|
b867969e2c | ||
|
|
0b89a422bb | ||
|
|
8e34ec311e | ||
|
|
2ebf6c16b6 | ||
|
|
6ee9dac50f | ||
|
|
6a52fd8590 | ||
|
|
e9e9ca4ffd | ||
|
|
bd15fce066 | ||
|
|
f702ae5f0f | ||
|
|
0a92d64f0f | ||
|
|
d6cfb58bf4 | ||
|
|
d9a867a4b9 | ||
|
|
1abfc0ea24 | ||
|
|
28f0e5b3b5 | ||
|
|
a7816cfb23 | ||
|
|
4142f89148 | ||
|
|
134341d2b4 | ||
|
|
642d04b9f2 | ||
|
|
fadce1001d | ||
|
|
40b9a92891 | ||
|
|
81c453cb8c | ||
|
|
1f48dcebed | ||
|
|
26870c905c | ||
|
|
101e63e881 | ||
|
|
324c0d1a39 | ||
|
|
0eb470fd88 | ||
|
|
c3bfff58d9 | ||
|
|
37ad295114 | ||
|
|
b17133a9e9 | ||
|
|
acc42467b0 | ||
|
|
bad726f943 | ||
|
|
dc36d5601c | ||
|
|
9086b8f203 | ||
|
|
6d1ea1c14e | ||
|
|
a7d7eeab24 | ||
|
|
25d23201b6 | ||
|
|
bd88717ddc | ||
|
|
0c79e5e0dd | ||
|
|
57ab7f18d1 | ||
|
|
9c37549e0c | ||
|
|
61cd9602ce | ||
|
|
8618b32c0c | ||
|
|
7934e64507 | ||
|
|
72900ec688 | ||
|
|
c820c2b0d3 | ||
|
|
db7bcd79ff | ||
|
|
caa4f54b2e | ||
|
|
5196be53e2 | ||
|
|
f00e1816ef | ||
|
|
356d0e491c | ||
|
|
b8b9d70cdd | ||
|
|
755b41e85b | ||
|
|
d29aa58db0 | ||
|
|
40da082349 | ||
|
|
5bdc32265d | ||
|
|
01924e9993 | ||
|
|
d963a05907 | ||
|
|
8ccd17fe87 | ||
|
|
1c84f87b14 | ||
|
|
0e29617d4b | ||
|
|
f0807e22e2 | ||
|
|
c766c14928 | ||
|
|
525315cf39 | ||
|
|
85a2ebb717 | ||
|
|
f673ed8821 | ||
|
|
a67ac081f1 | ||
|
|
622d36a553 | ||
|
|
b0a6feca57 | ||
|
|
012a2bab92 | ||
|
|
8197a5f7af | ||
|
|
c50db1dbfb | ||
|
|
312a86fd43 | ||
|
|
8826b4f5f5 | ||
|
|
f65e280521 | ||
|
|
a686b9fc39 | ||
|
|
a3cf974752 | ||
|
|
de366cfe4b | ||
|
|
98d1a9d110 | ||
|
|
3395774c8c |
@@ -48,6 +48,14 @@ 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 \
|
||||
@@ -110,14 +118,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 && \
|
||||
@@ -147,7 +155,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
|
||||
@@ -161,4 +169,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,6 +16,7 @@ import {
|
||||
getEmoji,
|
||||
getEnv,
|
||||
getLastSuccessfulBuild,
|
||||
getSecret,
|
||||
isBuildkite,
|
||||
isBuildManual,
|
||||
isFork,
|
||||
@@ -108,9 +109,9 @@ const buildPlatforms = [
|
||||
{ os: "linux", arch: "x64", distro: "amazonlinux", release: "2023", features: ["docker"] },
|
||||
{ os: "linux", arch: "x64", baseline: true, distro: "amazonlinux", release: "2023", features: ["docker"] },
|
||||
{ os: "linux", arch: "x64", profile: "asan", distro: "amazonlinux", release: "2023", features: ["docker"] },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.21" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.21" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.21" },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.22" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.22" },
|
||||
{ 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" },
|
||||
];
|
||||
@@ -133,9 +134,9 @@ const testPlatforms = [
|
||||
{ os: "linux", arch: "x64", distro: "ubuntu", release: "24.04", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "25.04", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "24.04", tier: "latest" },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.21", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.21", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.21", tier: "latest" },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.22", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.22", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.22", tier: "latest" },
|
||||
{ os: "windows", arch: "x64", release: "2019", tier: "oldest" },
|
||||
{ os: "windows", arch: "x64", release: "2019", baseline: true, tier: "oldest" },
|
||||
];
|
||||
@@ -223,7 +224,7 @@ function getImageName(platform, options) {
|
||||
* @param {number} [limit]
|
||||
* @link https://buildkite.com/docs/pipelines/command-step#retry-attributes
|
||||
*/
|
||||
function getRetry(limit = 0) {
|
||||
function getRetry() {
|
||||
return {
|
||||
manual: {
|
||||
permit_on_passed: true,
|
||||
@@ -292,7 +293,7 @@ function getEc2Agent(platform, options, ec2Options) {
|
||||
* @returns {string}
|
||||
*/
|
||||
function getCppAgent(platform, options) {
|
||||
const { os, arch, distro } = platform;
|
||||
const { os, arch } = platform;
|
||||
|
||||
if (os === "darwin") {
|
||||
return {
|
||||
@@ -313,7 +314,7 @@ function getCppAgent(platform, options) {
|
||||
* @returns {string}
|
||||
*/
|
||||
function getLinkBunAgent(platform, options) {
|
||||
const { os, arch, distro } = platform;
|
||||
const { os, arch } = platform;
|
||||
|
||||
if (os === "darwin") {
|
||||
return {
|
||||
@@ -343,7 +344,7 @@ function getZigPlatform() {
|
||||
arch: "aarch64",
|
||||
abi: "musl",
|
||||
distro: "alpine",
|
||||
release: "3.21",
|
||||
release: "3.22",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -352,14 +353,7 @@ function getZigPlatform() {
|
||||
* @param {PipelineOptions} options
|
||||
* @returns {Agent}
|
||||
*/
|
||||
function getZigAgent(platform, options) {
|
||||
const { arch } = platform;
|
||||
|
||||
// Uncomment to restore to using macOS on-prem for Zig.
|
||||
// return {
|
||||
// queue: "build-zig",
|
||||
// };
|
||||
|
||||
function getZigAgent(_platform, options) {
|
||||
return getEc2Agent(getZigPlatform(), options, {
|
||||
instanceType: "r8g.large",
|
||||
});
|
||||
@@ -461,23 +455,6 @@ 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
|
||||
@@ -527,9 +504,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}`,
|
||||
@@ -1227,6 +1204,43 @@ 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) {
|
||||
|
||||
43
.claude/commands/dedupe.md
Normal file
43
.claude/commands/dedupe.md
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*), Bash(gh api:*), Bash(gh issue comment:*)
|
||||
description: Find duplicate GitHub issues
|
||||
---
|
||||
|
||||
# Issue deduplication command
|
||||
|
||||
Find up to 3 likely duplicate issues for a given GitHub issue.
|
||||
|
||||
To do this, follow these steps precisely:
|
||||
|
||||
1. Use an agent to check if the GitHub issue (a) is closed, (b) does not need to be deduped (eg. because it is broad product feedback without a specific solution, or positive feedback), or (c) already has a duplicate detection comment (check for the exact HTML marker `<!-- dedupe-bot:marker -->` in the issue comments - ignore other bot comments). If so, do not proceed.
|
||||
2. Use an agent to view a GitHub issue, and ask the agent to return a summary of the issue
|
||||
3. Then, launch 5 parallel agents to search GitHub for duplicates of this issue, using diverse keywords and search approaches, using the summary from Step 2. **IMPORTANT**: Always scope searches with `repo:owner/repo` to constrain results to the current repository only.
|
||||
4. Next, feed the results from Steps 2 and 3 into another agent, so that it can filter out false positives, that are likely not actually duplicates of the original issue. If there are no duplicates remaining, do not proceed.
|
||||
5. Finally, comment back on the issue with a list of up to three duplicate issues (or zero, if there are no likely duplicates)
|
||||
|
||||
Notes (be sure to tell this to your agents, too):
|
||||
|
||||
- Use `gh` to interact with GitHub, rather than web fetch
|
||||
- Do not use other tools, beyond `gh` (eg. don't use other MCP servers, file edit, etc.)
|
||||
- Make a todo list first
|
||||
- Always scope searches with `repo:owner/repo` to prevent cross-repo false positives
|
||||
- For your comment, follow the following format precisely (assuming for this example that you found 3 suspected duplicates):
|
||||
|
||||
---
|
||||
|
||||
Found 3 possible duplicate issues:
|
||||
|
||||
1. <link to issue>
|
||||
2. <link to issue>
|
||||
3. <link to issue>
|
||||
|
||||
This issue will be automatically closed as a duplicate in 3 days.
|
||||
|
||||
- If your issue is a duplicate, please close it and 👍 the existing issue instead
|
||||
- To prevent auto-closure, add a comment or 👎 this comment
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.ai/code)
|
||||
|
||||
<!-- dedupe-bot:marker -->
|
||||
|
||||
---
|
||||
@@ -92,7 +92,39 @@ if (argv0 === "timeout") {
|
||||
|
||||
// Check if it's "bun bd" or "bun-debug bd" without other positional args
|
||||
if (actualCommand === "bun" || actualCommand.includes("bun-debug")) {
|
||||
const positionalArgs = restArgs.filter(arg => !arg.startsWith("-"));
|
||||
// Claude is a sneaky fucker
|
||||
let positionalArgs = restArgs.filter(arg => !arg.startsWith("-"));
|
||||
const redirectStderrToStdoutIndex = positionalArgs.findIndex(arg => arg === "2>&1");
|
||||
if (redirectStderrToStdoutIndex !== -1) {
|
||||
positionalArgs.splice(redirectStderrToStdoutIndex, 1);
|
||||
}
|
||||
const redirectStdoutToStderrIndex = positionalArgs.findIndex(arg => arg === "1>&2");
|
||||
if (redirectStdoutToStderrIndex !== -1) {
|
||||
positionalArgs.splice(redirectStdoutToStderrIndex, 1);
|
||||
}
|
||||
|
||||
const redirectToFileIndex = positionalArgs.findIndex(arg => arg === ">");
|
||||
if (redirectToFileIndex !== -1) {
|
||||
positionalArgs.splice(redirectToFileIndex, 2);
|
||||
}
|
||||
|
||||
const redirectToFileAppendIndex = positionalArgs.findIndex(arg => arg === ">>");
|
||||
if (redirectToFileAppendIndex !== -1) {
|
||||
positionalArgs.splice(redirectToFileAppendIndex, 2);
|
||||
}
|
||||
|
||||
const redirectTOFileInlineIndex = positionalArgs.findIndex(arg => arg.startsWith(">"));
|
||||
if (redirectTOFileInlineIndex !== -1) {
|
||||
positionalArgs.splice(redirectTOFileInlineIndex, 1);
|
||||
}
|
||||
|
||||
const pipeIndex = positionalArgs.findIndex(arg => arg === "|");
|
||||
if (pipeIndex !== -1) {
|
||||
positionalArgs = positionalArgs.slice(0, pipeIndex);
|
||||
}
|
||||
|
||||
positionalArgs = positionalArgs.map(arg => arg.trim()).filter(Boolean);
|
||||
|
||||
if (positionalArgs.length === 1 && positionalArgs[0] === "bd") {
|
||||
denyWithReason("error: Run `bun bd` without a timeout");
|
||||
}
|
||||
|
||||
143
.coderabbit.yaml
Normal file
143
.coderabbit.yaml
Normal file
@@ -0,0 +1,143 @@
|
||||
language: en-US
|
||||
|
||||
reviews:
|
||||
profile: assertive
|
||||
request_changes_workflow: false
|
||||
high_level_summary: false
|
||||
high_level_summary_placeholder: "@coderabbitai summary"
|
||||
high_level_summary_in_walkthrough: true
|
||||
auto_title_placeholder: "@coderabbitai"
|
||||
review_status: false
|
||||
commit_status: false
|
||||
fail_commit_status: false
|
||||
collapse_walkthrough: false
|
||||
changed_files_summary: true
|
||||
sequence_diagrams: false
|
||||
estimate_code_review_effort: false
|
||||
assess_linked_issues: true
|
||||
related_issues: true
|
||||
related_prs: true
|
||||
suggested_labels: false
|
||||
suggested_reviewers: true
|
||||
in_progress_fortune: false
|
||||
poem: false
|
||||
abort_on_close: true
|
||||
|
||||
path_filters:
|
||||
- "!test/js/node/test/"
|
||||
|
||||
auto_review:
|
||||
enabled: true
|
||||
auto_incremental_review: true
|
||||
drafts: false
|
||||
|
||||
finishing_touches:
|
||||
docstrings:
|
||||
enabled: false
|
||||
unit_tests:
|
||||
enabled: false
|
||||
|
||||
pre_merge_checks:
|
||||
docstrings:
|
||||
mode: off
|
||||
title:
|
||||
mode: warning
|
||||
description:
|
||||
mode: warning
|
||||
issue_assessment:
|
||||
mode: warning
|
||||
|
||||
tools:
|
||||
shellcheck:
|
||||
enabled: true
|
||||
ruff:
|
||||
enabled: true
|
||||
markdownlint:
|
||||
enabled: true
|
||||
github-checks:
|
||||
enabled: true
|
||||
timeout_ms: 90000
|
||||
languagetool:
|
||||
enabled: true
|
||||
enabled_only: false
|
||||
level: default
|
||||
biome:
|
||||
enabled: true
|
||||
hadolint:
|
||||
enabled: true
|
||||
swiftlint:
|
||||
enabled: true
|
||||
phpstan:
|
||||
enabled: true
|
||||
level: default
|
||||
phpmd:
|
||||
enabled: true
|
||||
phpcs:
|
||||
enabled: true
|
||||
golangci-lint:
|
||||
enabled: true
|
||||
yamllint:
|
||||
enabled: true
|
||||
gitleaks:
|
||||
enabled: true
|
||||
checkov:
|
||||
enabled: true
|
||||
detekt:
|
||||
enabled: true
|
||||
eslint:
|
||||
enabled: true
|
||||
flake8:
|
||||
enabled: true
|
||||
rubocop:
|
||||
enabled: true
|
||||
buf:
|
||||
enabled: true
|
||||
regal:
|
||||
enabled: true
|
||||
actionlint:
|
||||
enabled: true
|
||||
pmd:
|
||||
enabled: true
|
||||
clang:
|
||||
enabled: true
|
||||
cppcheck:
|
||||
enabled: true
|
||||
semgrep:
|
||||
enabled: true
|
||||
circleci:
|
||||
enabled: true
|
||||
clippy:
|
||||
enabled: true
|
||||
sqlfluff:
|
||||
enabled: true
|
||||
prismaLint:
|
||||
enabled: true
|
||||
pylint:
|
||||
enabled: true
|
||||
oxc:
|
||||
enabled: true
|
||||
shopifyThemeCheck:
|
||||
enabled: true
|
||||
luacheck:
|
||||
enabled: true
|
||||
brakeman:
|
||||
enabled: true
|
||||
dotenvLint:
|
||||
enabled: true
|
||||
htmlhint:
|
||||
enabled: true
|
||||
checkmake:
|
||||
enabled: true
|
||||
osvScanner:
|
||||
enabled: true
|
||||
|
||||
chat:
|
||||
auto_reply: true
|
||||
|
||||
knowledge_base:
|
||||
opt_out: false
|
||||
code_guidelines:
|
||||
enabled: true
|
||||
filePatterns:
|
||||
- "**/.cursor/rules/*.mdc"
|
||||
- "**/CLAUDE.md"
|
||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -16,6 +16,7 @@
|
||||
*.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
|
||||
|
||||
|
||||
29
.github/workflows/auto-close-duplicates.yml
vendored
Normal file
29
.github/workflows/auto-close-duplicates.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: Auto-close duplicate issues
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 9 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
auto-close-duplicates:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
concurrency:
|
||||
group: auto-close-duplicates-${{ github.repository }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Bun
|
||||
uses: ./.github/actions/setup-bun
|
||||
|
||||
- name: Auto-close duplicate issues
|
||||
run: bun run scripts/auto-close-duplicates.ts
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
34
.github/workflows/claude-dedupe-issues.yml
vendored
Normal file
34
.github/workflows/claude-dedupe-issues.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
name: Claude Issue Dedupe
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
issue_number:
|
||||
description: 'Issue number to process for duplicate detection'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
claude-dedupe-issues:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
concurrency:
|
||||
group: claude-dedupe-issues-${{ github.event.issue.number || inputs.issue_number }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run Claude Code slash command
|
||||
uses: anthropics/claude-code-base-action@beta
|
||||
with:
|
||||
prompt: "/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}"
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
claude_args: "--model claude-sonnet-4-5-20250929"
|
||||
claude_env: |
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
3
.github/workflows/claude.yml
vendored
3
.github/workflows/claude.yml
vendored
@@ -57,8 +57,7 @@ jobs:
|
||||
git reset --hard origin/${{ github.event.pull_request.head.ref }}
|
||||
- name: Run Claude Code
|
||||
id: claude
|
||||
# TODO: switch this out once they merge their v1
|
||||
uses: km-anthropic/claude-code-action@v1-dev
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
timeout_minutes: "180"
|
||||
claude_args: |
|
||||
|
||||
24
.github/workflows/docs.yml
vendored
24
.github/workflows/docs.yml
vendored
@@ -1,24 +0,0 @@
|
||||
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 }}
|
||||
6
.github/workflows/labeled.yml
vendored
6
.github/workflows/labeled.yml
vendored
@@ -142,8 +142,8 @@ jobs:
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const closeAction = JSON.parse('${{ steps.add-labels.outputs.close-action }}');
|
||||
|
||||
const closeAction = ${{ fromJson(steps.add-labels.outputs.close-action) }};
|
||||
|
||||
// Comment with the reason
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
@@ -151,7 +151,7 @@ jobs:
|
||||
issue_number: context.issue.number,
|
||||
body: closeAction.comment
|
||||
});
|
||||
|
||||
|
||||
// Close the issue
|
||||
await github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
|
||||
19
.github/workflows/typos.yml
vendored
19
.github/workflows/typos.yml
vendored
@@ -1,19 +0,0 @@
|
||||
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,4 +7,5 @@ src/react-refresh.js
|
||||
*.min.js
|
||||
test/snippets
|
||||
test/js/node/test
|
||||
test/napi/node-napi-tests
|
||||
bun.lock
|
||||
|
||||
45
CLAUDE.md
45
CLAUDE.md
@@ -23,28 +23,51 @@ Tip: Bun is already installed and in $PATH. The `bd` subcommand is a package.jso
|
||||
|
||||
### Test Organization
|
||||
|
||||
If a test is for a specific numbered GitHub Issue, it should be placed in `test/regression/issue/${issueNumber}.test.ts`. Ensure the issue number is **REAL** and not a placeholder!
|
||||
|
||||
If no valid issue number is provided, find the best existing file to modify instead, such as;
|
||||
|
||||
- `test/js/bun/` - Bun-specific API tests (http, crypto, ffi, shell, etc.)
|
||||
- `test/js/node/` - Node.js compatibility tests
|
||||
- `test/js/web/` - Web API tests (fetch, WebSocket, streams, etc.)
|
||||
- `test/cli/` - CLI command tests (install, run, test, etc.)
|
||||
- `test/regression/issue/` - Regression tests (create one per bug fix)
|
||||
- `test/bundler/` - Bundler and transpiler tests
|
||||
- `test/bundler/` - Bundler and transpiler tests. Use `itBundled` helper.
|
||||
- `test/integration/` - End-to-end integration tests
|
||||
- `test/napi/` - N-API compatibility tests
|
||||
- `test/v8/` - V8 C++ API compatibility tests
|
||||
|
||||
### Writing Tests
|
||||
|
||||
Tests use Bun's Jest-compatible test runner with proper test fixtures:
|
||||
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`.
|
||||
|
||||
```typescript
|
||||
import { test, expect } from "bun:test";
|
||||
import { bunEnv, bunExe, normalizeBunSnapshot, tempDir } from "harness";
|
||||
|
||||
test("my feature", async () => {
|
||||
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 () => {
|
||||
// Create temp directory with test files
|
||||
using dir = tempDir("test-prefix", {
|
||||
"index.js": `console.log("hello");`,
|
||||
"index.js": `import { foo } from "./foo.ts"; foo();`,
|
||||
"foo.ts": `export function foo() { console.log("foo"); }`,
|
||||
});
|
||||
|
||||
// Spawn Bun process
|
||||
@@ -61,15 +84,21 @@ test("my feature", async () => {
|
||||
proc.exited,
|
||||
]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
// Prefer snapshot tests over expect(stdout).toBe("hello\n");
|
||||
expect(normalizeBunSnapshot(stdout, dir)).toMatchInlineSnapshot(`"hello"`);
|
||||
|
||||
// Assert the exit code last. This gives you a more useful error message on test failure.
|
||||
expect(exitCode).toBe(0);
|
||||
});
|
||||
```
|
||||
|
||||
- Always use `port: 0`. Do not hardcode ports. Do not use your own random port number function.
|
||||
- Use `normalizeBunSnapshot` to normalize snapshot output of the test.
|
||||
- NEVER write tests that check for no "panic" or "uncaught exception" or similar in the test output. That is NOT a valid test.
|
||||
- Use `tempDir` from `"harness"` to create a temporary directory. **Do not** use `tmpdirSync` or `fs.mkdtempSync` to create temporary directories.
|
||||
- When spawning processes, tests should expect(stdout).toBe(...) BEFORE expect(exitCode).toBe(0). This gives you a more useful error message on test failure.
|
||||
- **CRITICAL**: Do not write flaky tests. Do not use `setTimeout` in tests. Instead, `await` the condition to be met. You are not testing the TIME PASSING, you are testing the CONDITION.
|
||||
- **CRITICAL**: Verify your test fails with `USE_SYSTEM_BUN=1 bun test <file>` and passes with `bun bd test <file>`. Your test is NOT VALID if it passes with `USE_SYSTEM_BUN=1`.
|
||||
|
||||
## Code Architecture
|
||||
|
||||
@@ -78,7 +107,7 @@ test("my feature", async () => {
|
||||
- **Zig code** (`src/*.zig`): Core runtime, JavaScript bindings, package manager
|
||||
- **C++ code** (`src/bun.js/bindings/*.cpp`): JavaScriptCore bindings, Web APIs
|
||||
- **TypeScript** (`src/js/`): Built-in JavaScript modules with special syntax (see JavaScript Modules section)
|
||||
- **Generated code**: Many files are auto-generated from `.classes.ts` and other sources
|
||||
- **Generated code**: Many files are auto-generated from `.classes.ts` and other sources. Bun will automatically rebuild these files when you make changes to them.
|
||||
|
||||
### Core Source Organization
|
||||
|
||||
@@ -178,3 +207,5 @@ Built-in JavaScript modules use special syntax and are organized as:
|
||||
10. **Debug builds** - Use `BUN_DEBUG_QUIET_LOGS=1` to disable debug logging, or `BUN_DEBUG_<scopeName>=1` to enable specific `Output.scoped(.${scopeName}, .visible)`s
|
||||
11. **Be humble & honest** - NEVER overstate what you got done or what actually works in commits, PRs or in messages to the user.
|
||||
12. **Branch names must start with `claude/`** - This is a requirement for the CI to work.
|
||||
|
||||
**ONLY** push up changes after running `bun bd test <file>` and ensuring your tests pass.
|
||||
|
||||
@@ -24,7 +24,16 @@ if(CMAKE_HOST_APPLE)
|
||||
include(SetupMacSDK)
|
||||
endif()
|
||||
include(SetupLLVM)
|
||||
include(SetupCcache)
|
||||
|
||||
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()
|
||||
|
||||
# --- Project ---
|
||||
|
||||
|
||||
@@ -2,26 +2,40 @@ Configuring a development environment for Bun can take 10-30 minutes depending o
|
||||
|
||||
If you are using Windows, please refer to [this guide](https://bun.com/docs/project/building-windows)
|
||||
|
||||
## Install Dependencies
|
||||
## Using Nix (Alternative)
|
||||
|
||||
A Nix flake is provided as an alternative to manual dependency installation:
|
||||
|
||||
```bash
|
||||
nix develop
|
||||
# or explicitly use the pure shell
|
||||
# nix develop .#pure
|
||||
export CMAKE_SYSTEM_PROCESSOR=$(uname -m)
|
||||
bun bd
|
||||
```
|
||||
|
||||
This provides all dependencies in an isolated, reproducible environment without requiring sudo.
|
||||
|
||||
## Install Dependencies (Manual)
|
||||
|
||||
Using your system's package manager, install Bun's dependencies:
|
||||
|
||||
{% codetabs group="os" %}
|
||||
|
||||
```bash#macOS (Homebrew)
|
||||
$ brew install automake ccache cmake coreutils gnu-sed go icu4c libiconv libtool ninja pkg-config rust ruby
|
||||
$ brew install automake cmake coreutils gnu-sed go icu4c libiconv libtool ninja pkg-config rust ruby sccache
|
||||
```
|
||||
|
||||
```bash#Ubuntu/Debian
|
||||
$ 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
|
||||
$ sudo apt install curl wget lsb-release software-properties-common cargo cmake git golang libtool ninja-build pkg-config rustc ruby-full xz-utils
|
||||
```
|
||||
|
||||
```bash#Arch
|
||||
$ sudo pacman -S base-devel ccache cmake git go libiconv libtool make ninja pkg-config python rust sed unzip ruby
|
||||
$ sudo pacman -S base-devel cmake git go libiconv libtool make ninja pkg-config python rust sed unzip ruby
|
||||
```
|
||||
|
||||
```bash#Fedora
|
||||
$ 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)'
|
||||
$ 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)'
|
||||
```
|
||||
|
||||
```bash#openSUSE Tumbleweed
|
||||
@@ -51,6 +65,44 @@ $ 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:
|
||||
@@ -317,15 +369,6 @@ $ 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)
|
||||
|
||||
@@ -1,40 +1,29 @@
|
||||
# `install` benchmark
|
||||
# Create T3 App
|
||||
|
||||
Requires [`hyperfine`](https://github.com/sharkdp/hyperfine). The goal of this benchmark is to compare installation performance of Bun with other package managers _when caches are hot_.
|
||||
This is a [T3 Stack](https://create.t3.gg/) project bootstrapped with `create-t3-app`.
|
||||
|
||||
### With lockfile, online mode
|
||||
## What's next? How do I make an app with this?
|
||||
|
||||
To run the benchmark with the standard "install" command for each package manager:
|
||||
We try to keep this project as simple as possible, so you can start with just the scaffolding we set up for you, and add additional things later when they become necessary.
|
||||
|
||||
```sh
|
||||
$ hyperfine --prepare 'rm -rf node_modules' --warmup 1 --runs 3 'bun install' 'pnpm install' 'yarn' 'npm install'
|
||||
```
|
||||
If you are not familiar with the different technologies used in this project, please refer to the respective docs. If you still are in the wind, please join our [Discord](https://t3.gg/discord) and ask for help.
|
||||
|
||||
### With lockfile, offline mode
|
||||
- [Next.js](https://nextjs.org)
|
||||
- [NextAuth.js](https://next-auth.js.org)
|
||||
- [Prisma](https://prisma.io)
|
||||
- [Drizzle](https://orm.drizzle.team)
|
||||
- [Tailwind CSS](https://tailwindcss.com)
|
||||
- [tRPC](https://trpc.io)
|
||||
|
||||
Even though all packages are cached, some tools may hit the npm API during the version resolution step. (This is not the same as re-downloading a package.) To entirely avoid network calls, the other package managers require `--prefer-offline/--offline` flag. To run the benchmark using "offline" mode:
|
||||
## Learn More
|
||||
|
||||
```sh
|
||||
$ hyperfine --prepare 'rm -rf node_modules' --runs 1 'bun install' 'pnpm install --prefer-offline' 'yarn --offline' 'npm install --prefer-offline'
|
||||
```
|
||||
To learn more about the [T3 Stack](https://create.t3.gg/), take a look at the following resources:
|
||||
|
||||
### Without lockfile, offline mode
|
||||
- [Documentation](https://create.t3.gg/)
|
||||
- [Learn the T3 Stack](https://create.t3.gg/en/faq#what-learning-resources-are-currently-available) — Check out these awesome tutorials
|
||||
|
||||
To run the benchmark with offline mode but without lockfiles:
|
||||
You can check out the [create-t3-app GitHub repository](https://github.com/t3-oss/create-t3-app) — your feedback and contributions are welcome!
|
||||
|
||||
```sh
|
||||
$ hyperfine --prepare 'rm -rf node_modules' --warmup 1 'rm bun.lock && bun install' 'rm pnpm-lock.yaml && pnpm install --prefer-offline' 'rm yarn.lock && yarn --offline' 'rm package-lock.json && npm install --prefer-offline'
|
||||
```
|
||||
## How do I deploy this?
|
||||
|
||||
##
|
||||
|
||||
To check that the app is working as expected:
|
||||
|
||||
```
|
||||
$ bun run dev
|
||||
$ npm run dev
|
||||
$ yarn dev
|
||||
$ pnpm dev
|
||||
```
|
||||
|
||||
Then visit [http://localhost:3000](http://localhost:3000).
|
||||
Follow our deployment guides for [Vercel](https://create.t3.gg/en/deployment/vercel), [Netlify](https://create.t3.gg/en/deployment/netlify) and [Docker](https://create.t3.gg/en/deployment/docker) for more information.
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
* By default, Remix will handle hydrating your app on the client for you.
|
||||
* You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨
|
||||
* For more information, see https://remix.run/docs/en/main/file-conventions/entry.client
|
||||
*/
|
||||
|
||||
import { RemixBrowser } from "@remix-run/react";
|
||||
import { startTransition, StrictMode } from "react";
|
||||
import { hydrateRoot } from "react-dom/client";
|
||||
|
||||
startTransition(() => {
|
||||
hydrateRoot(
|
||||
document,
|
||||
<StrictMode>
|
||||
<RemixBrowser />
|
||||
</StrictMode>,
|
||||
);
|
||||
});
|
||||
@@ -1,101 +0,0 @@
|
||||
/**
|
||||
* By default, Remix will handle generating the HTTP Response for you.
|
||||
* You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨
|
||||
* For more information, see https://remix.run/docs/en/main/file-conventions/entry.server
|
||||
*/
|
||||
|
||||
import type { EntryContext } from "@remix-run/node";
|
||||
import { Response } from "@remix-run/node";
|
||||
import { RemixServer } from "@remix-run/react";
|
||||
import isbot from "isbot";
|
||||
import { PassThrough } from "node:stream";
|
||||
import { renderToPipeableStream } from "react-dom/server";
|
||||
|
||||
const ABORT_DELAY = 5_000;
|
||||
|
||||
export default function handleRequest(
|
||||
request: Request,
|
||||
responseStatusCode: number,
|
||||
responseHeaders: Headers,
|
||||
remixContext: EntryContext,
|
||||
) {
|
||||
return isbot(request.headers.get("user-agent"))
|
||||
? handleBotRequest(request, responseStatusCode, responseHeaders, remixContext)
|
||||
: handleBrowserRequest(request, responseStatusCode, responseHeaders, remixContext);
|
||||
}
|
||||
|
||||
function handleBotRequest(
|
||||
request: Request,
|
||||
responseStatusCode: number,
|
||||
responseHeaders: Headers,
|
||||
remixContext: EntryContext,
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { pipe, abort } = renderToPipeableStream(
|
||||
<RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />,
|
||||
{
|
||||
onAllReady() {
|
||||
const body = new PassThrough();
|
||||
|
||||
responseHeaders.set("Content-Type", "text/html");
|
||||
|
||||
resolve(
|
||||
new Response(body, {
|
||||
headers: responseHeaders,
|
||||
status: responseStatusCode,
|
||||
}),
|
||||
);
|
||||
|
||||
pipe(body);
|
||||
},
|
||||
onShellError(error: unknown) {
|
||||
reject(error);
|
||||
},
|
||||
onError(error: unknown) {
|
||||
responseStatusCode = 500;
|
||||
console.error(error);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
setTimeout(abort, ABORT_DELAY);
|
||||
});
|
||||
}
|
||||
|
||||
function handleBrowserRequest(
|
||||
request: Request,
|
||||
responseStatusCode: number,
|
||||
responseHeaders: Headers,
|
||||
remixContext: EntryContext,
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { pipe, abort } = renderToPipeableStream(
|
||||
<RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />,
|
||||
{
|
||||
onShellReady() {
|
||||
const body = new PassThrough();
|
||||
|
||||
responseHeaders.set("Content-Type", "text/html");
|
||||
|
||||
resolve(
|
||||
new Response(body, {
|
||||
headers: responseHeaders,
|
||||
status: responseStatusCode,
|
||||
}),
|
||||
);
|
||||
|
||||
pipe(body);
|
||||
},
|
||||
onShellError(error: unknown) {
|
||||
reject(error);
|
||||
},
|
||||
onError(error: unknown) {
|
||||
console.error(error);
|
||||
responseStatusCode = 500;
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
setTimeout(abort, ABORT_DELAY);
|
||||
});
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration } from "@remix-run/react";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<Meta />
|
||||
<Links />
|
||||
</head>
|
||||
<body>
|
||||
<Outlet />
|
||||
<ScrollRestoration />
|
||||
<Scripts />
|
||||
<LiveReload />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import type { V2_MetaFunction } from "@remix-run/node";
|
||||
|
||||
export const meta: V2_MetaFunction = () => {
|
||||
return [{ title: "New Remix App" }];
|
||||
};
|
||||
|
||||
export default function Index() {
|
||||
return (
|
||||
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
|
||||
<h1>Welcome to Remix</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<a target="_blank" href="https://remix.run/tutorials/blog" rel="noreferrer">
|
||||
15m Quickstart Blog Tutorial
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a target="_blank" href="https://remix.run/tutorials/jokes" rel="noreferrer">
|
||||
Deep Dive Jokes App Tutorial
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a target="_blank" href="https://remix.run/docs" rel="noreferrer">
|
||||
Remix Docs
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
488
bench/install/bun.lock
Normal file
488
bench/install/bun.lock
Normal file
@@ -0,0 +1,488 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "installbench",
|
||||
"dependencies": {
|
||||
"@auth/drizzle-adapter": "^1.7.2",
|
||||
"@t3-oss/env-nextjs": "^0.12.0",
|
||||
"@tanstack/react-query": "^5.69.0",
|
||||
"@trpc/client": "^11.0.0",
|
||||
"@trpc/react-query": "^11.0.0",
|
||||
"@trpc/server": "^11.0.0",
|
||||
"drizzle-orm": "^0.41.0",
|
||||
"esbuild": "^0.25.11",
|
||||
"next": "^15.2.3",
|
||||
"next-auth": "5.0.0-beta.25",
|
||||
"postgres": "^3.4.4",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"server-only": "^0.0.1",
|
||||
"superjson": "^2.2.1",
|
||||
"zod": "^3.24.2",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@tailwindcss/postcss": "^4.0.15",
|
||||
"@types/node": "^20.14.10",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"drizzle-kit": "^0.30.5",
|
||||
"postcss": "^8.5.3",
|
||||
"tailwindcss": "^4.0.15",
|
||||
"typescript": "^5.8.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
||||
|
||||
"@auth/core": ["@auth/core@0.41.1", "", { "dependencies": { "@panva/hkdf": "1.2.1", "jose": "6.1.0", "oauth4webapi": "3.8.2", "preact": "10.24.3", "preact-render-to-string": "6.5.11" } }, "sha512-t9cJ2zNYAdWMacGRMT6+r4xr1uybIdmYa49calBPeTqwgAFPV/88ac9TEvCR85pvATiSPt8VaNf+Gt24JIT/uw=="],
|
||||
|
||||
"@auth/drizzle-adapter": ["@auth/drizzle-adapter@1.11.1", "", { "dependencies": { "@auth/core": "0.41.1" } }, "sha512-cQTvDZqsyF7RPhDm/B6SvqdVP9EzQhy3oM4Muu7fjjmSYFLbSR203E6dH631ZHSKDn2b4WZkfMnjPDzRsPSAeA=="],
|
||||
|
||||
"@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="],
|
||||
|
||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="],
|
||||
|
||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="],
|
||||
|
||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="],
|
||||
|
||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="],
|
||||
|
||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="],
|
||||
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="],
|
||||
|
||||
"@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="],
|
||||
|
||||
"@emnapi/runtime": ["@emnapi/runtime@1.6.0", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA=="],
|
||||
|
||||
"@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "0.18.20", "source-map-support": "0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
|
||||
|
||||
"@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "3.3.2", "get-tsconfig": "4.13.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.11", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.11", "", { "os": "android", "cpu": "arm" }, "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg=="],
|
||||
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.11", "", { "os": "android", "cpu": "arm64" }, "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ=="],
|
||||
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.11", "", { "os": "android", "cpu": "x64" }, "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g=="],
|
||||
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w=="],
|
||||
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ=="],
|
||||
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.11", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA=="],
|
||||
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw=="],
|
||||
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.11", "", { "os": "linux", "cpu": "arm" }, "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw=="],
|
||||
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA=="],
|
||||
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.11", "", { "os": "linux", "cpu": "ia32" }, "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw=="],
|
||||
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw=="],
|
||||
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ=="],
|
||||
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.11", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw=="],
|
||||
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww=="],
|
||||
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.11", "", { "os": "linux", "cpu": "s390x" }, "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw=="],
|
||||
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.11", "", { "os": "linux", "cpu": "x64" }, "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ=="],
|
||||
|
||||
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg=="],
|
||||
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.11", "", { "os": "none", "cpu": "x64" }, "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A=="],
|
||||
|
||||
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.11", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg=="],
|
||||
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.11", "", { "os": "openbsd", "cpu": "x64" }, "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw=="],
|
||||
|
||||
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ=="],
|
||||
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.11", "", { "os": "sunos", "cpu": "x64" }, "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA=="],
|
||||
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q=="],
|
||||
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA=="],
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.11", "", { "os": "win32", "cpu": "x64" }, "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA=="],
|
||||
|
||||
"@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="],
|
||||
|
||||
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.3" }, "os": "darwin", "cpu": "arm64" }, "sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA=="],
|
||||
|
||||
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.3" }, "os": "darwin", "cpu": "x64" }, "sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg=="],
|
||||
|
||||
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw=="],
|
||||
|
||||
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA=="],
|
||||
|
||||
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.3", "", { "os": "linux", "cpu": "arm" }, "sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA=="],
|
||||
|
||||
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ=="],
|
||||
|
||||
"@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg=="],
|
||||
|
||||
"@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w=="],
|
||||
|
||||
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.3", "", { "os": "linux", "cpu": "x64" }, "sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg=="],
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw=="],
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.3", "", { "os": "linux", "cpu": "x64" }, "sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g=="],
|
||||
|
||||
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.3" }, "os": "linux", "cpu": "arm" }, "sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA=="],
|
||||
|
||||
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.3" }, "os": "linux", "cpu": "arm64" }, "sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ=="],
|
||||
|
||||
"@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.3" }, "os": "linux", "cpu": "ppc64" }, "sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ=="],
|
||||
|
||||
"@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.3" }, "os": "linux", "cpu": "s390x" }, "sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw=="],
|
||||
|
||||
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.3" }, "os": "linux", "cpu": "x64" }, "sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A=="],
|
||||
|
||||
"@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.3" }, "os": "linux", "cpu": "arm64" }, "sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA=="],
|
||||
|
||||
"@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.4", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.3" }, "os": "linux", "cpu": "x64" }, "sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg=="],
|
||||
|
||||
"@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.4", "", { "dependencies": { "@emnapi/runtime": "1.6.0" }, "cpu": "none" }, "sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA=="],
|
||||
|
||||
"@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA=="],
|
||||
|
||||
"@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw=="],
|
||||
|
||||
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.4", "", { "os": "win32", "cpu": "x64" }, "sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig=="],
|
||||
|
||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "1.5.5", "@jridgewell/trace-mapping": "0.3.31" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
||||
|
||||
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "0.3.13", "@jridgewell/trace-mapping": "0.3.31" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
||||
|
||||
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||
|
||||
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "3.1.2", "@jridgewell/sourcemap-codec": "1.5.5" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"@next/env": ["@next/env@15.5.6", "", {}, "sha512-3qBGRW+sCGzgbpc5TS1a0p7eNxnOarGVQhZxfvTdnV0gFI61lX7QNtQ4V1TSREctXzYn5NetbUsLvyqwLFJM6Q=="],
|
||||
|
||||
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.5.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ES3nRz7N+L5Umz4KoGfZ4XX6gwHplwPhioVRc25+QNsDa7RtUF/z8wJcbuQ2Tffm5RZwuN2A063eapoJ1u4nPg=="],
|
||||
|
||||
"@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.5.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-JIGcytAyk9LQp2/nuVZPAtj8uaJ/zZhsKOASTjxDug0SPU9LAM3wy6nPU735M1OqacR4U20LHVF5v5Wnl9ptTA=="],
|
||||
|
||||
"@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-qvz4SVKQ0P3/Im9zcS2RmfFL/UCQnsJKJwQSkissbngnB/12c6bZTCB0gHTexz1s6d/mD0+egPKXAIRFVS7hQg=="],
|
||||
|
||||
"@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.5.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-FsbGVw3SJz1hZlvnWD+T6GFgV9/NYDeLTNQB2MXoPN5u9VA9OEDy6fJEfePfsUKAhJufFbZLgp0cPxMuV6SV0w=="],
|
||||
|
||||
"@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-3QnHGFWlnvAgyxFxt2Ny8PTpXtQD7kVEeaFat5oPAHHI192WKYB+VIKZijtHLGdBBvc16tiAkPTDmQNOQ0dyrA=="],
|
||||
|
||||
"@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.5.6", "", { "os": "linux", "cpu": "x64" }, "sha512-OsGX148sL+TqMK9YFaPFPoIaJKbFJJxFzkXZljIgA9hjMjdruKht6xDCEv1HLtlLNfkx3c5w2GLKhj7veBQizQ=="],
|
||||
|
||||
"@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.5.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-ONOMrqWxdzXDJNh2n60H6gGyKed42Ieu6UTVPZteXpuKbLZTH4G4eBMsr5qWgOBA+s7F+uB4OJbZnrkEDnZ5Fg=="],
|
||||
|
||||
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.5.6", "", { "os": "win32", "cpu": "x64" }, "sha512-pxK4VIjFRx1MY92UycLOOw7dTdvccWsNETQ0kDHkBlcFH1GrTLUjSiHU1ohrznnux6TqRHgv5oflhfIWZwVROQ=="],
|
||||
|
||||
"@panva/hkdf": ["@panva/hkdf@1.2.1", "", {}, "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw=="],
|
||||
|
||||
"@petamoriken/float16": ["@petamoriken/float16@3.9.3", "", {}, "sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g=="],
|
||||
|
||||
"@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
|
||||
|
||||
"@t3-oss/env-core": ["@t3-oss/env-core@0.12.0", "", { "optionalDependencies": { "typescript": "5.9.3", "zod": "3.25.76" } }, "sha512-lOPj8d9nJJTt81mMuN9GMk8x5veOt7q9m11OSnCBJhwp1QrL/qR+M8Y467ULBSm9SunosryWNbmQQbgoiMgcdw=="],
|
||||
|
||||
"@t3-oss/env-nextjs": ["@t3-oss/env-nextjs@0.12.0", "", { "dependencies": { "@t3-oss/env-core": "0.12.0" }, "optionalDependencies": { "typescript": "5.9.3", "zod": "3.25.76" } }, "sha512-rFnvYk1049RnNVUPvY8iQ55AuQh1Rr+qZzQBh3t++RttCGK4COpXGNxS4+45afuQq02lu+QAOy/5955aU8hRKw=="],
|
||||
|
||||
"@tailwindcss/node": ["@tailwindcss/node@4.1.16", "", { "dependencies": { "@jridgewell/remapping": "2.3.5", "enhanced-resolve": "5.18.3", "jiti": "2.6.1", "lightningcss": "1.30.2", "magic-string": "0.30.21", "source-map-js": "1.2.1", "tailwindcss": "4.1.16" } }, "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw=="],
|
||||
|
||||
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.16", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.16", "@tailwindcss/oxide-darwin-arm64": "4.1.16", "@tailwindcss/oxide-darwin-x64": "4.1.16", "@tailwindcss/oxide-freebsd-x64": "4.1.16", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.16", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.16", "@tailwindcss/oxide-linux-arm64-musl": "4.1.16", "@tailwindcss/oxide-linux-x64-gnu": "4.1.16", "@tailwindcss/oxide-linux-x64-musl": "4.1.16", "@tailwindcss/oxide-wasm32-wasi": "4.1.16", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.16", "@tailwindcss/oxide-win32-x64-msvc": "4.1.16" } }, "sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg=="],
|
||||
|
||||
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.16", "", { "os": "android", "cpu": "arm64" }, "sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA=="],
|
||||
|
||||
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.16", "", { "os": "darwin", "cpu": "arm64" }, "sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA=="],
|
||||
|
||||
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.16", "", { "os": "darwin", "cpu": "x64" }, "sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg=="],
|
||||
|
||||
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.16", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16", "", { "os": "linux", "cpu": "arm" }, "sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.16", "", { "os": "linux", "cpu": "x64" }, "sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.16", "", { "os": "linux", "cpu": "x64" }, "sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.16", "", { "cpu": "none" }, "sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q=="],
|
||||
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.16", "", { "os": "win32", "cpu": "arm64" }, "sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A=="],
|
||||
|
||||
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.16", "", { "os": "win32", "cpu": "x64" }, "sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg=="],
|
||||
|
||||
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.16", "", { "dependencies": { "@alloc/quick-lru": "5.2.0", "@tailwindcss/node": "4.1.16", "@tailwindcss/oxide": "4.1.16", "postcss": "8.5.6", "tailwindcss": "4.1.16" } }, "sha512-Qn3SFGPXYQMKR/UtqS+dqvPrzEeBZHrFA92maT4zijCVggdsXnDBMsPFJo1eArX3J+O+Gi+8pV4PkqjLCNBk3A=="],
|
||||
|
||||
"@tanstack/query-core": ["@tanstack/query-core@5.90.5", "", {}, "sha512-wLamYp7FaDq6ZnNehypKI5fNvxHPfTYylE0m/ZpuuzJfJqhR5Pxg9gvGBHZx4n7J+V5Rg5mZxHHTlv25Zt5u+w=="],
|
||||
|
||||
"@tanstack/react-query": ["@tanstack/react-query@5.90.5", "", { "dependencies": { "@tanstack/query-core": "5.90.5" }, "peerDependencies": { "react": "19.2.0" } }, "sha512-pN+8UWpxZkEJ/Rnnj2v2Sxpx1WFlaa9L6a4UO89p6tTQbeo+m0MS8oYDjbggrR8QcTyjKoYWKS3xJQGr3ExT8Q=="],
|
||||
|
||||
"@trpc/client": ["@trpc/client@11.7.1", "", { "peerDependencies": { "@trpc/server": "11.7.1", "typescript": "5.9.3" } }, "sha512-uOnAjElKI892/U6aQMcBHYs3x7mme3Cvv1F87ytBL56rBvs7+DyK7r43zgaXKf13+GtPEI6ex5xjVUfyDW8XcQ=="],
|
||||
|
||||
"@trpc/react-query": ["@trpc/react-query@11.7.1", "", { "peerDependencies": { "@tanstack/react-query": "5.90.5", "@trpc/client": "11.7.1", "@trpc/server": "11.7.1", "react": "19.2.0", "react-dom": "19.2.0", "typescript": "5.9.3" } }, "sha512-dEHDjIqSTzO8nLlCbtiFBMBwhbSkK1QP7aYVo3nP3sYBna0b+iCtrPXdxVPCSopr9/aIqDTEh+dMRZa7yBgjfQ=="],
|
||||
|
||||
"@trpc/server": ["@trpc/server@11.7.1", "", { "peerDependencies": { "typescript": "5.9.3" } }, "sha512-N3U8LNLIP4g9C7LJ/sLkjuPHwqlvE3bnspzC4DEFVdvx2+usbn70P80E3wj5cjOTLhmhRiwJCSXhlB+MHfGeCw=="],
|
||||
|
||||
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
|
||||
|
||||
"@types/node": ["@types/node@20.19.24", "", { "dependencies": { "undici-types": "6.21.0" } }, "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA=="],
|
||||
|
||||
"@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "3.1.3" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
|
||||
|
||||
"@types/react-dom": ["@types/react-dom@19.2.2", "", { "peerDependencies": { "@types/react": "19.2.2" } }, "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw=="],
|
||||
|
||||
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001752", "", {}, "sha512-vKUk7beoukxE47P5gcVNKkDRzXdVofotshHwfR9vmpeFKxmI5PBpgOMC18LUJUA/DvJ70Y7RveasIBraqsyO/g=="],
|
||||
|
||||
"client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="],
|
||||
|
||||
"cookie": ["cookie@0.7.1", "", {}, "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="],
|
||||
|
||||
"copy-anything": ["copy-anything@4.0.5", "", { "dependencies": { "is-what": "5.5.0" } }, "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||
|
||||
"drizzle-kit": ["drizzle-kit@0.30.6", "", { "dependencies": { "@drizzle-team/brocli": "0.10.2", "@esbuild-kit/esm-loader": "2.6.5", "esbuild": "0.19.12", "esbuild-register": "3.6.0", "gel": "2.1.1" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-U4wWit0fyZuGuP7iNmRleQyK2V8wCuv57vf5l3MnG4z4fzNTjY/U13M8owyQ5RavqvqxBifWORaR3wIUzlN64g=="],
|
||||
|
||||
"drizzle-orm": ["drizzle-orm@0.41.0", "", { "optionalDependencies": { "gel": "2.1.1", "postgres": "3.4.7" } }, "sha512-7A4ZxhHk9gdlXmTdPj/lREtP+3u8KvZ4yEN6MYVxBzZGex5Wtdc+CWSbu7btgF6TB0N+MNPrvW7RKBbxJchs/Q=="],
|
||||
|
||||
"enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "4.2.11", "tapable": "2.3.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="],
|
||||
|
||||
"env-paths": ["env-paths@3.0.0", "", {}, "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A=="],
|
||||
|
||||
"esbuild": ["esbuild@0.25.11", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.11", "@esbuild/android-arm": "0.25.11", "@esbuild/android-arm64": "0.25.11", "@esbuild/android-x64": "0.25.11", "@esbuild/darwin-arm64": "0.25.11", "@esbuild/darwin-x64": "0.25.11", "@esbuild/freebsd-arm64": "0.25.11", "@esbuild/freebsd-x64": "0.25.11", "@esbuild/linux-arm": "0.25.11", "@esbuild/linux-arm64": "0.25.11", "@esbuild/linux-ia32": "0.25.11", "@esbuild/linux-loong64": "0.25.11", "@esbuild/linux-mips64el": "0.25.11", "@esbuild/linux-ppc64": "0.25.11", "@esbuild/linux-riscv64": "0.25.11", "@esbuild/linux-s390x": "0.25.11", "@esbuild/linux-x64": "0.25.11", "@esbuild/netbsd-arm64": "0.25.11", "@esbuild/netbsd-x64": "0.25.11", "@esbuild/openbsd-arm64": "0.25.11", "@esbuild/openbsd-x64": "0.25.11", "@esbuild/openharmony-arm64": "0.25.11", "@esbuild/sunos-x64": "0.25.11", "@esbuild/win32-arm64": "0.25.11", "@esbuild/win32-ia32": "0.25.11", "@esbuild/win32-x64": "0.25.11" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q=="],
|
||||
|
||||
"esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "4.4.3" }, "peerDependencies": { "esbuild": "0.19.12" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="],
|
||||
|
||||
"gel": ["gel@2.1.1", "", { "dependencies": { "@petamoriken/float16": "3.9.3", "debug": "4.4.3", "env-paths": "3.0.0", "semver": "7.7.3", "shell-quote": "1.8.3", "which": "4.0.0" }, "bin": { "gel": "dist/cli.mjs" } }, "sha512-Newg9X7mRYskoBjSw70l1YnJ/ZGbq64VPyR821H5WVkTGpHG2O0mQILxCeUhxdYERLFY9B4tUyKLyf3uMTjtKw=="],
|
||||
|
||||
"get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="],
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
|
||||
"is-what": ["is-what@5.5.0", "", {}, "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw=="],
|
||||
|
||||
"isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="],
|
||||
|
||||
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
||||
|
||||
"jose": ["jose@6.1.0", "", {}, "sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA=="],
|
||||
|
||||
"lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "2.1.2" }, "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-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="],
|
||||
|
||||
"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.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="],
|
||||
|
||||
"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.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="],
|
||||
|
||||
"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.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="],
|
||||
|
||||
"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.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="],
|
||||
|
||||
"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=="],
|
||||
|
||||
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||
|
||||
"next": ["next@15.5.6", "", { "dependencies": { "@next/env": "15.5.6", "@swc/helpers": "0.5.15", "caniuse-lite": "1.0.30001752", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.6", "@next/swc-darwin-x64": "15.5.6", "@next/swc-linux-arm64-gnu": "15.5.6", "@next/swc-linux-arm64-musl": "15.5.6", "@next/swc-linux-x64-gnu": "15.5.6", "@next/swc-linux-x64-musl": "15.5.6", "@next/swc-win32-arm64-msvc": "15.5.6", "@next/swc-win32-x64-msvc": "15.5.6", "sharp": "0.34.4" }, "peerDependencies": { "react": "19.2.0", "react-dom": "19.2.0" }, "bin": { "next": "dist/bin/next" } }, "sha512-zTxsnI3LQo3c9HSdSf91O1jMNsEzIXDShXd4wVdg9y5shwLqBXi4ZtUUJyB86KGVSJLZx0PFONvO54aheGX8QQ=="],
|
||||
|
||||
"next-auth": ["next-auth@5.0.0-beta.25", "", { "dependencies": { "@auth/core": "0.37.2" }, "peerDependencies": { "next": "15.5.6", "react": "19.2.0" } }, "sha512-2dJJw1sHQl2qxCrRk+KTQbeH+izFbGFPuJj5eGgBZFYyiYYtvlrBeUw1E/OJJxTRjuxbSYGnCTkUIRsIIW0bog=="],
|
||||
|
||||
"oauth4webapi": ["oauth4webapi@3.8.2", "", {}, "sha512-FzZZ+bht5X0FKe7Mwz3DAVAmlH1BV5blSak/lHMBKz0/EBMhX6B10GlQYI51+oRp8ObJaX0g6pXrAxZh5s8rjw=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "3.3.11", "picocolors": "1.1.1", "source-map-js": "1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
||||
|
||||
"postgres": ["postgres@3.4.7", "", {}, "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="],
|
||||
|
||||
"preact": ["preact@10.24.3", "", {}, "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA=="],
|
||||
|
||||
"preact-render-to-string": ["preact-render-to-string@6.5.11", "", { "peerDependencies": { "preact": "10.24.3" } }, "sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw=="],
|
||||
|
||||
"pretty-format": ["pretty-format@3.8.0", "", {}, "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="],
|
||||
|
||||
"react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="],
|
||||
|
||||
"react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "0.27.0" }, "peerDependencies": { "react": "19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="],
|
||||
|
||||
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
|
||||
|
||||
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||
|
||||
"semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||
|
||||
"server-only": ["server-only@0.0.1", "", {}, "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA=="],
|
||||
|
||||
"sharp": ["sharp@0.34.4", "", { "dependencies": { "@img/colour": "1.0.0", "detect-libc": "2.1.2", "semver": "7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.4", "@img/sharp-darwin-x64": "0.34.4", "@img/sharp-libvips-darwin-arm64": "1.2.3", "@img/sharp-libvips-darwin-x64": "1.2.3", "@img/sharp-libvips-linux-arm": "1.2.3", "@img/sharp-libvips-linux-arm64": "1.2.3", "@img/sharp-libvips-linux-ppc64": "1.2.3", "@img/sharp-libvips-linux-s390x": "1.2.3", "@img/sharp-libvips-linux-x64": "1.2.3", "@img/sharp-libvips-linuxmusl-arm64": "1.2.3", "@img/sharp-libvips-linuxmusl-x64": "1.2.3", "@img/sharp-linux-arm": "0.34.4", "@img/sharp-linux-arm64": "0.34.4", "@img/sharp-linux-ppc64": "0.34.4", "@img/sharp-linux-s390x": "0.34.4", "@img/sharp-linux-x64": "0.34.4", "@img/sharp-linuxmusl-arm64": "0.34.4", "@img/sharp-linuxmusl-x64": "0.34.4", "@img/sharp-wasm32": "0.34.4", "@img/sharp-win32-arm64": "0.34.4", "@img/sharp-win32-ia32": "0.34.4", "@img/sharp-win32-x64": "0.34.4" } }, "sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA=="],
|
||||
|
||||
"shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="],
|
||||
|
||||
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
|
||||
"source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "1.1.2", "source-map": "0.6.1" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="],
|
||||
|
||||
"styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": "19.2.0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="],
|
||||
|
||||
"superjson": ["superjson@2.2.5", "", { "dependencies": { "copy-anything": "4.0.5" } }, "sha512-zWPTX96LVsA/eVYnqOM2+ofcdPqdS1dAF1LN4TS2/MWuUpfitd9ctTa87wt4xrYnZnkLtS69xpBdSxVBP5Rm6w=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.1.16", "", {}, "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA=="],
|
||||
|
||||
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||
|
||||
"which": ["which@4.0.0", "", { "dependencies": { "isexe": "3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="],
|
||||
|
||||
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
|
||||
|
||||
"drizzle-kit/esbuild": ["esbuild@0.19.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", "@esbuild/android-arm64": "0.19.12", "@esbuild/android-x64": "0.19.12", "@esbuild/darwin-arm64": "0.19.12", "@esbuild/darwin-x64": "0.19.12", "@esbuild/freebsd-arm64": "0.19.12", "@esbuild/freebsd-x64": "0.19.12", "@esbuild/linux-arm": "0.19.12", "@esbuild/linux-arm64": "0.19.12", "@esbuild/linux-ia32": "0.19.12", "@esbuild/linux-loong64": "0.19.12", "@esbuild/linux-mips64el": "0.19.12", "@esbuild/linux-ppc64": "0.19.12", "@esbuild/linux-riscv64": "0.19.12", "@esbuild/linux-s390x": "0.19.12", "@esbuild/linux-x64": "0.19.12", "@esbuild/netbsd-x64": "0.19.12", "@esbuild/openbsd-x64": "0.19.12", "@esbuild/sunos-x64": "0.19.12", "@esbuild/win32-arm64": "0.19.12", "@esbuild/win32-ia32": "0.19.12", "@esbuild/win32-x64": "0.19.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg=="],
|
||||
|
||||
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "3.3.11", "picocolors": "1.1.1", "source-map-js": "1.2.1" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
|
||||
|
||||
"next-auth/@auth/core": ["@auth/core@0.37.2", "", { "dependencies": { "@panva/hkdf": "1.2.1", "@types/cookie": "0.6.0", "cookie": "0.7.1", "jose": "5.10.0", "oauth4webapi": "3.8.2", "preact": "10.11.3", "preact-render-to-string": "5.2.3" } }, "sha512-kUvzyvkcd6h1vpeMAojK2y7+PAV5H+0Cc9+ZlKYDFhDY31AlvsB+GW5vNO4qE3Y07KeQgvNO9U0QUx/fN62kBw=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="],
|
||||
|
||||
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.19.12", "", { "os": "android", "cpu": "arm" }, "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.19.12", "", { "os": "android", "cpu": "arm64" }, "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.19.12", "", { "os": "android", "cpu": "x64" }, "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.19.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.19.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.19.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.19.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.19.12", "", { "os": "linux", "cpu": "arm" }, "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.19.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.19.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.19.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.19.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.19.12", "", { "os": "linux", "cpu": "x64" }, "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.19.12", "", { "os": "none", "cpu": "x64" }, "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.19.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.19.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.19.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.19.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ=="],
|
||||
|
||||
"drizzle-kit/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.19.12", "", { "os": "win32", "cpu": "x64" }, "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA=="],
|
||||
|
||||
"next-auth/@auth/core/jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="],
|
||||
|
||||
"next-auth/@auth/core/preact": ["preact@10.11.3", "", {}, "sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg=="],
|
||||
|
||||
"next-auth/@auth/core/preact-render-to-string": ["preact-render-to-string@5.2.3", "", { "dependencies": { "pretty-format": "3.8.0" }, "peerDependencies": { "preact": "10.11.3" } }, "sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA=="],
|
||||
}
|
||||
}
|
||||
5
bench/install/next-env.d.ts
vendored
Normal file
5
bench/install/next-env.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
10
bench/install/next.config.js
Normal file
10
bench/install/next.config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful
|
||||
* for Docker builds.
|
||||
*/
|
||||
import "./src/env.js";
|
||||
|
||||
/** @type {import("next").NextConfig} */
|
||||
const config = {};
|
||||
|
||||
export default config;
|
||||
@@ -1,31 +1,52 @@
|
||||
{
|
||||
"name": "installbench",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "remix build",
|
||||
"dev": "remix dev",
|
||||
"start": "remix-serve build",
|
||||
"typecheck": "tsc",
|
||||
"clean": "rm -rf node_modules",
|
||||
"bench": "hyperfine --prepare 'rm -rf node_modules' --warmup 1 --runs 3 'bun install' 'pnpm install' 'yarn' 'npm install'"
|
||||
"build": "next build",
|
||||
"check": "biome check .",
|
||||
"check:unsafe": "biome check --write --unsafe .",
|
||||
"check:write": "biome check --write .",
|
||||
"db:generate": "drizzle-kit generate",
|
||||
"db:migrate": "drizzle-kit migrate",
|
||||
"db:push": "drizzle-kit push",
|
||||
"db:studio": "drizzle-kit studio",
|
||||
"dev": "next dev --turbo",
|
||||
"preview": "next build && next start",
|
||||
"start": "next start",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@remix-run/node": "^1.15.0",
|
||||
"@remix-run/react": "^1.15.0",
|
||||
"@remix-run/serve": "^1.15.0",
|
||||
"isbot": "^3.6.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"@auth/drizzle-adapter": "^1.7.2",
|
||||
"@t3-oss/env-nextjs": "^0.12.0",
|
||||
"@tanstack/react-query": "^5.69.0",
|
||||
"@trpc/client": "^11.0.0",
|
||||
"@trpc/react-query": "^11.0.0",
|
||||
"@trpc/server": "^11.0.0",
|
||||
"drizzle-orm": "^0.41.0",
|
||||
"esbuild": "^0.25.11",
|
||||
"next": "^15.2.3",
|
||||
"next-auth": "5.0.0-beta.25",
|
||||
"postgres": "^3.4.4",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"server-only": "^0.0.1",
|
||||
"superjson": "^2.2.1",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@remix-run/dev": "^1.15.0",
|
||||
"@remix-run/eslint-config": "^1.15.0",
|
||||
"@types/react": "^18.0.25",
|
||||
"@types/react-dom": "^18.0.8",
|
||||
"eslint": "^8.27.0",
|
||||
"typescript": "^4.8.4"
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@tailwindcss/postcss": "^4.0.15",
|
||||
"@types/node": "^20.14.10",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"drizzle-kit": "^0.30.5",
|
||||
"postcss": "^8.5.3",
|
||||
"tailwindcss": "^4.0.15",
|
||||
"typescript": "^5.8.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
"ct3aMetadata": {
|
||||
"initVersion": "7.39.3"
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
@@ -1,14 +0,0 @@
|
||||
/** @type {import('@remix-run/dev').AppConfig} */
|
||||
module.exports = {
|
||||
ignoredRouteFiles: ["**/.*"],
|
||||
// appDirectory: "app",
|
||||
// assetsBuildDirectory: "public/build",
|
||||
// serverBuildPath: "build/index.js",
|
||||
// publicPath: "/build/",
|
||||
future: {
|
||||
v2_errorBoundary: true,
|
||||
v2_meta: true,
|
||||
v2_normalizeFormMethod: true,
|
||||
v2_routeConvention: true,
|
||||
},
|
||||
};
|
||||
2
bench/install/remix.env.d.ts
vendored
2
bench/install/remix.env.d.ts
vendored
@@ -1,2 +0,0 @@
|
||||
/// <reference types="@remix-run/dev" />
|
||||
/// <reference types="@remix-run/node" />
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"compilerOptions": {
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2019"],
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react-jsx",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"target": "ES2019",
|
||||
"strict": true,
|
||||
"allowJs": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./app/*"]
|
||||
},
|
||||
|
||||
// Remix takes care of building everything in `remix build`.
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
@@ -4,20 +4,16 @@
|
||||
"": {
|
||||
"name": "react-hello-world",
|
||||
"dependencies": {
|
||||
"react": "next",
|
||||
"react-dom": "next",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
"react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="],
|
||||
|
||||
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
|
||||
"react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="],
|
||||
|
||||
"react": ["react@18.3.0-next-b72ed698f-20230303", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-l6RbwXa9Peerh9pQEq62DDypxSQfavbybY0wV1vwZ63X0P5VaaEesZAz1KPpnVvXjTtQaOMQsIPvnQwmaVqzTQ=="],
|
||||
|
||||
"react-dom": ["react-dom@18.3.0-next-b72ed698f-20230303", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "0.24.0-next-b72ed698f-20230303" }, "peerDependencies": { "react": "18.3.0-next-b72ed698f-20230303" } }, "sha512-0Gh/gmTT6H8KxswIQB/8shdTTfs6QIu86nNqZf3Y0RBqIwgTVxRaQVz14/Fw4/Nt81nK/Jt6KT4bx3yvOxZDGQ=="],
|
||||
|
||||
"scheduler": ["scheduler@0.24.0-next-b72ed698f-20230303", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-ct4DMMFbc2kFxCdvbG+i/Jn1S1oqrIFSn2VX/mam+Ya0iuNy+lb8rgT7A+YBUqrQNDaNEqABYI2sOQgqoRxp7w=="],
|
||||
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
"description": "",
|
||||
"main": "react-hello-world.node.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build:workerd": "bun build react-hello-world.workerd.jsx --outfile=react-hello-world.workerd.js --format=esm --production && (echo '// MessageChannel polyfill for workerd'; echo 'if (typeof MessageChannel === \"undefined\") {'; echo ' globalThis.MessageChannel = class MessageChannel {'; echo ' constructor() {'; echo ' this.port1 = { onmessage: null, postMessage: () => {} };'; echo ' this.port2 = {'; echo ' postMessage: (msg) => {'; echo ' if (this.port1.onmessage) {'; echo ' queueMicrotask(() => this.port1.onmessage({ data: msg }));'; echo ' }'; echo ' }'; echo ' };'; echo ' }'; echo ' };'; echo '}'; cat react-hello-world.workerd.js) > temp.js && mv temp.js react-hello-world.workerd.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Colin McDonnell",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"react": "next",
|
||||
"react-dom": "next"
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
using Workerd = import "/workerd/workerd.capnp";
|
||||
|
||||
const config :Workerd.Config = (
|
||||
services = [
|
||||
(name = "main", worker = .mainWorker),
|
||||
],
|
||||
|
||||
sockets = [
|
||||
( name = "http",
|
||||
address = "*:3001",
|
||||
http = (),
|
||||
service = "main"
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
const mainWorker :Workerd.Worker = (
|
||||
modules = [
|
||||
(name = "worker", esModule = embed "react-hello-world.workerd.js"),
|
||||
],
|
||||
compatibilityDate = "2025-01-01",
|
||||
compatibilityFlags = ["nodejs_compat_v2"],
|
||||
);
|
||||
68
bench/react-hello-world/react-hello-world.workerd.js
Normal file
68
bench/react-hello-world/react-hello-world.workerd.js
Normal file
File diff suppressed because one or more lines are too long
40
bench/react-hello-world/react-hello-world.workerd.jsx
Normal file
40
bench/react-hello-world/react-hello-world.workerd.jsx
Normal file
@@ -0,0 +1,40 @@
|
||||
// Cloudflare Workers version with export default fetch
|
||||
// Run with: workerd serve react-hello-world.workerd.config.capnp
|
||||
|
||||
// Polyfill MessageChannel for workerd
|
||||
if (typeof MessageChannel === 'undefined') {
|
||||
globalThis.MessageChannel = class MessageChannel {
|
||||
constructor() {
|
||||
this.port1 = { onmessage: null, postMessage: () => {} };
|
||||
this.port2 = {
|
||||
postMessage: (msg) => {
|
||||
if (this.port1.onmessage) {
|
||||
queueMicrotask(() => this.port1.onmessage({ data: msg }));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
import React from "react";
|
||||
import { renderToReadableStream } from "react-dom/server";
|
||||
|
||||
const headers = {
|
||||
"Content-Type": "text/html",
|
||||
};
|
||||
|
||||
const App = () => (
|
||||
<html>
|
||||
<body>
|
||||
<h1>Hello World</h1>
|
||||
<p>This is an example.</p>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
export default {
|
||||
async fetch(request) {
|
||||
return new Response(await renderToReadableStream(<App />), { headers });
|
||||
},
|
||||
};
|
||||
@@ -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");
|
||||
`,
|
||||
);
|
||||
const result = spawnSync(typeof Bun !== "undefined" ? "bun" : "node", [path], {
|
||||
spawnSync(process.execPath, [path], {
|
||||
stdio: ["inherit", "inherit", "inherit"],
|
||||
env: {
|
||||
...process.env,
|
||||
|
||||
@@ -86,11 +86,20 @@ elseif(APPLE)
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
register_compiler_flags(
|
||||
DESCRIPTION "Enable debug symbols"
|
||||
-g3 -gz=zstd ${DEBUG}
|
||||
-g1 ${RELEASE}
|
||||
)
|
||||
# Nix LLVM doesn't support zstd compression, use zlib instead
|
||||
if(DEFINED ENV{NIX_CC})
|
||||
register_compiler_flags(
|
||||
DESCRIPTION "Enable debug symbols (zlib-compressed for Nix)"
|
||||
-g3 -gz=zlib ${DEBUG}
|
||||
-g1 ${RELEASE}
|
||||
)
|
||||
else()
|
||||
register_compiler_flags(
|
||||
DESCRIPTION "Enable debug symbols (zstd-compressed)"
|
||||
-g3 -gz=zstd ${DEBUG}
|
||||
-g1 ${RELEASE}
|
||||
)
|
||||
endif()
|
||||
|
||||
register_compiler_flags(
|
||||
DESCRIPTION "Optimize debug symbols for LLDB"
|
||||
@@ -206,43 +215,6 @@ if(ENABLE_ASSERTIONS)
|
||||
DESCRIPTION "Do not eliminate null-pointer checks"
|
||||
-fno-delete-null-pointer-checks
|
||||
)
|
||||
|
||||
register_compiler_definitions(
|
||||
DESCRIPTION "Enable libc++ assertions"
|
||||
_LIBCPP_ENABLE_ASSERTIONS=1
|
||||
_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE ${RELEASE}
|
||||
_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG ${DEBUG}
|
||||
)
|
||||
|
||||
register_compiler_definitions(
|
||||
DESCRIPTION "Enable fortified sources"
|
||||
_FORTIFY_SOURCE=3
|
||||
)
|
||||
|
||||
if(LINUX)
|
||||
register_compiler_definitions(
|
||||
DESCRIPTION "Enable glibc++ assertions"
|
||||
_GLIBCXX_ASSERTIONS=1
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
register_compiler_definitions(
|
||||
DESCRIPTION "Disable debug assertions"
|
||||
NDEBUG=1
|
||||
)
|
||||
|
||||
register_compiler_definitions(
|
||||
DESCRIPTION "Disable libc++ assertions"
|
||||
_LIBCPP_ENABLE_ASSERTIONS=0
|
||||
_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE
|
||||
)
|
||||
|
||||
if(LINUX)
|
||||
register_compiler_definitions(
|
||||
DESCRIPTION "Disable glibc++ assertions"
|
||||
_GLIBCXX_ASSERTIONS=0
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# --- Diagnostics ---
|
||||
@@ -293,14 +265,6 @@ if(UNIX AND CI)
|
||||
)
|
||||
endif()
|
||||
|
||||
# --- Features ---
|
||||
|
||||
# Valgrind cannot handle SSE4.2 instructions
|
||||
# This is needed for picohttpparser
|
||||
if(ENABLE_VALGRIND AND ARCH STREQUAL "x64")
|
||||
register_compiler_definitions(__SSE4_2__=0)
|
||||
endif()
|
||||
|
||||
# --- Other ---
|
||||
|
||||
# Workaround for CMake and clang-cl bug.
|
||||
|
||||
@@ -125,7 +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 "read-write|read-only|write-only|none" "The strategy to use for caching" DEFAULT "read-write")
|
||||
optionx(CACHE_STRATEGY "read-write|read-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)
|
||||
@@ -136,13 +136,6 @@ else()
|
||||
set(WARNING WARNING)
|
||||
endif()
|
||||
|
||||
# TODO: This causes flaky zig builds in CI, so temporarily disable it.
|
||||
# if(CI)
|
||||
# set(DEFAULT_VENDOR_PATH ${CACHE_PATH}/vendor)
|
||||
# else()
|
||||
# set(DEFAULT_VENDOR_PATH ${CWD}/vendor)
|
||||
# endif()
|
||||
|
||||
optionx(VENDOR_PATH FILEPATH "The path to the vendor directory" DEFAULT ${CWD}/vendor)
|
||||
optionx(TMP_PATH FILEPATH "The path to the temporary directory" DEFAULT ${BUILD_PATH}/tmp)
|
||||
|
||||
@@ -317,7 +310,7 @@ function(find_command)
|
||||
${FIND_VALIDATOR}
|
||||
)
|
||||
|
||||
if(NOT FIND_REQUIRED STREQUAL "OFF" AND ${FIND_VARIABLE} MATCHES "NOTFOUND")
|
||||
if(FIND_REQUIRED AND ${FIND_VARIABLE} MATCHES "NOTFOUND")
|
||||
set(error "Command not found: \"${FIND_NAME}\"")
|
||||
|
||||
if(FIND_VERSION)
|
||||
@@ -917,10 +910,6 @@ function(register_compiler_flags)
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
function(register_compiler_definitions)
|
||||
|
||||
endfunction()
|
||||
|
||||
# register_linker_flags()
|
||||
# Description:
|
||||
# Registers a linker flag, similar to `add_link_options()`.
|
||||
|
||||
@@ -140,11 +140,6 @@ if(ENABLE_ASAN AND ENABLE_LTO)
|
||||
setx(ENABLE_LTO OFF)
|
||||
endif()
|
||||
|
||||
if(USE_VALGRIND AND NOT USE_BASELINE)
|
||||
message(WARNING "If valgrind is enabled, baseline must also be enabled")
|
||||
setx(USE_BASELINE ON)
|
||||
endif()
|
||||
|
||||
if(BUILDKITE_COMMIT)
|
||||
set(DEFAULT_REVISION ${BUILDKITE_COMMIT})
|
||||
else()
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
# https://cppcheck.sourceforge.io/
|
||||
|
||||
find_command(
|
||||
VARIABLE
|
||||
CPPCHECK_EXECUTABLE
|
||||
COMMAND
|
||||
cppcheck
|
||||
REQUIRED
|
||||
OFF
|
||||
)
|
||||
|
||||
set(CPPCHECK_COMMAND ${CPPCHECK_EXECUTABLE}
|
||||
--cppcheck-build-dir=${BUILD_PATH}/cppcheck
|
||||
--project=${BUILD_PATH}/compile_commands.json
|
||||
--clang=${CMAKE_CXX_COMPILER}
|
||||
--std=c++${CMAKE_CXX_STANDARD}
|
||||
--report-progress
|
||||
--showtime=summary
|
||||
)
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
cppcheck
|
||||
COMMENT
|
||||
"Running cppcheck"
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E make_directory cppcheck
|
||||
&& ${CPPCHECK_COMMAND}
|
||||
CWD
|
||||
${BUILD_PATH}
|
||||
TARGETS
|
||||
${bun}
|
||||
)
|
||||
@@ -1,22 +0,0 @@
|
||||
find_command(
|
||||
VARIABLE
|
||||
CPPLINT_PROGRAM
|
||||
COMMAND
|
||||
cpplint
|
||||
REQUIRED
|
||||
OFF
|
||||
)
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
cpplint
|
||||
COMMENT
|
||||
"Running cpplint"
|
||||
COMMAND
|
||||
${CPPLINT_PROGRAM}
|
||||
${BUN_CPP_SOURCES}
|
||||
CWD
|
||||
${BUILD_PATH}
|
||||
TARGETS
|
||||
${bun}
|
||||
)
|
||||
@@ -1,67 +0,0 @@
|
||||
# IWYU = "Include What You Use"
|
||||
# https://include-what-you-use.org/
|
||||
|
||||
setx(IWYU_SOURCE_PATH ${CACHE_PATH}/iwyu-${LLVM_VERSION})
|
||||
setx(IWYU_BUILD_PATH ${IWYU_SOURCE_PATH}/build)
|
||||
setx(IWYU_PROGRAM ${IWYU_BUILD_PATH}/bin/include-what-you-use)
|
||||
|
||||
register_repository(
|
||||
NAME
|
||||
iwyu
|
||||
REPOSITORY
|
||||
include-what-you-use/include-what-you-use
|
||||
BRANCH
|
||||
clang_${LLVM_VERSION}
|
||||
PATH
|
||||
${IWYU_SOURCE_PATH}
|
||||
)
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
build-iwyu
|
||||
COMMENT
|
||||
"Building iwyu"
|
||||
COMMAND
|
||||
${CMAKE_COMMAND}
|
||||
-B${IWYU_BUILD_PATH}
|
||||
-G${CMAKE_GENERATOR}
|
||||
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER}
|
||||
-DIWYU_LLVM_ROOT_PATH=${LLVM_PREFIX}
|
||||
&& ${CMAKE_COMMAND}
|
||||
--build ${IWYU_BUILD_PATH}
|
||||
CWD
|
||||
${IWYU_SOURCE_PATH}
|
||||
TARGETS
|
||||
clone-iwyu
|
||||
)
|
||||
|
||||
find_command(
|
||||
VARIABLE
|
||||
PYTHON_EXECUTABLE
|
||||
COMMAND
|
||||
python3
|
||||
python
|
||||
VERSION
|
||||
>=3.0.0
|
||||
REQUIRED
|
||||
OFF
|
||||
)
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
iwyu
|
||||
COMMENT
|
||||
"Running iwyu"
|
||||
COMMAND
|
||||
${CMAKE_COMMAND}
|
||||
-E env IWYU_BINARY=${IWYU_PROGRAM}
|
||||
${PYTHON_EXECUTABLE}
|
||||
${IWYU_SOURCE_PATH}/iwyu_tool.py
|
||||
-p ${BUILD_PATH}
|
||||
CWD
|
||||
${BUILD_PATH}
|
||||
TARGETS
|
||||
build-iwyu
|
||||
${bun}
|
||||
)
|
||||
@@ -45,12 +45,6 @@ else()
|
||||
endif()
|
||||
|
||||
set(LLVM_ZIG_CODEGEN_THREADS 0)
|
||||
# This makes the build slower, so we turn it off for now.
|
||||
# if (DEBUG)
|
||||
# include(ProcessorCount)
|
||||
# ProcessorCount(CPU_COUNT)
|
||||
# set(LLVM_ZIG_CODEGEN_THREADS ${CPU_COUNT})
|
||||
# endif()
|
||||
|
||||
# --- Dependencies ---
|
||||
|
||||
@@ -71,9 +65,6 @@ set(BUN_DEPENDENCIES
|
||||
)
|
||||
|
||||
include(CloneZstd)
|
||||
# foreach(dependency ${BUN_DEPENDENCIES})
|
||||
# include(Clone${dependency})
|
||||
# endforeach()
|
||||
|
||||
# --- Codegen ---
|
||||
|
||||
@@ -819,7 +810,7 @@ set_target_properties(${bun} PROPERTIES
|
||||
CXX_STANDARD_REQUIRED YES
|
||||
CXX_EXTENSIONS YES
|
||||
CXX_VISIBILITY_PRESET hidden
|
||||
C_STANDARD 17
|
||||
C_STANDARD 17 # Cannot uprev to C23 because MSVC doesn't have support.
|
||||
C_STANDARD_REQUIRED YES
|
||||
VISIBILITY_INLINES_HIDDEN YES
|
||||
)
|
||||
@@ -944,7 +935,7 @@ if(NOT WIN32)
|
||||
if (NOT ABI STREQUAL "musl")
|
||||
target_compile_options(${bun} PUBLIC
|
||||
-fsanitize=null
|
||||
-fsanitize-recover=all
|
||||
-fno-sanitize-recover=all
|
||||
-fsanitize=bounds
|
||||
-fsanitize=return
|
||||
-fsanitize=nullability-arg
|
||||
@@ -999,6 +990,20 @@ if(NOT WIN32)
|
||||
)
|
||||
|
||||
if(ENABLE_ASAN)
|
||||
target_compile_options(${bun} PUBLIC
|
||||
-fsanitize=null
|
||||
-fno-sanitize-recover=all
|
||||
-fsanitize=bounds
|
||||
-fsanitize=return
|
||||
-fsanitize=nullability-arg
|
||||
-fsanitize=nullability-assign
|
||||
-fsanitize=nullability-return
|
||||
-fsanitize=returns-nonnull-attribute
|
||||
-fsanitize=unreachable
|
||||
)
|
||||
target_link_libraries(${bun} PRIVATE
|
||||
-fsanitize=null
|
||||
)
|
||||
target_compile_options(${bun} PUBLIC -fsanitize=address)
|
||||
target_link_libraries(${bun} PUBLIC -fsanitize=address)
|
||||
endif()
|
||||
@@ -1045,7 +1050,6 @@ if(APPLE)
|
||||
-Wl,-no_compact_unwind
|
||||
-Wl,-stack_size,0x1200000
|
||||
-fno-keep-static-consts
|
||||
-Wl,-map,${bun}.linker-map
|
||||
)
|
||||
|
||||
if(DEBUG)
|
||||
@@ -1065,6 +1069,7 @@ if(APPLE)
|
||||
target_link_options(${bun} PUBLIC
|
||||
-dead_strip
|
||||
-dead_strip_dylibs
|
||||
-Wl,-map,${bun}.linker-map
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
@@ -1098,6 +1103,17 @@ if(LINUX)
|
||||
)
|
||||
endif()
|
||||
|
||||
if (ENABLE_LTO)
|
||||
# We are optimizing for size at a slight debug-ability cost
|
||||
target_link_options(${bun} PUBLIC
|
||||
-Wl,--no-eh-frame-hdr
|
||||
)
|
||||
else()
|
||||
target_link_options(${bun} PUBLIC
|
||||
-Wl,--eh-frame-hdr
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_options(${bun} PUBLIC
|
||||
--ld-path=${LLD_PROGRAM}
|
||||
-fno-pic
|
||||
@@ -1112,11 +1128,9 @@ if(LINUX)
|
||||
# make debug info faster to load
|
||||
-Wl,--gdb-index
|
||||
-Wl,-z,combreloc
|
||||
-Wl,--no-eh-frame-hdr
|
||||
-Wl,--sort-section=name
|
||||
-Wl,--hash-style=both
|
||||
-Wl,--build-id=sha1 # Better for debugging than default
|
||||
-Wl,-Map=${bun}.linker-map
|
||||
)
|
||||
|
||||
# don't strip in debug, this seems to be needed so that the Zig std library
|
||||
@@ -1131,6 +1145,7 @@ if(LINUX)
|
||||
if (NOT DEBUG AND NOT ENABLE_ASAN AND NOT ENABLE_VALGRIND)
|
||||
target_link_options(${bun} PUBLIC
|
||||
-Wl,-icf=safe
|
||||
-Wl,-Map=${bun}.linker-map
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -1237,15 +1252,9 @@ if(LINUX)
|
||||
target_link_libraries(${bun} PUBLIC libatomic.so)
|
||||
endif()
|
||||
|
||||
if(USE_SYSTEM_ICU)
|
||||
target_link_libraries(${bun} PRIVATE libicudata.a)
|
||||
target_link_libraries(${bun} PRIVATE libicui18n.a)
|
||||
target_link_libraries(${bun} PRIVATE libicuuc.a)
|
||||
else()
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicudata.a)
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicui18n.a)
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicuuc.a)
|
||||
endif()
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicudata.a)
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicui18n.a)
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicuuc.a)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
@@ -1298,32 +1307,32 @@ if(NOT BUN_CPP_ONLY)
|
||||
OUTPUTS
|
||||
${BUILD_PATH}/${bunStripExe}
|
||||
)
|
||||
|
||||
|
||||
# Then sign both executables on Windows
|
||||
if(WIN32 AND ENABLE_WINDOWS_CODESIGNING)
|
||||
set(SIGN_SCRIPT "${CMAKE_SOURCE_DIR}/.buildkite/scripts/sign-windows.ps1")
|
||||
|
||||
|
||||
# Verify signing script exists
|
||||
if(NOT EXISTS "${SIGN_SCRIPT}")
|
||||
message(FATAL_ERROR "Windows signing script not found: ${SIGN_SCRIPT}")
|
||||
endif()
|
||||
|
||||
|
||||
# Use PowerShell for Windows code signing (native Windows, no path issues)
|
||||
find_program(POWERSHELL_EXECUTABLE
|
||||
find_program(POWERSHELL_EXECUTABLE
|
||||
NAMES pwsh.exe powershell.exe
|
||||
PATHS
|
||||
PATHS
|
||||
"C:/Program Files/PowerShell/7"
|
||||
"C:/Program Files (x86)/PowerShell/7"
|
||||
"C:/Windows/System32/WindowsPowerShell/v1.0"
|
||||
DOC "Path to PowerShell executable"
|
||||
)
|
||||
|
||||
|
||||
if(NOT POWERSHELL_EXECUTABLE)
|
||||
set(POWERSHELL_EXECUTABLE "powershell.exe")
|
||||
endif()
|
||||
|
||||
|
||||
message(STATUS "Using PowerShell executable: ${POWERSHELL_EXECUTABLE}")
|
||||
|
||||
|
||||
# Sign both bun-profile.exe and bun.exe after stripping
|
||||
register_command(
|
||||
TARGET
|
||||
@@ -1452,7 +1461,7 @@ if(NOT BUN_CPP_ONLY)
|
||||
list(APPEND bunFiles ${bun}.dSYM)
|
||||
endif()
|
||||
|
||||
if(APPLE OR LINUX)
|
||||
if((APPLE OR LINUX) AND NOT ENABLE_ASAN)
|
||||
list(APPEND bunFiles ${bun}.linker-map)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
google/highway
|
||||
COMMIT
|
||||
12b325bc1793dee68ab2157995a690db859fe9e0
|
||||
ac0d5d297b13ab1b89f48484fc7911082d76a93f
|
||||
)
|
||||
|
||||
set(HIGHWAY_CMAKE_ARGS
|
||||
|
||||
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
ebiggers/libdeflate
|
||||
COMMIT
|
||||
96836d7d9d10e3e0d53e6edb54eb908514e336c4
|
||||
c8c56a20f8f621e6a966b716b31f1dedab6a41e3
|
||||
)
|
||||
|
||||
register_cmake_command(
|
||||
|
||||
@@ -4,8 +4,8 @@ register_repository(
|
||||
REPOSITORY
|
||||
libuv/libuv
|
||||
COMMIT
|
||||
# Corresponds to v1.51.0
|
||||
5152db2cbfeb5582e9c27c5ea1dba2cd9e10759b
|
||||
# Latest HEAD (includes recursion bug fix #4784)
|
||||
f3ce527ea940d926c40878ba5de219640c362811
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
|
||||
@@ -49,3 +49,6 @@ else()
|
||||
setenv(CCACHE_MAXSIZE 100G)
|
||||
setenv(CCACHE_SLOPPINESS "pch_defines,time_macros,locale,random_seed,clang_index_store,gcno_cwd")
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
|
||||
90
cmake/tools/SetupSccache.cmake
Normal file
90
cmake/tools/SetupSccache.cmake
Normal file
@@ -0,0 +1,90 @@
|
||||
if(CACHE_STRATEGY STREQUAL "none")
|
||||
return()
|
||||
endif()
|
||||
|
||||
function(check_aws_credentials OUT_VAR)
|
||||
set(HAS_CREDENTIALS FALSE)
|
||||
|
||||
if(DEFINED ENV{AWS_ACCESS_KEY_ID} AND DEFINED ENV{AWS_SECRET_ACCESS_KEY})
|
||||
set(HAS_CREDENTIALS TRUE)
|
||||
message(NOTICE
|
||||
"sccache: Using AWS credentials found in environment variables")
|
||||
endif()
|
||||
|
||||
# Check for ~/.aws directory since sccache may use that.
|
||||
if(NOT HAS_CREDENTIALS)
|
||||
if(WIN32)
|
||||
set(AWS_CONFIG_DIR "$ENV{USERPROFILE}/.aws")
|
||||
else()
|
||||
set(AWS_CONFIG_DIR "$ENV{HOME}/.aws")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${AWS_CONFIG_DIR}/credentials")
|
||||
set(HAS_CREDENTIALS TRUE)
|
||||
message(NOTICE
|
||||
"sccache: Using AWS credentials found in ${AWS_CONFIG_DIR}/credentials")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(${OUT_VAR} ${HAS_CREDENTIALS} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(check_running_in_ci OUT_VAR)
|
||||
set(IS_CI FALSE)
|
||||
|
||||
# Query EC2 instance metadata service to check if running on buildkite-agent
|
||||
# The IP address 169.254.169.254 is a well-known link-local address for querying EC2 instance
|
||||
# metdata:
|
||||
# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html
|
||||
execute_process(
|
||||
COMMAND curl -s -m 0.5 http://169.254.169.254/latest/meta-data/tags/instance/Service
|
||||
OUTPUT_VARIABLE METADATA_OUTPUT
|
||||
ERROR_VARIABLE METADATA_ERROR
|
||||
RESULT_VARIABLE METADATA_RESULT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
# Check if the request succeeded and returned exactly "buildkite-agent"
|
||||
if(METADATA_RESULT EQUAL 0 AND METADATA_OUTPUT STREQUAL "buildkite-agent")
|
||||
set(IS_CI TRUE)
|
||||
endif()
|
||||
|
||||
set(${OUT_VAR} ${IS_CI} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
check_running_in_ci(IS_IN_CI)
|
||||
find_command(VARIABLE SCCACHE_PROGRAM COMMAND sccache REQUIRED ${IS_IN_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()
|
||||
|
||||
# Configure S3 bucket for distributed caching
|
||||
setenv(SCCACHE_BUCKET "bun-build-sccache-store")
|
||||
setenv(SCCACHE_REGION "us-west-1")
|
||||
setenv(SCCACHE_DIR "${CACHE_PATH}/sccache")
|
||||
|
||||
# Handle credentials based on cache strategy
|
||||
if (CACHE_STRATEGY STREQUAL "read-only")
|
||||
setenv(SCCACHE_S3_NO_CREDENTIALS "1")
|
||||
message(STATUS "sccache configured in read-only mode.")
|
||||
else()
|
||||
# Check for AWS credentials and enable anonymous access if needed
|
||||
check_aws_credentials(HAS_AWS_CREDENTIALS)
|
||||
if(NOT IS_IN_CI AND NOT HAS_AWS_CREDENTIALS)
|
||||
setenv(SCCACHE_S3_NO_CREDENTIALS "1")
|
||||
message(NOTICE "sccache: No AWS credentials found, enabling anonymous S3 "
|
||||
"access. Writing to the cache will be disabled.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
setenv(SCCACHE_LOG "info")
|
||||
|
||||
message(STATUS "sccache configured for bun-build-sccache-store (us-west-1).")
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.20 AS build
|
||||
FROM alpine:3.22 AS build
|
||||
|
||||
# https://github.com/oven-sh/bun/releases
|
||||
ARG BUN_VERSION=latest
|
||||
@@ -44,7 +44,7 @@ RUN apk --no-cache add ca-certificates curl dirmngr gpg gpg-agent unzip \
|
||||
&& rm -f "bun-linux-$build.zip" SHASUMS256.txt.asc SHASUMS256.txt \
|
||||
&& chmod +x /usr/local/bin/bun
|
||||
|
||||
FROM alpine:3.20
|
||||
FROM alpine:3.22
|
||||
|
||||
# Disable the runtime transpiler cache by default inside Docker containers.
|
||||
# On ephemeral containers, the cache is not useful
|
||||
|
||||
28
docs/README.md
Normal file
28
docs/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
<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,19 +0,0 @@
|
||||
Bun.js has fast paths for common use cases that make Web APIs live up to the performance demands of servers and CLIs.
|
||||
|
||||
`Bun.file(path)` returns a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) that represents a lazily-loaded file.
|
||||
|
||||
When you pass a file blob to `Bun.write`, Bun automatically uses a faster system call:
|
||||
|
||||
```js
|
||||
const blob = Bun.file("input.txt");
|
||||
await Bun.write("output.txt", blob);
|
||||
```
|
||||
|
||||
On Linux, this uses the [`copy_file_range`](https://man7.org/linux/man-pages/man2/copy_file_range.2.html) syscall and on macOS, this becomes `clonefile` (or [`fcopyfile`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/copyfile.3.html)).
|
||||
|
||||
`Bun.write` also supports [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects. It automatically converts to a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob).
|
||||
|
||||
```js
|
||||
// Eventually, this will stream the response to disk but today it buffers
|
||||
await Bun.write("index.html", await fetch("https://example.com"));
|
||||
```
|
||||
@@ -1,387 +0,0 @@
|
||||
Bun implements the following globals.
|
||||
|
||||
{% table %}
|
||||
|
||||
- Global
|
||||
- Source
|
||||
- Notes
|
||||
|
||||
---
|
||||
|
||||
- [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`alert`](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert)
|
||||
- Web
|
||||
- Intended for command-line tools
|
||||
|
||||
---
|
||||
|
||||
- [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`Buffer`](https://nodejs.org/api/buffer.html#class-buffer)
|
||||
- Node.js
|
||||
- See [Node.js > `Buffer`](https://bun.com/docs/runtime/nodejs-apis#node-buffer)
|
||||
|
||||
---
|
||||
|
||||
- `Bun`
|
||||
- Bun
|
||||
- Subject to change as additional APIs are added
|
||||
|
||||
---
|
||||
|
||||
- [`ByteLengthQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/ByteLengthQueuingStrategy)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm)
|
||||
- Web
|
||||
- Intended for command-line tools
|
||||
|
||||
---
|
||||
|
||||
- [`__dirname`](https://nodejs.org/api/globals.html#__dirname)
|
||||
- Node.js
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`__filename`](https://nodejs.org/api/globals.html#__filename)
|
||||
- Node.js
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`atob()`](https://developer.mozilla.org/en-US/docs/Web/API/atob)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`btoa()`](https://developer.mozilla.org/en-US/docs/Web/API/btoa)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- `BuildMessage`
|
||||
- Bun
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`clearImmediate()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearImmediate)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`clearInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearInterval)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`clearTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearTimeout)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`console`](https://developer.mozilla.org/en-US/docs/Web/API/console)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`CountQueuingStrategy`](https://developer.mozilla.org/en-US/docs/Web/API/CountQueuingStrategy)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`Crypto`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`crypto`](https://developer.mozilla.org/en-US/docs/Web/API/crypto)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`CryptoKey`](https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event)
|
||||
- Web
|
||||
- Also [`ErrorEvent`](https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent) [`CloseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) [`MessageEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent).
|
||||
|
||||
---
|
||||
|
||||
- [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`exports`](https://nodejs.org/api/globals.html#exports)
|
||||
- Node.js
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`global`](https://nodejs.org/api/globals.html#global)
|
||||
- Node.js
|
||||
- See [Node.js > `global`](https://bun.com/docs/runtime/nodejs-apis#global).
|
||||
|
||||
---
|
||||
|
||||
- [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis)
|
||||
- Cross-platform
|
||||
- Aliases to `global`
|
||||
|
||||
---
|
||||
|
||||
- [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`HTMLRewriter`](https://bun.com/docs/api/html-rewriter)
|
||||
- Cloudflare
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`JSON`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`MessageEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`module`](https://nodejs.org/api/globals.html#module)
|
||||
- Node.js
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`performance`](https://developer.mozilla.org/en-US/docs/Web/API/performance)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`process`](https://nodejs.org/api/process.html)
|
||||
- Node.js
|
||||
- See [Node.js > `process`](https://bun.com/docs/runtime/nodejs-apis#node-process)
|
||||
|
||||
---
|
||||
|
||||
- [`prompt`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt)
|
||||
- Web
|
||||
- Intended for command-line tools
|
||||
|
||||
---
|
||||
|
||||
- [`queueMicrotask()`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`ReadableByteStreamController`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableByteStreamController)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`ReadableStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultController)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`ReadableStreamDefaultReader`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`reportError`](https://developer.mozilla.org/en-US/docs/Web/API/reportError)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`require()`](https://nodejs.org/api/globals.html#require)
|
||||
- Node.js
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- `ResolveMessage`
|
||||
- Bun
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`setImmediate()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`ShadowRealm`](https://github.com/tc39/proposal-shadowrealm)
|
||||
- Web
|
||||
- Stage 3 proposal
|
||||
|
||||
---
|
||||
|
||||
- [`SubtleCrypto`](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`DOMException`](https://developer.mozilla.org/en-US/docs/Web/API/DOMException)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`TransformStream`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`TransformStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/TransformStreamDefaultController)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`WebAssembly`](https://nodejs.org/api/globals.html#webassembly)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`WritableStreamDefaultController`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultController)
|
||||
- Web
|
||||
-
|
||||
|
||||
---
|
||||
|
||||
- [`WritableStreamDefaultWriter`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStreamDefaultWriter)
|
||||
- Web
|
||||
-
|
||||
|
||||
{% /table %}
|
||||
1408
docs/api/http.md
1408
docs/api/http.md
File diff suppressed because it is too large
Load Diff
@@ -1,69 +0,0 @@
|
||||
The `import.meta` object is a way for a module to access information about itself. It's part of the JavaScript language, but its contents are not standardized. Each "host" (browser, runtime, etc) is free to implement any properties it wishes on the `import.meta` object.
|
||||
|
||||
Bun implements the following properties.
|
||||
|
||||
```ts#/path/to/project/file.ts
|
||||
import.meta.dir; // => "/path/to/project"
|
||||
import.meta.file; // => "file.ts"
|
||||
import.meta.path; // => "/path/to/project/file.ts"
|
||||
import.meta.url; // => "file:///path/to/project/file.ts"
|
||||
|
||||
import.meta.main; // `true` if this file is directly executed by `bun run`
|
||||
// `false` otherwise
|
||||
|
||||
import.meta.resolve("zod"); // => "file:///path/to/project/node_modules/zod/index.js"
|
||||
```
|
||||
|
||||
{% table %}
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.dir`
|
||||
- Absolute path to the directory containing the current file, e.g. `/path/to/project`. Equivalent to `__dirname` in CommonJS modules (and Node.js)
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.dirname`
|
||||
- An alias to `import.meta.dir`, for Node.js compatibility
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.env`
|
||||
- An alias to `process.env`.
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.file`
|
||||
- The name of the current file, e.g. `index.tsx`
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.path`
|
||||
- Absolute path to the current file, e.g. `/path/to/project/index.ts`. Equivalent to `__filename` in CommonJS modules (and Node.js)
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.filename`
|
||||
- An alias to `import.meta.path`, for Node.js compatibility
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.main`
|
||||
- Indicates whether the current file is the entrypoint to the current `bun` process. Is the file being directly executed by `bun run` or is it being imported?
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.resolve`
|
||||
- Resolve a module specifier (e.g. `"zod"` or `"./file.tsx"`) to a url. Equivalent to [`import.meta.resolve` in browsers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta#resolve)
|
||||
|
||||
```ts
|
||||
import.meta.resolve("zod");
|
||||
// => "file:///path/to/project/node_modules/zod/index.ts"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
- `import.meta.url`
|
||||
- A `string` url to the current file, e.g. `file:///path/to/project/index.ts`. Equivalent to [`import.meta.url` in browsers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta#url)
|
||||
|
||||
{% /table %}
|
||||
@@ -1,120 +0,0 @@
|
||||
Bun.js focuses on performance, developer experience, and compatibility with the JavaScript ecosystem.
|
||||
|
||||
## HTTP Requests
|
||||
|
||||
```ts
|
||||
// http.ts
|
||||
export default {
|
||||
port: 3000,
|
||||
fetch(request: Request) {
|
||||
return new Response("Hello World");
|
||||
},
|
||||
};
|
||||
|
||||
// bun ./http.ts
|
||||
```
|
||||
|
||||
| Requests per second | OS | CPU | Bun version |
|
||||
| ---------------------------------------------------------------------- | ----- | ------------------------------ | ----------- |
|
||||
| [260,000](https://twitter.com/jarredsumner/status/1512040623200616449) | macOS | Apple Silicon M1 Max | 0.0.76 |
|
||||
| [160,000](https://twitter.com/jarredsumner/status/1511988933587976192) | Linux | AMD Ryzen 5 3600 6-Core 2.2ghz | 0.0.76 |
|
||||
|
||||
{% details summary="See benchmark details" %}
|
||||
Measured with [`http_load_test`](https://github.com/uNetworking/uSockets/blob/master/examples/http_load_test.c) by running:
|
||||
|
||||
```bash
|
||||
$ ./http_load_test 20 127.0.0.1 3000
|
||||
```
|
||||
|
||||
{% /details %}
|
||||
|
||||
## File System
|
||||
|
||||
`cat` clone that runs [2x faster than GNU cat](https://twitter.com/jarredsumner/status/1511707890708586496) for large files on Linux
|
||||
|
||||
```js
|
||||
// cat.js
|
||||
import { resolve } from "path";
|
||||
import { write, stdout, file, argv } from "bun";
|
||||
|
||||
const path = resolve(argv.at(-1));
|
||||
|
||||
await write(
|
||||
// stdout is a Blob
|
||||
stdout,
|
||||
// file(path) returns a Blob - https://developer.mozilla.org/en-US/docs/Web/API/Blob
|
||||
file(path),
|
||||
);
|
||||
```
|
||||
|
||||
Run this with `bun cat.js /path/to/big/file`.
|
||||
|
||||
## Reading from standard input
|
||||
|
||||
```ts
|
||||
for await (const line of console) {
|
||||
// line of text from stdin
|
||||
console.log(line);
|
||||
}
|
||||
```
|
||||
|
||||
## React SSR
|
||||
|
||||
```js
|
||||
import { renderToReadableStream } from "react-dom/server";
|
||||
|
||||
const dt = new Intl.DateTimeFormat();
|
||||
|
||||
export default {
|
||||
port: 3000,
|
||||
async fetch(request: Request) {
|
||||
return new Response(
|
||||
await renderToReadableStream(
|
||||
<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"));
|
||||
```
|
||||
@@ -1,42 +0,0 @@
|
||||
[TOML](https://toml.io/) is a minimal configuration file format designed to be easy for humans to read.
|
||||
|
||||
Bun implements a TOML parser with a few tweaks designed for better interoperability with INI files and with JavaScript.
|
||||
|
||||
### ; and # are comments
|
||||
|
||||
In Bun-flavored TOML, comments start with `#` or `;`
|
||||
|
||||
```ini
|
||||
# This is a comment
|
||||
; This is also a comment
|
||||
```
|
||||
|
||||
This matches the behavior of INI files.
|
||||
|
||||
In TOML, comments start with `#`
|
||||
|
||||
```toml
|
||||
# This is a comment
|
||||
```
|
||||
|
||||
### String escape characters
|
||||
|
||||
Bun-flavored adds a few more escape sequences to TOML to work better with JavaScript strings.
|
||||
|
||||
```
|
||||
# Bun-flavored TOML extras
|
||||
\x{XX} - ASCII (U+00XX)
|
||||
\u{x+} - unicode (U+0000000X) - (U+XXXXXXXX)
|
||||
\v - vertical tab
|
||||
|
||||
# Regular TOML
|
||||
\b - backspace (U+0008)
|
||||
\t - tab (U+0009)
|
||||
\n - linefeed (U+000A)
|
||||
\f - form feed (U+000C)
|
||||
\r - carriage return (U+000D)
|
||||
\" - quote (U+0022)
|
||||
\\ - backslash (U+005C)
|
||||
\uXXXX - unicode (U+XXXX)
|
||||
\UXXXXXXXX - unicode (U+XXXXXXXX)
|
||||
```
|
||||
465
docs/bundler/bytecode.mdx
Normal file
465
docs/bundler/bytecode.mdx
Normal file
@@ -0,0 +1,465 @@
|
||||
---
|
||||
title: Bytecode Caching
|
||||
description: Speed up JavaScript execution with bytecode caching in Bun's bundler
|
||||
---
|
||||
|
||||
Bytecode caching is a build-time optimization that dramatically improves application startup time by pre-compiling your JavaScript to bytecode. For example, when compiling TypeScript's `tsc` with bytecode enabled, startup time improves by **2x**.
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic usage
|
||||
|
||||
Enable bytecode caching with the `--bytecode` flag:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build ./index.ts --target=bun --bytecode --outdir=./dist
|
||||
```
|
||||
|
||||
This generates two files:
|
||||
|
||||
- `dist/index.js` - Your bundled JavaScript
|
||||
- `dist/index.jsc` - The bytecode cache file
|
||||
|
||||
At runtime, Bun automatically detects and uses the `.jsc` file:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun ./dist/index.js # Automatically uses index.jsc
|
||||
```
|
||||
|
||||
### With standalone executables
|
||||
|
||||
When creating executables with `--compile`, bytecode is embedded into the binary:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build ./cli.ts --compile --bytecode --outfile=mycli
|
||||
```
|
||||
|
||||
The resulting executable contains both the code and bytecode, giving you maximum performance in a single file.
|
||||
|
||||
### Combining with other optimizations
|
||||
|
||||
Bytecode works great with minification and source maps:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build --compile --bytecode --minify --sourcemap ./cli.ts --outfile=mycli
|
||||
```
|
||||
|
||||
- `--minify` reduces code size before generating bytecode (less code -> less bytecode)
|
||||
- `--sourcemap` preserves error reporting (errors still point to original source)
|
||||
- `--bytecode` eliminates parsing overhead
|
||||
|
||||
## Performance impact
|
||||
|
||||
The performance improvement scales with your codebase size:
|
||||
|
||||
| Application size | Typical startup improvement |
|
||||
| ------------------------- | --------------------------- |
|
||||
| Small CLI (< 100 KB) | 1.5-2x faster |
|
||||
| Medium-large app (> 5 MB) | 2.5x-4x faster |
|
||||
|
||||
Larger applications benefit more because they have more code to parse.
|
||||
|
||||
## When to use bytecode
|
||||
|
||||
### Great for:
|
||||
|
||||
#### CLI tools
|
||||
|
||||
- Invoked frequently (linters, formatters, git hooks)
|
||||
- Startup time is the entire user experience
|
||||
- Users notice the difference between 90ms and 45ms startup
|
||||
- Example: TypeScript compiler, Prettier, ESLint
|
||||
|
||||
#### Build tools and task runners
|
||||
|
||||
- Run hundreds or thousands of times during development
|
||||
- Milliseconds saved per run compound quickly
|
||||
- Developer experience improvement
|
||||
- Example: Build scripts, test runners, code generators
|
||||
|
||||
#### Standalone executables
|
||||
|
||||
- Distributed to users who care about snappy performance
|
||||
- Single-file distribution is convenient
|
||||
- File size less important than startup time
|
||||
- Example: CLIs distributed via npm or as binaries
|
||||
|
||||
### Skip it for:
|
||||
|
||||
- ❌ **Small scripts**
|
||||
- ❌ **Code that runs once**
|
||||
- ❌ **Development builds**
|
||||
- ❌ **Size-constrained environments**
|
||||
- ❌ **Code with top-level await** (not supported)
|
||||
|
||||
## Limitations
|
||||
|
||||
### CommonJS only
|
||||
|
||||
Bytecode caching currently works with CommonJS output format. Bun's bundler automatically converts most ESM code to CommonJS, but **top-level await** is the exception:
|
||||
|
||||
```js
|
||||
// This prevents bytecode caching
|
||||
const data = await fetch("https://api.example.com");
|
||||
export default data;
|
||||
```
|
||||
|
||||
**Why**: Top-level await requires async module evaluation, which can't be represented in CommonJS. The module graph becomes asynchronous, and the CommonJS wrapper function model breaks down.
|
||||
|
||||
**Workaround**: Move async initialization into a function:
|
||||
|
||||
```js
|
||||
async function init() {
|
||||
const data = await fetch("https://api.example.com");
|
||||
return data;
|
||||
}
|
||||
|
||||
export default init;
|
||||
```
|
||||
|
||||
Now the module exports a function that the consumer can await when needed.
|
||||
|
||||
### Version compatibility
|
||||
|
||||
Bytecode is **not portable across Bun versions**. The bytecode format is tied to JavaScriptCore's internal representation, which changes between versions.
|
||||
|
||||
When you update Bun, you must regenerate bytecode:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
# After updating Bun
|
||||
bun build --bytecode ./index.ts --outdir=./dist
|
||||
```
|
||||
|
||||
If bytecode doesn't match the current Bun version, it's automatically ignored and your code falls back to parsing the JavaScript source. Your app still runs - you just lose the performance optimization.
|
||||
|
||||
**Best practice**: Generate bytecode as part of your CI/CD build process. Don't commit `.jsc` files to git. Regenerate them whenever you update Bun.
|
||||
|
||||
### Source code still required
|
||||
|
||||
- The `.js` file (your bundled source code)
|
||||
- The `.jsc` file (the bytecode cache)
|
||||
|
||||
At runtime:
|
||||
|
||||
1. Bun loads the `.js` file, sees a `@bytecode` pragma, and checks the `.jsc` file
|
||||
2. Bun loads the `.jsc` file
|
||||
3. Bun validates the bytecode hash matches the source
|
||||
4. If valid, Bun uses the bytecode
|
||||
5. If invalid, Bun falls back to parsing the source
|
||||
|
||||
### Bytecode is not obfuscation
|
||||
|
||||
Bytecode **does not obscure your source code**. It's an optimization, not a security measure.
|
||||
|
||||
## Production deployment
|
||||
|
||||
### Docker
|
||||
|
||||
Include bytecode generation in your Dockerfile:
|
||||
|
||||
```dockerfile Dockerfile icon="docker"
|
||||
FROM oven/bun:1 AS builder
|
||||
WORKDIR /app
|
||||
COPY package.json bun.lock ./
|
||||
RUN bun install --frozen-lockfile
|
||||
|
||||
COPY . .
|
||||
RUN bun build --bytecode --minify --sourcemap \
|
||||
--target=bun \
|
||||
--outdir=./dist \
|
||||
--compile \
|
||||
./src/server.ts --outfile=./dist/server
|
||||
|
||||
FROM oven/bun:1 AS runner
|
||||
WORKDIR /app
|
||||
COPY --from=builder /dist/server /app/server
|
||||
CMD ["./server"]
|
||||
```
|
||||
|
||||
The bytecode is architecture-independent.
|
||||
|
||||
### CI/CD
|
||||
|
||||
Generate bytecode during your build pipeline:
|
||||
|
||||
```yaml workflow.yml icon="file-code"
|
||||
# GitHub Actions
|
||||
- name: Build with bytecode
|
||||
run: |
|
||||
bun install
|
||||
bun build --bytecode --minify \
|
||||
--outdir=./dist \
|
||||
--target=bun \
|
||||
./src/index.ts
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
### Verify bytecode is being used
|
||||
|
||||
Check that the `.jsc` file exists:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
ls -lh dist/
|
||||
```
|
||||
|
||||
```txt
|
||||
-rw-r--r-- 1 user staff 245K index.js
|
||||
-rw-r--r-- 1 user staff 1.1M index.jsc
|
||||
```
|
||||
|
||||
The `.jsc` file should be 2-8x larger than the `.js` file.
|
||||
|
||||
To log if bytecode is being used, set `BUN_JSC_verboseDiskCache=1` in your environment.
|
||||
|
||||
On success, it will log something like:
|
||||
|
||||
```txt
|
||||
[Disk cache] cache hit for sourceCode
|
||||
```
|
||||
|
||||
If you see a cache miss, it will log something like:
|
||||
|
||||
```txt
|
||||
[Disk cache] cache miss for sourceCode
|
||||
```
|
||||
|
||||
It's normal for it it to log a cache miss multiple times since Bun doesn't currently bytecode cache JavaScript code used in builtin modules.
|
||||
|
||||
### Common issues
|
||||
|
||||
**Bytecode silently ignored**: Usually caused by a Bun version update. The cache version doesn't match, so bytecode is rejected. Regenerate to fix.
|
||||
|
||||
**File size too large**: This is expected. Consider:
|
||||
|
||||
- Using `--minify` to reduce code size before bytecode generation
|
||||
- Compressing `.jsc` files for network transfer (gzip/brotli)
|
||||
- Evaluating if the startup performance gain is worth the size increase
|
||||
|
||||
**Top-level await**: Not supported. Refactor to use async initialization functions.
|
||||
|
||||
## What is bytecode?
|
||||
|
||||
When you run JavaScript, the JavaScript engine doesn't execute your source code directly. Instead, it goes through several steps:
|
||||
|
||||
1. **Parsing**: The engine reads your JavaScript source code and converts it into an Abstract Syntax Tree (AST)
|
||||
2. **Bytecode compilation**: The AST is compiled into bytecode - a lower-level representation that's faster to execute
|
||||
3. **Execution**: The bytecode is executed by the engine's interpreter or JIT compiler
|
||||
|
||||
Bytecode is an intermediate representation - it's lower-level than JavaScript source code, but higher-level than machine code. Think of it as assembly language for a virtual machine. Each bytecode instruction represents a single operation like "load this variable," "add two numbers," or "call this function."
|
||||
|
||||
This happens **every single time** you run your code. If you have a CLI tool that runs 100 times a day, your code gets parsed 100 times. If you have a serverless function with frequent cold starts, parsing happens on every cold start.
|
||||
|
||||
With bytecode caching, Bun moves steps 1 and 2 to the build step. At runtime, the engine loads the pre-compiled bytecode and jumps straight to execution.
|
||||
|
||||
### Why lazy parsing makes this even better
|
||||
|
||||
Modern JavaScript engines use a clever optimization called **lazy parsing**. They don't parse all your code upfront - instead, functions are only parsed when they're first called:
|
||||
|
||||
```js
|
||||
// Without bytecode caching:
|
||||
function rarely_used() {
|
||||
// This 500-line function is only parsed
|
||||
// when it's actually called
|
||||
}
|
||||
|
||||
function main() {
|
||||
console.log("Starting app");
|
||||
// rarely_used() is never called, so it's never parsed
|
||||
}
|
||||
```
|
||||
|
||||
This means parsing overhead isn't just a startup cost - it happens throughout your application's lifetime as different code paths execute. With bytecode caching, **all functions are pre-compiled**, even the ones that are lazily parsed. The parsing work happens once at build time instead of being distributed throughout your application's execution.
|
||||
|
||||
## The bytecode format
|
||||
|
||||
### Inside a .jsc file
|
||||
|
||||
A `.jsc` file contains a serialized bytecode structure. Understanding what's inside helps explain both the performance benefits and the file size tradeoff.
|
||||
|
||||
**Header section** (validated on every load):
|
||||
|
||||
- **Cache version**: A hash tied to the JavaScriptCore framework version. This ensures bytecode generated with one version of Bun only runs with that exact version.
|
||||
- **Code block type tag**: Identifies whether this is a Program, Module, Eval, or Function code block.
|
||||
|
||||
**SourceCodeKey** (validates bytecode matches source):
|
||||
|
||||
- **Source code hash**: A hash of the original JavaScript source code. Bun verifies this matches before using the bytecode.
|
||||
- **Source code length**: The exact length of the source, for additional validation.
|
||||
- **Compilation flags**: Critical compilation context like strict mode, whether it's a script vs module, eval context type, etc. The same source code compiled with different flags produces different bytecode.
|
||||
|
||||
**Bytecode instructions**:
|
||||
|
||||
- **Instruction stream**: The actual bytecode opcodes - the compiled representation of your JavaScript. This is a variable-length sequence of bytecode instructions.
|
||||
- **Metadata table**: Each opcode has associated metadata - things like profiling counters, type hints, and execution counts (even if not yet populated).
|
||||
- **Jump targets**: Pre-computed addresses for control flow (if/else, loops, switch statements).
|
||||
- **Switch tables**: Optimized lookup tables for switch statements.
|
||||
|
||||
**Constants and identifiers**:
|
||||
|
||||
- **Constant pool**: All literal values in your code - numbers, strings, booleans, null, undefined. These are stored as actual JavaScript values (JSValues) so they don't need to be parsed from source at runtime.
|
||||
- **Identifier table**: All variable and function names used in the code. Stored as deduplicated strings.
|
||||
- **Source code representation markers**: Flags indicating how constants should be represented (as integers, doubles, big ints, etc.).
|
||||
|
||||
**Function metadata** (for each function in your code):
|
||||
|
||||
- **Register allocation**: How many registers (local variables) the function needs - `thisRegister`, `scopeRegister`, `numVars`, `numCalleeLocals`, `numParameters`.
|
||||
- **Code features**: A bitmask of function characteristics: is it a constructor? an arrow function? does it use `super`? does it have tail calls? These affect how the function is executed.
|
||||
- **Lexically scoped features**: Strict mode and other lexical context.
|
||||
- **Parse mode**: The mode in which the function was parsed (normal, async, generator, async generator).
|
||||
|
||||
**Nested structures**:
|
||||
|
||||
- **Function declarations and expressions**: Each nested function gets its own bytecode block, recursively. A file with 100 functions has 100 separate bytecode blocks, all nested in the structure.
|
||||
- **Exception handlers**: Try/catch/finally blocks with their boundaries and handler addresses pre-computed.
|
||||
- **Expression info**: Maps bytecode positions back to source code locations for error reporting and debugging.
|
||||
|
||||
### What bytecode does NOT contain
|
||||
|
||||
Importantly, **bytecode does not embed your source code**. Instead:
|
||||
|
||||
- The JavaScript source is stored separately (in the `.js` file)
|
||||
- The bytecode only stores a hash and length of the source
|
||||
- At load time, Bun validates the bytecode matches the current source code
|
||||
|
||||
This is why you need to deploy both the `.js` and `.jsc` files. The `.jsc` file is useless without its corresponding `.js` file.
|
||||
|
||||
## The tradeoff: file size
|
||||
|
||||
Bytecode files are significantly larger than source code - typically 2-8x larger.
|
||||
|
||||
### Why is bytecode so much larger?
|
||||
|
||||
**Bytecode instructions are verbose**:
|
||||
A single line of minified JavaScript might compile to dozens of bytecode instructions. For example:
|
||||
|
||||
```js
|
||||
const sum = arr.reduce((a, b) => a + b, 0);
|
||||
```
|
||||
|
||||
Compiles to bytecode that:
|
||||
|
||||
- Loads the `arr` variable
|
||||
- Gets the `reduce` property
|
||||
- Creates the arrow function (which itself has bytecode)
|
||||
- Loads the initial value `0`
|
||||
- Sets up the call with the right number of arguments
|
||||
- Actually performs the call
|
||||
- Stores the result in `sum`
|
||||
|
||||
Each of these steps is a separate bytecode instruction with its own metadata.
|
||||
|
||||
**Constant pools store everything**:
|
||||
Every string literal, number, property name - everything gets stored in the constant pool. Even if your source code has `"hello"` a hundred times, the constant pool stores it once, but the identifier table and constant references add overhead.
|
||||
|
||||
**Per-function metadata**:
|
||||
Each function - even small one-line functions - gets its own complete metadata:
|
||||
|
||||
- Register allocation info
|
||||
- Code features bitmask
|
||||
- Parse mode
|
||||
- Exception handlers
|
||||
- Expression info for debugging
|
||||
|
||||
A file with 1,000 small functions has 1,000 sets of metadata.
|
||||
|
||||
**Profiling data structures**:
|
||||
Even though profiling data isn't populated yet, the _structures_ to hold profiling data are allocated. This includes:
|
||||
|
||||
- Value profile slots (tracking what types flow through each operation)
|
||||
- Array profile slots (tracking array access patterns)
|
||||
- Binary arithmetic profile slots (tracking number types in math operations)
|
||||
- Unary arithmetic profile slots
|
||||
|
||||
These take up space even when empty.
|
||||
|
||||
**Pre-computed control flow**:
|
||||
Jump targets, switch tables, and exception handler boundaries are all pre-computed and stored. This makes execution faster but increases file size.
|
||||
|
||||
### Mitigation strategies
|
||||
|
||||
**Compression**:
|
||||
Bytecode compresses extremely well with gzip/brotli (60-70% compression). The repetitive structure and metadata compress efficiently.
|
||||
|
||||
**Minification first**:
|
||||
Using `--minify` before bytecode generation helps:
|
||||
|
||||
- Shorter identifiers → smaller identifier table
|
||||
- Dead code elimination → less bytecode generated
|
||||
- Constant folding → fewer constants in the pool
|
||||
|
||||
**The tradeoff**:
|
||||
You're trading 2-4x larger files for 2-4x faster startup. For CLIs, this is usually worth it. For long-running servers where a few megabytes of disk space don't matter, it's even less of an issue.
|
||||
|
||||
## Versioning and portability
|
||||
|
||||
### Cross-architecture portability: ✅
|
||||
|
||||
Bytecode is **architecture-independent**. You can:
|
||||
|
||||
- Build on macOS ARM64, deploy to Linux x64
|
||||
- Build on Linux x64, deploy to AWS Lambda ARM64
|
||||
- Build on Windows x64, deploy to macOS ARM64
|
||||
|
||||
The bytecode contains abstract instructions that work on any architecture. Architecture-specific optimizations happen during JIT compilation at runtime, not in the cached bytecode.
|
||||
|
||||
### Cross-version portability: ❌
|
||||
|
||||
Bytecode is **not stable across Bun versions**. Here's why:
|
||||
|
||||
**Bytecode format changes**:
|
||||
JavaScriptCore's bytecode format evolves. New opcodes get added, old ones get removed or changed, metadata structures change. Each version of JavaScriptCore has a different bytecode format.
|
||||
|
||||
**Version validation**:
|
||||
The cache version in the `.jsc` file header is a hash of the JavaScriptCore framework. When Bun loads bytecode:
|
||||
|
||||
1. It extracts the cache version from the `.jsc` file
|
||||
2. It computes the current JavaScriptCore version
|
||||
3. If they don't match, the bytecode is **silently rejected**
|
||||
4. Bun falls back to parsing the `.js` source code
|
||||
|
||||
Your application still runs - you just lose the performance optimization.
|
||||
|
||||
**Graceful degradation**:
|
||||
This design means bytecode caching "fails open" - if anything goes wrong (version mismatch, corrupted file, missing file), your code still runs normally. You might see slower startup, but you won't see errors.
|
||||
|
||||
## Unlinked vs. linked bytecode
|
||||
|
||||
JavaScriptCore makes a crucial distinction between "unlinked" and "linked" bytecode. This separation is what makes bytecode caching possible:
|
||||
|
||||
### Unlinked bytecode (what's cached)
|
||||
|
||||
The bytecode saved in `.jsc` files is **unlinked bytecode**. It contains:
|
||||
|
||||
- The compiled bytecode instructions
|
||||
- Structural information about the code
|
||||
- Constants and identifiers
|
||||
- Control flow information
|
||||
|
||||
But it **doesn't** contain:
|
||||
|
||||
- Pointers to actual runtime objects
|
||||
- JIT-compiled machine code
|
||||
- Profiling data from previous runs
|
||||
- Call link information (which functions call which)
|
||||
|
||||
Unlinked bytecode is **immutable and shareable**. Multiple executions of the same code can all reference the same unlinked bytecode.
|
||||
|
||||
### Linked bytecode (runtime execution)
|
||||
|
||||
When Bun runs bytecode, it "links" it - creating a runtime wrapper that adds:
|
||||
|
||||
- **Call link information**: As your code runs, the engine learns which functions call which and optimizes those call sites.
|
||||
- **Profiling data**: The engine tracks how many times each instruction executes, what types of values flow through the code, array access patterns, etc.
|
||||
- **JIT compilation state**: References to baseline JIT or optimizing JIT (DFG/FTL) compiled versions of hot code.
|
||||
- **Runtime objects**: Pointers to actual JavaScript objects, prototypes, scopes, etc.
|
||||
|
||||
This linked representation is created fresh every time you run your code. This allows:
|
||||
|
||||
1. **Caching the expensive work** (parsing and compilation to unlinked bytecode)
|
||||
2. **Still collecting runtime profiling data** to guide optimizations
|
||||
3. **Still applying JIT optimizations** based on actual execution patterns
|
||||
|
||||
Bytecode caching moves expensive work (parsing and compiling to bytecode) from runtime to build time. For applications that start frequently, this can halve your startup time at the cost of larger files on disk.
|
||||
|
||||
For production CLIs and serverless deployments, the combination of `--bytecode --minify --sourcemap` gives you the best performance while maintaining debuggability.
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: CSS
|
||||
description: Bun's bundler has built-in support for CSS with modern features
|
||||
---
|
||||
|
||||
Bun's bundler has built-in support for CSS with the following features:
|
||||
|
||||
- Transpiling modern/future features to work on all browsers (including vendor prefixing)
|
||||
@@ -9,11 +14,11 @@ Bun's bundler has built-in support for CSS with the following features:
|
||||
|
||||
Bun's CSS bundler lets you use modern/future CSS features without having to worry about browser compatibility — all thanks to its transpiling and vendor prefixing features which are enabled by default.
|
||||
|
||||
Bun's CSS parser and bundler is a direct Rust → Zig port of [LightningCSS](https://lightningcss.dev/), with a bundling approach inspired by esbuild. The transpiler converts modern CSS syntax into backwards-compatible equivalents that work across browsers.
|
||||
Bun's CSS parser and bundler is a direct Rust → Zig port of LightningCSS, with a bundling approach inspired by esbuild. The transpiler converts modern CSS syntax into backwards-compatible equivalents that work across browsers.
|
||||
|
||||
A huge thanks goes to the amazing work from the authors of [LightningCSS](https://lightningcss.dev/) and [esbuild](https://esbuild.github.io/).
|
||||
<Note>A huge thanks goes to the amazing work from the authors of LightningCSS and esbuild.</Note>
|
||||
|
||||
### Browser Compatibility
|
||||
## Browser Compatibility
|
||||
|
||||
By default, Bun's CSS bundler targets the following browsers:
|
||||
|
||||
@@ -23,13 +28,13 @@ By default, Bun's CSS bundler targets the following browsers:
|
||||
- Chrome 87+
|
||||
- Safari 14+
|
||||
|
||||
### Syntax Lowering
|
||||
## Syntax Lowering
|
||||
|
||||
#### Nesting
|
||||
### Nesting
|
||||
|
||||
The CSS Nesting specification allows you to write more concise and intuitive stylesheets by nesting selectors inside one another. Instead of repeating parent selectors across your CSS file, you can write child styles directly within their parent blocks.
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
/* With nesting */
|
||||
.card {
|
||||
background: white;
|
||||
@@ -48,7 +53,7 @@ The CSS Nesting specification allows you to write more concise and intuitive sty
|
||||
|
||||
Bun's CSS bundler automatically converts this nested syntax into traditional flat CSS that works in all browsers:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
/* Compiled output */
|
||||
.card {
|
||||
background: white;
|
||||
@@ -67,7 +72,7 @@ Bun's CSS bundler automatically converts this nested syntax into traditional fla
|
||||
|
||||
You can also nest media queries and other at-rules inside selectors, eliminating the need to repeat selector patterns:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.responsive-element {
|
||||
display: block;
|
||||
|
||||
@@ -79,7 +84,7 @@ You can also nest media queries and other at-rules inside selectors, eliminating
|
||||
|
||||
This compiles to:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.responsive-element {
|
||||
display: block;
|
||||
}
|
||||
@@ -91,11 +96,11 @@ This compiles to:
|
||||
}
|
||||
```
|
||||
|
||||
#### Color mix
|
||||
### Color mix
|
||||
|
||||
The `color-mix()` function gives you an easy way to blend two colors together according to a specified ratio in a chosen color space. This powerful feature lets you create color variations without manually calculating the resulting values.
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.button {
|
||||
/* Mix blue and red in the RGB color space with a 30/70 proportion */
|
||||
background-color: color-mix(in srgb, blue 30%, red);
|
||||
@@ -109,7 +114,7 @@ The `color-mix()` function gives you an easy way to blend two colors together ac
|
||||
|
||||
Bun's CSS bundler evaluates these color mixes at build time when all color values are known (not CSS variables), generating static color values that work in all browsers:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.button {
|
||||
/* Computed to the exact resulting color */
|
||||
background-color: #b31a1a;
|
||||
@@ -122,11 +127,11 @@ Bun's CSS bundler evaluates these color mixes at build time when all color value
|
||||
|
||||
This feature is particularly useful for creating color systems with programmatically derived shades, tints, and accents without needing preprocessors or custom tooling.
|
||||
|
||||
#### Relative colors
|
||||
### Relative colors
|
||||
|
||||
CSS now allows you to modify individual components of a color using relative color syntax. This powerful feature lets you create color variations by adjusting specific attributes like lightness, saturation, or individual channels without having to recalculate the entire color.
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.theme-color {
|
||||
/* Start with a base color and increase lightness by 15% */
|
||||
--accent: lch(from purple calc(l + 15%) c h);
|
||||
@@ -147,27 +152,23 @@ Bun's CSS bundler computes these relative color modifications at build time (whe
|
||||
|
||||
This approach is extremely useful for theme generation, creating accessible color variants, or building color scales based on mathematical relationships instead of hard-coding each value.
|
||||
|
||||
#### LAB colors
|
||||
### LAB colors
|
||||
|
||||
Modern CSS supports perceptually uniform color spaces like LAB, LCH, OKLAB, and OKLCH that offer significant advantages over traditional RGB. These color spaces can represent colors outside the standard RGB gamut, resulting in more vibrant and visually consistent designs.
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.vibrant-element {
|
||||
/* A vibrant red that exceeds sRGB gamut boundaries */
|
||||
color: lab(55% 78 35);
|
||||
|
||||
/* A smooth gradient using perceptual color space */
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
oklch(65% 0.25 10deg),
|
||||
oklch(65% 0.25 250deg)
|
||||
);
|
||||
background: linear-gradient(to right, oklch(65% 0.25 10deg), oklch(65% 0.25 250deg));
|
||||
}
|
||||
```
|
||||
|
||||
Bun's CSS bundler automatically converts these advanced color formats to backwards-compatible alternatives for browsers that don't yet support them:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.vibrant-element {
|
||||
/* Fallback to closest RGB approximation */
|
||||
color: #ff0f52;
|
||||
@@ -177,21 +178,17 @@ Bun's CSS bundler automatically converts these advanced color formats to backwar
|
||||
color: lab(55% 78 35);
|
||||
|
||||
background: linear-gradient(to right, #cd4e15, #3887ab);
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
oklch(65% 0.25 10deg),
|
||||
oklch(65% 0.25 250deg)
|
||||
);
|
||||
background: linear-gradient(to right, oklch(65% 0.25 10deg), oklch(65% 0.25 250deg));
|
||||
}
|
||||
```
|
||||
|
||||
This layered approach ensures optimal color rendering across all browsers while allowing you to use the latest color technologies in your designs.
|
||||
|
||||
#### Color function
|
||||
### Color function
|
||||
|
||||
The `color()` function provides a standardized way to specify colors in various predefined color spaces, expanding your design options beyond the traditional RGB space. This allows you to access wider color gamuts and create more vibrant designs.
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.vivid-element {
|
||||
/* Using the Display P3 color space for wider gamut colors */
|
||||
color: color(display-p3 1 0.1 0.3);
|
||||
@@ -203,7 +200,7 @@ The `color()` function provides a standardized way to specify colors in various
|
||||
|
||||
For browsers that don't support these advanced color functions yet, Bun's CSS bundler provides appropriate RGB fallbacks:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.vivid-element {
|
||||
/* RGB fallback first for maximum compatibility */
|
||||
color: #fa1a4c;
|
||||
@@ -217,11 +214,11 @@ For browsers that don't support these advanced color functions yet, Bun's CSS bu
|
||||
|
||||
This functionality lets you use modern color spaces immediately while ensuring your designs remain functional across all browsers, with optimal colors displayed in supporting browsers and reasonable approximations elsewhere.
|
||||
|
||||
#### HWB colors
|
||||
### HWB colors
|
||||
|
||||
The HWB (Hue, Whiteness, Blackness) color model provides an intuitive way to express colors based on how much white or black is mixed with a pure hue. Many designers find this approach more natural for creating color variations compared to manipulating RGB or HSL values.
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.easy-theming {
|
||||
/* Pure cyan with no white or black added */
|
||||
--primary: hwb(180 0% 0%);
|
||||
@@ -239,7 +236,7 @@ The HWB (Hue, Whiteness, Blackness) color model provides an intuitive way to exp
|
||||
|
||||
Bun's CSS bundler automatically converts HWB colors to RGB for compatibility with all browsers:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.easy-theming {
|
||||
--primary: #00ffff;
|
||||
--primary-light: #33ffff;
|
||||
@@ -250,11 +247,11 @@ Bun's CSS bundler automatically converts HWB colors to RGB for compatibility wit
|
||||
|
||||
The HWB model makes it particularly easy to create systematic color variations for design systems, providing a more intuitive approach to creating consistent tints and shades than working directly with RGB or HSL values.
|
||||
|
||||
#### Color notation
|
||||
### Color notation
|
||||
|
||||
Modern CSS has introduced more intuitive and concise ways to express colors. Space-separated color syntax eliminates the need for commas in RGB and HSL values, while hex colors with alpha channels provide a compact way to specify transparency.
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.modern-styling {
|
||||
/* Space-separated RGB notation (no commas) */
|
||||
color: rgb(50 100 200);
|
||||
@@ -272,7 +269,7 @@ Modern CSS has introduced more intuitive and concise ways to express colors. Spa
|
||||
|
||||
Bun's CSS bundler automatically converts these modern color formats to ensure compatibility with older browsers:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.modern-styling {
|
||||
/* Converted to comma format for older browsers */
|
||||
color: rgb(50, 100, 200);
|
||||
@@ -289,11 +286,11 @@ Bun's CSS bundler automatically converts these modern color formats to ensure co
|
||||
|
||||
This conversion process lets you write cleaner, more modern CSS while ensuring your styles work correctly across all browsers.
|
||||
|
||||
#### light-dark() color function
|
||||
### light-dark() color function
|
||||
|
||||
The `light-dark()` function provides an elegant solution for implementing color schemes that respect the user's system preference without requiring complex media queries. This function accepts two color values and automatically selects the appropriate one based on the current color scheme context.
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
:root {
|
||||
/* Define color scheme support */
|
||||
color-scheme: light dark;
|
||||
@@ -318,7 +315,7 @@ The `light-dark()` function provides an elegant solution for implementing color
|
||||
|
||||
For browsers that don't support this feature yet, Bun's CSS bundler converts it to use CSS variables with proper fallbacks:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
:root {
|
||||
--lightningcss-light: initial;
|
||||
--lightningcss-dark: ;
|
||||
@@ -345,21 +342,19 @@ For browsers that don't support this feature yet, Bun's CSS bundler converts it
|
||||
}
|
||||
|
||||
.themed-component {
|
||||
background-color: var(--lightningcss-light, #ffffff)
|
||||
var(--lightningcss-dark, #121212);
|
||||
background-color: var(--lightningcss-light, #ffffff) var(--lightningcss-dark, #121212);
|
||||
color: var(--lightningcss-light, #333333) var(--lightningcss-dark, #eeeeee);
|
||||
border-color: var(--lightningcss-light, #dddddd)
|
||||
var(--lightningcss-dark, #555555);
|
||||
border-color: var(--lightningcss-light, #dddddd) var(--lightningcss-dark, #555555);
|
||||
}
|
||||
```
|
||||
|
||||
This approach gives you a clean way to handle light and dark themes without duplicating styles or writing complex media queries, while maintaining compatibility with browsers that don't yet support the feature natively.
|
||||
|
||||
#### Logical properties
|
||||
### Logical properties
|
||||
|
||||
CSS logical properties let you define layout, spacing, and sizing relative to the document's writing mode and text direction rather than physical screen directions. This is crucial for creating truly international layouts that automatically adapt to different writing systems.
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.multilingual-component {
|
||||
/* Margin that adapts to writing direction */
|
||||
margin-inline-start: 1rem;
|
||||
@@ -378,7 +373,7 @@ CSS logical properties let you define layout, spacing, and sizing relative to th
|
||||
|
||||
For browsers that don't fully support logical properties, Bun's CSS bundler compiles them to physical properties with appropriate directional adjustments:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
/* For left-to-right languages */
|
||||
.multilingual-component:dir(ltr) {
|
||||
margin-left: 1rem;
|
||||
@@ -402,11 +397,11 @@ For browsers that don't fully support logical properties, Bun's CSS bundler comp
|
||||
|
||||
If the `:dir()` selector isn't supported, additional fallbacks are automatically generated to ensure your layouts work properly across all browsers and writing systems. This makes creating internationalized designs much simpler while maintaining compatibility with older browsers.
|
||||
|
||||
#### :dir() selector
|
||||
### :dir() selector
|
||||
|
||||
The `:dir()` pseudo-class selector allows you to style elements based on their text direction (RTL or LTR), providing a powerful way to create direction-aware designs without JavaScript. This selector matches elements based on their directionality as determined by the document or explicit direction attributes.
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
/* Apply different styles based on text direction */
|
||||
.nav-arrow:dir(ltr) {
|
||||
transform: rotate(0deg);
|
||||
@@ -428,7 +423,7 @@ The `:dir()` pseudo-class selector allows you to style elements based on their t
|
||||
|
||||
For browsers that don't support the `:dir()` selector yet, Bun's CSS bundler converts it to the more widely supported `:lang()` selector with appropriate language mappings:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
/* Converted to use language-based selectors as fallback */
|
||||
.nav-arrow:lang(en, fr, de, es, it, pt, nl) {
|
||||
transform: rotate(0deg);
|
||||
@@ -449,11 +444,11 @@ For browsers that don't support the `:dir()` selector yet, Bun's CSS bundler con
|
||||
|
||||
This conversion lets you write direction-aware CSS that works reliably across browsers, even those that don't yet support the `:dir()` selector natively. If multiple arguments to `:lang()` aren't supported, further fallbacks are automatically provided.
|
||||
|
||||
#### :lang() selector
|
||||
### :lang() selector
|
||||
|
||||
The `:lang()` pseudo-class selector allows you to target elements based on the language they're in, making it easy to apply language-specific styling. Modern CSS allows the `:lang()` selector to accept multiple language codes, letting you group language-specific rules more efficiently.
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
/* Typography adjustments for CJK languages */
|
||||
:lang(zh, ja, ko) {
|
||||
line-height: 1.8;
|
||||
@@ -472,7 +467,7 @@ blockquote:lang(de, nl, da, sv) {
|
||||
|
||||
For browsers that don't support multiple arguments in the `:lang()` selector, Bun's CSS bundler converts this syntax to use the `:is()` selector to maintain the same behavior:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
/* Multiple languages grouped with :is() for better browser support */
|
||||
:is(:lang(zh), :lang(ja), :lang(ko)) {
|
||||
line-height: 1.8;
|
||||
@@ -490,11 +485,11 @@ blockquote:is(:lang(de), :lang(nl), :lang(da), :lang(sv)) {
|
||||
|
||||
If needed, Bun can provide additional fallbacks for `:is()` as well, ensuring your language-specific styles work across all browsers. This approach simplifies creating internationalized designs with distinct typographic and styling rules for different language groups.
|
||||
|
||||
#### :is() selector
|
||||
### :is() selector
|
||||
|
||||
The `:is()` pseudo-class function (formerly `:matches()`) allows you to create more concise and readable selectors by grouping multiple selectors together. It accepts a selector list as its argument and matches if any of the selectors in that list match, significantly reducing repetition in your CSS.
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
/* Instead of writing these separately */
|
||||
/*
|
||||
.article h1,
|
||||
@@ -547,13 +542,17 @@ For browsers that don't support `:is()`, Bun's CSS bundler provides fallbacks us
|
||||
}
|
||||
```
|
||||
|
||||
It's worth noting that the vendor-prefixed versions have some limitations compared to the standardized `:is()` selector, particularly with complex selectors. Bun handles these limitations intelligently, only using prefixed versions when they'll work correctly.
|
||||
<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>
|
||||
|
||||
#### :not() selector
|
||||
### :not() selector
|
||||
|
||||
The `:not()` pseudo-class allows you to exclude elements that match a specific selector. The modern version of this selector accepts multiple arguments, letting you exclude multiple patterns with a single, concise selector.
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
/* Select all buttons except primary and secondary variants */
|
||||
button:not(.primary, .secondary) {
|
||||
background-color: #f5f5f5;
|
||||
@@ -568,7 +567,7 @@ h2:not(.sidebar *, footer *) {
|
||||
|
||||
For browsers that don't support multiple arguments in `:not()`, Bun's CSS bundler converts this syntax to a more compatible form while preserving the same behavior:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
/* Converted to use :not with :is() for compatibility */
|
||||
button:not(:is(.primary, .secondary)) {
|
||||
background-color: #f5f5f5;
|
||||
@@ -582,7 +581,7 @@ h2:not(:is(.sidebar *, footer *)) {
|
||||
|
||||
And if `:is()` isn't supported, Bun can generate further fallbacks:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
/* Even more fallbacks for maximum compatibility */
|
||||
button:not(:-webkit-any(.primary, .secondary)) {
|
||||
background-color: #f5f5f5;
|
||||
@@ -602,11 +601,11 @@ button:not(:is(.primary, .secondary)) {
|
||||
|
||||
This conversion ensures your negative selectors work correctly across all browsers while maintaining the correct specificity and behavior of the original selector.
|
||||
|
||||
#### Math functions
|
||||
### Math functions
|
||||
|
||||
CSS now includes a rich set of mathematical functions that let you perform complex calculations directly in your stylesheets. These include standard math functions (`round()`, `mod()`, `rem()`, `abs()`, `sign()`), trigonometric functions (`sin()`, `cos()`, `tan()`, `asin()`, `acos()`, `atan()`, `atan2()`), and exponential functions (`pow()`, `sqrt()`, `exp()`, `log()`, `hypot()`).
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.dynamic-sizing {
|
||||
/* Clamp a value between minimum and maximum */
|
||||
width: clamp(200px, 50%, 800px);
|
||||
@@ -625,7 +624,7 @@ CSS now includes a rich set of mathematical functions that let you perform compl
|
||||
|
||||
Bun's CSS bundler evaluates these mathematical expressions at build time when all values are known constants (not variables), resulting in optimized output:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.dynamic-sizing {
|
||||
width: clamp(200px, 50%, 800px);
|
||||
padding: 15px;
|
||||
@@ -637,11 +636,11 @@ Bun's CSS bundler evaluates these mathematical expressions at build time when al
|
||||
|
||||
This approach lets you write more expressive and maintainable CSS with meaningful mathematical relationships, which then gets compiled to optimized values for maximum browser compatibility and performance.
|
||||
|
||||
#### Media query ranges
|
||||
### Media query ranges
|
||||
|
||||
Modern CSS supports intuitive range syntax for media queries, allowing you to specify breakpoints using comparison operators like `<`, `>`, `<=`, and `>=` instead of the more verbose `min-` and `max-` prefixes. This syntax is more readable and matches how we normally think about values and ranges.
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
/* Modern syntax with comparison operators */
|
||||
@media (width >= 768px) {
|
||||
.container {
|
||||
@@ -666,7 +665,7 @@ Modern CSS supports intuitive range syntax for media queries, allowing you to sp
|
||||
|
||||
Bun's CSS bundler converts these modern range queries to traditional media query syntax for compatibility with all browsers:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
/* Converted to traditional min/max syntax */
|
||||
@media (min-width: 768px) {
|
||||
.container {
|
||||
@@ -689,11 +688,11 @@ Bun's CSS bundler converts these modern range queries to traditional media query
|
||||
|
||||
This lets you write more intuitive and mathematical media queries while ensuring your stylesheets work correctly across all browsers, including those that don't support the modern range syntax.
|
||||
|
||||
#### Shorthands
|
||||
### Shorthands
|
||||
|
||||
CSS has introduced several modern shorthand properties that improve code readability and maintainability. Bun's CSS bundler ensures these convenient shorthands work on all browsers by converting them to their longhand equivalents when needed.
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
/* Alignment shorthands */
|
||||
.flex-container {
|
||||
/* Shorthand for align-items and justify-items */
|
||||
@@ -729,7 +728,7 @@ CSS has introduced several modern shorthand properties that improve code readabi
|
||||
|
||||
For browsers that don't support these modern shorthands, Bun converts them to their component longhand properties:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.flex-container {
|
||||
/* Expanded alignment properties */
|
||||
align-items: center;
|
||||
@@ -766,11 +765,11 @@ For browsers that don't support these modern shorthands, Bun converts them to th
|
||||
|
||||
This conversion ensures your stylesheets remain clean and maintainable while providing the broadest possible browser compatibility.
|
||||
|
||||
#### Double position gradients
|
||||
### Double position gradients
|
||||
|
||||
The double position gradient syntax is a modern CSS feature that allows you to create hard color stops in gradients by specifying the same color at two adjacent positions. This creates a sharp transition rather than a smooth fade, which is useful for creating stripes, color bands, and other multi-color designs.
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.striped-background {
|
||||
/* Creates a sharp transition from green to red at 30%-40% */
|
||||
background: linear-gradient(
|
||||
@@ -799,7 +798,7 @@ The double position gradient syntax is a modern CSS feature that allows you to c
|
||||
|
||||
For browsers that don't support this syntax, Bun's CSS bundler automatically converts it to the traditional format by duplicating color stops:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.striped-background {
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
@@ -830,11 +829,11 @@ For browsers that don't support this syntax, Bun's CSS bundler automatically con
|
||||
|
||||
This conversion lets you use the cleaner double position syntax in your source code while ensuring gradients display correctly in all browsers.
|
||||
|
||||
#### system-ui font
|
||||
### system-ui font
|
||||
|
||||
The `system-ui` generic font family lets you use the device's native UI font, creating interfaces that feel more integrated with the operating system. This provides a more native look and feel without having to specify different font stacks for each platform.
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.native-interface {
|
||||
/* Use the system's default UI font */
|
||||
font-family: system-ui;
|
||||
@@ -848,7 +847,7 @@ The `system-ui` generic font family lets you use the device's native UI font, cr
|
||||
|
||||
For browsers that don't support `system-ui`, Bun's CSS bundler automatically expands it to a comprehensive cross-platform font stack:
|
||||
|
||||
```css
|
||||
```css title="styles.css" icon="file-code"
|
||||
.native-interface {
|
||||
/* Expanded to support all major platforms */
|
||||
font-family:
|
||||
@@ -883,7 +882,7 @@ This approach gives you the simplicity of writing just `system-ui` in your sourc
|
||||
|
||||
## CSS Modules
|
||||
|
||||
Bun's bundler also supports bundling [CSS modules](https://css-tricks.com/css-modules-part-1-need/) in addition to [regular CSS](/docs/bundler/css) with support for the following features:
|
||||
Bun's bundler also supports bundling CSS modules in addition to regular CSS with support for the following features:
|
||||
|
||||
- Automatically detecting CSS module files (`.module.css`) with zero configuration
|
||||
- Composition (`composes` property)
|
||||
@@ -894,17 +893,17 @@ A CSS module is a CSS file (with the `.module.css` extension) where are all clas
|
||||
|
||||
Under the hood, Bun's bundler transforms locally scoped class names into unique identifiers.
|
||||
|
||||
## Getting started
|
||||
### Getting started
|
||||
|
||||
Create a CSS file with the `.module.css` extension:
|
||||
|
||||
```css
|
||||
/* styles.module.css */
|
||||
```css title="styles.module.css" icon="file-code"
|
||||
.button {
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
|
||||
/* other-styles.module.css */
|
||||
```css title="other-styles.module.css" icon="file-code"
|
||||
.button {
|
||||
color: blue;
|
||||
}
|
||||
@@ -912,7 +911,7 @@ Create a CSS file with the `.module.css` extension:
|
||||
|
||||
You can then import this file, for example into a TSX file:
|
||||
|
||||
```tsx
|
||||
```tsx title="app.tsx" icon="/icons/typescript.svg"
|
||||
import styles from "./styles.module.css";
|
||||
import otherStyles from "./other-styles.module.css";
|
||||
|
||||
@@ -926,10 +925,9 @@ export default function App() {
|
||||
}
|
||||
```
|
||||
|
||||
The `styles` object from importing the CSS module file will be an object with all class names as keys and
|
||||
their unique identifiers as values:
|
||||
The styles object from importing the CSS module file will be an object with all class names as keys and their unique identifiers as values:
|
||||
|
||||
```tsx
|
||||
```ts title="app.tsx" icon="/icons/typescript.svg"
|
||||
import styles from "./styles.module.css";
|
||||
import otherStyles from "./other-styles.module.css";
|
||||
|
||||
@@ -939,7 +937,7 @@ console.log(otherStyles);
|
||||
|
||||
This will output:
|
||||
|
||||
```ts
|
||||
```ts title="app.tsx" icon="/icons/typescript.svg"
|
||||
{
|
||||
button: "button_123";
|
||||
}
|
||||
@@ -953,12 +951,11 @@ As you can see, the class names are unique to each file, avoiding any collisions
|
||||
|
||||
### Composition
|
||||
|
||||
CSS modules allow you to _compose_ class selectors together. This lets you reuse style rules across multiple classes.
|
||||
CSS modules allow you to compose class selectors together. This lets you reuse style rules across multiple classes.
|
||||
|
||||
For example:
|
||||
|
||||
```css
|
||||
/* styles.module.css */
|
||||
```css title="styles.module.css" icon="file-code"
|
||||
.button {
|
||||
composes: background;
|
||||
color: red;
|
||||
@@ -971,7 +968,7 @@ For example:
|
||||
|
||||
Would be the same as writing:
|
||||
|
||||
```css
|
||||
```css title="styles.module.css" icon="file-code"
|
||||
.button {
|
||||
background-color: blue;
|
||||
color: red;
|
||||
@@ -982,13 +979,14 @@ Would be the same as writing:
|
||||
}
|
||||
```
|
||||
|
||||
{% callout %}
|
||||
There are a couple rules to keep in mind when using `composes`:
|
||||
|
||||
- A `composes` property must come before any regular CSS properties or declarations
|
||||
- You can only use `composes` on a **simple selector with a single class name**:
|
||||
<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>
|
||||
|
||||
```css
|
||||
```css title="styles.module.css" icon="file-code"
|
||||
#button {
|
||||
/* Invalid! `#button` is not a class selector */
|
||||
composes: background;
|
||||
@@ -1001,28 +999,26 @@ There are a couple rules to keep in mind when using `composes`:
|
||||
}
|
||||
```
|
||||
|
||||
{% /callout %}
|
||||
|
||||
### Composing from a separate CSS module file
|
||||
|
||||
You can also compose from a separate CSS module file:
|
||||
|
||||
```css
|
||||
/* background.module.css */
|
||||
```css title="background.module.css" icon="file-code"
|
||||
.background {
|
||||
background-color: blue;
|
||||
}
|
||||
```
|
||||
|
||||
/* styles.module.css */
|
||||
```css title="styles.module.css" icon="file-code"
|
||||
.button {
|
||||
composes: background from "./background.module.css";
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
|
||||
{% callout %}
|
||||
<Warning>
|
||||
When composing classes from separate files, be sure that they do not contain the same properties.
|
||||
|
||||
The CSS module spec says that composing classes from separate files with conflicting properties is
|
||||
undefined behavior, meaning that the output may differ and be unreliable.
|
||||
{% /callout %}
|
||||
The CSS module spec says that composing classes from separate files with conflicting properties is undefined behavior, meaning that the output may differ and be unreliable.
|
||||
|
||||
</Warning>
|
||||
@@ -1,145 +0,0 @@
|
||||
# CSS Modules
|
||||
|
||||
Bun's bundler also supports bundling [CSS modules](https://css-tricks.com/css-modules-part-1-need/) in addition to [regular CSS](/docs/bundler/css) with support for the following features:
|
||||
|
||||
- Automatically detecting CSS module files (`.module.css`) with zero configuration
|
||||
- Composition (`composes` property)
|
||||
- Importing CSS modules into JSX/TSX
|
||||
- Warnings/errors for invalid usages of CSS modules
|
||||
|
||||
A CSS module is a CSS file (with the `.module.css` extension) where are all class names and animations are scoped to the file. This helps you avoid class name collisions as CSS declarations are globally scoped by default.
|
||||
|
||||
Under the hood, Bun's bundler transforms locally scoped class names into unique identifiers.
|
||||
|
||||
## Getting started
|
||||
|
||||
Create a CSS file with the `.module.css` extension:
|
||||
|
||||
```css
|
||||
/* styles.module.css */
|
||||
.button {
|
||||
color: red;
|
||||
}
|
||||
|
||||
/* other-styles.module.css */
|
||||
.button {
|
||||
color: blue;
|
||||
}
|
||||
```
|
||||
|
||||
You can then import this file, for example into a TSX file:
|
||||
|
||||
```tsx
|
||||
import styles from "./styles.module.css";
|
||||
import otherStyles from "./other-styles.module.css";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<>
|
||||
<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 %}
|
||||
253
docs/bundler/esbuild.mdx
Normal file
253
docs/bundler/esbuild.mdx
Normal file
@@ -0,0 +1,253 @@
|
||||
---
|
||||
title: esbuild
|
||||
description: Migration guide from esbuild to Bun's bundler
|
||||
---
|
||||
|
||||
Bun's bundler API is inspired heavily by esbuild. Migrating to Bun's bundler from esbuild should be relatively painless. This guide will briefly explain why you might consider migrating to Bun's bundler and provide a side-by-side API comparison reference for those who are already familiar with esbuild's API.
|
||||
|
||||
There are a few behavioral differences to note.
|
||||
|
||||
<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,33 +1,43 @@
|
||||
---
|
||||
title: "Single-file executable"
|
||||
description: "Generate standalone executables from TypeScript or JavaScript files with Bun"
|
||||
---
|
||||
|
||||
Bun's bundler implements a `--compile` flag for generating a standalone binary from a TypeScript or JavaScript file.
|
||||
|
||||
{% codetabs %}
|
||||
<CodeGroup>
|
||||
|
||||
```bash
|
||||
$ bun build ./cli.ts --compile --outfile mycli
|
||||
```bash terminal icon="terminal"
|
||||
bun build ./cli.ts --compile --outfile mycli
|
||||
```
|
||||
|
||||
```ts#cli.ts
|
||||
```ts cli.ts icon="/icons/typescript.svg"
|
||||
console.log("Hello world!");
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
</CodeGroup>
|
||||
|
||||
This bundles `cli.ts` into an executable that can be executed directly:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
./mycli
|
||||
```
|
||||
$ ./mycli
|
||||
|
||||
```txt
|
||||
Hello world!
|
||||
```
|
||||
|
||||
All imported files and packages are bundled into the executable, along with a copy of the Bun runtime. All built-in Bun and Node.js APIs are supported.
|
||||
|
||||
---
|
||||
|
||||
## Cross-compile to other platforms
|
||||
|
||||
The `--target` flag lets you compile your standalone executable for a different operating system, architecture, or version of Bun than the machine you're running `bun build` on.
|
||||
|
||||
To build for Linux x64 (most servers):
|
||||
|
||||
```sh
|
||||
```bash icon="terminal" terminal
|
||||
bun build --compile --target=bun-linux-x64 ./index.ts --outfile myapp
|
||||
|
||||
# To support CPUs from before 2013, use the baseline version (nehalem)
|
||||
@@ -40,14 +50,14 @@ bun build --compile --target=bun-linux-x64-modern ./index.ts --outfile myapp
|
||||
|
||||
To build for Linux ARM64 (e.g. Graviton or Raspberry Pi):
|
||||
|
||||
```sh
|
||||
```bash icon="terminal" terminal
|
||||
# Note: the default architecture is x64 if no architecture is specified.
|
||||
bun build --compile --target=bun-linux-arm64 ./index.ts --outfile myapp
|
||||
```
|
||||
|
||||
To build for Windows x64:
|
||||
|
||||
```sh
|
||||
```bash icon="terminal" terminal
|
||||
bun build --compile --target=bun-windows-x64 ./path/to/my/app.ts --outfile myapp
|
||||
|
||||
# To support CPUs from before 2013, use the baseline version (nehalem)
|
||||
@@ -61,17 +71,17 @@ bun build --compile --target=bun-windows-x64-modern ./path/to/my/app.ts --outfil
|
||||
|
||||
To build for macOS arm64:
|
||||
|
||||
```sh
|
||||
```bash icon="terminal" terminal
|
||||
bun build --compile --target=bun-darwin-arm64 ./path/to/my/app.ts --outfile myapp
|
||||
```
|
||||
|
||||
To build for macOS x64:
|
||||
|
||||
```sh
|
||||
```bash icon="terminal" terminal
|
||||
bun build --compile --target=bun-darwin-x64 ./path/to/my/app.ts --outfile myapp
|
||||
```
|
||||
|
||||
#### Supported targets
|
||||
### Supported targets
|
||||
|
||||
The order of the `--target` flag does not matter, as long as they're delimited by a `-`.
|
||||
|
||||
@@ -86,21 +96,32 @@ The order of the `--target` flag does not matter, as long as they're delimited b
|
||||
| bun-linux-x64-musl | Linux | x64 | ✅ | ✅ | musl |
|
||||
| bun-linux-arm64-musl | Linux | arm64 | ✅ | N/A | musl |
|
||||
|
||||
On x64 platforms, Bun uses SIMD optimizations which require a modern CPU supporting AVX2 instructions. The `-baseline` build of Bun is for older CPUs that don't support these optimizations. Normally, when you install Bun we automatically detect which version to use but this can be harder to do when cross-compiling since you might not know the target CPU. You usually don't need to worry about it on Darwin x64, but it is relevant for Windows x64 and Linux x64. If you or your users see `"Illegal instruction"` errors, you might need to use the baseline version.
|
||||
<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>
|
||||
|
||||
---
|
||||
|
||||
## Build-time constants
|
||||
|
||||
Use the `--define` flag to inject build-time constants into your executable, such as version numbers, build timestamps, or configuration values:
|
||||
|
||||
```bash
|
||||
$ bun build --compile --define BUILD_VERSION='"1.2.3"' --define BUILD_TIME='"2024-01-15T10:30:00Z"' src/cli.ts --outfile mycli
|
||||
```bash icon="terminal" terminal
|
||||
bun build --compile --define BUILD_VERSION='"1.2.3"' --define BUILD_TIME='"2024-01-15T10:30:00Z"' src/cli.ts --outfile mycli
|
||||
```
|
||||
|
||||
These constants are embedded directly into your compiled binary at build time, providing zero runtime overhead and enabling dead code elimination optimizations.
|
||||
|
||||
{% callout type="info" %}
|
||||
For comprehensive examples and advanced patterns, see the [Build-time constants guide](/guides/runtime/build-time-constants).
|
||||
{% /callout %}
|
||||
<Note>
|
||||
For comprehensive examples and advanced patterns, see the [Build-time constants
|
||||
guide](https://bun.com/guides/runtime/build-time-constants).
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Deploying to production
|
||||
|
||||
@@ -112,7 +133,7 @@ With compiled executables, you can move that cost from runtime to build-time.
|
||||
|
||||
When deploying to production, we recommend the following:
|
||||
|
||||
```sh
|
||||
```bash icon="terminal" terminal
|
||||
bun build --compile --minify --sourcemap ./path/to/my/app.ts --outfile myapp
|
||||
```
|
||||
|
||||
@@ -120,17 +141,22 @@ bun build --compile --minify --sourcemap ./path/to/my/app.ts --outfile myapp
|
||||
|
||||
To improve startup time, enable bytecode compilation:
|
||||
|
||||
```sh
|
||||
```bash icon="terminal" terminal
|
||||
bun build --compile --minify --sourcemap --bytecode ./path/to/my/app.ts --outfile myapp
|
||||
```
|
||||
|
||||
Using bytecode compilation, `tsc` starts 2x faster:
|
||||
|
||||
{% image src="https://github.com/user-attachments/assets/dc8913db-01d2-48f8-a8ef-ac4e984f9763" width="689" /%}
|
||||
<Frame>
|
||||

|
||||
</Frame>
|
||||
|
||||
Bytecode compilation moves parsing overhead for large input files from runtime to bundle time. Your app starts faster, in exchange for making the `bun build` command a little slower. It doesn't obscure source code.
|
||||
|
||||
**Experimental:** Bytecode compilation is an experimental feature introduced in Bun v1.1.30. Only `cjs` format is supported (which means no top-level-await). Let us know if you run into any issues!
|
||||
<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!
|
||||
</Warning>
|
||||
|
||||
### What do these flags do?
|
||||
|
||||
@@ -140,68 +166,77 @@ The `--sourcemap` argument embeds a sourcemap compressed with zstd, so that erro
|
||||
|
||||
The `--bytecode` argument enables bytecode compilation. Every time you run JavaScript code in Bun, JavaScriptCore (the engine) will compile your source code into bytecode. We can move this parsing work from runtime to bundle time, saving you startup time.
|
||||
|
||||
---
|
||||
|
||||
## Embedding runtime arguments
|
||||
|
||||
**`--compile-exec-argv="args"`** - Embed runtime arguments that are available via `process.execArgv`:
|
||||
|
||||
```bash
|
||||
```bash icon="terminal" terminal
|
||||
bun build --compile --compile-exec-argv="--smol --user-agent=MyBot" ./app.ts --outfile myapp
|
||||
```
|
||||
|
||||
```js
|
||||
```ts app.ts icon="/icons/typescript.svg"
|
||||
// In the compiled app
|
||||
console.log(process.execArgv); // ["--smol", "--user-agent=MyBot"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Act as the Bun CLI
|
||||
|
||||
{% note %}
|
||||
|
||||
New in Bun v1.2.16
|
||||
|
||||
{% /note %}
|
||||
<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:
|
||||
|
||||
```sh
|
||||
$ cat such-bun.js
|
||||
console.log("you shouldn't see this");
|
||||
```bash icon="terminal" terminal
|
||||
echo "console.log(\"you shouldn't see this\");" > such-bun.js
|
||||
bun build --compile ./such-bun.js
|
||||
```
|
||||
|
||||
$ bun build --compile ./such-bun.js
|
||||
[3ms] bundle 1 modules
|
||||
```txt
|
||||
[3ms] bundle 1 modules
|
||||
[89ms] compile such-bun
|
||||
```
|
||||
|
||||
Normally, running `./such-bun` with arguments would execute the script. However, with the `BUN_BE_BUN=1` environment variable, it acts just like the `bun` binary:
|
||||
Normally, running `./such-bun` with arguments would execute the script.
|
||||
|
||||
```sh
|
||||
```bash icon="terminal" terminal
|
||||
# Executable runs its own entrypoint by default
|
||||
$ ./such-bun install
|
||||
you shouldn't see this
|
||||
./such-bun install
|
||||
```
|
||||
|
||||
```txt
|
||||
you shouldn't see this
|
||||
```
|
||||
|
||||
However, with the `BUN_BE_BUN=1` environment variable, it acts just like the `bun` binary:
|
||||
|
||||
```bash icon="terminal" terminal
|
||||
# With the env var, the executable acts like the `bun` CLI
|
||||
$ BUN_BE_BUN=1 ./such-bun install
|
||||
bun_BE_BUN=1 ./such-bun install
|
||||
```
|
||||
|
||||
```txt
|
||||
bun install v1.2.16-canary.1 (1d1db811)
|
||||
Checked 63 installs across 64 packages (no changes) [5.00ms]
|
||||
```
|
||||
|
||||
This is useful for building CLI tools on top of Bun that may need to install packages, bundle dependencies, run different or local files and more without needing to download a separate binary or install bun.
|
||||
|
||||
---
|
||||
|
||||
## Full-stack executables
|
||||
|
||||
{% note %}
|
||||
|
||||
New in Bun v1.2.17
|
||||
|
||||
{% /note %}
|
||||
<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.
|
||||
|
||||
{% codetabs %}
|
||||
<CodeGroup>
|
||||
|
||||
```ts#server.ts
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
import { serve } from "bun";
|
||||
import index from "./index.html";
|
||||
|
||||
@@ -215,12 +250,12 @@ const server = serve({
|
||||
console.log(`Server running at http://localhost:${server.port}`);
|
||||
```
|
||||
|
||||
```html#index.html
|
||||
```html index.html icon="file-code"
|
||||
<!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>
|
||||
@@ -229,21 +264,21 @@ console.log(`Server running at http://localhost:${server.port}`);
|
||||
</html>
|
||||
```
|
||||
|
||||
```js#app.js
|
||||
```ts app.js icon="file-code"
|
||||
console.log("Hello from the client!");
|
||||
```
|
||||
|
||||
```css#styles.css
|
||||
```css styles.css icon="file-code"
|
||||
body {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
</CodeGroup>
|
||||
|
||||
To build this into a single executable:
|
||||
|
||||
```sh
|
||||
```bash terminal icon="terminal"
|
||||
bun build --compile ./server.ts --outfile myapp
|
||||
```
|
||||
|
||||
@@ -256,25 +291,27 @@ This creates a self-contained binary that includes:
|
||||
|
||||
The result is a single file that can be deployed anywhere without needing Node.js, Bun, or any dependencies installed. Just run:
|
||||
|
||||
```sh
|
||||
```bash terminal icon="terminal"
|
||||
./myapp
|
||||
```
|
||||
|
||||
Bun automatically handles serving the frontend assets with proper MIME types and cache headers. The HTML import is replaced with a manifest object that `Bun.serve` uses to efficiently serve pre-bundled assets.
|
||||
|
||||
For more details on building full-stack applications with Bun, see the [full-stack guide](/docs/bundler/fullstack).
|
||||
For more details on building full-stack applications with Bun, see the [full-stack guide](/bundler/fullstack).
|
||||
|
||||
---
|
||||
|
||||
## Worker
|
||||
|
||||
To use workers in a standalone executable, add the worker's entrypoint to the CLI arguments:
|
||||
|
||||
```sh
|
||||
$ bun build --compile ./index.ts ./my-worker.ts --outfile myapp
|
||||
```bash terminal icon="terminal"
|
||||
bun build --compile ./index.ts ./my-worker.ts --outfile myapp
|
||||
```
|
||||
|
||||
Then, reference the worker in your code:
|
||||
|
||||
```ts
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
console.log("Hello from Bun!");
|
||||
|
||||
// Any of these will work:
|
||||
@@ -289,13 +326,15 @@ In the future, we may automatically detect usages of statically-known paths in `
|
||||
|
||||
If you use a relative path to a file not included in the standalone executable, it will attempt to load that path from disk relative to the current working directory of the process (and then error if it doesn't exist).
|
||||
|
||||
---
|
||||
|
||||
## SQLite
|
||||
|
||||
You can use `bun:sqlite` imports with `bun build --compile`.
|
||||
|
||||
By default, the database is resolved relative to the current working directory of the process.
|
||||
|
||||
```js
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
import db from "./my.db" with { type: "sqlite" };
|
||||
|
||||
console.log(db.query("select * from users LIMIT 1").get());
|
||||
@@ -303,10 +342,12 @@ console.log(db.query("select * from users LIMIT 1").get());
|
||||
|
||||
That means if the executable is located at `/usr/bin/hello`, the user's terminal is located at `/home/me/Desktop`, it will look for `/home/me/Desktop/my.db`.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
cd /home/me/Desktop
|
||||
./hello
|
||||
```
|
||||
$ cd /home/me/Desktop
|
||||
$ ./hello
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Embed assets & files
|
||||
|
||||
@@ -314,7 +355,7 @@ Standalone executables support embedding files.
|
||||
|
||||
To embed files into an executable with `bun build --compile`, import the file in your code.
|
||||
|
||||
```ts
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
// this becomes an internal file path
|
||||
import icon from "./icon.png" with { type: "file" };
|
||||
import { file } from "bun";
|
||||
@@ -331,7 +372,7 @@ Embedded files can be read using `Bun.file`'s functions or the Node.js `fs.readF
|
||||
|
||||
For example, to read the contents of the embedded file:
|
||||
|
||||
```js
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
import icon from "./icon.png" with { type: "file" };
|
||||
import { file } from "bun";
|
||||
|
||||
@@ -344,7 +385,7 @@ const bytes = await file(icon).arrayBuffer();
|
||||
|
||||
If your application wants to embed a SQLite database, set `type: "sqlite"` in the import attribute and the `embed` attribute to `"true"`.
|
||||
|
||||
```js
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
import myEmbeddedDb from "./my.db" with { type: "sqlite", embed: "true" };
|
||||
|
||||
console.log(myEmbeddedDb.query("select * from users LIMIT 1").get());
|
||||
@@ -356,7 +397,7 @@ This database is read-write, but all changes are lost when the executable exits
|
||||
|
||||
As of Bun v1.0.23, you can embed `.node` files into executables.
|
||||
|
||||
```js
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
const addon = require("./addon.node");
|
||||
|
||||
console.log(addon.hello());
|
||||
@@ -368,13 +409,13 @@ Unfortunately, if you're using `@mapbox/node-pre-gyp` or other similar tools, yo
|
||||
|
||||
To embed a directory with `bun build --compile`, use a shell glob in your `bun build` command:
|
||||
|
||||
```sh
|
||||
$ bun build --compile ./index.ts ./public/**/*.png
|
||||
```bash terminal icon="terminal"
|
||||
bun build --compile ./index.ts ./public/**/*.png
|
||||
```
|
||||
|
||||
Then, you can reference the files in your code:
|
||||
|
||||
```ts
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
import icon from "./public/assets/icon.png" with { type: "file" };
|
||||
import { file } from "bun";
|
||||
|
||||
@@ -392,7 +433,7 @@ This is honestly a workaround, and we expect to improve this in the future with
|
||||
|
||||
To get a list of all embedded files, use `Bun.embeddedFiles`:
|
||||
|
||||
```js
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
import "./icon.png" with { type: "file" };
|
||||
import { embeddedFiles } from "bun";
|
||||
|
||||
@@ -413,141 +454,40 @@ By default, embedded files have a content hash appended to their name. This is u
|
||||
|
||||
To disable the content hash, pass `--asset-naming` to `bun build --compile` like this:
|
||||
|
||||
```sh
|
||||
$ bun build --compile --asset-naming="[name].[ext]" ./index.ts
|
||||
```bash terminal icon="terminal"
|
||||
bun build --compile --asset-naming="[name].[ext]" ./index.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Minification
|
||||
|
||||
To trim down the size of the executable a little, pass `--minify` to `bun build --compile`. This uses Bun's minifier to reduce the code size. Overall though, Bun's binary is still way too big and we need to make it smaller.
|
||||
|
||||
## Using Bun.build() API
|
||||
|
||||
You can also generate standalone executables using the `Bun.build()` JavaScript API. This is useful when you need programmatic control over the build process.
|
||||
|
||||
### Basic usage
|
||||
|
||||
```js
|
||||
await Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
outdir: "./dist",
|
||||
compile: {
|
||||
target: "bun-windows-x64",
|
||||
outfile: "myapp.exe",
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Windows metadata with Bun.build()
|
||||
|
||||
When targeting Windows, you can specify metadata through the `windows` object:
|
||||
|
||||
```js
|
||||
await Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
outdir: "./dist",
|
||||
compile: {
|
||||
target: "bun-windows-x64",
|
||||
outfile: "myapp.exe",
|
||||
windows: {
|
||||
title: "My Application",
|
||||
publisher: "My Company Inc",
|
||||
version: "1.2.3.4",
|
||||
description: "A powerful application built with Bun",
|
||||
copyright: "© 2024 My Company Inc",
|
||||
hideConsole: false, // Set to true for GUI applications
|
||||
icon: "./icon.ico", // Path to icon file
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Cross-compilation with Bun.build()
|
||||
|
||||
You can cross-compile for different platforms:
|
||||
|
||||
```js
|
||||
// Build for multiple platforms
|
||||
const platforms = [
|
||||
{ target: "bun-windows-x64", outfile: "app-windows.exe" },
|
||||
{ target: "bun-linux-x64", outfile: "app-linux" },
|
||||
{ target: "bun-darwin-arm64", outfile: "app-macos" },
|
||||
];
|
||||
|
||||
for (const platform of platforms) {
|
||||
await Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
outdir: "./dist",
|
||||
compile: platform,
|
||||
});
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
## Windows-specific flags
|
||||
|
||||
When compiling a standalone executable for Windows, there are several platform-specific options that can be used to customize the generated `.exe` file:
|
||||
When compiling a standalone executable on Windows, there are two platform-specific options that can be used to customize metadata on the generated `.exe` file:
|
||||
|
||||
### Visual customization
|
||||
- `--windows-icon=path/to/icon.ico` to customize the executable file icon.
|
||||
- `--windows-hide-console` to disable the background terminal, which can be used for applications that do not need a TTY.
|
||||
|
||||
- `--windows-icon=path/to/icon.ico` - Set the executable file icon
|
||||
- `--windows-hide-console` - Disable the background terminal window (useful for GUI applications)
|
||||
<Warning>These flags currently cannot be used when cross-compiling because they depend on Windows APIs.</Warning>
|
||||
|
||||
### 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.
|
||||
|
||||
```sh
|
||||
$ codesign --deep --force -vvvv --sign "XXXXXXXXXX" ./myapp
|
||||
```bash terminal icon="terminal"
|
||||
codesign --deep --force -vvvv --sign "XXXXXXXXXX" ./myapp
|
||||
```
|
||||
|
||||
We recommend including an `entitlements.plist` file with JIT permissions.
|
||||
|
||||
```xml#entitlements.plist
|
||||
```xml icon="xml" title="info.plist"
|
||||
<?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">
|
||||
@@ -568,23 +508,21 @@ We recommend including an `entitlements.plist` file with JIT permissions.
|
||||
|
||||
To codesign with JIT support, pass the `--entitlements` flag to `codesign`.
|
||||
|
||||
```sh
|
||||
$ codesign --deep --force -vvvv --sign "XXXXXXXXXX" --entitlements entitlements.plist ./myapp
|
||||
```bash terminal icon="terminal"
|
||||
codesign --deep --force -vvvv --sign "XXXXXXXXXX" --entitlements entitlements.plist ./myapp
|
||||
```
|
||||
|
||||
After codesigning, verify the executable:
|
||||
|
||||
```sh
|
||||
$ codesign -vvv --verify ./myapp
|
||||
```bash terminal icon="terminal"
|
||||
codesign -vvv --verify ./myapp
|
||||
./myapp: valid on disk
|
||||
./myapp: satisfies its Designated Requirement
|
||||
```
|
||||
|
||||
{% callout %}
|
||||
<Warning>Codesign support requires Bun v1.2.4 or newer.</Warning>
|
||||
|
||||
Codesign support requires Bun v1.2.4 or newer.
|
||||
|
||||
{% /callout %}
|
||||
---
|
||||
|
||||
## Unsupported CLI arguments
|
||||
|
||||
@@ -1,418 +0,0 @@
|
||||
To get started, import HTML files and pass them to the `routes` option in `Bun.serve()`.
|
||||
|
||||
```ts
|
||||
import { sql, serve } from "bun";
|
||||
import dashboard from "./dashboard.html";
|
||||
import homepage from "./index.html";
|
||||
|
||||
const server = serve({
|
||||
routes: {
|
||||
// ** HTML imports **
|
||||
// Bundle & route index.html to "/". This uses HTMLRewriter to scan the HTML for `<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.
|
||||
1064
docs/bundler/fullstack.mdx
Normal file
1064
docs/bundler/fullstack.mdx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,32 +1,35 @@
|
||||
Hot Module Replacement (HMR) allows you to update modules in a running
|
||||
application without needing a full page reload. This preserves the application
|
||||
state and improves the development experience.
|
||||
---
|
||||
title: Hot reloading
|
||||
description: Hot Module Replacement (HMR) for Bun's development server
|
||||
---
|
||||
|
||||
HMR is enabled by default when using Bun's full-stack development server.
|
||||
Hot Module Replacement (HMR) allows you to update modules in a running application without needing a full page reload. This preserves the application state and improves the development experience.
|
||||
|
||||
<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://vitejs.dev/guide/api-hmr.html). It can be checked for with `if (import.meta.hot)`, tree-shaking it in production
|
||||
Bun implements a client-side HMR API modeled after [Vite's `import.meta.hot` API](https://vitejs.dev/guide/api-hmr.html). It can be checked for with `if (import.meta.hot)`, tree-shaking it in production.
|
||||
|
||||
```ts
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
if (import.meta.hot) {
|
||||
// HMR APIs are available.
|
||||
}
|
||||
```
|
||||
|
||||
However, **this check is often not needed** as Bun will dead-code-eliminate
|
||||
calls to all of the HMR APIs in production builds.
|
||||
However, this check is often not needed as Bun will dead-code-eliminate calls to all of the HMR APIs in production builds.
|
||||
|
||||
```ts
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
// This entire function call will be removed in production!
|
||||
import.meta.hot.dispose(() => {
|
||||
console.log("dispose");
|
||||
});
|
||||
```
|
||||
|
||||
<Warning>
|
||||
For this to work, Bun forces these APIs to be called without indirection. That means the following do not work:
|
||||
|
||||
```ts#invalid-hmr-usage.ts
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
// INVALID: Assigning `hot` to a variable
|
||||
const hot = import.meta.hot;
|
||||
hot.accept();
|
||||
@@ -46,32 +49,32 @@ import.meta.hot.accept();
|
||||
doSomething(import.meta.hot.data);
|
||||
```
|
||||
|
||||
{% callout %}
|
||||
</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>
|
||||
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>
|
||||
|
||||
{% endcallout %}
|
||||
## API Methods
|
||||
|
||||
| | Method | Notes |
|
||||
| --- | ------------------ | --------------------------------------------------------------------- |
|
||||
| ✅ | `hot.accept()` | Indicate that a hot update can be replaced gracefully. |
|
||||
| ✅ | `hot.data` | Persist data between module evaluations. |
|
||||
| ✅ | `hot.dispose()` | Add a callback function to run when a module is about to be replaced. |
|
||||
| ❌ | `hot.invalidate()` | |
|
||||
| ✅ | `hot.on()` | Attach an event listener |
|
||||
| ✅ | `hot.off()` | Remove an event listener from `on`. |
|
||||
| ❌ | `hot.send()` | |
|
||||
| 🚧 | `hot.prune()` | **NOTE**: Callback is currently never called. |
|
||||
| ✅ | `hot.decline()` | No-op to match Vite's `import.meta.hot` |
|
||||
| Method | Status | Notes |
|
||||
| ------------------ | ------ | --------------------------------------------------------------------- |
|
||||
| `hot.accept()` | ✅ | Indicate that a hot update can be replaced gracefully. |
|
||||
| `hot.data` | ✅ | Persist data between module evaluations. |
|
||||
| `hot.dispose()` | ✅ | Add a callback function to run when a module is about to be replaced. |
|
||||
| `hot.invalidate()` | ❌ | |
|
||||
| `hot.on()` | ✅ | Attach an event listener |
|
||||
| `hot.off()` | ✅ | Remove an event listener from `on`. |
|
||||
| `hot.send()` | ❌ | |
|
||||
| `hot.prune()` | 🚧 | NOTE: Callback is currently never called. |
|
||||
| `hot.decline()` | ✅ | No-op to match Vite's `import.meta.hot` |
|
||||
|
||||
### `import.meta.hot.accept()`
|
||||
## import.meta.hot.accept()
|
||||
|
||||
The `accept()` method indicates that a module can be hot-replaced. When called
|
||||
without arguments, it indicates that this module can be replaced simply by
|
||||
re-evaluating the file. After a hot update, importers of this module will be
|
||||
automatically patched.
|
||||
The `accept()` method indicates that a module can be hot-replaced. When called without arguments, it indicates that this module can be replaced simply by re-evaluating the file. After a hot update, importers of this module will be automatically patched.
|
||||
|
||||
```ts#index.ts
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
// index.ts
|
||||
import { getCount } from "./foo.ts";
|
||||
|
||||
console.log("count is ", getCount());
|
||||
@@ -83,28 +86,21 @@ export function getNegativeCount() {
|
||||
}
|
||||
```
|
||||
|
||||
This creates a hot-reloading boundary for all of the files that `index.ts`
|
||||
imports. That means whenever `foo.ts` or any of its dependencies are saved, the
|
||||
update will bubble up to `index.ts` will re-evaluate. Files that import
|
||||
`index.ts` will then be patched to import the new version of
|
||||
`getNegativeCount()`. If only `index.ts` is updated, only the one file will be
|
||||
re-evaluated, and the counter in `foo.ts` is reused.
|
||||
This creates a hot-reloading boundary for all of the files that `index.ts` imports. That means whenever `foo.ts` or any of its dependencies are saved, the update will bubble up to `index.ts` will re-evaluate. Files that import `index.ts` will then be patched to import the new version of `getNegativeCount()`. If only `index.ts` is updated, only the one file will be re-evaluated, and the counter in `foo.ts` is reused.
|
||||
|
||||
This may be used in combination with `import.meta.hot.data` to transfer state
|
||||
from the previous module to the new one.
|
||||
This may be used in combination with `import.meta.hot.data` to transfer state from the previous module to the new one.
|
||||
|
||||
When no modules call `import.meta.hot.accept()` (and there isn't React Fast
|
||||
Refresh or a plugin calling it for you), the page will reload when the file
|
||||
updates, and a console warning shows which files were invalidated. This warning
|
||||
is safe to ignore if it makes more sense to rely on full page reloads.
|
||||
<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
|
||||
### With callback
|
||||
|
||||
When provided one callback, `import.meta.hot.accept` will function how it does
|
||||
in Vite. Instead of patching the importers of this module, it will call the
|
||||
callback with the new module.
|
||||
When provided one callback, `import.meta.hot.accept` will function how it does in Vite. Instead of patching the importers of this module, it will call the callback with the new module.
|
||||
|
||||
```ts
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
export const count = 0;
|
||||
|
||||
import.meta.hot.accept(newModule => {
|
||||
@@ -115,11 +111,13 @@ import.meta.hot.accept(newModule => {
|
||||
});
|
||||
```
|
||||
|
||||
Prefer using `import.meta.hot.accept()` without an argument as it usually makes your code easier to understand.
|
||||
<Tip>
|
||||
Prefer using `import.meta.hot.accept()` without an argument as it usually makes your code easier to understand.
|
||||
</Tip>
|
||||
|
||||
#### Accepting other modules
|
||||
### Accepting other modules
|
||||
|
||||
```ts
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import { count } from "./foo";
|
||||
|
||||
import.meta.hot.accept("./foo", () => {
|
||||
@@ -131,9 +129,9 @@ import.meta.hot.accept("./foo", () => {
|
||||
|
||||
Indicates that a dependency's module can be accepted. When the dependency is updated, the callback will be called with the new module.
|
||||
|
||||
#### With multiple dependencies
|
||||
### With multiple dependencies
|
||||
|
||||
```ts
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import.meta.hot.accept(["./foo", "./bar"], newModules => {
|
||||
// newModules is an array where each item corresponds to the updated module
|
||||
// or undefined if that module had a syntax error
|
||||
@@ -142,33 +140,33 @@ import.meta.hot.accept(["./foo", "./bar"], newModules => {
|
||||
|
||||
Indicates that multiple dependencies' modules can be accepted. This variant accepts an array of dependencies, where the callback will receive the updated modules, and `undefined` for any that had errors.
|
||||
|
||||
### `import.meta.hot.data`
|
||||
## import.meta.hot.data
|
||||
|
||||
`import.meta.hot.data` maintains state between module instances during hot
|
||||
replacement, enabling data transfer from previous to new versions. When
|
||||
`import.meta.hot.data` is written into, Bun will also mark this module as
|
||||
capable of self-accepting (equivalent of calling `import.meta.hot.accept()`).
|
||||
`import.meta.hot.data` maintains state between module instances during hot replacement, enabling data transfer from previous to new versions. When `import.meta.hot.data` is written into, Bun will also mark this module as capable of self-accepting (equivalent of calling `import.meta.hot.accept()`).
|
||||
|
||||
```ts
|
||||
```jsx title="index.ts" icon="/icons/typescript.svg"
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { App } from "./app";
|
||||
|
||||
const root = import.meta.hot.data.root ??= createRoot(elem);
|
||||
const root = (import.meta.hot.data.root ??= createRoot(elem));
|
||||
root.render(<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.
|
||||
<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()`
|
||||
## import.meta.hot.dispose()
|
||||
|
||||
Attaches an on-dispose callback. This is called:
|
||||
|
||||
- Just before the module is replaced with another copy (before the next is loaded)
|
||||
- After the module is detached (removing all imports to this module, see `import.meta.hot.prune()`)
|
||||
|
||||
```ts
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
const sideEffect = setupSideEffect();
|
||||
|
||||
import.meta.hot.dispose(() => {
|
||||
@@ -176,21 +174,17 @@ import.meta.hot.dispose(() => {
|
||||
});
|
||||
```
|
||||
|
||||
This callback is not called on route navigation or when the browser tab closes.
|
||||
<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.
|
||||
Returning a promise will delay module replacement until the module is disposed. All dispose callbacks are called in parallel.
|
||||
|
||||
### `import.meta.hot.prune()`
|
||||
## import.meta.hot.prune()
|
||||
|
||||
Attaches an on-prune callback. This is called when all imports to this module
|
||||
are removed, but the module was previously loaded.
|
||||
Attaches an on-prune callback. This is called when all imports to this module are removed, but the module was previously loaded.
|
||||
|
||||
This can be used to clean up resources that were created when the module was
|
||||
loaded. Unlike `import.meta.hot.dispose()`, this pairs much better with `accept`
|
||||
and `data` to manage stateful resources. A full example managing a `WebSocket`:
|
||||
This can be used to clean up resources that were created when the module was loaded. Unlike `import.meta.hot.dispose()`, this pairs much better with `accept` and `data` to manage stateful resources. A full example managing a WebSocket:
|
||||
|
||||
```ts
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import { something } from "./something";
|
||||
|
||||
// Initialize or re-use a WebSocket connection
|
||||
@@ -202,15 +196,16 @@ import.meta.hot.prune(() => {
|
||||
});
|
||||
```
|
||||
|
||||
If `dispose` was used instead, the WebSocket would close and re-open on every
|
||||
hot update. Both versions of the code will prevent page reloads when imported
|
||||
files are updated.
|
||||
<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()`
|
||||
## import.meta.hot.on() and off()
|
||||
|
||||
`on()` and `off()` are used to listen for events from the HMR runtime. Event names are prefixed with a prefix so that plugins do not conflict with each other.
|
||||
|
||||
```ts
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import.meta.hot.on("bun:beforeUpdate", () => {
|
||||
console.log("before a hot update");
|
||||
});
|
||||
@@ -218,7 +213,7 @@ import.meta.hot.on("bun:beforeUpdate", () => {
|
||||
|
||||
When a file is replaced, all of its event listeners are automatically removed.
|
||||
|
||||
A list of all built-in events:
|
||||
### Built-in events
|
||||
|
||||
| Event | Emitted when |
|
||||
| ---------------------- | ----------------------------------------------------------------------------------------------- |
|
||||
@@ -231,4 +226,4 @@ A list of all built-in events:
|
||||
| `bun:ws:disconnect` | when the HMR WebSocket connection is lost. This can indicate the development server is offline. |
|
||||
| `bun:ws:connect` | when the HMR WebSocket connects or re-connects. |
|
||||
|
||||
For compatibility with Vite, the above events are also available via `vite:*` prefix instead of `bun:*`.
|
||||
<Note>For compatibility with Vite, the above events are also available via `vite:*` prefix instead of `bun:*`.</Note>
|
||||
@@ -1,6 +1,11 @@
|
||||
---
|
||||
title: HTML & static sites
|
||||
description: Build static sites, landing pages, and web applications with Bun's bundler
|
||||
---
|
||||
|
||||
Bun's bundler has first-class support for HTML. Build static sites, landing pages, and web applications with zero configuration. Just point Bun at your HTML file and it handles everything else.
|
||||
|
||||
```html#index.html
|
||||
```html title="index.html" icon="file-code"
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
@@ -15,7 +20,16 @@ Bun's bundler has first-class support for HTML. Build static sites, landing page
|
||||
|
||||
To get started, pass HTML files to `bun`.
|
||||
|
||||
{% bunDevServerTerminal alt="bun ./index.html" path="./index.html" routes="" /%}
|
||||
```bash terminal icon="terminal"
|
||||
bun ./index.html
|
||||
```
|
||||
|
||||
```
|
||||
Bun v1.3.2
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Press h + Enter to show shortcuts
|
||||
```
|
||||
|
||||
Bun's development server provides powerful features with zero configuration:
|
||||
|
||||
@@ -26,19 +40,26 @@ Bun's development server provides powerful features with zero configuration:
|
||||
- **Plugins** - Plugins for TailwindCSS and more
|
||||
- **ESM & CommonJS** - Use ESM and CommonJS in your JavaScript, TypeScript, and JSX files
|
||||
- **CSS Bundling & Minification** - Bundles CSS from `<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:
|
||||
|
||||
{% bunDevServerTerminal alt="bun index.html" path="index.html" routes="" /%}
|
||||
```bash terminal icon="terminal"
|
||||
bun index.html
|
||||
```
|
||||
|
||||
```
|
||||
Bun v1.3.2
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Press h + Enter to show shortcuts
|
||||
```
|
||||
|
||||
Your React or other SPA will work out of the box — no configuration needed. All routes like `/about`, `/users/123`, etc. will serve the same HTML file, letting your client-side router handle the navigation.
|
||||
|
||||
```html#index.html
|
||||
```html title="index.html" icon="file-code"
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
@@ -53,9 +74,21 @@ Your React or other SPA will work out of the box — no configuration needed. Al
|
||||
|
||||
## Multi-page apps (MPA)
|
||||
|
||||
Some projects have several separate routes or HTML files as entry points. To support multiple entry points, pass them all to `bun`
|
||||
Some projects have several separate routes or HTML files as entry points. To support multiple entry points, pass them all to `bun`:
|
||||
|
||||
{% bunDevServerTerminal alt="bun ./index.html ./about.html" path="./index.html ./about.html" routes="[{\"path\": \"/\", \"file\": \"./index.html\"}, {\"path\": \"/about\", \"file\": \"./about.html\"}]" /%}
|
||||
```bash terminal icon="terminal"
|
||||
bun ./index.html ./about.html
|
||||
```
|
||||
|
||||
```txt
|
||||
Bun v1.3.2
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Routes:
|
||||
/ ./index.html
|
||||
/about ./about.html
|
||||
Press h + Enter to show shortcuts
|
||||
```
|
||||
|
||||
This will serve:
|
||||
|
||||
@@ -66,19 +99,44 @@ This will serve:
|
||||
|
||||
To specify multiple files, you can use glob patterns that end in `.html`:
|
||||
|
||||
{% bunDevServerTerminal alt="bun ./**/*.html" path="./**/*.html" routes="[{\"path\": \"/\", \"file\": \"./index.html\"}, {\"path\": \"/about\", \"file\": \"./about.html\"}]" /%}
|
||||
```bash terminal icon="terminal"
|
||||
bun ./**/*.html
|
||||
```
|
||||
|
||||
```
|
||||
Bun v1.3.2
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Routes:
|
||||
/ ./index.html
|
||||
/about ./about.html
|
||||
Press h + Enter to show shortcuts
|
||||
```
|
||||
|
||||
### Path normalization
|
||||
|
||||
The base path is chosen from the longest common prefix among all the files.
|
||||
|
||||
{% bunDevServerTerminal alt="bun ./index.html ./about/index.html ./about/foo/index.html" path="./index.html ./about/index.html ./about/foo/index.html" routes="[{\"path\": \"/\", \"file\": \"./index.html\"}, {\"path\": \"/about\", \"file\": \"./about/index.html\"}, {\"path\": \"/about/foo\", \"file\": \"./about/foo/index.html\"}]" /%}
|
||||
```bash terminal icon="terminal"
|
||||
bun ./index.html ./about/index.html ./about/foo/index.html
|
||||
```
|
||||
|
||||
```
|
||||
Bun v1.3.2
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Routes:
|
||||
/ ./index.html
|
||||
/about ./about/index.html
|
||||
/about/foo ./about/foo/index.html
|
||||
Press h + Enter to show shortcuts
|
||||
```
|
||||
|
||||
## JavaScript, TypeScript, and JSX
|
||||
|
||||
Bun's transpiler natively implements JavaScript, TypeScript, and JSX support. [Learn more about loaders in Bun](/docs/bundler/loaders).
|
||||
Bun's transpiler natively implements JavaScript, TypeScript, and JSX support. Learn more about loaders in Bun.
|
||||
|
||||
Bun's transpiler is also used at runtime.
|
||||
<Note>Bun's transpiler is also used at runtime.</Note>
|
||||
|
||||
### ES Modules & CommonJS
|
||||
|
||||
@@ -86,7 +144,7 @@ You can use ESM and CJS in your JavaScript, TypeScript, and JSX files. Bun will
|
||||
|
||||
There is no pre-build or separate optimization step. It's all done at the same time.
|
||||
|
||||
Learn more about [module resolution in Bun](/docs/runtime/modules).
|
||||
Learn more about module resolution in Bun.
|
||||
|
||||
## CSS
|
||||
|
||||
@@ -96,7 +154,9 @@ It's also a CSS bundler. You can use `@import` in your CSS files to import other
|
||||
|
||||
For example:
|
||||
|
||||
```css#styles.css
|
||||
<CodeGroup>
|
||||
|
||||
```css styles.css icon="file-code"
|
||||
@import "./abc.css";
|
||||
|
||||
.container {
|
||||
@@ -104,15 +164,17 @@ For example:
|
||||
}
|
||||
```
|
||||
|
||||
```css#abc.css
|
||||
```css abc.css
|
||||
body {
|
||||
background-color: red;
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
This outputs:
|
||||
|
||||
```css#styles.css
|
||||
```css
|
||||
body {
|
||||
background-color: red;
|
||||
}
|
||||
@@ -126,7 +188,7 @@ body {
|
||||
|
||||
You can reference local assets in your CSS files.
|
||||
|
||||
```css#styles.css
|
||||
```css styles.css icon="file-code"
|
||||
body {
|
||||
background-image: url("./logo.png");
|
||||
}
|
||||
@@ -134,7 +196,7 @@ body {
|
||||
|
||||
This will copy `./logo.png` to the output directory and rewrite the path in the CSS file to include a content hash.
|
||||
|
||||
```css#styles.css
|
||||
```css styles.css icon="file-code"
|
||||
body {
|
||||
background-image: url("./logo-[ABC123].png");
|
||||
}
|
||||
@@ -144,7 +206,7 @@ body {
|
||||
|
||||
To associate a CSS file with a JavaScript file, you can import it in your JavaScript file.
|
||||
|
||||
```ts#app.ts
|
||||
```ts app.ts icon="/icons/typescript.svg"
|
||||
import "./styles.css";
|
||||
import "./more-styles.css";
|
||||
```
|
||||
@@ -159,84 +221,57 @@ The dev server supports plugins.
|
||||
|
||||
To use TailwindCSS, install the `bun-plugin-tailwind` plugin:
|
||||
|
||||
```bash
|
||||
```bash terminal icon="terminal"
|
||||
# Or any npm client
|
||||
$ bun install --dev bun-plugin-tailwind
|
||||
bun install --dev bun-plugin-tailwind
|
||||
```
|
||||
|
||||
Then, add the plugin to your `bunfig.toml`:
|
||||
|
||||
```toml
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[serve.static]
|
||||
plugins = ["bun-plugin-tailwind"]
|
||||
```
|
||||
|
||||
Then, reference TailwindCSS in your HTML via `<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.
|
||||
|
||||
{% codetabs %}
|
||||
<Tabs>
|
||||
<Tab title="index.html">
|
||||
```html title="index.html" icon="file-code"
|
||||
{/* Reference TailwindCSS in your HTML */}
|
||||
<link rel="stylesheet" href="tailwindcss" />
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="styles.css">```css title="styles.css" icon="file-code" @import "tailwindcss"; ```</Tab>
|
||||
<Tab title="app.ts">```ts title="app.ts" icon="/icons/typescript.svg" import "tailwindcss"; ```</Tab>
|
||||
</Tabs>
|
||||
|
||||
```html#index.html
|
||||
<!-- Reference TailwindCSS in your HTML -->
|
||||
<link rel="stylesheet" href="tailwindcss" />
|
||||
```
|
||||
<Info>Only one of those are necessary, not all three.</Info>
|
||||
|
||||
```css#styles.css
|
||||
/* Import TailwindCSS in your CSS */
|
||||
@import "tailwindcss";
|
||||
```
|
||||
|
||||
```ts#app.ts
|
||||
/* Import TailwindCSS in your JavaScript */
|
||||
import "tailwindcss";
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
|
||||
Only one of those are necessary, not all three.
|
||||
|
||||
### Echo console logs from browser to terminal
|
||||
## Echo console logs from browser to terminal
|
||||
|
||||
Bun's dev server supports streaming console logs from the browser to the terminal.
|
||||
|
||||
To enable, pass the `--console` CLI flag.
|
||||
|
||||
{% bunDevServerTerminal alt="bun ./index.html --console" path="./index.html --console" routes="" /%}
|
||||
```bash terminal icon="terminal"
|
||||
bun ./index.html --console
|
||||
```
|
||||
|
||||
```
|
||||
Bun v1.3.2
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Press h + Enter to show shortcuts
|
||||
```
|
||||
|
||||
Each call to `console.log` or `console.error` will be broadcast to the terminal that started the server. This is useful to see errors from the browser in the same place you run your server. This is also useful for AI agents that watch terminal output.
|
||||
|
||||
Internally, this reuses the existing WebSocket connection from hot module reloading to send the logs.
|
||||
|
||||
### Edit files in the browser
|
||||
## Edit files in the browser
|
||||
|
||||
Bun's frontend dev server has support for [Automatic Workspace Folders](https://chromium.googlesource.com/devtools/devtools-frontend/+/main/docs/ecosystem/automatic_workspace_folders.md) in Chrome DevTools, which lets you save edits to files in the browser.
|
||||
|
||||
{% image src="/images/bun-chromedevtools.gif" alt="Bun's frontend dev server has support for Automatic Workspace Folders in Chrome DevTools, which lets you save edits to files in the browser." /%}
|
||||
|
||||
{% details summary="How it works" %}
|
||||
|
||||
Bun's dev server automatically adds a `/.well-known/appspecific/com.chrome.devtools.json` route to the server.
|
||||
|
||||
This route returns a JSON object with the following shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"workspace": {
|
||||
"root": "/path/to/your/project",
|
||||
"uuid": "a-unique-identifier-for-this-workspace"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For security reasons, this is only enabled when:
|
||||
|
||||
1. The request is coming from localhost, 127.0.0.1, or ::1.
|
||||
2. Hot Module Reloading is enabled.
|
||||
3. The `chromeDevToolsAutomaticWorkspaceFolders` flag is set to `true` or `undefined`.
|
||||
4. There are no other routes that match the request.
|
||||
|
||||
You can disable this by passing `development: { chromeDevToolsAutomaticWorkspaceFolders: false }` in `Bun.serve`'s options.
|
||||
|
||||
{% /details %}
|
||||
Bun's frontend dev server has support for Automatic Workspace Folders in Chrome DevTools, which lets you save edits to files in the browser.
|
||||
|
||||
## Keyboard Shortcuts
|
||||
|
||||
@@ -244,45 +279,45 @@ While the server is running:
|
||||
|
||||
- `o + Enter` - Open in browser
|
||||
- `c + Enter` - Clear console
|
||||
- `q + Enter` (or Ctrl+C) - Quit server
|
||||
- `q + Enter` (or `Ctrl+C`) - Quit server
|
||||
|
||||
## Build for Production
|
||||
|
||||
When you're ready to deploy, use `bun build` to create optimized production bundles:
|
||||
|
||||
{% codetabs %}
|
||||
<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>
|
||||
|
||||
```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.
|
||||
<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>
|
||||
|
||||
### Watch Mode
|
||||
|
||||
You can run `bun build --watch` to watch for changes and rebuild automatically. This works nicely for library development.
|
||||
|
||||
You've never seen a watch mode this fast.
|
||||
<Info>You've never seen a watch mode this fast.</Info>
|
||||
|
||||
### Plugin API
|
||||
## Plugin API
|
||||
|
||||
Need more control? Configure the bundler through the JavaScript API and use Bun's builtin `HTMLRewriter` to preprocess HTML.
|
||||
|
||||
```ts
|
||||
```ts title="build.ts" icon="/icons/typescript.svg"
|
||||
await Bun.build({
|
||||
entrypoints: ["./index.html"],
|
||||
outdir: "./dist",
|
||||
@@ -322,28 +357,30 @@ await Bun.build({
|
||||
|
||||
Bun automatically handles all common web assets:
|
||||
|
||||
- Scripts (`<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.
|
||||
|
||||
## This is a work in progress
|
||||
|
||||
<Warning>
|
||||
**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.
|
||||
|
||||
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.
|
||||
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>
|
||||
|
||||
## 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](/docs/bundler/fullstack).
|
||||
Learn more in the full-stack docs.
|
||||
File diff suppressed because it is too large
Load Diff
1499
docs/bundler/index.mdx
Normal file
1499
docs/bundler/index.mdx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,75 +0,0 @@
|
||||
<!-- 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
|
||||
356
docs/bundler/loaders.mdx
Normal file
356
docs/bundler/loaders.mdx
Normal file
@@ -0,0 +1,356 @@
|
||||
---
|
||||
title: Loaders
|
||||
description: Built-in loaders for the Bun bundler and runtime
|
||||
---
|
||||
|
||||
The Bun bundler implements a set of default loaders out of the box.
|
||||
|
||||
> As a rule of thumb: **the bundler and the runtime both support the same set of file types out of the box.**
|
||||
|
||||
`.js` `.cjs` `.mjs` `.mts` `.cts` `.ts` `.tsx` `.jsx` `.toml` `.json` `.txt` `.wasm` `.node` `.html`
|
||||
|
||||
Bun uses the file extension to determine which built-in loader should be used to parse the file. Every loader has a name, such as `js`, `tsx`, or `json`. These names are used when building plugins that extend Bun with custom loaders.
|
||||
|
||||
You can explicitly specify which loader to use using the `'loader'` import attribute.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import my_toml from "./my_file" with { loader: "toml" };
|
||||
```
|
||||
|
||||
## Built-in loaders
|
||||
|
||||
### `js`
|
||||
|
||||
**JavaScript loader.** Default for `.cjs` and `.mjs`.
|
||||
|
||||
Parses the code and applies a set of default transforms like dead-code elimination and tree shaking. Note that Bun does not attempt to down-convert syntax at the moment.
|
||||
|
||||
---
|
||||
|
||||
### `jsx`
|
||||
|
||||
**JavaScript + JSX loader.** Default for `.js` and `.jsx`.
|
||||
|
||||
Same as the `js` loader, but JSX syntax is supported. By default, JSX is down-converted to plain JavaScript; the details of how this is done depends on the `jsx*` compiler options in your `tsconfig.json`. Refer to the [TypeScript documentation on JSX](https://www.typescriptlang.org/tsconfig#jsx) for more information.
|
||||
|
||||
---
|
||||
|
||||
### `ts`
|
||||
|
||||
**TypeScript loader.** Default for `.ts`, `.mts`, and `.cts`.
|
||||
|
||||
Strips out all TypeScript syntax, then behaves identically to the `js` loader. Bun does not perform typechecking.
|
||||
|
||||
---
|
||||
|
||||
### `tsx`
|
||||
|
||||
**TypeScript + JSX loader.** Default for `.tsx`.
|
||||
|
||||
Transpiles both TypeScript and JSX to vanilla JavaScript.
|
||||
|
||||
---
|
||||
|
||||
### `json`
|
||||
|
||||
**JSON loader.** Default for `.json`.
|
||||
|
||||
JSON files can be directly imported.
|
||||
|
||||
```js
|
||||
import pkg from "./package.json";
|
||||
pkg.name; // => "my-package"
|
||||
```
|
||||
|
||||
During bundling, the parsed JSON is inlined into the bundle as a JavaScript object.
|
||||
|
||||
```js
|
||||
const pkg = {
|
||||
name: "my-package",
|
||||
// ... other fields
|
||||
};
|
||||
|
||||
pkg.name;
|
||||
```
|
||||
|
||||
If a `.json` file is passed as an entrypoint to the bundler, it will be converted to a `.js` module that `export default`s the parsed object.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```json Input
|
||||
{
|
||||
"name": "John Doe",
|
||||
"age": 35,
|
||||
"email": "johndoe@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
```ts Output
|
||||
export default {
|
||||
name: "John Doe",
|
||||
age: 35,
|
||||
email: "johndoe@example.com",
|
||||
};
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
---
|
||||
|
||||
### 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"
|
||||
```
|
||||
|
||||
```ts 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!
|
||||
```
|
||||
|
||||
```ts 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
|
||||
|
||||
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"
|
||||
<!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"
|
||||
<!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>
|
||||
|
||||
---
|
||||
|
||||
### 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,10 +1,13 @@
|
||||
Macros are a mechanism for running JavaScript functions _at bundle-time_. The value returned from these functions are directly inlined into your bundle.
|
||||
---
|
||||
title: Macros
|
||||
description: Run JavaScript functions at bundle-time with Bun macros
|
||||
---
|
||||
|
||||
<!-- 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. -->
|
||||
Macros are a mechanism for running JavaScript functions at bundle-time. The value returned from these functions are directly inlined into your bundle.
|
||||
|
||||
As a toy example, consider this simple function that returns a random number.
|
||||
|
||||
```ts
|
||||
```ts title="random.ts" icon="/icons/typescript.svg"
|
||||
export function random() {
|
||||
return Math.random();
|
||||
}
|
||||
@@ -12,24 +15,28 @@ export function random() {
|
||||
|
||||
This is just a regular function in a regular file, but we can use it as a macro like so:
|
||||
|
||||
```ts#cli.tsx
|
||||
import { random } from './random.ts' with { type: 'macro' };
|
||||
```tsx title="cli.tsx" icon="/icons/typescript.svg"
|
||||
import { random } from "./random.ts" with { type: "macro" };
|
||||
|
||||
console.log(`Your random number is ${random()}`);
|
||||
```
|
||||
|
||||
{% callout %}
|
||||
**Note** — Macros are indicated using [_import attribute_](https://github.com/tc39/proposal-import-attributes) syntax. If you haven't seen this syntax before, it's a Stage 3 TC39 proposal that lets you attach additional metadata to `import` statements.
|
||||
{% /callout %}
|
||||
<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>
|
||||
|
||||
Now we'll bundle this file with `bun build`. The bundled file will be printed to stdout.
|
||||
|
||||
```bash
|
||||
$ bun build ./cli.tsx
|
||||
```bash terminal icon="terminal"
|
||||
bun build ./cli.tsx
|
||||
```
|
||||
|
||||
```js
|
||||
console.log(`Your random number is ${0.6805550949689833}`);
|
||||
```
|
||||
|
||||
As you can see, the source code of the `random` function occurs nowhere in the bundle. Instead, it is executed _during bundling_ and function call (`random()`) is replaced with the result of the function. Since the source code will never be included in the bundle, macros can safely perform privileged operations like reading from a database.
|
||||
As you can see, the source code of the `random` function occurs nowhere in the bundle. Instead, it is executed during bundling and function call (`random()`) is replaced with the result of the function. Since the source code will never be included in the bundle, macros can safely perform privileged operations like reading from a database.
|
||||
|
||||
## When to use macros
|
||||
|
||||
@@ -41,8 +48,8 @@ If you find yourself running a lot of code at bundle-time though, consider runni
|
||||
|
||||
Bun Macros are import statements annotated using either:
|
||||
|
||||
- `with { type: 'macro' }` — an [import attribute](https://github.com/tc39/proposal-import-attributes), a Stage 3 ECMA Scrd
|
||||
- `assert { type: 'macro' }` — an import assertion, an earlier incarnation of import attributes that has now been abandoned (but is [already supported](https://caniuse.com/mdn-javascript_statements_import_import_assertions) by a number of browsers and runtimes)
|
||||
- `with { type: 'macro' }` — an import attribute, a Stage 3 ECMA Script proposal
|
||||
- `assert { type: 'macro' }` — an import assertion, an earlier incarnation of import attributes that has now been abandoned (but is already supported by a number of browsers and runtimes)
|
||||
|
||||
## Security considerations
|
||||
|
||||
@@ -50,7 +57,7 @@ Macros must explicitly be imported with `{ type: "macro" }` in order to be execu
|
||||
|
||||
You can disable macros entirely by passing the `--no-macros` flag to Bun. It produces a build error like this:
|
||||
|
||||
```js
|
||||
```
|
||||
error: Macros are disabled
|
||||
|
||||
foo();
|
||||
@@ -58,9 +65,9 @@ foo();
|
||||
./hello.js:3:1 53
|
||||
```
|
||||
|
||||
To reduce the potential attack surface for malicious packages, macros cannot be _invoked_ from inside `node_modules/**/*`. If a package attempts to invoke a macro, you'll see an error like this:
|
||||
To reduce the potential attack surface for malicious packages, macros cannot be invoked from inside `node_modules/**/*`. If a package attempts to invoke a macro, you'll see an error like this:
|
||||
|
||||
```js
|
||||
```
|
||||
error: For security reasons, macros cannot be run from node_modules.
|
||||
|
||||
beEvil();
|
||||
@@ -70,17 +77,17 @@ node_modules/evil/index.js:3:1 50
|
||||
|
||||
Your application code can still import macros from `node_modules` and invoke them.
|
||||
|
||||
```ts
|
||||
```ts title="cli.tsx" icon="/icons/typescript.svg"
|
||||
import { macro } from "some-package" with { type: "macro" };
|
||||
|
||||
macro();
|
||||
```
|
||||
|
||||
## Export condition `"macro"`
|
||||
## Export condition "macro"
|
||||
|
||||
When shipping a library containing a macro to `npm` or another package registry, use the `"macro"` [export condition](https://nodejs.org/api/packages.html#conditional-exports) to provide a special version of your package exclusively for the macro environment.
|
||||
When shipping a library containing a macro to npm or another package registry, use the `"macro"` export condition to provide a special version of your package exclusively for the macro environment.
|
||||
|
||||
```jsonc#package.json
|
||||
```json title="package.json" icon="file-code"
|
||||
{
|
||||
"name": "my-package",
|
||||
"exports": {
|
||||
@@ -94,7 +101,7 @@ When shipping a library containing a macro to `npm` or another package registry,
|
||||
|
||||
With this configuration, users can consume your package at runtime or at bundle-time using the same import specifier:
|
||||
|
||||
```ts
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import pkg from "my-package"; // runtime import
|
||||
import { macro } from "my-package" with { type: "macro" }; // macro import
|
||||
```
|
||||
@@ -105,15 +112,15 @@ The first import will resolve to `./node_modules/my-package/index.js`, while the
|
||||
|
||||
When Bun's transpiler sees a macro import, it calls the function inside the transpiler using Bun's JavaScript runtime and converts the return value from JavaScript into an AST node. These JavaScript functions are called at bundle-time, not runtime.
|
||||
|
||||
Macros are executed synchronously in the transpiler during the visiting phase—before plugins and before the transpiler generates the AST. They are executed in the order they are imported. The transpiler will wait for the macro to finish executing before continuing. The transpiler will also `await` any `Promise` returned by a macro.
|
||||
Macros are executed synchronously in the transpiler during the visiting phase—before plugins and before the transpiler generates the AST. They are executed in the order they are imported. The transpiler will wait for the macro to finish executing before continuing. The transpiler will also await any Promise returned by a macro.
|
||||
|
||||
Bun's bundler is multi-threaded. As such, macros execute in parallel inside of multiple spawned JavaScript "workers".
|
||||
|
||||
## Dead code elimination
|
||||
|
||||
The bundler performs dead code elimination _after_ running and inlining macros. So given the following macro:
|
||||
The bundler performs dead code elimination after running and inlining macros. So given the following macro:
|
||||
|
||||
```ts#returnFalse.ts
|
||||
```ts title="returnFalse.ts" icon="/icons/typescript.svg"
|
||||
export function returnFalse() {
|
||||
return false;
|
||||
}
|
||||
@@ -121,7 +128,7 @@ export function returnFalse() {
|
||||
|
||||
...then bundling the following file will produce an empty bundle, provided that the minify syntax option is enabled.
|
||||
|
||||
```ts
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import { returnFalse } from "./returnFalse.ts" with { type: "macro" };
|
||||
|
||||
if (returnFalse()) {
|
||||
@@ -133,19 +140,19 @@ if (returnFalse()) {
|
||||
|
||||
Bun's transpiler needs to be able to serialize the result of the macro so it can be inlined into the AST. All JSON-compatible data structures are supported:
|
||||
|
||||
```ts#macro.ts
|
||||
```ts title="macro.ts" icon="/icons/typescript.svg"
|
||||
export function getObject() {
|
||||
return {
|
||||
foo: "bar",
|
||||
baz: 123,
|
||||
array: [ 1, 2, { nested: "value" }],
|
||||
array: [1, 2, { nested: "value" }],
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Macros can be async, or return `Promise` instances. Bun's transpiler will automatically `await` the `Promise` and inline the result.
|
||||
Macros can be async, or return Promise instances. Bun's transpiler will automatically await the Promise and inline the result.
|
||||
|
||||
```ts#macro.ts
|
||||
```ts title="macro.ts" icon="/icons/typescript.svg"
|
||||
export async function getText() {
|
||||
return "async value";
|
||||
}
|
||||
@@ -153,21 +160,21 @@ export async function getText() {
|
||||
|
||||
The transpiler implements special logic for serializing common data formats like `Response`, `Blob`, `TypedArray`.
|
||||
|
||||
- `TypedArray`: Resolves to a base64-encoded string.
|
||||
- `Response`: Bun will read the `Content-Type` and serialize accordingly; for instance, a `Response` with type `application/json` will be automatically parsed into an object and `text/plain` will be inlined as a string. Responses with an unrecognized or `undefined` `type` will be base-64 encoded.
|
||||
- `Blob`: As with `Response`, the serialization depends on the `type` property.
|
||||
- **TypedArray**: Resolves to a base64-encoded string.
|
||||
- **Response**: Bun will read the `Content-Type` and serialize accordingly; for instance, a Response with type `application/json` will be automatically parsed into an object and `text/plain` will be inlined as a string. Responses with an unrecognized or undefined type will be base-64 encoded.
|
||||
- **Blob**: As with Response, the serialization depends on the `type` property.
|
||||
|
||||
The result of `fetch` is `Promise<Response>`, so it can be directly returned.
|
||||
|
||||
```ts#macro.ts
|
||||
```ts title="macro.ts" icon="/icons/typescript.svg"
|
||||
export function getObject() {
|
||||
return fetch("https://bun.com")
|
||||
return fetch("https://bun.com");
|
||||
}
|
||||
```
|
||||
|
||||
Functions and instances of most classes (except those mentioned above) are not serializable.
|
||||
|
||||
```ts
|
||||
```ts title="macro.ts" icon="/icons/typescript.svg"
|
||||
export function getText(url: string) {
|
||||
// this doesn't work!
|
||||
return () => {};
|
||||
@@ -178,7 +185,7 @@ export function getText(url: string) {
|
||||
|
||||
Macros can accept inputs, but only in limited cases. The value must be statically known. For example, the following is not allowed:
|
||||
|
||||
```ts
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import { getText } from "./getText.ts" with { type: "macro" };
|
||||
|
||||
export function howLong() {
|
||||
@@ -192,7 +199,7 @@ export function howLong() {
|
||||
|
||||
However, if the value of `foo` is known at bundle-time (say, if it's a constant or the result of another macro) then it's allowed:
|
||||
|
||||
```ts
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import { getText } from "./getText.ts" with { type: "macro" };
|
||||
import { getFoo } from "./getFoo.ts" with { type: "macro" };
|
||||
|
||||
@@ -206,7 +213,7 @@ export function howLong() {
|
||||
|
||||
This outputs:
|
||||
|
||||
```ts
|
||||
```js
|
||||
function howLong() {
|
||||
console.log("The page is", 1322, "characters long");
|
||||
}
|
||||
@@ -217,11 +224,9 @@ export { howLong };
|
||||
|
||||
### Embed latest git commit hash
|
||||
|
||||
{% codetabs %}
|
||||
|
||||
```ts#getGitCommitHash.ts
|
||||
```ts title="getGitCommitHash.ts" icon="/icons/typescript.svg"
|
||||
export function getGitCommitHash() {
|
||||
const {stdout} = Bun.spawnSync({
|
||||
const { stdout } = Bun.spawnSync({
|
||||
cmd: ["git", "rev-parse", "HEAD"],
|
||||
stdout: "pipe",
|
||||
});
|
||||
@@ -230,33 +235,32 @@ export function getGitCommitHash() {
|
||||
}
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
|
||||
<!-- --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:
|
||||
|
||||
{% codetabs %}
|
||||
<CodeGroup>
|
||||
|
||||
```ts#input
|
||||
import { getGitCommitHash } from './getGitCommitHash.ts' with { type: 'macro' };
|
||||
```ts input
|
||||
import { getGitCommitHash } from "./getGitCommitHash.ts" with { type: "macro" };
|
||||
|
||||
console.log(`The current Git commit hash is ${getGitCommitHash()}`);
|
||||
```
|
||||
|
||||
```bash#output
|
||||
console.log(`The current Git commit hash is 3ee3259104f`);
|
||||
```ts output
|
||||
console.log(`The current Git commit hash is 3ee3259104e4507cf62c160f0ff5357ec4c7a7f8`);
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
</CodeGroup>
|
||||
|
||||
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?
|
||||
</Info>
|
||||
|
||||
### Make `fetch()` requests at bundle-time
|
||||
### Make fetch() requests at bundle-time
|
||||
|
||||
In this example, we make an outgoing HTTP request using `fetch()`, parse the HTML response using `HTMLRewriter`, and return an object containing the title and meta tags–all at bundle-time.
|
||||
|
||||
```ts
|
||||
```ts title="meta.ts" icon="/icons/typescript.svg"
|
||||
export async function extractMetaTags(url: string) {
|
||||
const response = await fetch(url);
|
||||
const meta = {
|
||||
@@ -271,9 +275,7 @@ export async function extractMetaTags(url: string) {
|
||||
.on("meta", {
|
||||
element(element) {
|
||||
const name =
|
||||
element.getAttribute("name") ||
|
||||
element.getAttribute("property") ||
|
||||
element.getAttribute("itemprop");
|
||||
element.getAttribute("name") || element.getAttribute("property") || element.getAttribute("itemprop");
|
||||
|
||||
if (name) meta[name] = element.getAttribute("content");
|
||||
},
|
||||
@@ -284,14 +286,12 @@ export async function extractMetaTags(url: string) {
|
||||
}
|
||||
```
|
||||
|
||||
<!-- --target=browser so they can clearly see it's for browsers -->
|
||||
The `extractMetaTags` function is erased at bundle-time and replaced with the result of the function call. This means that the fetch request happens at bundle-time, and the result is embedded in the bundle. Also, the branch throwing the error is eliminated since it's unreachable.
|
||||
|
||||
The `extractMetaTags` function is erased at bundle-time and replaced with the result of the function call. This means that the `fetch` request happens at bundle-time, and the result is embedded in the bundle. Also, the branch throwing the error is eliminated since it's unreachable.
|
||||
<CodeGroup>
|
||||
|
||||
{% codetabs %}
|
||||
|
||||
```ts#input
|
||||
import { extractMetaTags } from './meta.ts' with { type: 'macro' };
|
||||
```jsx input
|
||||
import { extractMetaTags } from "./meta.ts" with { type: "macro" };
|
||||
|
||||
export const Head = () => {
|
||||
const headTags = extractMetaTags("https://example.com");
|
||||
@@ -300,30 +300,29 @@ export const Head = () => {
|
||||
throw new Error("Expected title to be 'Example Domain'");
|
||||
}
|
||||
|
||||
return <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>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
```ts#output
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
```jsx output
|
||||
export const Head = () => {
|
||||
jsxs("head", {
|
||||
children: [
|
||||
jsx("title", {
|
||||
children: "Example Domain",
|
||||
}),
|
||||
jsx("meta", {
|
||||
name: "viewport",
|
||||
content: "width=device-width, initial-scale=1",
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
const headTags = {
|
||||
title: "Example Domain",
|
||||
viewport: "width=device-width, initial-scale=1",
|
||||
};
|
||||
|
||||
export { Head };
|
||||
return (
|
||||
<head>
|
||||
<title>{headTags.title}</title>
|
||||
<meta name="viewport" content={headTags.viewport} />
|
||||
</head>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
</CodeGroup>
|
||||
1306
docs/bundler/minifier.mdx
Normal file
1306
docs/bundler/minifier.mdx
Normal file
File diff suppressed because it is too large
Load Diff
411
docs/bundler/plugins.mdx
Normal file
411
docs/bundler/plugins.mdx
Normal file
@@ -0,0 +1,411 @@
|
||||
---
|
||||
title: Plugins
|
||||
description: Universal plugin API for extending Bun's runtime and bundler
|
||||
---
|
||||
|
||||
Bun provides a universal plugin API that can be used to extend both the runtime and bundler.
|
||||
|
||||
Plugins intercept imports and perform custom loading logic: reading files, transpiling code, etc. They can be used to add support for additional file types, like `.scss` or `.yaml`. In the context of Bun's bundler, plugins can be used to implement framework-level features like CSS extraction, macros, and client-server code co-location.
|
||||
|
||||
## Lifecycle hooks
|
||||
|
||||
Plugins can register callbacks to be run at various points in the lifecycle of a bundle:
|
||||
|
||||
- `onStart()`: Run once the bundler has started a bundle
|
||||
- `onResolve()`: Run before a module is resolved
|
||||
- `onLoad()`: Run before a module is loaded
|
||||
- `onBeforeParse()`: Run zero-copy native addons in the parser thread before a file is parsed
|
||||
|
||||
## Reference
|
||||
|
||||
A rough overview of the types (please refer to Bun's `bun.d.ts` for the full type definitions):
|
||||
|
||||
```ts title="bun.d.ts" icon="/icons/typescript.svg"
|
||||
type PluginBuilder = {
|
||||
onStart(callback: () => void): void;
|
||||
onResolve: (
|
||||
args: { filter: RegExp; namespace?: string },
|
||||
callback: (args: { path: string; importer: string }) => {
|
||||
path: string;
|
||||
namespace?: string;
|
||||
} | void,
|
||||
) => void;
|
||||
onLoad: (
|
||||
args: { filter: RegExp; namespace?: string },
|
||||
defer: () => Promise<void>,
|
||||
callback: (args: { path: string }) => {
|
||||
loader?: Loader;
|
||||
contents?: string;
|
||||
exports?: Record<string, any>;
|
||||
},
|
||||
) => void;
|
||||
config: BuildConfig;
|
||||
};
|
||||
|
||||
type Loader = "js" | "jsx" | "ts" | "tsx" | "css" | "json" | "toml";
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
A plugin is defined as simple JavaScript object containing a `name` property and a `setup` function.
|
||||
|
||||
```ts title="myPlugin.ts" icon="/icons/typescript.svg"
|
||||
import type { BunPlugin } from "bun";
|
||||
|
||||
const myPlugin: BunPlugin = {
|
||||
name: "Custom loader",
|
||||
setup(build) {
|
||||
// implementation
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
This plugin can be passed into the `plugins` array when calling `Bun.build`.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
await Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
outdir: "./out",
|
||||
plugins: [myPlugin],
|
||||
});
|
||||
```
|
||||
|
||||
## Plugin lifecycle
|
||||
|
||||
### Namespaces
|
||||
|
||||
`onLoad` and `onResolve` accept an optional `namespace` string. What is a namespace?
|
||||
|
||||
Every module has a namespace. Namespaces are used to prefix the import in transpiled code; for instance, a loader with a `filter: /\.yaml$/` and `namespace: "yaml:"` will transform an import from `./myfile.yaml` into `yaml:./myfile.yaml`.
|
||||
|
||||
The default namespace is `"file"` and it is not necessary to specify it, for instance: `import myModule from "./my-module.ts"` is the same as `import myModule from "file:./my-module.ts"`.
|
||||
|
||||
Other common namespaces are:
|
||||
|
||||
- `"bun"`: for Bun-specific modules (e.g. `"bun:test"`, `"bun:sqlite"`)
|
||||
- `"node"`: for Node.js modules (e.g. `"node:fs"`, `"node:path"`)
|
||||
|
||||
### onStart
|
||||
|
||||
```ts
|
||||
onStart(callback: () => void): Promise<void> | 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>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
This command installs completions for `zsh` and/or `fish`. It runs automatically on every `bun upgrade` and on install. It reads from `$SHELL` to determine which shell to install for. It tries several common shell completion directories for your shell and OS.
|
||||
|
||||
If you want to copy the completions manually, run `bun completions > path-to-file`. If you know the completions directory to install them to, run `bun completions /path/to/directory`.
|
||||
@@ -1,349 +0,0 @@
|
||||
### `bun install`
|
||||
|
||||
bun install is a fast package manager & npm client.
|
||||
|
||||
bun install can be configured via `bunfig.toml`, environment variables, and CLI flags.
|
||||
|
||||
#### Configuring `bun install` with `bunfig.toml`
|
||||
|
||||
`bunfig.toml` is searched for in the following paths on `bun install`, `bun remove`, and `bun add`:
|
||||
|
||||
1. `$XDG_CONFIG_HOME/.bunfig.toml` or `$HOME/.bunfig.toml`
|
||||
2. `./bunfig.toml`
|
||||
|
||||
If both are found, the results are merged together.
|
||||
|
||||
Configuring with `bunfig.toml` is optional. Bun tries to be zero configuration in general, but that's not always possible.
|
||||
|
||||
```toml
|
||||
# Using scoped packages with bun install
|
||||
[install.scopes]
|
||||
|
||||
# Scope name The value can be a URL string or an object
|
||||
"@mybigcompany" = { token = "123456", url = "https://registry.mybigcompany.com" }
|
||||
# URL is optional and falls back to the default registry
|
||||
|
||||
# The "@" in the scope is optional
|
||||
mybigcompany2 = { token = "123456" }
|
||||
|
||||
# Environment variables can be referenced as a string that starts with $ and it will be replaced
|
||||
mybigcompany3 = { token = "$npm_config_token" }
|
||||
|
||||
# Setting username and password turns it into a Basic Auth header by taking base64("username:password")
|
||||
mybigcompany4 = { username = "myusername", password = "$npm_config_password", url = "https://registry.yarnpkg.com/" }
|
||||
# You can set username and password in the registry URL. This is the same as above.
|
||||
mybigcompany5 = "https://username:password@registry.yarnpkg.com/"
|
||||
|
||||
# You can set a token for a registry URL:
|
||||
mybigcompany6 = "https://:$NPM_CONFIG_TOKEN@registry.yarnpkg.com/"
|
||||
|
||||
[install]
|
||||
# Default registry
|
||||
# can be a URL string or an object
|
||||
registry = "https://registry.yarnpkg.com/"
|
||||
# as an object
|
||||
#registry = { url = "https://registry.yarnpkg.com/", token = "123456" }
|
||||
|
||||
# Install for production? This is the equivalent to the "--production" CLI argument
|
||||
production = false
|
||||
|
||||
# Save a text-based lockfile? This is equivalent to the "--save-text-lockfile" CLI argument
|
||||
saveTextLockfile = false
|
||||
|
||||
# Disallow changes to lockfile? This is the equivalent to the "--frozen-lockfile" CLI argument
|
||||
frozenLockfile = false
|
||||
|
||||
# Don't actually install
|
||||
dryRun = true
|
||||
|
||||
# Install optionalDependencies (default: true)
|
||||
# Setting this to false is equivalent to the `--omit=optional` CLI argument
|
||||
optional = true
|
||||
|
||||
# Install local devDependencies (default: true)
|
||||
# Setting this to false is equivalent to the `--omit=dev` CLI argument
|
||||
dev = true
|
||||
|
||||
# Install peerDependencies (default: true)
|
||||
# Setting this to false is equivalent to the `--omit=peer` CLI argument
|
||||
peer = true
|
||||
|
||||
# Max number of concurrent lifecycle scripts (default: (cpu count or GOMAXPROCS) x2)
|
||||
concurrentScripts = 16
|
||||
|
||||
# When using `bun install -g`, install packages here
|
||||
globalDir = "~/.bun/install/global"
|
||||
|
||||
# When using `bun install -g`, link package bins here
|
||||
globalBinDir = "~/.bun/bin"
|
||||
|
||||
# cache-related configuration
|
||||
[install.cache]
|
||||
# The directory to use for the cache
|
||||
dir = "~/.bun/install/cache"
|
||||
|
||||
# Don't load from the global cache.
|
||||
# Note: Bun may still write to node_modules/.cache
|
||||
disable = false
|
||||
|
||||
|
||||
# Always resolve the latest versions from the registry
|
||||
disableManifest = false
|
||||
|
||||
|
||||
# Lockfile-related configuration
|
||||
[install.lockfile]
|
||||
|
||||
# Print a yarn v1 lockfile
|
||||
# Note: it does not load the lockfile, it just converts bun.lock into a yarn.lock
|
||||
print = "yarn"
|
||||
|
||||
# Save the lockfile to disk
|
||||
save = true
|
||||
|
||||
```
|
||||
|
||||
If it's easier to read as TypeScript types:
|
||||
|
||||
```ts
|
||||
export interface Root {
|
||||
install: Install;
|
||||
}
|
||||
|
||||
export interface Install {
|
||||
scopes: Scopes;
|
||||
registry: Registry;
|
||||
production: boolean;
|
||||
saveTextLockfile: boolean;
|
||||
frozenLockfile: boolean;
|
||||
dryRun: boolean;
|
||||
optional: boolean;
|
||||
dev: boolean;
|
||||
peer: boolean;
|
||||
globalDir: string;
|
||||
globalBinDir: string;
|
||||
cache: Cache;
|
||||
lockfile: Lockfile;
|
||||
logLevel: "debug" | "error" | "warn";
|
||||
}
|
||||
|
||||
type Registry =
|
||||
| string
|
||||
| {
|
||||
url?: string;
|
||||
token?: string;
|
||||
username?: string;
|
||||
password?: string;
|
||||
};
|
||||
|
||||
type Scopes = Record<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.
|
||||
@@ -1,39 +0,0 @@
|
||||
To upgrade Bun, run `bun upgrade`.
|
||||
|
||||
It automatically downloads the latest version of Bun and overwrites the currently-running version.
|
||||
|
||||
This works by checking the latest version of Bun in [bun-releases-for-updater](https://github.com/Jarred-Sumner/bun-releases-for-updater/releases) and unzipping it using the system-provided `unzip` library (so that Gatekeeper works on macOS)
|
||||
|
||||
If for any reason you run into issues, you can also use the curl install script:
|
||||
|
||||
```bash
|
||||
$ curl https://bun.com/install | bash
|
||||
```
|
||||
|
||||
It will still work when Bun is already installed.
|
||||
|
||||
Bun is distributed as a single binary file, so you can also do this manually:
|
||||
|
||||
- Download the latest version of Bun for your platform in [bun-releases-for-updater](https://github.com/Jarred-Sumner/bun-releases-for-updater/releases/latest) (`darwin` == macOS)
|
||||
- Unzip the folder
|
||||
- Move the `bun` binary to `~/.bun/bin` (or anywhere)
|
||||
|
||||
## `--canary`
|
||||
|
||||
[Canary](https://github.com/oven-sh/bun/releases/tag/canary) builds are generated on every commit.
|
||||
|
||||
To install a [canary](https://github.com/oven-sh/bun/releases/tag/canary) build of Bun, run:
|
||||
|
||||
```bash
|
||||
$ bun upgrade --canary
|
||||
```
|
||||
|
||||
This flag is not persistent (though that might change in the future). If you want to always run the canary build of Bun, set the `BUN_CANARY` environment variable to `1` in your shell's startup script.
|
||||
|
||||
This will download the release zip from https://github.com/oven-sh/bun/releases/tag/canary.
|
||||
|
||||
To revert to the latest published version of Bun, run:
|
||||
|
||||
```bash
|
||||
$ bun upgrade
|
||||
```
|
||||
@@ -1,65 +0,0 @@
|
||||
`bun info` displays package metadata from the npm registry.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
$ bun info react
|
||||
```
|
||||
|
||||
This will display information about the `react` package, including its latest version, description, homepage, dependencies, and more.
|
||||
|
||||
## Viewing specific versions
|
||||
|
||||
To view information about a specific version:
|
||||
|
||||
```bash
|
||||
$ bun info react@18.0.0
|
||||
```
|
||||
|
||||
## Viewing specific properties
|
||||
|
||||
You can also query specific properties from the package metadata:
|
||||
|
||||
```bash
|
||||
$ bun info react version
|
||||
$ bun info react dependencies
|
||||
$ bun info react repository.url
|
||||
```
|
||||
|
||||
## JSON output
|
||||
|
||||
To get the output in JSON format, use the `--json` flag:
|
||||
|
||||
```bash
|
||||
$ bun info react --json
|
||||
```
|
||||
|
||||
## Alias
|
||||
|
||||
`bun pm view` is an alias for `bun info`:
|
||||
|
||||
```bash
|
||||
$ bun pm view react # equivalent to: bun info react
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
# View basic package information
|
||||
$ bun info is-number
|
||||
|
||||
# View a specific version
|
||||
$ bun info is-number@7.0.0
|
||||
|
||||
# View all available versions
|
||||
$ bun info is-number versions
|
||||
|
||||
# View package dependencies
|
||||
$ bun info express dependencies
|
||||
|
||||
# View package homepage
|
||||
$ bun info lodash homepage
|
||||
|
||||
# Get JSON output
|
||||
$ bun info react --json
|
||||
```
|
||||
@@ -1,76 +0,0 @@
|
||||
Scaffold an empty Bun project with the interactive `bun init` command.
|
||||
|
||||
```bash
|
||||
$ bun init
|
||||
bun init helps you get started with a minimal project and tries to
|
||||
guess sensible defaults. Press ^C anytime to quit.
|
||||
|
||||
package name (quickstart):
|
||||
entry point (index.ts):
|
||||
|
||||
Done! A package.json file was saved in the current directory.
|
||||
+ index.ts
|
||||
+ .gitignore
|
||||
+ tsconfig.json (for editor auto-complete)
|
||||
+ README.md
|
||||
|
||||
To get started, run:
|
||||
bun run index.ts
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
It creates:
|
||||
|
||||
- a `package.json` file with a name that defaults to the current directory name
|
||||
- a `tsconfig.json` file or a `jsconfig.json` file, depending if the entry point is a TypeScript file or not
|
||||
- an entry point which defaults to `index.ts` unless any of `index.{tsx, jsx, js, mts, mjs}` exist or the `package.json` specifies a `module` or `main` field
|
||||
- a `README.md` file
|
||||
|
||||
AI Agent rules (disable with `$BUN_AGENT_RULE_DISABLED=1`):
|
||||
|
||||
- a `CLAUDE.md` file when Claude CLI is detected (disable with `CLAUDE_CODE_AGENT_RULE_DISABLED` env var)
|
||||
- a `.cursor/rules/*.mdc` file to guide [Cursor AI](https://cursor.sh) to use Bun instead of Node.js and npm when Cursor is detected
|
||||
|
||||
If you pass `-y` or `--yes`, it will assume you want to continue without asking questions.
|
||||
|
||||
At the end, it runs `bun install` to install `@types/bun`.
|
||||
|
||||
{% /details %}
|
||||
|
||||
{% 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
|
||||
```
|
||||
@@ -1,11 +0,0 @@
|
||||
An alias for `bun patch --commit` to maintain compatibility with pnpm.
|
||||
|
||||
To get started with patch, first prepare the package for patching with [`bun patch <pkg>`](https://bun.com/docs/install/patch).
|
||||
|
||||
### `--patches-dir`
|
||||
|
||||
By default, `bun patch-commit` will use the `patches` directory in the temporary directory.
|
||||
|
||||
You can specify a different directory with the `--patches-dir` flag.
|
||||
|
||||
{% bunCLIUsage command="patch-commit" /%}
|
||||
@@ -1,7 +0,0 @@
|
||||
To remove a dependency:
|
||||
|
||||
```bash
|
||||
$ bun remove ts-node
|
||||
```
|
||||
|
||||
{% bunCLIUsage command="remove" /%}
|
||||
@@ -1,9 +0,0 @@
|
||||
Use `bun unlink` in the root directory to unregister a local package.
|
||||
|
||||
```bash
|
||||
$ cd /path/to/cool-pkg
|
||||
$ bun unlink
|
||||
bun unlink v1.x (7416672e)
|
||||
```
|
||||
|
||||
{% bunCLIUsage command="unlink" /%}
|
||||
@@ -1,57 +0,0 @@
|
||||
Bun uses [a fork](https://github.com/oven-sh/WebKit) of WebKit with a small number of changes.
|
||||
|
||||
It's important to periodically update WebKit for many reasons:
|
||||
|
||||
- Security
|
||||
- Performance
|
||||
- Compatibility
|
||||
- …and many more.
|
||||
|
||||
To upgrade, first find the commit in **Bun's WebKit fork** (not Bun!) between when we last upgraded and now.
|
||||
|
||||
```bash
|
||||
$ cd src/bun.js/WebKit # In the WebKit directory! not bun
|
||||
$ git checkout $COMMIT
|
||||
```
|
||||
|
||||
This is the main command to run:
|
||||
|
||||
```bash
|
||||
$ git merge upstream main
|
||||
# If you get an error saying histories are unrelated, run this and try again:
|
||||
$ git fetch --unshallow
|
||||
```
|
||||
|
||||
Then, you will likely see some silly merge conflicts. Fix them and then run:
|
||||
|
||||
```bash
|
||||
# You might have to run this multiple times.
|
||||
$ rm -rf WebKitBuild
|
||||
|
||||
# Go to Bun's directory! Not WebKit.
|
||||
cd ../../../../
|
||||
make jsc-build-mac-compile
|
||||
```
|
||||
|
||||
Make sure that JSC's CLI is able to load successfully. This verifies that the build is working.
|
||||
|
||||
You know this worked when it printed help options. If it complains about symbols, crashes, or anything else that looks wrong, something is wrong.
|
||||
|
||||
```bash
|
||||
src/bun.js/WebKit/WebKitBuild/Release/bin/jsc --help
|
||||
```
|
||||
|
||||
Then, clear out our bindings and regenerate the C++<>Zig headers:
|
||||
|
||||
```bash
|
||||
make clean-bindings headers builtins
|
||||
```
|
||||
|
||||
Now update Bun's bindings wherever there are compiler errors:
|
||||
|
||||
```bash
|
||||
# It will take awhile if you don't pass -j here
|
||||
make bindings -j10
|
||||
```
|
||||
|
||||
This is the hard part. It might involve digging through WebKit's commit history to figure out what changed and why. Fortunately, WebKit contributors write great commit messages.
|
||||
626
docs/docs.json
Normal file
626
docs/docs.json
Normal file
@@ -0,0 +1,626 @@
|
||||
{
|
||||
"$schema": "https://mintlify.com/docs.json",
|
||||
"theme": "aspen",
|
||||
"name": "Bun",
|
||||
"seo": {
|
||||
"metatags": {
|
||||
"canonical": "https://bun.com/docs"
|
||||
}
|
||||
},
|
||||
"colors": {
|
||||
"light": "#ff73a8",
|
||||
"primary": "#ff73a8",
|
||||
"dark": "#ff73a8"
|
||||
},
|
||||
"background": {
|
||||
"decoration": "gradient"
|
||||
},
|
||||
"favicon": "/logo/bun.png",
|
||||
"icons": {
|
||||
"library": "lucide"
|
||||
},
|
||||
"fonts": {
|
||||
"heading": {
|
||||
"family": "Inter Display Bold",
|
||||
"source": "https://mintlify-assets.b-cdn.net/fonts/InterDisplay-Bold.woff2",
|
||||
"format": "woff2"
|
||||
}
|
||||
},
|
||||
"appearance": {
|
||||
"default": "system"
|
||||
},
|
||||
"logo": {
|
||||
"light": "/logo/logo-with-wordmark-dark.svg",
|
||||
"dark": "/logo/logo-with-wordmark-light.svg"
|
||||
},
|
||||
"navbar": {
|
||||
"links": [
|
||||
{
|
||||
"label": "Install Bun",
|
||||
"href": "https://www.bun.com/docs/installation",
|
||||
"icon": "download",
|
||||
"primary": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"contextual": {
|
||||
"options": ["copy", "view", "chatgpt", "claude", "perplexity", "mcp", "cursor", "vscode"]
|
||||
},
|
||||
"styling": {
|
||||
"codeblocks": {
|
||||
"theme": {
|
||||
"light": "github-light",
|
||||
"dark": "dracula"
|
||||
}
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Runtime",
|
||||
"icon": "cog",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Get Started",
|
||||
"icon": "terminal",
|
||||
"pages": [
|
||||
"/index",
|
||||
"/installation",
|
||||
"/quickstart",
|
||||
"/typescript",
|
||||
"/runtime/templating/init",
|
||||
"/runtime/templating/create"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Core Runtime",
|
||||
"icon": "cog",
|
||||
"pages": ["/runtime/index", "/runtime/watch-mode", "/runtime/debugger", "/runtime/bunfig"]
|
||||
},
|
||||
{
|
||||
"group": "File & Module System",
|
||||
"icon": "file",
|
||||
"pages": [
|
||||
"/runtime/file-types",
|
||||
"/runtime/module-resolution",
|
||||
"/runtime/jsx",
|
||||
"/runtime/auto-install",
|
||||
"/runtime/plugins",
|
||||
"/runtime/file-system-router"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "HTTP server",
|
||||
"icon": "server",
|
||||
"pages": [
|
||||
"/runtime/http/server",
|
||||
"/runtime/http/routing",
|
||||
"/runtime/http/cookies",
|
||||
"/runtime/http/tls",
|
||||
"/runtime/http/error-handling",
|
||||
"/runtime/http/metrics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Networking",
|
||||
"icon": "globe",
|
||||
"expanded": true,
|
||||
"pages": [
|
||||
"/runtime/networking/fetch",
|
||||
"/runtime/http/websockets",
|
||||
"/runtime/networking/tcp",
|
||||
"/runtime/networking/udp",
|
||||
"/runtime/networking/dns"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Data & Storage",
|
||||
"icon": "database",
|
||||
"pages": [
|
||||
"/runtime/cookies",
|
||||
"/runtime/file-io",
|
||||
"/runtime/streams",
|
||||
"/runtime/binary-data",
|
||||
"/runtime/sql",
|
||||
"/runtime/sqlite",
|
||||
"/runtime/s3",
|
||||
"/runtime/redis"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Concurrency",
|
||||
"icon": "split",
|
||||
"pages": ["/runtime/workers"]
|
||||
},
|
||||
{
|
||||
"group": "Process & System",
|
||||
"icon": "computer",
|
||||
"pages": ["/runtime/environment-variables", "/runtime/shell", "/runtime/child-process"]
|
||||
},
|
||||
{
|
||||
"group": "Interop & Tooling",
|
||||
"icon": "puzzle",
|
||||
"pages": ["/runtime/node-api", "/runtime/ffi", "/runtime/c-compiler", "/runtime/transpiler"]
|
||||
},
|
||||
{
|
||||
"group": "Utilities",
|
||||
"icon": "wrench",
|
||||
"pages": [
|
||||
"/runtime/secrets",
|
||||
"/runtime/console",
|
||||
"/runtime/yaml",
|
||||
"/runtime/html-rewriter",
|
||||
"/runtime/hashing",
|
||||
"/runtime/glob",
|
||||
"/runtime/semver",
|
||||
"/runtime/color",
|
||||
"/runtime/utils"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Standards & Compatibility",
|
||||
"icon": "badge-check",
|
||||
"pages": ["/runtime/globals", "/runtime/bun-apis", "/runtime/web-apis", "/runtime/nodejs-compat"]
|
||||
},
|
||||
{
|
||||
"group": "Contributing",
|
||||
"icon": "heart",
|
||||
"pages": [
|
||||
"/project/roadmap",
|
||||
"/project/benchmarking",
|
||||
"/project/contributing",
|
||||
"/project/building-windows",
|
||||
"/project/bindgen",
|
||||
"/project/license"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Package Manager",
|
||||
"icon": "box",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Core Commands",
|
||||
"icon": "terminal",
|
||||
"pages": ["/pm/cli/install", "/pm/cli/add", "/pm/cli/remove", "/pm/cli/update", "/pm/bunx"]
|
||||
},
|
||||
{
|
||||
"group": "Publishing & Analysis",
|
||||
"icon": "upload",
|
||||
"pages": ["/pm/cli/publish", "/pm/cli/outdated", "/pm/cli/why", "/pm/cli/audit"]
|
||||
},
|
||||
{
|
||||
"group": "Workspace Management",
|
||||
"icon": "folders",
|
||||
"pages": ["/pm/workspaces", "/pm/catalogs", "/pm/cli/link", "/pm/cli/pm"]
|
||||
},
|
||||
{
|
||||
"group": "Advanced Configuration",
|
||||
"icon": "settings",
|
||||
"pages": [
|
||||
"/pm/cli/patch",
|
||||
"/pm/filter",
|
||||
"/pm/global-cache",
|
||||
"/pm/isolated-installs",
|
||||
"/pm/lockfile",
|
||||
"/pm/lifecycle",
|
||||
"/pm/scopes-registries",
|
||||
"/pm/overrides",
|
||||
"/pm/security-scanner-api",
|
||||
"/pm/npmrc"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Bundler",
|
||||
"icon": "combine",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Core",
|
||||
"icon": "package",
|
||||
"pages": ["/bundler/index"]
|
||||
},
|
||||
{
|
||||
"group": "Development Server",
|
||||
"icon": "monitor",
|
||||
"pages": ["/bundler/fullstack", "/bundler/hot-reloading"]
|
||||
},
|
||||
{
|
||||
"group": "Asset Processing",
|
||||
"icon": "image",
|
||||
"pages": ["/bundler/html-static", "/bundler/css", "/bundler/loaders"]
|
||||
},
|
||||
{
|
||||
"group": "Single File Executable",
|
||||
"icon": "binary",
|
||||
"pages": ["/bundler/executables"]
|
||||
},
|
||||
{
|
||||
"group": "Extensions",
|
||||
"icon": "plug",
|
||||
"pages": ["/bundler/plugins", "/bundler/macros"]
|
||||
},
|
||||
{
|
||||
"group": "Optimization",
|
||||
"icon": "zap",
|
||||
"pages": ["/bundler/bytecode", "/bundler/minifier"]
|
||||
},
|
||||
{
|
||||
"group": "Migration",
|
||||
"icon": "arrow-right",
|
||||
"pages": ["/bundler/esbuild"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Test Runner",
|
||||
"icon": "flask-conical",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"icon": "circle-play",
|
||||
"pages": ["/test/index", "/test/writing-tests", "/test/configuration"]
|
||||
},
|
||||
{
|
||||
"group": "Test Execution",
|
||||
"icon": "zap",
|
||||
"pages": ["/test/runtime-behavior", "/test/discovery"]
|
||||
},
|
||||
{
|
||||
"group": "Test Features",
|
||||
"icon": "sparkles",
|
||||
"pages": ["/test/lifecycle", "/test/mocks", "/test/snapshots", "/test/dates-times"]
|
||||
},
|
||||
{
|
||||
"group": "Specialized Testing",
|
||||
"icon": "microscope",
|
||||
"pages": ["/test/dom"]
|
||||
},
|
||||
{
|
||||
"group": "Reporting",
|
||||
"icon": "file-text",
|
||||
"pages": ["/test/code-coverage", "/test/reporters"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"tab": "Guides",
|
||||
"icon": "map",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Overview",
|
||||
"icon": "globe",
|
||||
"pages": ["/guides/index"]
|
||||
},
|
||||
{
|
||||
"group": "Deployment",
|
||||
"icon": "rocket",
|
||||
"pages": [
|
||||
"/guides/deployment/vercel",
|
||||
"/guides/deployment/railway",
|
||||
"/guides/deployment/render",
|
||||
"/guides/deployment/aws-lambda",
|
||||
"/guides/deployment/digital-ocean",
|
||||
"/guides/deployment/google-cloud-run"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Runtime & Debugging",
|
||||
"icon": "bug",
|
||||
"pages": [
|
||||
"/guides/runtime/typescript",
|
||||
"/guides/runtime/tsconfig-paths",
|
||||
"/guides/runtime/vscode-debugger",
|
||||
"/guides/runtime/web-debugger",
|
||||
"/guides/runtime/heap-snapshot",
|
||||
"/guides/runtime/build-time-constants",
|
||||
"/guides/runtime/define-constant",
|
||||
"/guides/runtime/cicd",
|
||||
"/guides/runtime/codesign-macos-executable"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Utilities",
|
||||
"icon": "wrench",
|
||||
"pages": [
|
||||
"/guides/util/detect-bun",
|
||||
"/guides/util/version",
|
||||
"/guides/util/hash-a-password",
|
||||
"/guides/util/javascript-uuid",
|
||||
"/guides/util/base64",
|
||||
"/guides/util/gzip",
|
||||
"/guides/util/deflate",
|
||||
"/guides/util/escape-html",
|
||||
"/guides/util/deep-equals",
|
||||
"/guides/util/sleep",
|
||||
"/guides/util/file-url-to-path",
|
||||
"/guides/util/path-to-file-url",
|
||||
"/guides/util/which-path-to-executable-bin",
|
||||
"/guides/util/import-meta-dir",
|
||||
"/guides/util/import-meta-file",
|
||||
"/guides/util/import-meta-path",
|
||||
"/guides/util/entrypoint",
|
||||
"/guides/util/main"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Ecosystem & Frameworks",
|
||||
"icon": "puzzle",
|
||||
"pages": [
|
||||
"/guides/ecosystem/astro",
|
||||
"/guides/ecosystem/discordjs",
|
||||
"/guides/ecosystem/docker",
|
||||
"/guides/ecosystem/drizzle",
|
||||
"/guides/ecosystem/edgedb",
|
||||
"/guides/ecosystem/elysia",
|
||||
"/guides/ecosystem/express",
|
||||
"/guides/ecosystem/hono",
|
||||
"/guides/ecosystem/mongoose",
|
||||
"/guides/ecosystem/neon-drizzle",
|
||||
"/guides/ecosystem/neon-serverless-postgres",
|
||||
"/guides/ecosystem/nextjs",
|
||||
"/guides/ecosystem/nuxt",
|
||||
"/guides/ecosystem/pm2",
|
||||
"/guides/ecosystem/prisma",
|
||||
"/guides/ecosystem/prisma-postgres",
|
||||
"/guides/ecosystem/qwik",
|
||||
"/guides/ecosystem/react",
|
||||
"/guides/ecosystem/remix",
|
||||
"/guides/ecosystem/sentry",
|
||||
"/guides/ecosystem/solidstart",
|
||||
"/guides/ecosystem/ssr-react",
|
||||
"/guides/ecosystem/stric",
|
||||
"/guides/ecosystem/sveltekit",
|
||||
"/guides/ecosystem/systemd",
|
||||
"/guides/ecosystem/vite",
|
||||
"/guides/ecosystem/upstash"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "HTTP & Networking",
|
||||
"icon": "globe",
|
||||
"pages": [
|
||||
"/guides/http/server",
|
||||
"/guides/http/simple",
|
||||
"/guides/http/fetch",
|
||||
"/guides/http/hot",
|
||||
"/guides/http/cluster",
|
||||
"/guides/http/tls",
|
||||
"/guides/http/proxy",
|
||||
"/guides/http/stream-file",
|
||||
"/guides/http/file-uploads",
|
||||
"/guides/http/fetch-unix",
|
||||
"/guides/http/stream-iterator",
|
||||
"/guides/http/stream-node-streams-in-bun"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "WebSocket",
|
||||
"icon": "radio",
|
||||
"pages": [
|
||||
"/guides/websocket/simple",
|
||||
"/guides/websocket/pubsub",
|
||||
"/guides/websocket/context",
|
||||
"/guides/websocket/compression"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Processes & System",
|
||||
"icon": "cpu",
|
||||
"pages": [
|
||||
"/guides/process/spawn",
|
||||
"/guides/process/spawn-stdout",
|
||||
"/guides/process/spawn-stderr",
|
||||
"/guides/process/argv",
|
||||
"/guides/process/stdin",
|
||||
"/guides/process/ipc",
|
||||
"/guides/process/ctrl-c",
|
||||
"/guides/process/os-signals",
|
||||
"/guides/process/nanoseconds",
|
||||
"/guides/runtime/shell",
|
||||
"/guides/runtime/timezone",
|
||||
"/guides/runtime/set-env",
|
||||
"/guides/runtime/read-env"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Package Manager",
|
||||
"icon": "package",
|
||||
"pages": [
|
||||
"/guides/install/add",
|
||||
"/guides/install/add-dev",
|
||||
"/guides/install/add-optional",
|
||||
"/guides/install/add-peer",
|
||||
"/guides/install/add-git",
|
||||
"/guides/install/add-tarball",
|
||||
"/guides/install/npm-alias",
|
||||
"/guides/install/workspaces",
|
||||
"/guides/install/custom-registry",
|
||||
"/guides/install/registry-scope",
|
||||
"/guides/install/azure-artifacts",
|
||||
"/guides/install/jfrog-artifactory",
|
||||
"/guides/install/trusted",
|
||||
"/guides/install/yarnlock",
|
||||
"/guides/install/from-npm-install-to-bun-install",
|
||||
"/guides/install/git-diff-bun-lockfile",
|
||||
"/guides/install/cicd"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Test Runner",
|
||||
"icon": "flask-conical",
|
||||
"pages": [
|
||||
"/guides/test/run-tests",
|
||||
"/guides/test/watch-mode",
|
||||
"/guides/test/migrate-from-jest",
|
||||
"/guides/test/mock-functions",
|
||||
"/guides/test/spy-on",
|
||||
"/guides/test/mock-clock",
|
||||
"/guides/test/snapshot",
|
||||
"/guides/test/update-snapshots",
|
||||
"/guides/test/coverage",
|
||||
"/guides/test/coverage-threshold",
|
||||
"/guides/test/skip-tests",
|
||||
"/guides/test/todo-tests",
|
||||
"/guides/test/timeout",
|
||||
"/guides/test/bail",
|
||||
"/guides/test/rerun-each",
|
||||
"/guides/test/testing-library",
|
||||
"/guides/test/happy-dom",
|
||||
"/guides/test/svelte-test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Runtime & Debugging",
|
||||
"icon": "bug",
|
||||
"pages": [
|
||||
"/guides/runtime/vscode-debugger",
|
||||
"/guides/runtime/web-debugger",
|
||||
"/guides/runtime/heap-snapshot",
|
||||
"/guides/runtime/build-time-constants",
|
||||
"/guides/runtime/define-constant",
|
||||
"/guides/runtime/cicd",
|
||||
"/guides/runtime/codesign-macos-executable"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Module System",
|
||||
"icon": "box",
|
||||
"pages": [
|
||||
"/guides/runtime/import-json",
|
||||
"/guides/runtime/import-toml",
|
||||
"/guides/runtime/import-yaml",
|
||||
"/guides/runtime/import-html",
|
||||
"/guides/util/import-meta-dir",
|
||||
"/guides/util/import-meta-file",
|
||||
"/guides/util/import-meta-path",
|
||||
"/guides/util/entrypoint",
|
||||
"/guides/util/main"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "File System",
|
||||
"icon": "folder",
|
||||
"pages": [
|
||||
"/guides/read-file/string",
|
||||
"/guides/read-file/buffer",
|
||||
"/guides/read-file/uint8array",
|
||||
"/guides/read-file/arraybuffer",
|
||||
"/guides/read-file/json",
|
||||
"/guides/read-file/mime",
|
||||
"/guides/read-file/exists",
|
||||
"/guides/read-file/watch",
|
||||
"/guides/read-file/stream",
|
||||
"/guides/write-file/basic",
|
||||
"/guides/write-file/blob",
|
||||
"/guides/write-file/response",
|
||||
"/guides/write-file/append",
|
||||
"/guides/write-file/filesink",
|
||||
"/guides/write-file/stream",
|
||||
"/guides/write-file/stdout",
|
||||
"/guides/write-file/cat",
|
||||
"/guides/write-file/file-cp",
|
||||
"/guides/write-file/unlink",
|
||||
"/guides/runtime/delete-file",
|
||||
"/guides/runtime/delete-directory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Utilities",
|
||||
"icon": "wrench",
|
||||
"pages": [
|
||||
"/guides/util/hash-a-password",
|
||||
"/guides/util/javascript-uuid",
|
||||
"/guides/util/base64",
|
||||
"/guides/util/gzip",
|
||||
"/guides/util/deflate",
|
||||
"/guides/util/escape-html",
|
||||
"/guides/util/deep-equals",
|
||||
"/guides/util/sleep",
|
||||
"/guides/util/file-url-to-path",
|
||||
"/guides/util/path-to-file-url",
|
||||
"/guides/util/which-path-to-executable-bin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "HTML Processing",
|
||||
"icon": "file-code",
|
||||
"pages": ["/guides/html-rewriter/extract-links", "/guides/html-rewriter/extract-social-meta"]
|
||||
},
|
||||
{
|
||||
"group": "Binary Data",
|
||||
"icon": "binary",
|
||||
"pages": [
|
||||
"/guides/binary/arraybuffer-to-string",
|
||||
"/guides/binary/arraybuffer-to-buffer",
|
||||
"/guides/binary/arraybuffer-to-blob",
|
||||
"/guides/binary/arraybuffer-to-array",
|
||||
"/guides/binary/arraybuffer-to-typedarray",
|
||||
"/guides/binary/buffer-to-string",
|
||||
"/guides/binary/buffer-to-arraybuffer",
|
||||
"/guides/binary/buffer-to-blob",
|
||||
"/guides/binary/buffer-to-typedarray",
|
||||
"/guides/binary/buffer-to-readablestream",
|
||||
"/guides/binary/blob-to-string",
|
||||
"/guides/binary/blob-to-arraybuffer",
|
||||
"/guides/binary/blob-to-typedarray",
|
||||
"/guides/binary/blob-to-dataview",
|
||||
"/guides/binary/blob-to-stream",
|
||||
"/guides/binary/typedarray-to-string",
|
||||
"/guides/binary/typedarray-to-arraybuffer",
|
||||
"/guides/binary/typedarray-to-buffer",
|
||||
"/guides/binary/typedarray-to-blob",
|
||||
"/guides/binary/typedarray-to-dataview",
|
||||
"/guides/binary/typedarray-to-readablestream",
|
||||
"/guides/binary/dataview-to-string"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Streams",
|
||||
"icon": "waves",
|
||||
"pages": [
|
||||
"/guides/streams/to-string",
|
||||
"/guides/streams/to-json",
|
||||
"/guides/streams/to-blob",
|
||||
"/guides/streams/to-buffer",
|
||||
"/guides/streams/to-arraybuffer",
|
||||
"/guides/streams/to-typedarray",
|
||||
"/guides/streams/to-array",
|
||||
"/guides/streams/node-readable-to-string",
|
||||
"/guides/streams/node-readable-to-json",
|
||||
"/guides/streams/node-readable-to-blob",
|
||||
"/guides/streams/node-readable-to-uint8array",
|
||||
"/guides/streams/node-readable-to-arraybuffer"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Reference",
|
||||
"icon": "book",
|
||||
"href": "https://bun.com/reference"
|
||||
},
|
||||
{
|
||||
"tab": "Blog",
|
||||
"icon": "newspaper",
|
||||
"href": "https://bun.com/blog"
|
||||
},
|
||||
{
|
||||
"tab": "Feedback",
|
||||
"icon": "lightbulb",
|
||||
"pages": ["/feedback"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"footer": {
|
||||
"socials": {
|
||||
"x": "https://x.com/bunjavascript",
|
||||
"github": "https://github.com/oven-sh/bun",
|
||||
"discord": "https://bun.com/discord",
|
||||
"youtube": "https://www.youtube.com/@bunjs"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
[Elysia](https://elysiajs.com) is a Bun-first performance focused web framework that takes full advantage of Bun's HTTP, file system, and hot reloading APIs.
|
||||
Designed with TypeScript in mind, you don't need to understand TypeScript to gain the benefit of TypeScript with Elysia. The library understands what you want and automatically infers the type from your code.
|
||||
|
||||
⚡️ Elysia is [one of the fastest Bun web frameworks](https://github.com/SaltyAom/bun-http-framework-benchmark)
|
||||
|
||||
```ts#server.ts
|
||||
import { Elysia } from 'elysia'
|
||||
|
||||
const app = new Elysia()
|
||||
.get('/', () => 'Hello Elysia')
|
||||
.listen(8080)
|
||||
|
||||
console.log(`🦊 Elysia is running at on port ${app.server.port}...`)
|
||||
```
|
||||
|
||||
Get started with `bun create`.
|
||||
|
||||
```bash
|
||||
$ bun create elysia ./myapp
|
||||
$ cd myapp
|
||||
$ bun run dev
|
||||
```
|
||||
|
||||
Refer to the Elysia [documentation](https://elysiajs.com/quick-start.html) for more information.
|
||||
@@ -1,37 +0,0 @@
|
||||
Projects that use Express and other major Node.js HTTP libraries should work out of the box.
|
||||
|
||||
{% callout %}
|
||||
If you run into bugs, [please file an issue](https://bun.com/issues) _in Bun's repo_, not the library. It is Bun's responsibility to address Node.js compatibility issues.
|
||||
{% /callout %}
|
||||
|
||||
```ts
|
||||
import express from "express";
|
||||
|
||||
const app = express();
|
||||
const port = 8080;
|
||||
|
||||
app.get("/", (req, res) => {
|
||||
res.send("Hello World!");
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Listening on port ${port}...`);
|
||||
});
|
||||
```
|
||||
|
||||
Bun implements the [`node:http`](https://nodejs.org/api/http.html) and [`node:https`](https://nodejs.org/api/https.html) modules that these libraries rely on. These modules can also be used directly, though [`Bun.serve`](https://bun.com/docs/api/http) is recommended for most use cases.
|
||||
|
||||
{% callout %}
|
||||
**Note** — Refer to the [Runtime > Node.js APIs](https://bun.com/docs/runtime/nodejs-apis#node-http) page for more detailed compatibility information.
|
||||
{% /callout %}
|
||||
|
||||
```ts
|
||||
import * as http from "node:http";
|
||||
|
||||
http
|
||||
.createServer(function (req, res) {
|
||||
res.write("Hello World!");
|
||||
res.end();
|
||||
})
|
||||
.listen(8080);
|
||||
```
|
||||
@@ -1,18 +0,0 @@
|
||||
[Hono](https://github.com/honojs/hono) is a lightweight ultrafast web framework designed for the edge.
|
||||
|
||||
```ts
|
||||
import { Hono } from "hono";
|
||||
const app = new Hono();
|
||||
|
||||
app.get("/", c => c.text("Hono!"));
|
||||
|
||||
export default app;
|
||||
```
|
||||
|
||||
Get started with `bun create` or follow Hono's [Bun quickstart](https://hono.dev/getting-started/bun).
|
||||
|
||||
```bash
|
||||
$ bun create hono ./myapp
|
||||
$ cd myapp
|
||||
$ bun run start
|
||||
```
|
||||
@@ -1,65 +0,0 @@
|
||||
Bun supports `.jsx` and `.tsx` files out of the box. Bun's internal transpiler converts JSX syntax into vanilla JavaScript before execution.
|
||||
|
||||
```tsx#react.tsx
|
||||
function Component(props: {message: string}) {
|
||||
return (
|
||||
<body>
|
||||
<h1 style={{color: 'red'}}>{props.message}</h1>
|
||||
</body>
|
||||
);
|
||||
}
|
||||
|
||||
console.log(<Component message="Hello world!" />);
|
||||
```
|
||||
|
||||
Bun implements special logging for JSX to make debugging easier.
|
||||
|
||||
```bash
|
||||
$ bun run react.tsx
|
||||
<Component message="Hello world!" />
|
||||
```
|
||||
|
||||
### Prop punning
|
||||
|
||||
The Bun runtime also supports "prop punning" for JSX. This is a shorthand syntax useful for assigning a variable to a prop with the same name.
|
||||
|
||||
```tsx
|
||||
function Div(props: {className: string;}) {
|
||||
const {className} = props;
|
||||
|
||||
// without punning
|
||||
return <div className={className} />;
|
||||
// with punning
|
||||
return <div {className} />;
|
||||
}
|
||||
```
|
||||
|
||||
### Server-side rendering
|
||||
|
||||
To server-side render (SSR) React in an [HTTP server](https://bun.com/docs/api/http):
|
||||
|
||||
```tsx#ssr.tsx
|
||||
import {renderToReadableStream} from 'react-dom/server';
|
||||
|
||||
function Component(props: {message: string}) {
|
||||
return (
|
||||
<body>
|
||||
<h1 style={{color: 'red'}}>{props.message}</h1>
|
||||
</body>
|
||||
);
|
||||
}
|
||||
|
||||
Bun.serve({
|
||||
port: 4000,
|
||||
async fetch() {
|
||||
const stream = await renderToReadableStream(
|
||||
<Component message="Hello from server!" />
|
||||
);
|
||||
return new Response(stream, {
|
||||
headers: {'Content-Type': 'text/html'},
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
React `18.3` and later includes an [SSR optimization](https://github.com/facebook/react/pull/25597) that takes advantage of Bun's "direct" `ReadableStream` implementation.
|
||||
@@ -1,38 +0,0 @@
|
||||
[Stric](https://github.com/bunsvr) is a minimalist, fast web framework for Bun.
|
||||
|
||||
```ts#index.ts
|
||||
import { Router } from '@stricjs/router';
|
||||
|
||||
// Export the fetch handler and serve with Bun
|
||||
export default new Router()
|
||||
// Return 'Hi' on every request
|
||||
.get('/', () => new Response('Hi'));
|
||||
```
|
||||
|
||||
Stric provides support for [ArrowJS](https://www.arrow-js.com), a library for building reactive interfaces.
|
||||
|
||||
{% codetabs %}
|
||||
|
||||
```ts#src/App.ts
|
||||
import { html } from '@stricjs/arrow/utils';
|
||||
|
||||
// Code inside this function can use web APIs
|
||||
export function render() {
|
||||
// Render a <p> element with text 'Hi'
|
||||
html`<p>Hi</p>`;
|
||||
};
|
||||
|
||||
// Set the path to handle
|
||||
export const path = '/';
|
||||
```
|
||||
|
||||
```ts#index.ts
|
||||
import { PageRouter } from '@stricjs/arrow';
|
||||
|
||||
// Create a page router, build and serve directly
|
||||
new PageRouter().serve();
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
|
||||
For more info, see Stric's [documentation](https://stricjs.gitbook.io/docs).
|
||||
85
docs/feedback.mdx
Normal file
85
docs/feedback.mdx
Normal file
@@ -0,0 +1,85 @@
|
||||
---
|
||||
title: Feedback
|
||||
description: Share feedback, bug reports, and feature requests
|
||||
mode: center
|
||||
---
|
||||
|
||||
import Feedback from "/snippets/cli/feedback.mdx";
|
||||
|
||||
Whether you've found a bug, have a performance issue, or just want to suggest an improvement, here's how you can open a helpful issue:
|
||||
|
||||
<Callout icon="discord">
|
||||
For general questions, please join our [Discord](https://discord.com/invite/CXdq2DP29u).
|
||||
</Callout>
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
<Steps>
|
||||
<Step title="Upgrade Bun">
|
||||
Try upgrading Bun to the latest version with `bun upgrade`. This might fix your problem without having to open an issue.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun upgrade
|
||||
```
|
||||
|
||||
You can also try the latest canary release, which includes the most recent changes and bug fixes that haven't been released in a stable version yet.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun upgrade --canary
|
||||
|
||||
# To revert back to the stable
|
||||
bun upgrade --stable
|
||||
```
|
||||
|
||||
If the issue still persists after upgrading, continue to the next step.
|
||||
</Step>
|
||||
<Step title="Review Existing Issues">
|
||||
First take a minute to check if the issue has already been reported. Don't open a new issue if it has already been reported, it saves time for everyone and helps us focus on fixing things faster.
|
||||
|
||||
- 🔍 [**Search existing issues**](https://github.com/oven-sh/bun/issues)
|
||||
- 💬 [**Check discussions**](https://github.com/oven-sh/bun/discussions)
|
||||
|
||||
If you find a related issue, add a 👍 reaction or comment with extra details instead of opening a new one.
|
||||
</Step>
|
||||
<Step title="Report the Issue">
|
||||
If no one has reported the issue, please open a new issue or suggest an improvement.
|
||||
|
||||
- 🐞 [**Report a Bug**](https://github.com/oven-sh/bun/issues/new?template=2-bug-report.yml)
|
||||
- ⚡ [**Suggest an Improvement**](https://github.com/oven-sh/bun/issues/new?template=4-feature-request.yml)
|
||||
|
||||
Please provide as much detail as possible, including:
|
||||
- A clear and concise title
|
||||
- A code example or steps to reproduce the issue
|
||||
- The version of Bun you are using (run `bun --version`)
|
||||
- A detailed description of the issue (what happened, what you expected to happen, and what actually happened)
|
||||
- The operating system and version you are using
|
||||
<Note>
|
||||
- For MacOS and Linux: copy the output of `uname -mprs`
|
||||
- For Windows: copy the output of this command in the powershell console:
|
||||
```powershell
|
||||
"$([Environment]::OSVersion | ForEach-Object VersionString) $(if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" })"
|
||||
```
|
||||
</Note>
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
The Bun team will review the issue and get back to you as soon as possible!
|
||||
|
||||
---
|
||||
|
||||
## Use `bun feedback`
|
||||
|
||||
Alternatively, you can use `bun feedback` to share feedback, bug reports, and feature requests directly with the Bun team.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun feedback "Love the new release!"
|
||||
bun feedback report.txt details.log
|
||||
echo "please document X" | bun feedback --email you@example.com
|
||||
```
|
||||
|
||||
You can provide feedback as text arguments, file paths, or piped input.
|
||||
|
||||
---
|
||||
|
||||
<Feedback />
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
name: Convert an ArrayBuffer to an array of numbers
|
||||
title: Convert an ArrayBuffer to an array of numbers
|
||||
sidebarTitle: "ArrayBuffer to Array"
|
||||
mode: center
|
||||
---
|
||||
|
||||
To retrieve the contents of an `ArrayBuffer` as an array of numbers, create a [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) over of the buffer. and use the [`Array.from()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) method to convert it to an array.
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
name: Convert an ArrayBuffer to a Blob
|
||||
title: Convert an ArrayBuffer to a Blob
|
||||
sidebarTitle: "ArrayBuffer to Blob"
|
||||
mode: center
|
||||
---
|
||||
|
||||
A [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) can be constructed from an array of "chunks", where each chunk is a string, binary data structure, or another `Blob`.
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
name: Convert an ArrayBuffer to a Buffer
|
||||
title: Convert an ArrayBuffer to a Buffer
|
||||
sidebarTitle: "ArrayBuffer to Buffer"
|
||||
mode: center
|
||||
---
|
||||
|
||||
The Node.js [`Buffer`](https://nodejs.org/api/buffer.html) API predates the introduction of `ArrayBuffer` into the JavaScript language. Bun implements both.
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
name: Convert an ArrayBuffer to a string
|
||||
title: Convert an ArrayBuffer to a string
|
||||
sidebarTitle: "ArrayBuffer to string"
|
||||
mode: center
|
||||
---
|
||||
|
||||
Bun implements the Web-standard [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder) class for converting between binary data types and strings.
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
name: Convert an ArrayBuffer to a Uint8Array
|
||||
title: Convert an ArrayBuffer to a Uint8Array
|
||||
sidebarTitle: "ArrayBuffer to Uint8Array"
|
||||
mode: center
|
||||
---
|
||||
|
||||
A `Uint8Array` is a _typed array_, meaning it is a mechanism for viewing the data in an underlying `ArrayBuffer`.
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
name: Convert a Blob to an ArrayBuffer
|
||||
title: Convert a Blob to an ArrayBuffer
|
||||
sidebarTitle: "Blob to ArrayBuffer"
|
||||
mode: center
|
||||
---
|
||||
|
||||
The [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) class provides a number of methods for consuming its contents in different formats, including `.arrayBuffer()`.
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
name: Convert a Blob to a DataView
|
||||
title: Convert a Blob to a DataView
|
||||
sidebarTitle: "Blob to DataView"
|
||||
mode: center
|
||||
---
|
||||
|
||||
The [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) class provides a number of methods for consuming its contents in different formats. This snippets reads the contents to an `ArrayBuffer`, then creates a `DataView` from the buffer.
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
name: Convert a Blob to a ReadableStream
|
||||
title: Convert a Blob to a ReadableStream
|
||||
sidebarTitle: "Blob to ReadableStream"
|
||||
mode: center
|
||||
---
|
||||
|
||||
The [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) class provides a number of methods for consuming its contents in different formats, including `.stream()`. This returns `Promise<ReadableStream>`.
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
name: Convert a Blob to a string
|
||||
title: Convert a Blob to a string
|
||||
sidebarTitle: "Blob to string"
|
||||
mode: center
|
||||
---
|
||||
|
||||
The [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) class provides a number of methods for consuming its contents in different formats, including `.text()`.
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
name: Convert a Blob to a Uint8Array
|
||||
title: Convert a Blob to a Uint8Array
|
||||
sidebarTitle: "Blob to Uint8Array"
|
||||
mode: center
|
||||
---
|
||||
|
||||
The [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) class provides a number of methods for consuming its contents in different formats. This snippets reads the contents to an `ArrayBuffer`, then creates a `Uint8Array` from the buffer.
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
name: Convert a Buffer to an ArrayBuffer
|
||||
title: Convert a Buffer to an ArrayBuffer
|
||||
sidebarTitle: "Buffer to ArrayBuffer"
|
||||
mode: center
|
||||
---
|
||||
|
||||
The Node.js [`Buffer`](https://nodejs.org/api/buffer.html) class provides a way to view and manipulate data in an underlying `ArrayBuffer`, which is available via the `buffer` property.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user