mirror of
https://github.com/oven-sh/bun
synced 2026-02-25 02:57:27 +01:00
Compare commits
4 Commits
claude/imp
...
claude/doc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b9951cf77 | ||
|
|
ad9a16f5a1 | ||
|
|
3ce7e90ced | ||
|
|
65c68ef645 |
@@ -114,8 +114,6 @@ const buildPlatforms = [
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.22" },
|
||||
{ os: "windows", arch: "x64", release: "2019" },
|
||||
{ os: "windows", arch: "x64", baseline: true, release: "2019" },
|
||||
// TODO: Enable when Windows ARM64 CI runners are ready
|
||||
// { os: "windows", arch: "aarch64", release: "2019" },
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -138,8 +136,6 @@ const testPlatforms = [
|
||||
{ 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" },
|
||||
// TODO: Enable when Windows ARM64 CI runners are ready
|
||||
// { os: "windows", arch: "aarch64", release: "2019", tier: "oldest" },
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -592,7 +588,7 @@ function getTestBunStep(platform, options, testOptions = {}) {
|
||||
agents: getTestAgent(platform, options),
|
||||
retry: getRetry(),
|
||||
cancel_on_build_failing: isMergeQueue(),
|
||||
parallelism: os === "darwin" ? 2 : 20,
|
||||
parallelism: os === "darwin" ? 2 : 10,
|
||||
timeout_in_minutes: profile === "asan" || os === "windows" ? 45 : 30,
|
||||
env: {
|
||||
ASAN_OPTIONS: "allow_user_segv_handler=1:disable_coredump=0:detect_leaks=0",
|
||||
|
||||
@@ -36,20 +36,16 @@ function Log-Debug {
|
||||
}
|
||||
}
|
||||
|
||||
# Detect system architecture
|
||||
$script:IsARM64 = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq [System.Runtime.InteropServices.Architecture]::Arm64
|
||||
$script:VsArch = if ($script:IsARM64) { "arm64" } else { "amd64" }
|
||||
|
||||
# Load Visual Studio environment if not already loaded
|
||||
function Ensure-VSEnvironment {
|
||||
if ($null -eq $env:VSINSTALLDIR) {
|
||||
Log-Info "Loading Visual Studio environment for $script:VsArch..."
|
||||
|
||||
Log-Info "Loading Visual Studio environment..."
|
||||
|
||||
$vswhere = "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||
if (!(Test-Path $vswhere)) {
|
||||
throw "Command not found: vswhere (did you install Visual Studio?)"
|
||||
}
|
||||
|
||||
|
||||
$vsDir = & $vswhere -prerelease -latest -property installationPath
|
||||
if ($null -eq $vsDir) {
|
||||
$vsDir = Get-ChildItem -Path "C:\Program Files\Microsoft Visual Studio\2022" -Directory -ErrorAction SilentlyContinue
|
||||
@@ -58,20 +54,20 @@ function Ensure-VSEnvironment {
|
||||
}
|
||||
$vsDir = $vsDir.FullName
|
||||
}
|
||||
|
||||
|
||||
Push-Location $vsDir
|
||||
try {
|
||||
$vsShell = Join-Path -Path $vsDir -ChildPath "Common7\Tools\Launch-VsDevShell.ps1"
|
||||
. $vsShell -Arch $script:VsArch -HostArch $script:VsArch
|
||||
. $vsShell -Arch amd64 -HostArch amd64
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
|
||||
Log-Success "Visual Studio environment loaded"
|
||||
}
|
||||
|
||||
|
||||
if ($env:VSCMD_ARG_TGT_ARCH -eq "x86") {
|
||||
throw "Visual Studio environment is targeting 32 bit x86, but only 64-bit architectures (x64/arm64) are supported."
|
||||
throw "Visual Studio environment is targeting 32 bit, but only 64 bit is supported."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,10 +186,8 @@ function Install-KeyLocker {
|
||||
}
|
||||
|
||||
# Download MSI installer
|
||||
# Note: KeyLocker tools currently only available for x64, but works on ARM64 via emulation
|
||||
$msiArch = "x64"
|
||||
$msiUrl = "https://bun-ci-assets.bun.sh/Keylockertools-windows-${msiArch}.msi"
|
||||
$msiPath = Join-Path $env:TEMP "Keylockertools-windows-${msiArch}.msi"
|
||||
$msiUrl = "https://bun-ci-assets.bun.sh/Keylockertools-windows-x64.msi"
|
||||
$msiPath = Join-Path $env:TEMP "Keylockertools-windows-x64.msi"
|
||||
|
||||
Log-Info "Downloading MSI from: $msiUrl"
|
||||
Log-Info "Downloading to: $msiPath"
|
||||
|
||||
@@ -219,9 +219,6 @@ function create_release() {
|
||||
bun-windows-x64-profile.zip
|
||||
bun-windows-x64-baseline.zip
|
||||
bun-windows-x64-baseline-profile.zip
|
||||
# TODO: Enable when Windows ARM64 CI runners are ready
|
||||
# bun-windows-aarch64.zip
|
||||
# bun-windows-aarch64-profile.zip
|
||||
)
|
||||
|
||||
function upload_artifact() {
|
||||
|
||||
@@ -6,7 +6,8 @@ To do that:
|
||||
- git fetch upstream
|
||||
- git merge upstream main
|
||||
- Fix the merge conflicts
|
||||
- bun build.ts debug
|
||||
- cd ../../ (back to bun)
|
||||
- make jsc-build (this will take about 7 minutes)
|
||||
- While it compiles, in another task review the JSC commits between the last version of Webkit and the new version. Write up a summary of the webkit changes in a file called "webkit-changes.md"
|
||||
- bun run build:local (build a build of Bun with the new Webkit, make sure it compiles)
|
||||
- After making sure it compiles, run some code to make sure things work. something like ./build/debug-local/bun-debug --print '42' should be all you need
|
||||
@@ -20,7 +21,3 @@ To do that:
|
||||
- commit + push (without adding the webkit-changes.md file)
|
||||
- create PR titled "Upgrade Webkit to the <commit-sha>", paste your webkit-changes.md into the PR description
|
||||
- delete the webkit-changes.md file
|
||||
|
||||
Things to check for a successful upgrade:
|
||||
- Did JSType in vendor/WebKit/Source/JavaScriptCore have any recent changes? Does the enum values align with whats present in src/bun.js/bindings/JSType.zig?
|
||||
- Were there any changes to the webcore code generator? If there are C++ compilation errors, check for differences in some of the generated code in like vendor/WebKit/source/WebCore/bindings/scripts/test/JS/
|
||||
|
||||
2
.github/workflows/update-cares.yml
vendored
2
.github/workflows/update-cares.yml
vendored
@@ -88,7 +88,7 @@ jobs:
|
||||
commit-message: "deps: update c-ares to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update c-ares to ${{ steps.check-version.outputs.tag }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-cares
|
||||
branch: deps/update-cares-${{ github.run_number }}
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-hdrhistogram.yml
vendored
2
.github/workflows/update-hdrhistogram.yml
vendored
@@ -91,7 +91,7 @@ jobs:
|
||||
commit-message: "deps: update hdrhistogram to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update hdrhistogram to ${{ steps.check-version.outputs.tag }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-hdrhistogram
|
||||
branch: deps/update-hdrhistogram-${{ github.run_number }}
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-highway.yml
vendored
2
.github/workflows/update-highway.yml
vendored
@@ -107,7 +107,7 @@ jobs:
|
||||
commit-message: "deps: update highway to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update highway to ${{ steps.check-version.outputs.tag }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-highway
|
||||
branch: deps/update-highway-${{ github.run_number }}
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-libarchive.yml
vendored
2
.github/workflows/update-libarchive.yml
vendored
@@ -88,7 +88,7 @@ jobs:
|
||||
commit-message: "deps: update libarchive to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update libarchive to ${{ steps.check-version.outputs.tag }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-libarchive
|
||||
branch: deps/update-libarchive-${{ github.run_number }}
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-libdeflate.yml
vendored
2
.github/workflows/update-libdeflate.yml
vendored
@@ -88,7 +88,7 @@ jobs:
|
||||
commit-message: "deps: update libdeflate to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update libdeflate to ${{ steps.check-version.outputs.tag }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-libdeflate
|
||||
branch: deps/update-libdeflate-${{ github.run_number }}
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-lolhtml.yml
vendored
2
.github/workflows/update-lolhtml.yml
vendored
@@ -100,7 +100,7 @@ jobs:
|
||||
commit-message: "deps: update lolhtml to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update lolhtml to ${{ steps.check-version.outputs.tag }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-lolhtml
|
||||
branch: deps/update-lolhtml-${{ github.run_number }}
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-lshpack.yml
vendored
2
.github/workflows/update-lshpack.yml
vendored
@@ -105,7 +105,7 @@ jobs:
|
||||
commit-message: "deps: update lshpack to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update lshpack to ${{ steps.check-version.outputs.tag }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-lshpack
|
||||
branch: deps/update-lshpack-${{ github.run_number }}
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-root-certs.yml
vendored
2
.github/workflows/update-root-certs.yml
vendored
@@ -74,7 +74,7 @@ jobs:
|
||||
```
|
||||
${{ env.changed_files }}
|
||||
```
|
||||
branch: certs/update-root-certs
|
||||
branch: certs/update-root-certs-${{ github.run_number }}
|
||||
base: main
|
||||
delete-branch: true
|
||||
labels:
|
||||
|
||||
2
.github/workflows/update-sqlite3.yml
vendored
2
.github/workflows/update-sqlite3.yml
vendored
@@ -83,7 +83,7 @@ jobs:
|
||||
commit-message: "deps: update sqlite to ${{ steps.check-version.outputs.latest }}"
|
||||
title: "deps: update sqlite to ${{ steps.check-version.outputs.latest }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-sqlite
|
||||
branch: deps/update-sqlite-${{ steps.check-version.outputs.latest }}
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-vendor.yml
vendored
2
.github/workflows/update-vendor.yml
vendored
@@ -68,7 +68,7 @@ jobs:
|
||||
commit-message: "deps: update ${{ matrix.package }} to ${{ steps.check-version.outputs.latest }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update ${{ matrix.package }} to ${{ steps.check-version.outputs.latest }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-${{ matrix.package }}
|
||||
branch: deps/update-${{ matrix.package }}-${{ github.run_number }}
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
2
.github/workflows/update-zstd.yml
vendored
2
.github/workflows/update-zstd.yml
vendored
@@ -88,7 +88,7 @@ jobs:
|
||||
commit-message: "deps: update zstd to ${{ steps.check-version.outputs.tag }} (${{ steps.check-version.outputs.latest }})"
|
||||
title: "deps: update zstd to ${{ steps.check-version.outputs.tag }}"
|
||||
delete-branch: true
|
||||
branch: deps/update-zstd
|
||||
branch: deps/update-zstd-${{ github.run_number }}
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,4 @@
|
||||
.claude/settings.local.json
|
||||
.direnv
|
||||
.DS_Store
|
||||
.env
|
||||
.envrc
|
||||
|
||||
23
CLAUDE.md
23
CLAUDE.md
@@ -10,8 +10,6 @@ This is the Bun repository - an all-in-one JavaScript runtime & toolkit designed
|
||||
- **Run tests with your debug build**: `bun bd test <test-file>`
|
||||
- **CRITICAL**: Never use `bun test` directly - it won't include your changes
|
||||
- **Run any command with debug build**: `bun bd <command>`
|
||||
- **Run with JavaScript exception scope verification**: `BUN_JSC_validateExceptionChecks=1
|
||||
BUN_JSC_dumpSimulatedThrows=1 bun bd <command>`
|
||||
|
||||
Tip: Bun is already installed and in $PATH. The `bd` subcommand is a package.json script.
|
||||
|
||||
@@ -211,24 +209,3 @@ Built-in JavaScript modules use special syntax and are organized as:
|
||||
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.
|
||||
|
||||
## Debugging CI Failures
|
||||
|
||||
Use `scripts/buildkite-failures.ts` to fetch and analyze CI build failures:
|
||||
|
||||
```bash
|
||||
# View failures for current branch
|
||||
bun run scripts/buildkite-failures.ts
|
||||
|
||||
# View failures for a specific build number
|
||||
bun run scripts/buildkite-failures.ts 35051
|
||||
|
||||
# View failures for a GitHub PR
|
||||
bun run scripts/buildkite-failures.ts #26173
|
||||
bun run scripts/buildkite-failures.ts https://github.com/oven-sh/bun/pull/26173
|
||||
|
||||
# Wait for build to complete (polls every 10s until pass/fail)
|
||||
bun run scripts/buildkite-failures.ts --wait
|
||||
```
|
||||
|
||||
The script fetches logs from BuildKite's public API and saves complete logs to `/tmp/bun-build-{number}-{platform}-{step}.log`. It displays a summary of errors and the file path for each failed job. Use `--wait` to poll continuously until the build completes or fails.
|
||||
|
||||
@@ -36,7 +36,6 @@ Bun statically links these libraries:
|
||||
| [`libbase64`](https://github.com/aklomp/base64/blob/master/LICENSE) | BSD 2-Clause |
|
||||
| [`libuv`](https://github.com/libuv/libuv) (on Windows) | MIT |
|
||||
| [`libdeflate`](https://github.com/ebiggers/libdeflate) | MIT |
|
||||
| [`uucode`](https://github.com/jacobsandlund/uucode) | MIT |
|
||||
| A fork of [`uWebsockets`](https://github.com/jarred-sumner/uwebsockets) | Apache 2.0 licensed |
|
||||
| Parts of [Tigerbeetle's IO code](https://github.com/tigerbeetle/tigerbeetle/blob/532c8b70b9142c17e07737ab6d3da68d7500cbca/src/io/windows.zig#L1) | Apache 2.0 licensed |
|
||||
|
||||
|
||||
@@ -23,9 +23,7 @@
|
||||
"react-dom": "^18.3.1",
|
||||
"string-width": "7.1.0",
|
||||
"strip-ansi": "^7.1.0",
|
||||
"tar": "^7.4.3",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"wrap-ansi": "^9.0.0",
|
||||
"zx": "^7.2.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -110,8 +108,6 @@
|
||||
|
||||
"@fastify/proxy-addr": ["@fastify/proxy-addr@5.0.0", "", { "dependencies": { "@fastify/forwarded": "^3.0.0", "ipaddr.js": "^2.1.0" } }, "sha512-37qVVA1qZ5sgH7KpHkkC4z9SK6StIsIcOmpjvMPXNb3vx2GQxhZocogVYbr2PbbeLCQxYIPDok307xEvRZOzGA=="],
|
||||
|
||||
"@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
|
||||
|
||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.1.1", "", { "dependencies": { "@jridgewell/set-array": "^1.0.0", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w=="],
|
||||
|
||||
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.0", "", {}, "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="],
|
||||
@@ -170,7 +166,7 @@
|
||||
|
||||
"ansi-regex": ["ansi-regex@6.0.1", "", {}, "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA=="],
|
||||
|
||||
"ansi-styles": ["ansi-styles@6.2.3", "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/ansi-styles/-/ansi-styles-6.2.3.tgz", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||
"ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
|
||||
|
||||
"atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="],
|
||||
|
||||
@@ -186,8 +182,6 @@
|
||||
|
||||
"chalk": ["chalk@5.3.0", "", {}, "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w=="],
|
||||
|
||||
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
|
||||
|
||||
"color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
@@ -368,10 +362,6 @@
|
||||
|
||||
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
||||
|
||||
"minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
||||
|
||||
"minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="],
|
||||
|
||||
"mitata": ["mitata@1.0.25", "", {}, "sha512-0v5qZtVW5vwj9FDvYfraR31BMDcRLkhSFWPTLaxx/Z3/EvScfVtAAWtMI2ArIbBcwh7P86dXh0lQWKiXQPlwYA=="],
|
||||
|
||||
"ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
|
||||
@@ -468,8 +458,6 @@
|
||||
|
||||
"supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="],
|
||||
|
||||
"tar": ["tar@7.5.2", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg=="],
|
||||
|
||||
"thread-stream": ["thread-stream@3.1.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A=="],
|
||||
|
||||
"through": ["through@2.3.8", "", {}, "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="],
|
||||
@@ -494,9 +482,7 @@
|
||||
|
||||
"which": ["which@3.0.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg=="],
|
||||
|
||||
"wrap-ansi": ["wrap-ansi@9.0.2", "https://artifactory.infra.ant.dev:443/artifactory/api/npm/npm-all/wrap-ansi/-/wrap-ansi-9.0.2.tgz", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
|
||||
|
||||
"yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
|
||||
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
|
||||
"yaml": ["yaml@2.3.4", "", {}, "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA=="],
|
||||
|
||||
@@ -506,6 +492,8 @@
|
||||
|
||||
"@babel/highlight/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
|
||||
|
||||
"ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
|
||||
|
||||
"avvio/fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
|
||||
|
||||
"cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
@@ -514,14 +502,8 @@
|
||||
|
||||
"light-my-request/process-warning": ["process-warning@4.0.1", "", {}, "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q=="],
|
||||
|
||||
"lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
|
||||
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
|
||||
|
||||
"@babel/highlight/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
|
||||
|
||||
"@babel/highlight/chalk/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
|
||||
|
||||
"@babel/highlight/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
|
||||
"ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "json5-benchmark",
|
||||
"dependencies": {
|
||||
"json5": "^2.2.3",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
import JSON5 from "json5";
|
||||
import { bench, group, run } from "../runner.mjs";
|
||||
|
||||
const isBun = typeof Bun !== "undefined" && Bun.JSON5;
|
||||
|
||||
function sizeLabel(n) {
|
||||
if (n >= 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)}MB`;
|
||||
if (n >= 1024) return `${(n / 1024).toFixed(0)}KB`;
|
||||
return `${n}B`;
|
||||
}
|
||||
|
||||
// -- parse inputs --
|
||||
|
||||
const smallJson5 = `{
|
||||
// User profile
|
||||
name: "John Doe",
|
||||
age: 30,
|
||||
email: 'john@example.com',
|
||||
active: true,
|
||||
}`;
|
||||
|
||||
function generateLargeJson5(count) {
|
||||
const lines = ["{\n // Auto-generated dataset\n items: [\n"];
|
||||
for (let i = 0; i < count; i++) {
|
||||
lines.push(` {
|
||||
id: ${i},
|
||||
name: 'item_${i}',
|
||||
value: ${(Math.random() * 1000).toFixed(2)},
|
||||
hex: 0x${i.toString(16).toUpperCase()},
|
||||
active: ${i % 2 === 0},
|
||||
tags: ['tag_${i % 10}', 'category_${i % 5}',],
|
||||
// entry ${i}
|
||||
},\n`);
|
||||
}
|
||||
lines.push(" ],\n total: " + count + ",\n status: 'complete',\n}\n");
|
||||
return lines.join("");
|
||||
}
|
||||
|
||||
const largeJson5 = generateLargeJson5(6500);
|
||||
|
||||
// -- stringify inputs --
|
||||
|
||||
const smallObject = {
|
||||
name: "John Doe",
|
||||
age: 30,
|
||||
email: "john@example.com",
|
||||
active: true,
|
||||
};
|
||||
|
||||
const largeObject = {
|
||||
items: Array.from({ length: 10000 }, (_, i) => ({
|
||||
id: i,
|
||||
name: `item_${i}`,
|
||||
value: +(Math.random() * 1000).toFixed(2),
|
||||
active: i % 2 === 0,
|
||||
tags: [`tag_${i % 10}`, `category_${i % 5}`],
|
||||
})),
|
||||
total: 10000,
|
||||
status: "complete",
|
||||
};
|
||||
|
||||
const stringify = isBun ? Bun.JSON5.stringify : JSON5.stringify;
|
||||
|
||||
// -- parse benchmarks --
|
||||
|
||||
group(`parse small (${sizeLabel(smallJson5.length)})`, () => {
|
||||
if (isBun) bench("Bun.JSON5.parse", () => Bun.JSON5.parse(smallJson5));
|
||||
bench("json5.parse", () => JSON5.parse(smallJson5));
|
||||
});
|
||||
|
||||
group(`parse large (${sizeLabel(largeJson5.length)})`, () => {
|
||||
if (isBun) bench("Bun.JSON5.parse", () => Bun.JSON5.parse(largeJson5));
|
||||
bench("json5.parse", () => JSON5.parse(largeJson5));
|
||||
});
|
||||
|
||||
// -- stringify benchmarks --
|
||||
|
||||
group(`stringify small (${sizeLabel(stringify(smallObject).length)})`, () => {
|
||||
if (isBun) bench("Bun.JSON5.stringify", () => Bun.JSON5.stringify(smallObject));
|
||||
bench("json5.stringify", () => JSON5.stringify(smallObject));
|
||||
});
|
||||
|
||||
group(`stringify large (${sizeLabel(stringify(largeObject).length)})`, () => {
|
||||
if (isBun) bench("Bun.JSON5.stringify", () => Bun.JSON5.stringify(largeObject));
|
||||
bench("json5.stringify", () => JSON5.stringify(largeObject));
|
||||
});
|
||||
|
||||
await run();
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"name": "json5-benchmark",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"json5": "^2.2.3"
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,7 @@
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"string-width": "7.1.0",
|
||||
"wrap-ansi": "^9.0.0",
|
||||
"strip-ansi": "^7.1.0",
|
||||
"tar": "^7.4.3",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"zx": "^7.2.3"
|
||||
},
|
||||
|
||||
@@ -14,4 +14,3 @@ export function run(opts = {}) {
|
||||
|
||||
export const bench = Mitata.bench;
|
||||
export const group = Mitata.group;
|
||||
export const summary = Mitata.summary;
|
||||
|
||||
@@ -1,477 +0,0 @@
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { Pack, Unpack } from "tar";
|
||||
import { bench, group, run } from "../runner.mjs";
|
||||
|
||||
// Check if Bun.Archive is available
|
||||
const hasBunArchive = typeof Bun !== "undefined" && typeof Bun.Archive !== "undefined";
|
||||
|
||||
// Test data sizes
|
||||
const smallContent = "Hello, World!";
|
||||
const mediumContent = Buffer.alloc(10 * 1024, "x").toString(); // 10KB
|
||||
const largeContent = Buffer.alloc(100 * 1024, "x").toString(); // 100KB
|
||||
|
||||
// Create test files for node-tar (it reads from filesystem)
|
||||
const setupDir = mkdtempSync(join(tmpdir(), "archive-bench-setup-"));
|
||||
|
||||
function setupNodeTarFiles(prefix, files) {
|
||||
const dir = join(setupDir, prefix);
|
||||
mkdirSync(dir, { recursive: true });
|
||||
for (const [name, content] of Object.entries(files)) {
|
||||
const filePath = join(dir, name);
|
||||
const fileDir = join(filePath, "..");
|
||||
mkdirSync(fileDir, { recursive: true });
|
||||
writeFileSync(filePath, content);
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
// Setup directories for different test cases
|
||||
const smallFilesDir = setupNodeTarFiles("small", {
|
||||
"file1.txt": smallContent,
|
||||
"file2.txt": smallContent,
|
||||
"file3.txt": smallContent,
|
||||
});
|
||||
|
||||
const mediumFilesDir = setupNodeTarFiles("medium", {
|
||||
"file1.txt": mediumContent,
|
||||
"file2.txt": mediumContent,
|
||||
"file3.txt": mediumContent,
|
||||
});
|
||||
|
||||
const largeFilesDir = setupNodeTarFiles("large", {
|
||||
"file1.txt": largeContent,
|
||||
"file2.txt": largeContent,
|
||||
"file3.txt": largeContent,
|
||||
});
|
||||
|
||||
const manyFilesEntries = {};
|
||||
for (let i = 0; i < 100; i++) {
|
||||
manyFilesEntries[`file${i}.txt`] = smallContent;
|
||||
}
|
||||
const manyFilesDir = setupNodeTarFiles("many", manyFilesEntries);
|
||||
|
||||
// Pre-create archives for extraction benchmarks
|
||||
let smallTarGzBuffer, mediumTarGzBuffer, largeTarGzBuffer, manyFilesTarGzBuffer;
|
||||
let smallTarBuffer, mediumTarBuffer, largeTarBuffer, manyFilesTarBuffer;
|
||||
let smallBunArchiveGz, mediumBunArchiveGz, largeBunArchiveGz, manyFilesBunArchiveGz;
|
||||
let smallBunArchive, mediumBunArchive, largeBunArchive, manyFilesBunArchive;
|
||||
|
||||
// Create tar buffer using node-tar (with optional gzip)
|
||||
async function createNodeTarBuffer(cwd, files, gzip = false) {
|
||||
return new Promise(resolve => {
|
||||
const pack = new Pack({ cwd, gzip });
|
||||
const bufs = [];
|
||||
pack.on("data", chunk => bufs.push(chunk));
|
||||
pack.on("end", () => resolve(Buffer.concat(bufs)));
|
||||
for (const file of files) {
|
||||
pack.add(file);
|
||||
}
|
||||
pack.end();
|
||||
});
|
||||
}
|
||||
|
||||
// Extract tar buffer using node-tar
|
||||
async function extractNodeTarBuffer(buffer, cwd) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const unpack = new Unpack({ cwd });
|
||||
unpack.on("end", resolve);
|
||||
unpack.on("error", reject);
|
||||
unpack.end(buffer);
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize gzipped archives
|
||||
smallTarGzBuffer = await createNodeTarBuffer(smallFilesDir, ["file1.txt", "file2.txt", "file3.txt"], true);
|
||||
mediumTarGzBuffer = await createNodeTarBuffer(mediumFilesDir, ["file1.txt", "file2.txt", "file3.txt"], true);
|
||||
largeTarGzBuffer = await createNodeTarBuffer(largeFilesDir, ["file1.txt", "file2.txt", "file3.txt"], true);
|
||||
manyFilesTarGzBuffer = await createNodeTarBuffer(manyFilesDir, Object.keys(manyFilesEntries), true);
|
||||
|
||||
// Initialize uncompressed archives
|
||||
smallTarBuffer = await createNodeTarBuffer(smallFilesDir, ["file1.txt", "file2.txt", "file3.txt"], false);
|
||||
mediumTarBuffer = await createNodeTarBuffer(mediumFilesDir, ["file1.txt", "file2.txt", "file3.txt"], false);
|
||||
largeTarBuffer = await createNodeTarBuffer(largeFilesDir, ["file1.txt", "file2.txt", "file3.txt"], false);
|
||||
manyFilesTarBuffer = await createNodeTarBuffer(manyFilesDir, Object.keys(manyFilesEntries), false);
|
||||
|
||||
const smallFiles = { "file1.txt": smallContent, "file2.txt": smallContent, "file3.txt": smallContent };
|
||||
const mediumFiles = { "file1.txt": mediumContent, "file2.txt": mediumContent, "file3.txt": mediumContent };
|
||||
const largeFiles = { "file1.txt": largeContent, "file2.txt": largeContent, "file3.txt": largeContent };
|
||||
|
||||
if (hasBunArchive) {
|
||||
smallBunArchiveGz = await Bun.Archive.from(smallFiles).bytes("gzip");
|
||||
mediumBunArchiveGz = await Bun.Archive.from(mediumFiles).bytes("gzip");
|
||||
largeBunArchiveGz = await Bun.Archive.from(largeFiles).bytes("gzip");
|
||||
manyFilesBunArchiveGz = await Bun.Archive.from(manyFilesEntries).bytes("gzip");
|
||||
|
||||
smallBunArchive = await Bun.Archive.from(smallFiles).bytes();
|
||||
mediumBunArchive = await Bun.Archive.from(mediumFiles).bytes();
|
||||
largeBunArchive = await Bun.Archive.from(largeFiles).bytes();
|
||||
manyFilesBunArchive = await Bun.Archive.from(manyFilesEntries).bytes();
|
||||
}
|
||||
|
||||
// Create reusable extraction directories (overwriting is fine)
|
||||
const extractDirNodeTar = mkdtempSync(join(tmpdir(), "archive-bench-extract-node-"));
|
||||
const extractDirBun = mkdtempSync(join(tmpdir(), "archive-bench-extract-bun-"));
|
||||
const writeDirNodeTar = mkdtempSync(join(tmpdir(), "archive-bench-write-node-"));
|
||||
const writeDirBun = mkdtempSync(join(tmpdir(), "archive-bench-write-bun-"));
|
||||
|
||||
// ============================================================================
|
||||
// Create .tar (uncompressed) benchmarks
|
||||
// ============================================================================
|
||||
|
||||
group("create .tar (3 small files)", () => {
|
||||
bench("node-tar", async () => {
|
||||
await createNodeTarBuffer(smallFilesDir, ["file1.txt", "file2.txt", "file3.txt"], false);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive", async () => {
|
||||
await Bun.Archive.from(smallFiles).bytes();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("create .tar (3 x 100KB files)", () => {
|
||||
bench("node-tar", async () => {
|
||||
await createNodeTarBuffer(largeFilesDir, ["file1.txt", "file2.txt", "file3.txt"], false);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive", async () => {
|
||||
await Bun.Archive.from(largeFiles).bytes();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("create .tar (100 small files)", () => {
|
||||
bench("node-tar", async () => {
|
||||
await createNodeTarBuffer(manyFilesDir, Object.keys(manyFilesEntries), false);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive", async () => {
|
||||
await Bun.Archive.from(manyFilesEntries).bytes();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Create .tar.gz (compressed) benchmarks
|
||||
// ============================================================================
|
||||
|
||||
group("create .tar.gz (3 small files)", () => {
|
||||
bench("node-tar", async () => {
|
||||
await createNodeTarBuffer(smallFilesDir, ["file1.txt", "file2.txt", "file3.txt"], true);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive", async () => {
|
||||
await Bun.Archive.from(smallFiles).bytes("gzip");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("create .tar.gz (3 x 100KB files)", () => {
|
||||
bench("node-tar", async () => {
|
||||
await createNodeTarBuffer(largeFilesDir, ["file1.txt", "file2.txt", "file3.txt"], true);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive", async () => {
|
||||
await Bun.Archive.from(largeFiles).bytes("gzip");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("create .tar.gz (100 small files)", () => {
|
||||
bench("node-tar", async () => {
|
||||
await createNodeTarBuffer(manyFilesDir, Object.keys(manyFilesEntries), true);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive", async () => {
|
||||
await Bun.Archive.from(manyFilesEntries).bytes("gzip");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Extract .tar (uncompressed) benchmarks
|
||||
// ============================================================================
|
||||
|
||||
group("extract .tar (3 small files)", () => {
|
||||
bench("node-tar", async () => {
|
||||
await extractNodeTarBuffer(smallTarBuffer, extractDirNodeTar);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive", async () => {
|
||||
await Bun.Archive.from(smallBunArchive).extract(extractDirBun);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("extract .tar (3 x 100KB files)", () => {
|
||||
bench("node-tar", async () => {
|
||||
await extractNodeTarBuffer(largeTarBuffer, extractDirNodeTar);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive", async () => {
|
||||
await Bun.Archive.from(largeBunArchive).extract(extractDirBun);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("extract .tar (100 small files)", () => {
|
||||
bench("node-tar", async () => {
|
||||
await extractNodeTarBuffer(manyFilesTarBuffer, extractDirNodeTar);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive", async () => {
|
||||
await Bun.Archive.from(manyFilesBunArchive).extract(extractDirBun);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Extract .tar.gz (compressed) benchmarks
|
||||
// ============================================================================
|
||||
|
||||
group("extract .tar.gz (3 small files)", () => {
|
||||
bench("node-tar", async () => {
|
||||
await extractNodeTarBuffer(smallTarGzBuffer, extractDirNodeTar);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive", async () => {
|
||||
await Bun.Archive.from(smallBunArchiveGz).extract(extractDirBun);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("extract .tar.gz (3 x 100KB files)", () => {
|
||||
bench("node-tar", async () => {
|
||||
await extractNodeTarBuffer(largeTarGzBuffer, extractDirNodeTar);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive", async () => {
|
||||
await Bun.Archive.from(largeBunArchiveGz).extract(extractDirBun);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("extract .tar.gz (100 small files)", () => {
|
||||
bench("node-tar", async () => {
|
||||
await extractNodeTarBuffer(manyFilesTarGzBuffer, extractDirNodeTar);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive", async () => {
|
||||
await Bun.Archive.from(manyFilesBunArchiveGz).extract(extractDirBun);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Write .tar to disk benchmarks
|
||||
// ============================================================================
|
||||
|
||||
let writeCounter = 0;
|
||||
|
||||
group("write .tar to disk (3 small files)", () => {
|
||||
bench("node-tar + writeFileSync", async () => {
|
||||
const buffer = await createNodeTarBuffer(smallFilesDir, ["file1.txt", "file2.txt", "file3.txt"], false);
|
||||
writeFileSync(join(writeDirNodeTar, `archive-${writeCounter++}.tar`), buffer);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive.write", async () => {
|
||||
await Bun.Archive.write(join(writeDirBun, `archive-${writeCounter++}.tar`), smallFiles);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("write .tar to disk (3 x 100KB files)", () => {
|
||||
bench("node-tar + writeFileSync", async () => {
|
||||
const buffer = await createNodeTarBuffer(largeFilesDir, ["file1.txt", "file2.txt", "file3.txt"], false);
|
||||
writeFileSync(join(writeDirNodeTar, `archive-${writeCounter++}.tar`), buffer);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive.write", async () => {
|
||||
await Bun.Archive.write(join(writeDirBun, `archive-${writeCounter++}.tar`), largeFiles);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("write .tar to disk (100 small files)", () => {
|
||||
bench("node-tar + writeFileSync", async () => {
|
||||
const buffer = await createNodeTarBuffer(manyFilesDir, Object.keys(manyFilesEntries), false);
|
||||
writeFileSync(join(writeDirNodeTar, `archive-${writeCounter++}.tar`), buffer);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive.write", async () => {
|
||||
await Bun.Archive.write(join(writeDirBun, `archive-${writeCounter++}.tar`), manyFilesEntries);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Write .tar.gz to disk benchmarks
|
||||
// ============================================================================
|
||||
|
||||
group("write .tar.gz to disk (3 small files)", () => {
|
||||
bench("node-tar + writeFileSync", async () => {
|
||||
const buffer = await createNodeTarBuffer(smallFilesDir, ["file1.txt", "file2.txt", "file3.txt"], true);
|
||||
writeFileSync(join(writeDirNodeTar, `archive-${writeCounter++}.tar.gz`), buffer);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive.write", async () => {
|
||||
await Bun.Archive.write(join(writeDirBun, `archive-${writeCounter++}.tar.gz`), smallFiles, "gzip");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("write .tar.gz to disk (3 x 100KB files)", () => {
|
||||
bench("node-tar + writeFileSync", async () => {
|
||||
const buffer = await createNodeTarBuffer(largeFilesDir, ["file1.txt", "file2.txt", "file3.txt"], true);
|
||||
writeFileSync(join(writeDirNodeTar, `archive-${writeCounter++}.tar.gz`), buffer);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive.write", async () => {
|
||||
await Bun.Archive.write(join(writeDirBun, `archive-${writeCounter++}.tar.gz`), largeFiles, "gzip");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("write .tar.gz to disk (100 small files)", () => {
|
||||
bench("node-tar + writeFileSync", async () => {
|
||||
const buffer = await createNodeTarBuffer(manyFilesDir, Object.keys(manyFilesEntries), true);
|
||||
writeFileSync(join(writeDirNodeTar, `archive-${writeCounter++}.tar.gz`), buffer);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive.write", async () => {
|
||||
await Bun.Archive.write(join(writeDirBun, `archive-${writeCounter++}.tar.gz`), manyFilesEntries, "gzip");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Get files array from archive (files() method) benchmarks
|
||||
// ============================================================================
|
||||
|
||||
// Helper to get files array from node-tar (reads all entries into memory)
|
||||
async function getFilesArrayNodeTar(buffer) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const files = new Map();
|
||||
let pending = 0;
|
||||
let closed = false;
|
||||
|
||||
const maybeResolve = () => {
|
||||
if (closed && pending === 0) {
|
||||
resolve(files);
|
||||
}
|
||||
};
|
||||
|
||||
const unpack = new Unpack({
|
||||
onReadEntry: entry => {
|
||||
if (entry.type === "File") {
|
||||
pending++;
|
||||
const chunks = [];
|
||||
entry.on("data", chunk => chunks.push(chunk));
|
||||
entry.on("end", () => {
|
||||
const content = Buffer.concat(chunks);
|
||||
// Create a File-like object similar to Bun.Archive.files()
|
||||
files.set(entry.path, new Blob([content]));
|
||||
pending--;
|
||||
maybeResolve();
|
||||
});
|
||||
}
|
||||
entry.resume(); // Drain the entry
|
||||
},
|
||||
});
|
||||
unpack.on("close", () => {
|
||||
closed = true;
|
||||
maybeResolve();
|
||||
});
|
||||
unpack.on("error", reject);
|
||||
unpack.end(buffer);
|
||||
});
|
||||
}
|
||||
|
||||
group("files() - get all files as Map (3 small files)", () => {
|
||||
bench("node-tar", async () => {
|
||||
await getFilesArrayNodeTar(smallTarBuffer);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive.files()", async () => {
|
||||
await Bun.Archive.from(smallBunArchive).files();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("files() - get all files as Map (3 x 100KB files)", () => {
|
||||
bench("node-tar", async () => {
|
||||
await getFilesArrayNodeTar(largeTarBuffer);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive.files()", async () => {
|
||||
await Bun.Archive.from(largeBunArchive).files();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("files() - get all files as Map (100 small files)", () => {
|
||||
bench("node-tar", async () => {
|
||||
await getFilesArrayNodeTar(manyFilesTarBuffer);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive.files()", async () => {
|
||||
await Bun.Archive.from(manyFilesBunArchive).files();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("files() - get all files as Map from .tar.gz (3 small files)", () => {
|
||||
bench("node-tar", async () => {
|
||||
await getFilesArrayNodeTar(smallTarGzBuffer);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive.files()", async () => {
|
||||
await Bun.Archive.from(smallBunArchiveGz).files();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
group("files() - get all files as Map from .tar.gz (100 small files)", () => {
|
||||
bench("node-tar", async () => {
|
||||
await getFilesArrayNodeTar(manyFilesTarGzBuffer);
|
||||
});
|
||||
|
||||
if (hasBunArchive) {
|
||||
bench("Bun.Archive.files()", async () => {
|
||||
await Bun.Archive.from(manyFilesBunArchiveGz).files();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await run();
|
||||
|
||||
// Cleanup
|
||||
rmSync(setupDir, { recursive: true, force: true });
|
||||
rmSync(extractDirNodeTar, { recursive: true, force: true });
|
||||
rmSync(extractDirBun, { recursive: true, force: true });
|
||||
rmSync(writeDirNodeTar, { recursive: true, force: true });
|
||||
rmSync(writeDirBun, { recursive: true, force: true });
|
||||
@@ -1,38 +0,0 @@
|
||||
// @runtime bun,node
|
||||
import { Buffer } from "node:buffer";
|
||||
import { bench, group, run } from "../runner.mjs";
|
||||
|
||||
// Small arrays (common case)
|
||||
const int32Array8 = [1, 2, 3, 4, 5, 6, 7, 8];
|
||||
const doubleArray8 = [1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5];
|
||||
|
||||
// Medium arrays
|
||||
const int32Array64 = Array.from({ length: 64 }, (_, i) => i % 256);
|
||||
const doubleArray64 = Array.from({ length: 64 }, (_, i) => i + 0.5);
|
||||
|
||||
// Large arrays
|
||||
const int32Array1024 = Array.from({ length: 1024 }, (_, i) => i % 256);
|
||||
|
||||
// Array-like objects (fallback path)
|
||||
const arrayLike8 = { 0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, length: 8 };
|
||||
|
||||
// Empty array
|
||||
const emptyArray = [];
|
||||
|
||||
group("Buffer.from(array) - Int32 arrays", () => {
|
||||
bench("Buffer.from(int32[8])", () => Buffer.from(int32Array8));
|
||||
bench("Buffer.from(int32[64])", () => Buffer.from(int32Array64));
|
||||
bench("Buffer.from(int32[1024])", () => Buffer.from(int32Array1024));
|
||||
});
|
||||
|
||||
group("Buffer.from(array) - Double arrays", () => {
|
||||
bench("Buffer.from(double[8])", () => Buffer.from(doubleArray8));
|
||||
bench("Buffer.from(double[64])", () => Buffer.from(doubleArray64));
|
||||
});
|
||||
|
||||
group("Buffer.from(array) - Edge cases", () => {
|
||||
bench("Buffer.from([])", () => Buffer.from(emptyArray));
|
||||
bench("Buffer.from(arrayLike[8])", () => Buffer.from(arrayLike8));
|
||||
});
|
||||
|
||||
await run();
|
||||
@@ -1,4 +0,0 @@
|
||||
// Child process for IPC benchmarks - echoes messages back to parent
|
||||
process.on("message", message => {
|
||||
process.send(message);
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
import { fork } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { bench, run } from "../runner.mjs";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const childPath = path.join(__dirname, "ipc-json-child.mjs");
|
||||
|
||||
const smallMessage = { type: "ping", id: 1 };
|
||||
const largeString = Buffer.alloc(10 * 1024 * 1024, "A").toString();
|
||||
const largeMessage = { type: "ping", id: 1, data: largeString };
|
||||
|
||||
async function runBenchmark(message, count) {
|
||||
let received = 0;
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
|
||||
const child = fork(childPath, [], {
|
||||
stdio: ["ignore", "ignore", "ignore", "ipc"],
|
||||
serialization: "json",
|
||||
});
|
||||
|
||||
child.on("message", () => {
|
||||
received++;
|
||||
if (received >= count) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
child.send(message);
|
||||
}
|
||||
|
||||
await promise;
|
||||
child.kill();
|
||||
}
|
||||
|
||||
bench("ipc json - small messages (1000 roundtrips)", async () => {
|
||||
await runBenchmark(smallMessage, 1000);
|
||||
});
|
||||
|
||||
bench("ipc json - 10MB messages (10 roundtrips)", async () => {
|
||||
await runBenchmark(largeMessage, 10);
|
||||
});
|
||||
|
||||
await run();
|
||||
@@ -1,20 +0,0 @@
|
||||
// Benchmark for [...set] optimization (WebKit#56539)
|
||||
// https://github.com/WebKit/WebKit/pull/56539
|
||||
import { bench, run } from "../runner.mjs";
|
||||
|
||||
const intSet10 = new Set([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
const intSet100 = new Set(Array.from({ length: 100 }, (_, i) => i));
|
||||
const strSet10 = new Set(Array.from({ length: 10 }, (_, i) => `key-${i}`));
|
||||
const strSet100 = new Set(Array.from({ length: 100 }, (_, i) => `key-${i}`));
|
||||
|
||||
const objSet10 = new Set(Array.from({ length: 10 }, (_, i) => ({ id: i })));
|
||||
const objSet100 = new Set(Array.from({ length: 100 }, (_, i) => ({ id: i })));
|
||||
|
||||
bench("[...set] - integers (10)", () => [...intSet10]);
|
||||
bench("[...set] - integers (100)", () => [...intSet100]);
|
||||
bench("[...set] - strings (10)", () => [...strSet10]);
|
||||
bench("[...set] - strings (100)", () => [...strSet100]);
|
||||
bench("[...set] - objects (10)", () => [...objSet10]);
|
||||
bench("[...set] - objects (100)", () => [...objSet100]);
|
||||
|
||||
await run();
|
||||
@@ -1,103 +0,0 @@
|
||||
import wrapAnsi from "wrap-ansi";
|
||||
import { bench, run, summary } from "../runner.mjs";
|
||||
|
||||
// Test fixtures
|
||||
const shortText = "The quick brown fox jumped over the lazy dog.";
|
||||
const mediumText = "The quick brown fox jumped over the lazy dog and then ran away with the unicorn. ".repeat(10);
|
||||
const longText = "The quick brown fox jumped over the lazy dog and then ran away with the unicorn. ".repeat(100);
|
||||
|
||||
// ANSI colored text
|
||||
const red = s => `\u001B[31m${s}\u001B[39m`;
|
||||
const green = s => `\u001B[32m${s}\u001B[39m`;
|
||||
const blue = s => `\u001B[34m${s}\u001B[39m`;
|
||||
|
||||
const coloredShort = `The quick ${red("brown fox")} jumped over the ${green("lazy dog")}.`;
|
||||
const coloredMedium =
|
||||
`The quick ${red("brown fox jumped over")} the ${green("lazy dog and then ran away")} with the ${blue("unicorn")}. `.repeat(
|
||||
10,
|
||||
);
|
||||
const coloredLong =
|
||||
`The quick ${red("brown fox jumped over")} the ${green("lazy dog and then ran away")} with the ${blue("unicorn")}. `.repeat(
|
||||
100,
|
||||
);
|
||||
|
||||
// Full-width characters (Japanese)
|
||||
const japaneseText = "日本語のテキストを折り返すテストです。全角文字は幅2としてカウントされます。".repeat(5);
|
||||
|
||||
// Emoji text
|
||||
const emojiText = "Hello 👋 World 🌍! Let's test 🧪 some emoji 😀 wrapping 📦!".repeat(5);
|
||||
|
||||
// Hyperlink text
|
||||
const hyperlinkText = "Check out \u001B]8;;https://bun.sh\u0007Bun\u001B]8;;\u0007, it's fast! ".repeat(10);
|
||||
|
||||
// Options
|
||||
const hardOpts = { hard: true };
|
||||
const noTrimOpts = { trim: false };
|
||||
|
||||
// Basic text benchmarks
|
||||
summary(() => {
|
||||
bench("Short text (45 chars) - npm", () => wrapAnsi(shortText, 20));
|
||||
bench("Short text (45 chars) - Bun", () => Bun.wrapAnsi(shortText, 20));
|
||||
});
|
||||
|
||||
summary(() => {
|
||||
bench("Medium text (810 chars) - npm", () => wrapAnsi(mediumText, 40));
|
||||
bench("Medium text (810 chars) - Bun", () => Bun.wrapAnsi(mediumText, 40));
|
||||
});
|
||||
|
||||
summary(() => {
|
||||
bench("Long text (8100 chars) - npm", () => wrapAnsi(longText, 80));
|
||||
bench("Long text (8100 chars) - Bun", () => Bun.wrapAnsi(longText, 80));
|
||||
});
|
||||
|
||||
// ANSI colored text benchmarks
|
||||
summary(() => {
|
||||
bench("Colored short - npm", () => wrapAnsi(coloredShort, 20));
|
||||
bench("Colored short - Bun", () => Bun.wrapAnsi(coloredShort, 20));
|
||||
});
|
||||
|
||||
summary(() => {
|
||||
bench("Colored medium - npm", () => wrapAnsi(coloredMedium, 40));
|
||||
bench("Colored medium - Bun", () => Bun.wrapAnsi(coloredMedium, 40));
|
||||
});
|
||||
|
||||
summary(() => {
|
||||
bench("Colored long - npm", () => wrapAnsi(coloredLong, 80));
|
||||
bench("Colored long - Bun", () => Bun.wrapAnsi(coloredLong, 80));
|
||||
});
|
||||
|
||||
// Hard wrap benchmarks
|
||||
summary(() => {
|
||||
bench("Hard wrap long - npm", () => wrapAnsi(longText, 80, hardOpts));
|
||||
bench("Hard wrap long - Bun", () => Bun.wrapAnsi(longText, 80, hardOpts));
|
||||
});
|
||||
|
||||
summary(() => {
|
||||
bench("Hard wrap colored - npm", () => wrapAnsi(coloredLong, 80, hardOpts));
|
||||
bench("Hard wrap colored - Bun", () => Bun.wrapAnsi(coloredLong, 80, hardOpts));
|
||||
});
|
||||
|
||||
// Unicode benchmarks
|
||||
summary(() => {
|
||||
bench("Japanese (full-width) - npm", () => wrapAnsi(japaneseText, 40));
|
||||
bench("Japanese (full-width) - Bun", () => Bun.wrapAnsi(japaneseText, 40));
|
||||
});
|
||||
|
||||
summary(() => {
|
||||
bench("Emoji text - npm", () => wrapAnsi(emojiText, 30));
|
||||
bench("Emoji text - Bun", () => Bun.wrapAnsi(emojiText, 30));
|
||||
});
|
||||
|
||||
// Hyperlink benchmarks
|
||||
summary(() => {
|
||||
bench("Hyperlink (OSC 8) - npm", () => wrapAnsi(hyperlinkText, 40));
|
||||
bench("Hyperlink (OSC 8) - Bun", () => Bun.wrapAnsi(hyperlinkText, 40));
|
||||
});
|
||||
|
||||
// No trim option
|
||||
summary(() => {
|
||||
bench("No trim long - npm", () => wrapAnsi(longText, 80, noTrimOpts));
|
||||
bench("No trim long - Bun", () => Bun.wrapAnsi(longText, 80, noTrimOpts));
|
||||
});
|
||||
|
||||
await run();
|
||||
150
build.zig
150
build.zig
@@ -34,7 +34,6 @@ const BunBuildOptions = struct {
|
||||
enable_asan: bool,
|
||||
enable_fuzzilli: bool,
|
||||
enable_valgrind: bool,
|
||||
enable_tinycc: bool,
|
||||
use_mimalloc: bool,
|
||||
tracy_callstack_depth: u16,
|
||||
reported_nodejs_version: Version,
|
||||
@@ -85,7 +84,6 @@ const BunBuildOptions = struct {
|
||||
opts.addOption(bool, "enable_asan", this.enable_asan);
|
||||
opts.addOption(bool, "enable_fuzzilli", this.enable_fuzzilli);
|
||||
opts.addOption(bool, "enable_valgrind", this.enable_valgrind);
|
||||
opts.addOption(bool, "enable_tinycc", this.enable_tinycc);
|
||||
opts.addOption(bool, "use_mimalloc", this.use_mimalloc);
|
||||
opts.addOption([]const u8, "reported_nodejs_version", b.fmt("{f}", .{this.reported_nodejs_version}));
|
||||
opts.addOption(bool, "zig_self_hosted_backend", this.no_llvm);
|
||||
@@ -261,7 +259,6 @@ pub fn build(b: *Build) !void {
|
||||
.enable_asan = b.option(bool, "enable_asan", "Enable asan") orelse false,
|
||||
.enable_fuzzilli = b.option(bool, "enable_fuzzilli", "Enable fuzzilli instrumentation") orelse false,
|
||||
.enable_valgrind = b.option(bool, "enable_valgrind", "Enable valgrind") orelse false,
|
||||
.enable_tinycc = b.option(bool, "enable_tinycc", "Enable TinyCC for FFI JIT compilation") orelse true,
|
||||
.use_mimalloc = b.option(bool, "use_mimalloc", "Use mimalloc as default allocator") orelse false,
|
||||
.llvm_codegen_threads = b.option(u32, "llvm_codegen_threads", "Number of threads to use for LLVM codegen") orelse 1,
|
||||
};
|
||||
@@ -345,7 +342,6 @@ pub fn build(b: *Build) !void {
|
||||
const step = b.step("check-debug", "Check for semantic analysis errors on some platforms");
|
||||
addMultiCheck(b, step, build_options, &.{
|
||||
.{ .os = .windows, .arch = .x86_64 },
|
||||
.{ .os = .windows, .arch = .aarch64 },
|
||||
.{ .os = .mac, .arch = .aarch64 },
|
||||
.{ .os = .linux, .arch = .x86_64 },
|
||||
}, &.{.Debug});
|
||||
@@ -356,7 +352,6 @@ pub fn build(b: *Build) !void {
|
||||
const step = b.step("check-all", "Check for semantic analysis errors on all supported platforms");
|
||||
addMultiCheck(b, step, build_options, &.{
|
||||
.{ .os = .windows, .arch = .x86_64 },
|
||||
.{ .os = .windows, .arch = .aarch64 },
|
||||
.{ .os = .mac, .arch = .x86_64 },
|
||||
.{ .os = .mac, .arch = .aarch64 },
|
||||
.{ .os = .linux, .arch = .x86_64 },
|
||||
@@ -371,7 +366,6 @@ pub fn build(b: *Build) !void {
|
||||
const step = b.step("check-all-debug", "Check for semantic analysis errors on all supported platforms in debug mode");
|
||||
addMultiCheck(b, step, build_options, &.{
|
||||
.{ .os = .windows, .arch = .x86_64 },
|
||||
.{ .os = .windows, .arch = .aarch64 },
|
||||
.{ .os = .mac, .arch = .x86_64 },
|
||||
.{ .os = .mac, .arch = .aarch64 },
|
||||
.{ .os = .linux, .arch = .x86_64 },
|
||||
@@ -386,14 +380,12 @@ pub fn build(b: *Build) !void {
|
||||
const step = b.step("check-windows", "Check for semantic analysis errors on Windows");
|
||||
addMultiCheck(b, step, build_options, &.{
|
||||
.{ .os = .windows, .arch = .x86_64 },
|
||||
.{ .os = .windows, .arch = .aarch64 },
|
||||
}, &.{ .Debug, .ReleaseFast });
|
||||
}
|
||||
{
|
||||
const step = b.step("check-windows-debug", "Check for semantic analysis errors on Windows");
|
||||
addMultiCheck(b, step, build_options, &.{
|
||||
.{ .os = .windows, .arch = .x86_64 },
|
||||
.{ .os = .windows, .arch = .aarch64 },
|
||||
}, &.{.Debug});
|
||||
}
|
||||
{
|
||||
@@ -430,7 +422,6 @@ pub fn build(b: *Build) !void {
|
||||
const step = b.step("translate-c", "Copy generated translated-c-headers.zig to zig-out");
|
||||
for ([_]TargetDescription{
|
||||
.{ .os = .windows, .arch = .x86_64 },
|
||||
.{ .os = .windows, .arch = .aarch64 },
|
||||
.{ .os = .mac, .arch = .x86_64 },
|
||||
.{ .os = .mac, .arch = .aarch64 },
|
||||
.{ .os = .linux, .arch = .x86_64 },
|
||||
@@ -459,146 +450,6 @@ pub fn build(b: *Build) !void {
|
||||
// const run = b.addRunArtifact(exe);
|
||||
// step.dependOn(&run.step);
|
||||
}
|
||||
|
||||
// zig build generate-grapheme-tables
|
||||
// Regenerates src/string/immutable/grapheme_tables.zig from the vendored uucode.
|
||||
// Run this when updating src/deps/uucode. Normal builds use the committed file.
|
||||
{
|
||||
const step = b.step("generate-grapheme-tables", "Regenerate grapheme property tables from vendored uucode");
|
||||
|
||||
// --- Phase 1: Build uucode tables (separate module graph, no tables dependency) ---
|
||||
const bt_config_mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/deps/uucode/src/config.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
const bt_types_mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/deps/uucode/src/types.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
bt_types_mod.addImport("config.zig", bt_config_mod);
|
||||
bt_config_mod.addImport("types.zig", bt_types_mod);
|
||||
|
||||
const bt_config_x_mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/deps/uucode/src/x/config.x.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
const bt_types_x_mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/deps/uucode/src/x/types.x.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
bt_types_x_mod.addImport("config.x.zig", bt_config_x_mod);
|
||||
bt_config_x_mod.addImport("types.x.zig", bt_types_x_mod);
|
||||
bt_config_x_mod.addImport("types.zig", bt_types_mod);
|
||||
bt_config_x_mod.addImport("config.zig", bt_config_mod);
|
||||
|
||||
const bt_build_config_mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/unicode/uucode/uucode_config.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
bt_build_config_mod.addImport("types.zig", bt_types_mod);
|
||||
bt_build_config_mod.addImport("config.zig", bt_config_mod);
|
||||
bt_build_config_mod.addImport("types.x.zig", bt_types_x_mod);
|
||||
bt_build_config_mod.addImport("config.x.zig", bt_config_x_mod);
|
||||
|
||||
const build_tables_mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/deps/uucode/src/build/tables.zig"),
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
});
|
||||
build_tables_mod.addImport("config.zig", bt_config_mod);
|
||||
build_tables_mod.addImport("build_config", bt_build_config_mod);
|
||||
build_tables_mod.addImport("types.zig", bt_types_mod);
|
||||
|
||||
const build_tables_exe = b.addExecutable(.{
|
||||
.name = "uucode_build_tables",
|
||||
.root_module = build_tables_mod,
|
||||
.use_llvm = true,
|
||||
});
|
||||
const run_build_tables = b.addRunArtifact(build_tables_exe);
|
||||
run_build_tables.setCwd(b.path("src/deps/uucode"));
|
||||
const tables_path = run_build_tables.addOutputFileArg("tables.zig");
|
||||
|
||||
// --- Phase 2: Build grapheme-gen with full uucode (separate module graph) ---
|
||||
const rt_config_mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/deps/uucode/src/config.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
const rt_types_mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/deps/uucode/src/types.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
rt_types_mod.addImport("config.zig", rt_config_mod);
|
||||
rt_config_mod.addImport("types.zig", rt_types_mod);
|
||||
|
||||
const rt_config_x_mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/deps/uucode/src/x/config.x.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
const rt_types_x_mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/deps/uucode/src/x/types.x.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
rt_types_x_mod.addImport("config.x.zig", rt_config_x_mod);
|
||||
rt_config_x_mod.addImport("types.x.zig", rt_types_x_mod);
|
||||
rt_config_x_mod.addImport("types.zig", rt_types_mod);
|
||||
rt_config_x_mod.addImport("config.zig", rt_config_mod);
|
||||
|
||||
const rt_build_config_mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/unicode/uucode/uucode_config.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
rt_build_config_mod.addImport("types.zig", rt_types_mod);
|
||||
rt_build_config_mod.addImport("config.zig", rt_config_mod);
|
||||
rt_build_config_mod.addImport("types.x.zig", rt_types_x_mod);
|
||||
rt_build_config_mod.addImport("config.x.zig", rt_config_x_mod);
|
||||
|
||||
const rt_tables_mod = b.createModule(.{
|
||||
.root_source_file = tables_path,
|
||||
.target = b.graph.host,
|
||||
});
|
||||
rt_tables_mod.addImport("types.zig", rt_types_mod);
|
||||
rt_tables_mod.addImport("types.x.zig", rt_types_x_mod);
|
||||
rt_tables_mod.addImport("config.zig", rt_config_mod);
|
||||
rt_tables_mod.addImport("build_config", rt_build_config_mod);
|
||||
|
||||
const rt_get_mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/deps/uucode/src/get.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
rt_get_mod.addImport("types.zig", rt_types_mod);
|
||||
rt_get_mod.addImport("tables", rt_tables_mod);
|
||||
rt_types_mod.addImport("get.zig", rt_get_mod);
|
||||
|
||||
const uucode_mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/deps/uucode/src/root.zig"),
|
||||
.target = b.graph.host,
|
||||
});
|
||||
uucode_mod.addImport("types.zig", rt_types_mod);
|
||||
uucode_mod.addImport("config.zig", rt_config_mod);
|
||||
uucode_mod.addImport("types.x.zig", rt_types_x_mod);
|
||||
uucode_mod.addImport("tables", rt_tables_mod);
|
||||
uucode_mod.addImport("get.zig", rt_get_mod);
|
||||
|
||||
// grapheme_gen executable
|
||||
const gen_exe = b.addExecutable(.{
|
||||
.name = "grapheme-gen",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/unicode/uucode/grapheme_gen.zig"),
|
||||
.target = b.graph.host,
|
||||
.optimize = .Debug,
|
||||
.imports = &.{
|
||||
.{ .name = "uucode", .module = uucode_mod },
|
||||
},
|
||||
}),
|
||||
.use_llvm = true,
|
||||
});
|
||||
|
||||
const run_gen = b.addRunArtifact(gen_exe);
|
||||
const gen_output = run_gen.captureStdOut();
|
||||
|
||||
const install = b.addInstallFile(gen_output, "../src/string/immutable/grapheme_tables.zig");
|
||||
step.dependOn(&install.step);
|
||||
}
|
||||
}
|
||||
|
||||
const TargetDescription = struct {
|
||||
@@ -642,7 +493,6 @@ fn addMultiCheck(
|
||||
.no_llvm = root_build_options.no_llvm,
|
||||
.enable_asan = root_build_options.enable_asan,
|
||||
.enable_valgrind = root_build_options.enable_valgrind,
|
||||
.enable_tinycc = root_build_options.enable_tinycc,
|
||||
.enable_fuzzilli = root_build_options.enable_fuzzilli,
|
||||
.use_mimalloc = root_build_options.use_mimalloc,
|
||||
.override_no_export_cpp_apis = root_build_options.override_no_export_cpp_apis,
|
||||
|
||||
@@ -21,10 +21,6 @@ endforeach()
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|ARM|arm64|ARM64|aarch64|AARCH64")
|
||||
if(APPLE)
|
||||
register_compiler_flags(-mcpu=apple-m1)
|
||||
elseif(WIN32)
|
||||
# Windows ARM64: use /clang: prefix for clang-cl, skip for MSVC cl.exe subprojects
|
||||
# These flags are only understood by clang-cl, not MSVC cl.exe
|
||||
register_compiler_flags(/clang:-march=armv8-a+crc /clang:-mtune=ampere1)
|
||||
else()
|
||||
register_compiler_flags(-march=armv8-a+crc -mtune=ampere1)
|
||||
endif()
|
||||
@@ -246,17 +242,10 @@ if(UNIX)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
register_compiler_flags(
|
||||
DESCRIPTION "Set C/C++ error limit"
|
||||
/clang:-ferror-limit=${ERROR_LIMIT}
|
||||
)
|
||||
else()
|
||||
register_compiler_flags(
|
||||
DESCRIPTION "Set C/C++ error limit"
|
||||
-ferror-limit=${ERROR_LIMIT}
|
||||
)
|
||||
endif()
|
||||
register_compiler_flags(
|
||||
DESCRIPTION "Set C/C++ error limit"
|
||||
-ferror-limit=${ERROR_LIMIT}
|
||||
)
|
||||
|
||||
# --- LTO ---
|
||||
if(ENABLE_LTO)
|
||||
|
||||
@@ -106,9 +106,9 @@ else()
|
||||
endif()
|
||||
|
||||
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "arm64|ARM64|aarch64|AARCH64")
|
||||
set(HOST_ARCH "aarch64")
|
||||
set(HOST_OS "aarch64")
|
||||
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "x86_64|X86_64|x64|X64|amd64|AMD64")
|
||||
set(HOST_ARCH "x64")
|
||||
set(HOST_OS "x64")
|
||||
else()
|
||||
unsupported(CMAKE_HOST_SYSTEM_PROCESSOR)
|
||||
endif()
|
||||
@@ -433,33 +433,6 @@ function(register_command)
|
||||
list(APPEND CMD_EFFECTIVE_DEPENDS ${CMD_EXECUTABLE})
|
||||
endif()
|
||||
|
||||
# SKIP_CODEGEN: Skip commands that use BUN_EXECUTABLE if all outputs exist
|
||||
# This is used for Windows ARM64 builds where x64 bun crashes under emulation
|
||||
if(SKIP_CODEGEN AND CMD_EXECUTABLE STREQUAL "${BUN_EXECUTABLE}")
|
||||
set(ALL_OUTPUTS_EXIST TRUE)
|
||||
foreach(output ${CMD_OUTPUTS})
|
||||
if(NOT EXISTS ${output})
|
||||
set(ALL_OUTPUTS_EXIST FALSE)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
if(ALL_OUTPUTS_EXIST AND CMD_OUTPUTS)
|
||||
message(STATUS "SKIP_CODEGEN: Skipping ${CMD_TARGET} (outputs exist)")
|
||||
if(CMD_TARGET)
|
||||
add_custom_target(${CMD_TARGET})
|
||||
endif()
|
||||
return()
|
||||
elseif(NOT CMD_OUTPUTS)
|
||||
message(STATUS "SKIP_CODEGEN: Skipping ${CMD_TARGET} (no outputs)")
|
||||
if(CMD_TARGET)
|
||||
add_custom_target(${CMD_TARGET})
|
||||
endif()
|
||||
return()
|
||||
else()
|
||||
message(FATAL_ERROR "SKIP_CODEGEN: Cannot skip ${CMD_TARGET} - missing outputs. Run codegen on x64 first.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
foreach(target ${CMD_TARGETS})
|
||||
if(target MATCHES "/|\\\\")
|
||||
message(FATAL_ERROR "register_command: TARGETS contains \"${target}\", if it's a path add it to SOURCES instead")
|
||||
@@ -677,7 +650,6 @@ function(register_bun_install)
|
||||
${NPM_CWD}
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE}
|
||||
${BUN_FLAGS}
|
||||
install
|
||||
--frozen-lockfile
|
||||
SOURCES
|
||||
@@ -785,7 +757,7 @@ function(register_cmake_command)
|
||||
set(MAKE_EFFECTIVE_ARGS -B${MAKE_BUILD_PATH} ${CMAKE_ARGS})
|
||||
|
||||
set(setFlags GENERATOR BUILD_TYPE)
|
||||
set(appendFlags C_FLAGS CXX_FLAGS LINKER_FLAGS STATIC_LINKER_FLAGS EXE_LINKER_FLAGS SHARED_LINKER_FLAGS MODULE_LINKER_FLAGS)
|
||||
set(appendFlags C_FLAGS CXX_FLAGS LINKER_FLAGS)
|
||||
set(specialFlags POSITION_INDEPENDENT_CODE)
|
||||
set(flags ${setFlags} ${appendFlags} ${specialFlags})
|
||||
|
||||
@@ -831,14 +803,6 @@ function(register_cmake_command)
|
||||
list(APPEND MAKE_EFFECTIVE_ARGS "-DCMAKE_${flag}=${MAKE_${flag}}")
|
||||
endforeach()
|
||||
|
||||
# Workaround for CMake 4.1.0 bug: Force correct machine type for Windows ARM64
|
||||
# Use toolchain file and set CMP0197 policy to prevent duplicate /machine: flags
|
||||
if(WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64|AARCH64")
|
||||
list(APPEND MAKE_EFFECTIVE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CWD}/cmake/toolchains/windows-aarch64.cmake")
|
||||
list(APPEND MAKE_EFFECTIVE_ARGS "-DCMAKE_POLICY_DEFAULT_CMP0197=NEW")
|
||||
list(APPEND MAKE_EFFECTIVE_ARGS "-DCMAKE_PROJECT_INCLUDE=${CWD}/cmake/arm64-static-lib-fix.cmake")
|
||||
endif()
|
||||
|
||||
if(DEFINED FRESH)
|
||||
list(APPEND MAKE_EFFECTIVE_ARGS --fresh)
|
||||
endif()
|
||||
|
||||
@@ -4,7 +4,6 @@ endif()
|
||||
|
||||
optionx(BUN_LINK_ONLY BOOL "If only the linking step should be built" DEFAULT OFF)
|
||||
optionx(BUN_CPP_ONLY BOOL "If only the C++ part of Bun should be built" DEFAULT OFF)
|
||||
optionx(SKIP_CODEGEN BOOL "Skip JavaScript codegen (for Windows ARM64 debug)" DEFAULT OFF)
|
||||
|
||||
optionx(BUILDKITE BOOL "If Buildkite is enabled" DEFAULT OFF)
|
||||
optionx(GITHUB_ACTIONS BOOL "If GitHub Actions is enabled" DEFAULT OFF)
|
||||
@@ -50,7 +49,7 @@ else()
|
||||
message(FATAL_ERROR "Unsupported operating system: ${CMAKE_SYSTEM_NAME}")
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64")
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|arm")
|
||||
setx(ARCH "aarch64")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|x64|AMD64")
|
||||
setx(ARCH "x64")
|
||||
@@ -58,18 +57,6 @@ else()
|
||||
message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
|
||||
# CMake 4.0+ policy CMP0197 controls how MSVC machine type flags are handled
|
||||
# Setting to NEW prevents duplicate /machine: flags being added to linker commands
|
||||
if(WIN32 AND ARCH STREQUAL "aarch64")
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0197 NEW)
|
||||
set(CMAKE_MSVC_CMP0197 NEW)
|
||||
# Set linker flags for exe/shared linking
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /machine:ARM64")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /machine:ARM64")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /machine:ARM64")
|
||||
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /machine:ARM64")
|
||||
endif()
|
||||
|
||||
# Windows Code Signing Option
|
||||
if(WIN32)
|
||||
optionx(ENABLE_WINDOWS_CODESIGNING BOOL "Enable Windows code signing with DigiCert KeyLocker" DEFAULT OFF)
|
||||
@@ -212,16 +199,6 @@ optionx(USE_WEBKIT_ICU BOOL "Use the ICU libraries from WebKit" DEFAULT ${DEFAUL
|
||||
|
||||
optionx(ERROR_LIMIT STRING "Maximum number of errors to show when compiling C++ code" DEFAULT "100")
|
||||
|
||||
# TinyCC is used for FFI JIT compilation
|
||||
# Disable on Windows ARM64 where it's not yet supported
|
||||
if(WIN32 AND ARCH STREQUAL "aarch64")
|
||||
set(DEFAULT_ENABLE_TINYCC OFF)
|
||||
else()
|
||||
set(DEFAULT_ENABLE_TINYCC ON)
|
||||
endif()
|
||||
|
||||
optionx(ENABLE_TINYCC BOOL "Enable TinyCC for FFI JIT compilation" DEFAULT ${DEFAULT_ENABLE_TINYCC})
|
||||
|
||||
# This is not an `option` because setting this variable to OFF is experimental
|
||||
# and unsupported. This replaces the `use_mimalloc` variable previously in
|
||||
# bun.zig, and enables C++ code to also be aware of the option.
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
# This file is included after project() via CMAKE_PROJECT_INCLUDE
|
||||
# It fixes the static library creation command to use ARM64 machine type
|
||||
|
||||
if(WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL \"aarch64\")
|
||||
# Override the static library creation commands to avoid spurious /machine:x64 flags
|
||||
set(CMAKE_C_CREATE_STATIC_LIBRARY \"<CMAKE_AR> /nologo /machine:ARM64 /out:<TARGET> <OBJECTS>\" CACHE STRING \"\" FORCE)
|
||||
set(CMAKE_CXX_CREATE_STATIC_LIBRARY \"<CMAKE_AR> /nologo /machine:ARM64 /out:<TARGET> <OBJECTS>\" CACHE STRING \"\" FORCE)
|
||||
endif()
|
||||
@@ -21,12 +21,7 @@ if(NOT DEFINED CMAKE_HOST_SYSTEM_PROCESSOR)
|
||||
endif()
|
||||
|
||||
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "arm64|ARM64|aarch64|AARCH64")
|
||||
# Windows ARM64 can run x86_64 via emulation, and no native ARM64 Zig build exists yet
|
||||
if(CMAKE_HOST_WIN32)
|
||||
set(ZIG_ARCH "x86_64")
|
||||
else()
|
||||
set(ZIG_ARCH "aarch64")
|
||||
endif()
|
||||
set(ZIG_ARCH "aarch64")
|
||||
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "amd64|AMD64|x86_64|X86_64|x64|X64")
|
||||
set(ZIG_ARCH "x86_64")
|
||||
else()
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
REM Wrapper for llvm-lib that strips conflicting /machine:x64 flag for ARM64 builds
|
||||
REM This is a workaround for CMake 4.1.0 bug
|
||||
|
||||
REM Find llvm-lib.exe - check LLVM_LIB env var, then PATH, then known locations
|
||||
if defined LLVM_LIB (
|
||||
set "LLVM_LIB_EXE=!LLVM_LIB!"
|
||||
) else (
|
||||
where llvm-lib.exe >nul 2>&1
|
||||
if !ERRORLEVEL! equ 0 (
|
||||
for /f "delims=" %%i in ('where llvm-lib.exe') do set "LLVM_LIB_EXE=%%i"
|
||||
) else if exist "C:\Program Files\LLVM\bin\llvm-lib.exe" (
|
||||
set "LLVM_LIB_EXE=C:\Program Files\LLVM\bin\llvm-lib.exe"
|
||||
) else (
|
||||
echo Error: Cannot find llvm-lib.exe. Set LLVM_LIB environment variable or add LLVM to PATH.
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
set "ARGS="
|
||||
|
||||
for %%a in (%*) do (
|
||||
set "ARG=%%a"
|
||||
if /i "!ARG!"=="/machine:x64" (
|
||||
REM Skip this argument
|
||||
) else (
|
||||
set "ARGS=!ARGS! %%a"
|
||||
)
|
||||
)
|
||||
|
||||
"!LLVM_LIB_EXE!" %ARGS%
|
||||
exit /b %ERRORLEVEL%
|
||||
@@ -1,18 +0,0 @@
|
||||
# Wrapper for llvm-lib that strips conflicting /machine:x64 flag for ARM64 builds
|
||||
# This is a workaround for CMake 4.1.0 bug where both /machine:ARM64 and /machine:x64 are added
|
||||
|
||||
# Find llvm-lib.exe - check LLVM_LIB env var, then PATH, then known locations
|
||||
if ($env:LLVM_LIB) {
|
||||
$llvmLib = $env:LLVM_LIB
|
||||
} elseif (Get-Command llvm-lib.exe -ErrorAction SilentlyContinue) {
|
||||
$llvmLib = (Get-Command llvm-lib.exe).Source
|
||||
} elseif (Test-Path "C:\Program Files\LLVM\bin\llvm-lib.exe") {
|
||||
$llvmLib = "C:\Program Files\LLVM\bin\llvm-lib.exe"
|
||||
} else {
|
||||
Write-Error "Cannot find llvm-lib.exe. Set LLVM_LIB environment variable or add LLVM to PATH."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$filteredArgs = $args | Where-Object { $_ -ne "/machine:x64" }
|
||||
& $llvmLib @filteredArgs
|
||||
exit $LASTEXITCODE
|
||||
@@ -1,34 +0,0 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
REM Wrapper for llvm-lib that strips conflicting /machine:x64 flag for ARM64 builds
|
||||
REM This is a workaround for CMake 4.1.0 bug
|
||||
|
||||
REM Find llvm-lib.exe - check LLVM_LIB env var, then PATH, then known locations
|
||||
if defined LLVM_LIB (
|
||||
set "LLVM_LIB_EXE=!LLVM_LIB!"
|
||||
) else (
|
||||
where llvm-lib.exe >nul 2>&1
|
||||
if !ERRORLEVEL! equ 0 (
|
||||
for /f "delims=" %%i in ('where llvm-lib.exe') do set "LLVM_LIB_EXE=%%i"
|
||||
) else if exist "C:\Program Files\LLVM\bin\llvm-lib.exe" (
|
||||
set "LLVM_LIB_EXE=C:\Program Files\LLVM\bin\llvm-lib.exe"
|
||||
) else (
|
||||
echo Error: Cannot find llvm-lib.exe. Set LLVM_LIB environment variable or add LLVM to PATH.
|
||||
exit /b 1
|
||||
)
|
||||
)
|
||||
|
||||
set NEWARGS=
|
||||
|
||||
for %%a in (%*) do (
|
||||
set "ARG=%%a"
|
||||
if /i "!ARG!"=="/machine:x64" (
|
||||
REM Skip /machine:x64 argument
|
||||
) else (
|
||||
set "NEWARGS=!NEWARGS! %%a"
|
||||
)
|
||||
)
|
||||
|
||||
"!LLVM_LIB_EXE!" %NEWARGS%
|
||||
exit /b %ERRORLEVEL%
|
||||
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
oven-sh/boringssl
|
||||
COMMIT
|
||||
4f4f5ef8ebc6e23cbf393428f0ab1b526773f7ac
|
||||
f1ffd9e83d4f5c28a9c70d73f9a4e6fcf310062f
|
||||
)
|
||||
|
||||
register_cmake_command(
|
||||
|
||||
@@ -57,17 +57,13 @@ set(BUN_DEPENDENCIES
|
||||
LolHtml
|
||||
Lshpack
|
||||
Mimalloc
|
||||
TinyCC
|
||||
Zlib
|
||||
LibArchive # must be loaded after zlib
|
||||
HdrHistogram # must be loaded after zlib
|
||||
Zstd
|
||||
)
|
||||
|
||||
# TinyCC is optional - disabled on Windows ARM64 where it's not supported
|
||||
if(ENABLE_TINYCC)
|
||||
list(APPEND BUN_DEPENDENCIES TinyCC)
|
||||
endif()
|
||||
|
||||
include(CloneZstd)
|
||||
|
||||
# --- Codegen ---
|
||||
@@ -189,7 +185,7 @@ register_command(
|
||||
CWD
|
||||
${BUN_NODE_FALLBACKS_SOURCE}
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE} ${BUN_FLAGS} run build-fallbacks
|
||||
${BUN_EXECUTABLE} run build-fallbacks
|
||||
${BUN_NODE_FALLBACKS_OUTPUT}
|
||||
${BUN_NODE_FALLBACKS_SOURCES}
|
||||
SOURCES
|
||||
@@ -210,7 +206,7 @@ register_command(
|
||||
CWD
|
||||
${BUN_NODE_FALLBACKS_SOURCE}
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE} ${BUN_FLAGS} build
|
||||
${BUN_EXECUTABLE} build
|
||||
${BUN_NODE_FALLBACKS_SOURCE}/node_modules/react-refresh/cjs/react-refresh-runtime.development.js
|
||||
--outfile=${BUN_REACT_REFRESH_OUTPUT}
|
||||
--target=browser
|
||||
@@ -247,7 +243,6 @@ register_command(
|
||||
"Generating ErrorCode.{zig,h}"
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE}
|
||||
${BUN_FLAGS}
|
||||
run
|
||||
${BUN_ERROR_CODE_SCRIPT}
|
||||
${CODEGEN_PATH}
|
||||
@@ -283,7 +278,6 @@ register_command(
|
||||
"Generating ZigGeneratedClasses.{zig,cpp,h}"
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE}
|
||||
${BUN_FLAGS}
|
||||
run
|
||||
${BUN_ZIG_GENERATED_CLASSES_SCRIPT}
|
||||
${BUN_ZIG_GENERATED_CLASSES_SOURCES}
|
||||
@@ -334,7 +328,6 @@ register_command(
|
||||
"Generating C++ --> Zig bindings"
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE}
|
||||
${BUN_FLAGS}
|
||||
${CWD}/src/codegen/cppbind.ts
|
||||
${CWD}/src
|
||||
${CODEGEN_PATH}
|
||||
@@ -352,7 +345,6 @@ register_command(
|
||||
"Generating CI info"
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE}
|
||||
${BUN_FLAGS}
|
||||
${CWD}/src/codegen/ci_info.ts
|
||||
${CODEGEN_PATH}/ci_info.zig
|
||||
SOURCES
|
||||
@@ -361,35 +353,24 @@ register_command(
|
||||
${BUN_CI_INFO_OUTPUTS}
|
||||
)
|
||||
|
||||
if(SKIP_CODEGEN)
|
||||
# Skip JavaScript codegen - useful for Windows ARM64 debug builds where bun crashes
|
||||
message(STATUS "SKIP_CODEGEN is ON - skipping bun-js-modules codegen")
|
||||
foreach(output ${BUN_JAVASCRIPT_OUTPUTS})
|
||||
if(NOT EXISTS ${output})
|
||||
message(FATAL_ERROR "SKIP_CODEGEN is ON but ${output} does not exist. Run codegen manually first.")
|
||||
endif()
|
||||
endforeach()
|
||||
else()
|
||||
register_command(
|
||||
TARGET
|
||||
bun-js-modules
|
||||
COMMENT
|
||||
"Generating JavaScript modules"
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE}
|
||||
${BUN_FLAGS}
|
||||
run
|
||||
${BUN_JAVASCRIPT_CODEGEN_SCRIPT}
|
||||
--debug=${DEBUG}
|
||||
${BUILD_PATH}
|
||||
SOURCES
|
||||
${BUN_JAVASCRIPT_SOURCES}
|
||||
${BUN_JAVASCRIPT_CODEGEN_SOURCES}
|
||||
register_command(
|
||||
TARGET
|
||||
bun-js-modules
|
||||
COMMENT
|
||||
"Generating JavaScript modules"
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE}
|
||||
run
|
||||
${BUN_JAVASCRIPT_CODEGEN_SCRIPT}
|
||||
OUTPUTS
|
||||
${BUN_JAVASCRIPT_OUTPUTS}
|
||||
)
|
||||
endif()
|
||||
--debug=${DEBUG}
|
||||
${BUILD_PATH}
|
||||
SOURCES
|
||||
${BUN_JAVASCRIPT_SOURCES}
|
||||
${BUN_JAVASCRIPT_CODEGEN_SOURCES}
|
||||
${BUN_JAVASCRIPT_CODEGEN_SCRIPT}
|
||||
OUTPUTS
|
||||
${BUN_JAVASCRIPT_OUTPUTS}
|
||||
)
|
||||
|
||||
set(BUN_BAKE_RUNTIME_CODEGEN_SCRIPT ${CWD}/src/codegen/bake-codegen.ts)
|
||||
|
||||
@@ -411,7 +392,6 @@ register_command(
|
||||
"Bundling Bake Runtime"
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE}
|
||||
${BUN_FLAGS}
|
||||
run
|
||||
${BUN_BAKE_RUNTIME_CODEGEN_SCRIPT}
|
||||
--debug=${DEBUG}
|
||||
@@ -435,13 +415,16 @@ string(REPLACE ";" "," BUN_BINDGENV2_SOURCES_COMMA_SEPARATED
|
||||
"${BUN_BINDGENV2_SOURCES}")
|
||||
|
||||
execute_process(
|
||||
COMMAND ${BUN_EXECUTABLE} ${BUN_FLAGS} run ${BUN_BINDGENV2_SCRIPT}
|
||||
COMMAND ${BUN_EXECUTABLE} run ${BUN_BINDGENV2_SCRIPT}
|
||||
--command=list-outputs
|
||||
--sources=${BUN_BINDGENV2_SOURCES_COMMA_SEPARATED}
|
||||
--codegen-path=${CODEGEN_PATH}
|
||||
RESULT_VARIABLE bindgen_result
|
||||
OUTPUT_VARIABLE bindgen_outputs
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
if(${bindgen_result})
|
||||
message(FATAL_ERROR "bindgenv2/script.ts exited with non-zero status")
|
||||
endif()
|
||||
foreach(output IN LISTS bindgen_outputs)
|
||||
if(output MATCHES "\.cpp$")
|
||||
list(APPEND BUN_BINDGENV2_CPP_OUTPUTS ${output})
|
||||
@@ -458,7 +441,7 @@ register_command(
|
||||
COMMENT
|
||||
"Generating bindings (v2)"
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE} ${BUN_FLAGS} run ${BUN_BINDGENV2_SCRIPT}
|
||||
${BUN_EXECUTABLE} run ${BUN_BINDGENV2_SCRIPT}
|
||||
--command=generate
|
||||
--codegen-path=${CODEGEN_PATH}
|
||||
--sources=${BUN_BINDGENV2_SOURCES_COMMA_SEPARATED}
|
||||
@@ -489,7 +472,6 @@ register_command(
|
||||
"Processing \".bind.ts\" files"
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE}
|
||||
${BUN_FLAGS}
|
||||
run
|
||||
${BUN_BINDGEN_SCRIPT}
|
||||
--debug=${DEBUG}
|
||||
@@ -522,7 +504,6 @@ register_command(
|
||||
"Generating JSSink.{cpp,h}"
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE}
|
||||
${BUN_FLAGS}
|
||||
run
|
||||
${BUN_JS_SINK_SCRIPT}
|
||||
${CODEGEN_PATH}
|
||||
@@ -595,7 +576,6 @@ foreach(i RANGE 0 ${BUN_OBJECT_LUT_SOURCES_MAX_INDEX})
|
||||
${BUN_OBJECT_LUT_SOURCE}
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE}
|
||||
${BUN_FLAGS}
|
||||
run
|
||||
${BUN_OBJECT_LUT_SCRIPT}
|
||||
${BUN_OBJECT_LUT_SOURCE}
|
||||
@@ -679,10 +659,6 @@ endif()
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|ARM|arm64|ARM64|aarch64|AARCH64")
|
||||
if(APPLE)
|
||||
set(ZIG_CPU "apple_m1")
|
||||
elseif(WIN32)
|
||||
# Windows ARM64: use a specific CPU with NEON support
|
||||
# Zig running under x64 emulation would detect wrong CPU with "native"
|
||||
set(ZIG_CPU "cortex_a76")
|
||||
else()
|
||||
set(ZIG_CPU "native")
|
||||
endif()
|
||||
@@ -721,7 +697,6 @@ register_command(
|
||||
-Denable_asan=$<IF:$<BOOL:${ENABLE_ZIG_ASAN}>,true,false>
|
||||
-Denable_fuzzilli=$<IF:$<BOOL:${ENABLE_FUZZILLI}>,true,false>
|
||||
-Denable_valgrind=$<IF:$<BOOL:${ENABLE_VALGRIND}>,true,false>
|
||||
-Denable_tinycc=$<IF:$<BOOL:${ENABLE_TINYCC}>,true,false>
|
||||
-Duse_mimalloc=$<IF:$<BOOL:${USE_MIMALLOC_AS_DEFAULT_ALLOCATOR}>,true,false>
|
||||
-Dllvm_codegen_threads=${LLVM_ZIG_CODEGEN_THREADS}
|
||||
-Dversion=${VERSION}
|
||||
@@ -939,7 +914,7 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
if(USE_MIMALLOC_AS_DEFAULT_ALLOCATOR)
|
||||
target_compile_definitions(${bun} PRIVATE USE_BUN_MIMALLOC=1)
|
||||
target_compile_definitions(${bun} PRIVATE USE_MIMALLOC=1)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(${bun} PRIVATE
|
||||
@@ -1239,7 +1214,7 @@ if(BUN_LINK_ONLY)
|
||||
WEBKIT_DOWNLOAD_URL=${WEBKIT_DOWNLOAD_URL}
|
||||
WEBKIT_VERSION=${WEBKIT_VERSION}
|
||||
ZIG_COMMIT=${ZIG_COMMIT}
|
||||
${BUN_EXECUTABLE} ${BUN_FLAGS} ${CWD}/scripts/create-link-metadata.mjs ${BUILD_PATH} ${bun}
|
||||
${BUN_EXECUTABLE} ${CWD}/scripts/create-link-metadata.mjs ${BUILD_PATH} ${bun}
|
||||
SOURCES
|
||||
${BUN_ZIG_OUTPUT}
|
||||
${BUN_CPP_OUTPUT}
|
||||
@@ -1253,7 +1228,6 @@ if(WIN32)
|
||||
target_link_libraries(${bun} PRIVATE
|
||||
${WEBKIT_LIB_PATH}/WTF.lib
|
||||
${WEBKIT_LIB_PATH}/JavaScriptCore.lib
|
||||
${WEBKIT_LIB_PATH}/bmalloc.lib
|
||||
${WEBKIT_LIB_PATH}/sicudtd.lib
|
||||
${WEBKIT_LIB_PATH}/sicuind.lib
|
||||
${WEBKIT_LIB_PATH}/sicuucd.lib
|
||||
@@ -1262,7 +1236,6 @@ if(WIN32)
|
||||
target_link_libraries(${bun} PRIVATE
|
||||
${WEBKIT_LIB_PATH}/WTF.lib
|
||||
${WEBKIT_LIB_PATH}/JavaScriptCore.lib
|
||||
${WEBKIT_LIB_PATH}/bmalloc.lib
|
||||
${WEBKIT_LIB_PATH}/sicudt.lib
|
||||
${WEBKIT_LIB_PATH}/sicuin.lib
|
||||
${WEBKIT_LIB_PATH}/sicuuc.lib
|
||||
@@ -1340,9 +1313,6 @@ if(WIN32)
|
||||
wsock32 # ws2_32 required by TransmitFile aka sendfile on windows
|
||||
delayimp.lib
|
||||
)
|
||||
# Required for static ICU linkage - without this, ICU headers expect DLL linkage
|
||||
# which causes ABI mismatch and crashes (STATUS_STACK_BUFFER_OVERRUN)
|
||||
target_compile_definitions(${bun} PRIVATE U_STATIC_IMPLEMENTATION)
|
||||
endif()
|
||||
|
||||
# --- Packaging ---
|
||||
|
||||
@@ -20,15 +20,6 @@ set(HIGHWAY_CMAKE_ARGS
|
||||
-DHWY_ENABLE_INSTALL=OFF
|
||||
)
|
||||
|
||||
# On Windows ARM64 with clang-cl, the __ARM_NEON macro isn't defined by default
|
||||
# but NEON intrinsics are supported. Define it so Highway can detect NEON support.
|
||||
if(WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64|AARCH64")
|
||||
list(APPEND HIGHWAY_CMAKE_ARGS
|
||||
-DCMAKE_C_FLAGS=-D__ARM_NEON=1
|
||||
-DCMAKE_CXX_FLAGS=-D__ARM_NEON=1
|
||||
)
|
||||
endif()
|
||||
|
||||
register_cmake_command(
|
||||
TARGET
|
||||
highway
|
||||
|
||||
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
cloudflare/lol-html
|
||||
COMMIT
|
||||
e9e16dca48dd4a8ffbc77642bc4be60407585f11
|
||||
d64457d9ff0143deef025d5df7e8586092b9afb7
|
||||
)
|
||||
|
||||
set(LOLHTML_CWD ${VENDOR_PATH}/lolhtml/c-api)
|
||||
@@ -33,37 +33,6 @@ if (NOT WIN32)
|
||||
set(RUSTFLAGS "-Cpanic=abort-Cdebuginfo=0-Cforce-unwind-tables=no-Copt-level=s")
|
||||
endif()
|
||||
|
||||
# On Windows, ensure MSVC link.exe is used instead of Git's link.exe
|
||||
set(LOLHTML_ENV
|
||||
CARGO_TERM_COLOR=always
|
||||
CARGO_TERM_VERBOSE=true
|
||||
CARGO_TERM_DIAGNOSTIC=true
|
||||
CARGO_ENCODED_RUSTFLAGS=${RUSTFLAGS}
|
||||
CARGO_HOME=${CARGO_HOME}
|
||||
RUSTUP_HOME=${RUSTUP_HOME}
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
# On Windows, tell Rust to use MSVC link.exe directly via the target-specific linker env var.
|
||||
# This avoids Git's /usr/bin/link being found first in PATH.
|
||||
# Find the MSVC link.exe from Visual Studio installation
|
||||
file(GLOB MSVC_VERSIONS "C:/Program Files/Microsoft Visual Studio/2022/*/VC/Tools/MSVC/*")
|
||||
if(MSVC_VERSIONS)
|
||||
list(GET MSVC_VERSIONS -1 MSVC_LATEST) # Get the latest version
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64")
|
||||
set(MSVC_LINK_PATH "${MSVC_LATEST}/bin/HostARM64/arm64/link.exe")
|
||||
set(CARGO_LINKER_VAR "CARGO_TARGET_AARCH64_PC_WINDOWS_MSVC_LINKER")
|
||||
else()
|
||||
set(MSVC_LINK_PATH "${MSVC_LATEST}/bin/Hostx64/x64/link.exe")
|
||||
set(CARGO_LINKER_VAR "CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER")
|
||||
endif()
|
||||
if(EXISTS "${MSVC_LINK_PATH}")
|
||||
list(APPEND LOLHTML_ENV "${CARGO_LINKER_VAR}=${MSVC_LINK_PATH}")
|
||||
message(STATUS "lolhtml: Using MSVC link.exe: ${MSVC_LINK_PATH}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
lolhtml
|
||||
@@ -76,7 +45,12 @@ register_command(
|
||||
ARTIFACTS
|
||||
${LOLHTML_LIBRARY}
|
||||
ENVIRONMENT
|
||||
${LOLHTML_ENV}
|
||||
CARGO_TERM_COLOR=always
|
||||
CARGO_TERM_VERBOSE=true
|
||||
CARGO_TERM_DIAGNOSTIC=true
|
||||
CARGO_ENCODED_RUSTFLAGS=${RUSTFLAGS}
|
||||
CARGO_HOME=${CARGO_HOME}
|
||||
RUSTUP_HOME=${RUSTUP_HOME}
|
||||
)
|
||||
|
||||
target_link_libraries(${bun} PRIVATE ${LOLHTML_LIBRARY})
|
||||
|
||||
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
oven-sh/mimalloc
|
||||
COMMIT
|
||||
ffa38ab8ac914f9eb7af75c1f8ad457643dc14f2
|
||||
1beadf9651a7bfdec6b5367c380ecc3fe1c40d1a
|
||||
)
|
||||
|
||||
set(MIMALLOC_CMAKE_ARGS
|
||||
@@ -14,7 +14,7 @@ set(MIMALLOC_CMAKE_ARGS
|
||||
-DMI_BUILD_TESTS=OFF
|
||||
-DMI_USE_CXX=ON
|
||||
-DMI_SKIP_COLLECT_ON_EXIT=ON
|
||||
|
||||
|
||||
# ```
|
||||
# ❯ mimalloc_allow_large_os_pages=0 BUN_PORT=3004 mem bun http-hello.js
|
||||
# Started development server: http://localhost:3004
|
||||
@@ -51,7 +51,7 @@ if(ENABLE_ASAN)
|
||||
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_DEBUG_UBSAN=ON)
|
||||
elseif(APPLE OR LINUX)
|
||||
if(APPLE)
|
||||
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OVERRIDE=OFF)
|
||||
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OVERRIDE=OFF)
|
||||
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OSX_ZONE=OFF)
|
||||
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OSX_INTERPOSE=OFF)
|
||||
else()
|
||||
@@ -77,9 +77,9 @@ endif()
|
||||
|
||||
if(WIN32)
|
||||
if(DEBUG)
|
||||
set(MIMALLOC_LIBRARY mimalloc-debug)
|
||||
set(MIMALLOC_LIBRARY mimalloc-static-debug)
|
||||
else()
|
||||
set(MIMALLOC_LIBRARY mimalloc)
|
||||
set(MIMALLOC_LIBRARY mimalloc-static)
|
||||
endif()
|
||||
elseif(DEBUG)
|
||||
if (ENABLE_ASAN)
|
||||
|
||||
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
oven-sh/tinycc
|
||||
COMMIT
|
||||
12882eee073cfe5c7621bcfadf679e1372d4537b
|
||||
29985a3b59898861442fa3b43f663fc1af2591d7
|
||||
)
|
||||
|
||||
register_cmake_command(
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||
|
||||
set(CMAKE_C_COMPILER_WORKS ON)
|
||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||
|
||||
# Force ARM64 architecture ID - this is what CMake uses to determine /machine: flag
|
||||
set(MSVC_C_ARCHITECTURE_ID ARM64 CACHE INTERNAL "")
|
||||
set(MSVC_CXX_ARCHITECTURE_ID ARM64 CACHE INTERNAL "")
|
||||
|
||||
# CMake 4.0+ policy CMP0197 controls how MSVC machine type flags are handled
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0197 NEW CACHE INTERNAL "")
|
||||
|
||||
# Clear any inherited static linker flags that might have wrong machine types
|
||||
set(CMAKE_STATIC_LINKER_FLAGS "" CACHE STRING "" FORCE)
|
||||
|
||||
# Use wrapper script for llvm-lib that strips /machine:x64 flags
|
||||
# This works around CMake 4.1.0 bug where both ARM64 and x64 machine flags are added
|
||||
get_filename_component(_TOOLCHAIN_DIR "${CMAKE_CURRENT_LIST_DIR}" DIRECTORY)
|
||||
set(CMAKE_AR "${_TOOLCHAIN_DIR}/scripts/llvm-lib-wrapper.bat" CACHE FILEPATH "" FORCE)
|
||||
@@ -6,8 +6,7 @@ endif()
|
||||
|
||||
optionx(BUILDKITE_ORGANIZATION_SLUG STRING "The organization slug to use on Buildkite" DEFAULT "bun")
|
||||
optionx(BUILDKITE_PIPELINE_SLUG STRING "The pipeline slug to use on Buildkite" DEFAULT "bun")
|
||||
optionx(BUILDKITE_BUILD_ID STRING "The build ID (UUID) to use on Buildkite")
|
||||
optionx(BUILDKITE_BUILD_NUMBER STRING "The build number to use on Buildkite")
|
||||
optionx(BUILDKITE_BUILD_ID STRING "The build ID to use on Buildkite")
|
||||
optionx(BUILDKITE_GROUP_ID STRING "The group ID to use on Buildkite")
|
||||
|
||||
if(ENABLE_BASELINE)
|
||||
@@ -33,13 +32,7 @@ if(NOT BUILDKITE_BUILD_ID)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Use BUILDKITE_BUILD_NUMBER for the URL if available, as the UUID format causes a 302 redirect
|
||||
# that CMake's file(DOWNLOAD) doesn't follow, resulting in empty response.
|
||||
if(BUILDKITE_BUILD_NUMBER)
|
||||
setx(BUILDKITE_BUILD_URL https://buildkite.com/${BUILDKITE_ORGANIZATION_SLUG}/${BUILDKITE_PIPELINE_SLUG}/builds/${BUILDKITE_BUILD_NUMBER})
|
||||
else()
|
||||
setx(BUILDKITE_BUILD_URL https://buildkite.com/${BUILDKITE_ORGANIZATION_SLUG}/${BUILDKITE_PIPELINE_SLUG}/builds/${BUILDKITE_BUILD_ID})
|
||||
endif()
|
||||
setx(BUILDKITE_BUILD_URL https://buildkite.com/${BUILDKITE_ORGANIZATION_SLUG}/${BUILDKITE_PIPELINE_SLUG}/builds/${BUILDKITE_BUILD_ID})
|
||||
setx(BUILDKITE_BUILD_PATH ${BUILDKITE_BUILDS_PATH}/builds/${BUILDKITE_BUILD_ID})
|
||||
|
||||
file(
|
||||
@@ -55,17 +48,6 @@ if(NOT BUILDKITE_BUILD_STATUS EQUAL 0)
|
||||
endif()
|
||||
|
||||
file(READ ${BUILDKITE_BUILD_PATH}/build.json BUILDKITE_BUILD)
|
||||
# CMake's string(JSON ...) interprets escape sequences like \n, \r, \t.
|
||||
# We need to escape these specific sequences while preserving valid JSON escapes like \" and \\.
|
||||
# Strategy: Use a unique placeholder to protect \\ sequences, escape \n/\r/\t, then restore \\.
|
||||
# This prevents \\n (literal backslash + n) from being corrupted to \\\n.
|
||||
set(BKSLASH_PLACEHOLDER "___BKSLASH_PLACEHOLDER_7f3a9b2c___")
|
||||
string(REPLACE "\\\\" "${BKSLASH_PLACEHOLDER}" BUILDKITE_BUILD "${BUILDKITE_BUILD}")
|
||||
string(REPLACE "\\n" "\\\\n" BUILDKITE_BUILD "${BUILDKITE_BUILD}")
|
||||
string(REPLACE "\\r" "\\\\r" BUILDKITE_BUILD "${BUILDKITE_BUILD}")
|
||||
string(REPLACE "\\t" "\\\\t" BUILDKITE_BUILD "${BUILDKITE_BUILD}")
|
||||
string(REPLACE "${BKSLASH_PLACEHOLDER}" "\\\\" BUILDKITE_BUILD "${BUILDKITE_BUILD}")
|
||||
|
||||
string(JSON BUILDKITE_BUILD_UUID GET ${BUILDKITE_BUILD} id)
|
||||
string(JSON BUILDKITE_JOBS GET ${BUILDKITE_BUILD} jobs)
|
||||
string(JSON BUILDKITE_JOBS_COUNT LENGTH ${BUILDKITE_JOBS})
|
||||
|
||||
@@ -17,14 +17,6 @@ if (NOT CI)
|
||||
set(BUN_EXECUTABLE ${BUN_EXECUTABLE} CACHE FILEPATH "Bun executable" FORCE)
|
||||
endif()
|
||||
|
||||
# On Windows ARM64, we need to add --smol flag to avoid crashes when running
|
||||
# x64 bun under WoW64 emulation
|
||||
if(WIN32 AND ARCH STREQUAL "aarch64")
|
||||
set(BUN_FLAGS "--smol" CACHE STRING "Extra flags for bun executable")
|
||||
else()
|
||||
set(BUN_FLAGS "" CACHE STRING "Extra flags for bun executable")
|
||||
endif()
|
||||
|
||||
# If this is not set, some advanced features are not checked.
|
||||
# https://github.com/oven-sh/bun/blob/cd7f6a1589db7f1e39dc4e3f4a17234afbe7826c/src/bun.js/javascript.zig#L1069-L1072
|
||||
setenv(BUN_GARBAGE_COLLECTOR_LEVEL 1)
|
||||
|
||||
@@ -12,13 +12,7 @@ if(NOT ENABLE_LLVM)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# LLVM 21 is required for Windows ARM64 (first version with ARM64 Windows builds)
|
||||
# Other platforms use LLVM 19.1.7
|
||||
if(WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64|AARCH64")
|
||||
set(DEFAULT_LLVM_VERSION "21.1.8")
|
||||
else()
|
||||
set(DEFAULT_LLVM_VERSION "19.1.7")
|
||||
endif()
|
||||
set(DEFAULT_LLVM_VERSION "19.1.7")
|
||||
|
||||
optionx(LLVM_VERSION STRING "The version of LLVM to use" DEFAULT ${DEFAULT_LLVM_VERSION})
|
||||
|
||||
|
||||
@@ -2,14 +2,10 @@ option(WEBKIT_VERSION "The version of WebKit to use")
|
||||
option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of downloading")
|
||||
|
||||
if(NOT WEBKIT_VERSION)
|
||||
set(WEBKIT_VERSION cc5e0bddf7eae1d820cf673158845fe9bd83c094)
|
||||
set(WEBKIT_VERSION 863778130931e0081a688f48e8479b8ee61b9507)
|
||||
endif()
|
||||
|
||||
# Use preview build URL for Windows ARM64 until the fix is merged to main
|
||||
set(WEBKIT_PREVIEW_PR 140)
|
||||
|
||||
string(SUBSTRING ${WEBKIT_VERSION} 0 16 WEBKIT_VERSION_PREFIX)
|
||||
string(SUBSTRING ${WEBKIT_VERSION} 0 8 WEBKIT_VERSION_SHORT)
|
||||
|
||||
if(WEBKIT_LOCAL)
|
||||
set(DEFAULT_WEBKIT_PATH ${VENDOR_PATH}/WebKit/WebKitBuild/${CMAKE_BUILD_TYPE})
|
||||
@@ -32,30 +28,13 @@ if(WEBKIT_LOCAL)
|
||||
# make jsc-compile-debug jsc-copy-headers
|
||||
include_directories(
|
||||
${WEBKIT_PATH}
|
||||
${WEBKIT_PATH}/JavaScriptCore/Headers
|
||||
${WEBKIT_PATH}/JavaScriptCore/Headers/JavaScriptCore
|
||||
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders
|
||||
${WEBKIT_PATH}/bmalloc/Headers
|
||||
${WEBKIT_PATH}/WTF/Headers
|
||||
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders/JavaScriptCore
|
||||
${WEBKIT_PATH}/JavaScriptCore/DerivedSources/inspector
|
||||
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders/JavaScriptCore
|
||||
)
|
||||
|
||||
# On Windows, add ICU include path from vcpkg
|
||||
if(WIN32)
|
||||
# Auto-detect vcpkg triplet
|
||||
set(VCPKG_ARM64_PATH ${VENDOR_PATH}/WebKit/vcpkg_installed/arm64-windows-static)
|
||||
set(VCPKG_X64_PATH ${VENDOR_PATH}/WebKit/vcpkg_installed/x64-windows-static)
|
||||
if(EXISTS ${VCPKG_ARM64_PATH})
|
||||
set(VCPKG_ICU_PATH ${VCPKG_ARM64_PATH})
|
||||
else()
|
||||
set(VCPKG_ICU_PATH ${VCPKG_X64_PATH})
|
||||
endif()
|
||||
if(EXISTS ${VCPKG_ICU_PATH}/include)
|
||||
include_directories(${VCPKG_ICU_PATH}/include)
|
||||
message(STATUS "Using ICU from vcpkg: ${VCPKG_ICU_PATH}/include")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# After this point, only prebuilt WebKit is supported
|
||||
@@ -72,7 +51,7 @@ else()
|
||||
message(FATAL_ERROR "Unsupported operating system: ${CMAKE_SYSTEM_NAME}")
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|ARM64|aarch64|AARCH64")
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64")
|
||||
set(WEBKIT_ARCH "arm64")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|x64|AMD64")
|
||||
set(WEBKIT_ARCH "amd64")
|
||||
@@ -101,14 +80,7 @@ endif()
|
||||
|
||||
setx(WEBKIT_NAME bun-webkit-${WEBKIT_OS}-${WEBKIT_ARCH}${WEBKIT_SUFFIX})
|
||||
set(WEBKIT_FILENAME ${WEBKIT_NAME}.tar.gz)
|
||||
|
||||
if(WEBKIT_VERSION MATCHES "^autobuild-")
|
||||
set(WEBKIT_TAG ${WEBKIT_VERSION})
|
||||
else()
|
||||
set(WEBKIT_TAG autobuild-${WEBKIT_VERSION})
|
||||
endif()
|
||||
|
||||
setx(WEBKIT_DOWNLOAD_URL https://github.com/oven-sh/WebKit/releases/download/${WEBKIT_TAG}/${WEBKIT_FILENAME})
|
||||
setx(WEBKIT_DOWNLOAD_URL https://github.com/oven-sh/WebKit/releases/download/autobuild-${WEBKIT_VERSION}/${WEBKIT_FILENAME})
|
||||
|
||||
if(EXISTS ${WEBKIT_PATH}/package.json)
|
||||
file(READ ${WEBKIT_PATH}/package.json WEBKIT_PACKAGE_JSON)
|
||||
@@ -118,14 +90,7 @@ if(EXISTS ${WEBKIT_PATH}/package.json)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
file(
|
||||
DOWNLOAD ${WEBKIT_DOWNLOAD_URL} ${CACHE_PATH}/${WEBKIT_FILENAME} SHOW_PROGRESS
|
||||
STATUS WEBKIT_DOWNLOAD_STATUS
|
||||
)
|
||||
if(NOT "${WEBKIT_DOWNLOAD_STATUS}" MATCHES "^0;")
|
||||
message(FATAL_ERROR "Failed to download WebKit: ${WEBKIT_DOWNLOAD_STATUS}")
|
||||
endif()
|
||||
|
||||
file(DOWNLOAD ${WEBKIT_DOWNLOAD_URL} ${CACHE_PATH}/${WEBKIT_FILENAME} SHOW_PROGRESS)
|
||||
file(ARCHIVE_EXTRACT INPUT ${CACHE_PATH}/${WEBKIT_FILENAME} DESTINATION ${CACHE_PATH} TOUCH)
|
||||
file(REMOVE ${CACHE_PATH}/${WEBKIT_FILENAME})
|
||||
file(REMOVE_RECURSE ${WEBKIT_PATH})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|ARM64|aarch64|AARCH64")
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64")
|
||||
set(DEFAULT_ZIG_ARCH "aarch64")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|x64|AMD64")
|
||||
set(DEFAULT_ZIG_ARCH "x86_64")
|
||||
|
||||
@@ -35,8 +35,8 @@ end
|
||||
set -l bun_install_boolean_flags yarn production optional development no-save dry-run force no-cache silent verbose global
|
||||
set -l bun_install_boolean_flags_descriptions "Write a yarn.lock file (yarn v1)" "Don't install devDependencies" "Add dependency to optionalDependencies" "Add dependency to devDependencies" "Don't update package.json or save a lockfile" "Don't install anything" "Always request the latest versions from the registry & reinstall all dependencies" "Ignore manifest cache entirely" "Don't output anything" "Excessively verbose logging" "Use global folder"
|
||||
|
||||
set -l bun_builtin_cmds_without_run dev create help bun upgrade discord install remove add update init pm x
|
||||
set -l bun_builtin_cmds_accepting_flags create help bun upgrade discord run init link unlink pm x update
|
||||
set -l bun_builtin_cmds_without_run dev create help bun upgrade discord install remove add init pm x
|
||||
set -l bun_builtin_cmds_accepting_flags create help bun upgrade discord run init link unlink pm x
|
||||
|
||||
function __bun_complete_bins_scripts --inherit-variable bun_builtin_cmds_without_run -d "Emit bun completions for bins and scripts"
|
||||
# Do nothing if we already have a builtin subcommand,
|
||||
@@ -148,14 +148,14 @@ complete -c bun \
|
||||
|
||||
for i in (seq (count $bun_install_boolean_flags))
|
||||
complete -c bun \
|
||||
-n "__fish_seen_subcommand_from install add remove update" -l "$bun_install_boolean_flags[$i]" -d "$bun_install_boolean_flags_descriptions[$i]"
|
||||
-n "__fish_seen_subcommand_from install add remove" -l "$bun_install_boolean_flags[$i]" -d "$bun_install_boolean_flags_descriptions[$i]"
|
||||
end
|
||||
|
||||
complete -c bun \
|
||||
-n "__fish_seen_subcommand_from install add remove update" -l 'cwd' -d 'Change working directory'
|
||||
-n "__fish_seen_subcommand_from install add remove" -l 'cwd' -d 'Change working directory'
|
||||
|
||||
complete -c bun \
|
||||
-n "__fish_seen_subcommand_from install add remove update" -l 'cache-dir' -d 'Choose a cache directory (default: $HOME/.bun/install/cache)'
|
||||
-n "__fish_seen_subcommand_from install add remove" -l 'cache-dir' -d 'Choose a cache directory (default: $HOME/.bun/install/cache)'
|
||||
|
||||
complete -c bun \
|
||||
-n "__fish_seen_subcommand_from add" -d 'Popular' -a '(__fish__get_bun_packages)'
|
||||
@@ -183,5 +183,4 @@ complete -c bun -n "__fish_use_subcommand" -a "unlink" -d "Unregister a local np
|
||||
complete -c bun -n "__fish_use_subcommand" -a "pm" -d "Additional package management utilities" -f
|
||||
complete -c bun -n "__fish_use_subcommand" -a "x" -d "Execute a package binary, installing if needed" -f
|
||||
complete -c bun -n "__fish_use_subcommand" -a "outdated" -d "Display the latest versions of outdated dependencies" -f
|
||||
complete -c bun -n "__fish_use_subcommand" -a "update" -d "Update dependencies to their latest versions" -f
|
||||
complete -c bun -n "__fish_use_subcommand" -a "publish" -d "Publish your package from local to npm" -f
|
||||
|
||||
@@ -220,78 +220,6 @@ An array of paths corresponding to the entrypoints of our application. One bundl
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### files
|
||||
|
||||
A map of file paths to their contents for in-memory bundling. This allows you to bundle virtual files that don't exist on disk, or override the contents of files that do exist. This option is only available in the JavaScript API.
|
||||
|
||||
File contents can be provided as a `string`, `Blob`, `TypedArray`, or `ArrayBuffer`.
|
||||
|
||||
#### Bundle entirely from memory
|
||||
|
||||
You can bundle code without any files on disk by providing all sources via `files`:
|
||||
|
||||
```ts title="build.ts" icon="/icons/typescript.svg"
|
||||
const result = await Bun.build({
|
||||
entrypoints: ["/app/index.ts"],
|
||||
files: {
|
||||
"/app/index.ts": `
|
||||
import { greet } from "./greet.ts";
|
||||
console.log(greet("World"));
|
||||
`,
|
||||
"/app/greet.ts": `
|
||||
export function greet(name: string) {
|
||||
return "Hello, " + name + "!";
|
||||
}
|
||||
`,
|
||||
},
|
||||
});
|
||||
|
||||
const output = await result.outputs[0].text();
|
||||
console.log(output);
|
||||
```
|
||||
|
||||
When all entrypoints are in the `files` map, the current working directory is used as the root.
|
||||
|
||||
#### Override files on disk
|
||||
|
||||
In-memory files take priority over files on disk. This lets you override specific files while keeping the rest of your codebase unchanged:
|
||||
|
||||
```ts title="build.ts" icon="/icons/typescript.svg"
|
||||
// Assume ./src/config.ts exists on disk with development settings
|
||||
await Bun.build({
|
||||
entrypoints: ["./src/index.ts"],
|
||||
files: {
|
||||
// Override config.ts with production values
|
||||
"./src/config.ts": `
|
||||
export const API_URL = "https://api.production.com";
|
||||
export const DEBUG = false;
|
||||
`,
|
||||
},
|
||||
outdir: "./dist",
|
||||
});
|
||||
```
|
||||
|
||||
#### Mix disk and virtual files
|
||||
|
||||
Real files on disk can import virtual files, and virtual files can import real files:
|
||||
|
||||
```ts title="build.ts" icon="/icons/typescript.svg"
|
||||
// ./src/index.ts exists on disk and imports "./generated.ts"
|
||||
await Bun.build({
|
||||
entrypoints: ["./src/index.ts"],
|
||||
files: {
|
||||
// Provide a virtual file that index.ts imports
|
||||
"./src/generated.ts": `
|
||||
export const BUILD_ID = "${crypto.randomUUID()}";
|
||||
export const BUILD_TIME = ${Date.now()};
|
||||
`,
|
||||
},
|
||||
outdir: "./dist",
|
||||
});
|
||||
```
|
||||
|
||||
This is useful for code generation, injecting build-time constants, or testing with mock modules.
|
||||
|
||||
### outdir
|
||||
|
||||
The directory where output files will be written.
|
||||
@@ -1291,79 +1219,6 @@ declare module "bun:bundle" {
|
||||
|
||||
Ensure the file is included in your `tsconfig.json` (e.g., `"include": ["src", "env.d.ts"]`). Now `feature()` only accepts those flags, and invalid strings like `feature("TYPO")` become type errors.
|
||||
|
||||
### metafile
|
||||
|
||||
Generate metadata about the build in a structured format. The metafile contains information about all input files, output files, their sizes, imports, and exports. This is useful for:
|
||||
|
||||
- **Bundle analysis**: Understand what's contributing to bundle size
|
||||
- **Visualization**: Feed into tools like [esbuild's bundle analyzer](https://esbuild.github.io/analyze/) or other visualization tools
|
||||
- **Dependency tracking**: See the full import graph of your application
|
||||
- **CI integration**: Track bundle size changes over time
|
||||
|
||||
<Tabs>
|
||||
<Tab title="JavaScript">
|
||||
```ts title="build.ts" icon="/icons/typescript.svg"
|
||||
const result = await Bun.build({
|
||||
entrypoints: ['./src/index.ts'],
|
||||
outdir: './dist',
|
||||
metafile: true,
|
||||
});
|
||||
|
||||
if (result.metafile) {
|
||||
// Analyze inputs
|
||||
for (const [path, meta] of Object.entries(result.metafile.inputs)) {
|
||||
console.log(`${path}: ${meta.bytes} bytes`);
|
||||
}
|
||||
|
||||
// Analyze outputs
|
||||
for (const [path, meta] of Object.entries(result.metafile.outputs)) {
|
||||
console.log(`${path}: ${meta.bytes} bytes`);
|
||||
}
|
||||
|
||||
// Save for external analysis tools
|
||||
await Bun.write('./dist/meta.json', JSON.stringify(result.metafile));
|
||||
}
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="CLI">
|
||||
```bash terminal icon="terminal"
|
||||
bun build ./src/index.ts --outdir ./dist --metafile ./dist/meta.json
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
The metafile structure contains:
|
||||
|
||||
```ts
|
||||
interface BuildMetafile {
|
||||
inputs: {
|
||||
[path: string]: {
|
||||
bytes: number;
|
||||
imports: Array<{
|
||||
path: string;
|
||||
kind: ImportKind;
|
||||
original?: string; // Original specifier before resolution
|
||||
external?: boolean;
|
||||
}>;
|
||||
format?: "esm" | "cjs" | "json" | "css";
|
||||
};
|
||||
};
|
||||
outputs: {
|
||||
[path: string]: {
|
||||
bytes: number;
|
||||
inputs: {
|
||||
[path: string]: { bytesInOutput: number };
|
||||
};
|
||||
imports: Array<{ path: string; kind: ImportKind }>;
|
||||
exports: string[];
|
||||
entryPoint?: string;
|
||||
cssBundle?: string; // Associated CSS file for JS entry points
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Outputs
|
||||
|
||||
The `Bun.build` function returns a `Promise<BuildOutput>`, defined as:
|
||||
@@ -1373,7 +1228,6 @@ interface BuildOutput {
|
||||
outputs: BuildArtifact[];
|
||||
success: boolean;
|
||||
logs: Array<object>; // see docs for details
|
||||
metafile?: BuildMetafile; // only when metafile: true
|
||||
}
|
||||
|
||||
interface BuildArtifact extends Blob {
|
||||
|
||||
@@ -121,7 +121,6 @@
|
||||
"/runtime/file-io",
|
||||
"/runtime/streams",
|
||||
"/runtime/binary-data",
|
||||
"/runtime/archive",
|
||||
"/runtime/sql",
|
||||
"/runtime/sqlite",
|
||||
"/runtime/s3",
|
||||
@@ -150,8 +149,6 @@
|
||||
"/runtime/secrets",
|
||||
"/runtime/console",
|
||||
"/runtime/yaml",
|
||||
"/runtime/json5",
|
||||
"/runtime/jsonl",
|
||||
"/runtime/html-rewriter",
|
||||
"/runtime/hashing",
|
||||
"/runtime/glob",
|
||||
@@ -499,7 +496,6 @@
|
||||
"/guides/runtime/import-json",
|
||||
"/guides/runtime/import-toml",
|
||||
"/guides/runtime/import-yaml",
|
||||
"/guides/runtime/import-json5",
|
||||
"/guides/runtime/import-html",
|
||||
"/guides/util/import-meta-dir",
|
||||
"/guides/util/import-meta-file",
|
||||
|
||||
@@ -33,7 +33,7 @@ Alternatively, you can create a PM2 configuration file. Create a file named `pm2
|
||||
|
||||
```js pm2.config.js icon="file-code"
|
||||
module.exports = {
|
||||
name: "app", // Name of your application
|
||||
title: "app", // Name of your application
|
||||
script: "index.ts", // Entry point of your application
|
||||
interpreter: "bun", // Bun interpreter
|
||||
env: {
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
---
|
||||
title: Import a JSON5 file
|
||||
sidebarTitle: Import JSON5
|
||||
mode: center
|
||||
---
|
||||
|
||||
Bun natively supports `.json5` imports.
|
||||
|
||||
```json5 config.json5 icon="file-code"
|
||||
{
|
||||
// Comments are allowed
|
||||
database: {
|
||||
host: "localhost",
|
||||
port: 5432,
|
||||
name: "myapp",
|
||||
},
|
||||
|
||||
server: {
|
||||
port: 3000,
|
||||
timeout: 30,
|
||||
},
|
||||
|
||||
features: {
|
||||
auth: true,
|
||||
rateLimit: true,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Import the file like any other source file.
|
||||
|
||||
```ts config.ts icon="/icons/typescript.svg"
|
||||
import config from "./config.json5";
|
||||
|
||||
config.database.host; // => "localhost"
|
||||
config.server.port; // => 3000
|
||||
config.features.auth; // => true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
You can also use named imports to destructure top-level properties:
|
||||
|
||||
```ts config.ts icon="/icons/typescript.svg"
|
||||
import { database, server, features } from "./config.json5";
|
||||
|
||||
console.log(database.name); // => "myapp"
|
||||
console.log(server.timeout); // => 30
|
||||
console.log(features.rateLimit); // => true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
For parsing JSON5 strings at runtime, use `Bun.JSON5.parse()`:
|
||||
|
||||
```ts config.ts icon="/icons/typescript.svg"
|
||||
const data = JSON5.parse(`{
|
||||
name: 'John Doe',
|
||||
age: 30,
|
||||
hobbies: [
|
||||
'reading',
|
||||
'coding',
|
||||
],
|
||||
}`);
|
||||
|
||||
console.log(data.name); // => "John Doe"
|
||||
console.log(data.hobbies); // => ["reading", "coding"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > JSON5](/runtime/json5) for complete documentation on JSON5 support in Bun.
|
||||
@@ -26,6 +26,21 @@ The `bun` CLI contains a Node.js-compatible package manager designed to be a dra
|
||||
|
||||
</Note>
|
||||
|
||||
<Accordion title="For Linux users">
|
||||
The recommended minimum Linux Kernel version is 5.6. If you're on Linux kernel 5.1 - 5.5, `bun install` will work, but HTTP requests will be slow due to a lack of support for io_uring's `connect()` operation.
|
||||
|
||||
If you're using Ubuntu 20.04, here's how to install a [newer kernel](https://wiki.ubuntu.com/Kernel/LTSEnablementStack):
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
# If this returns a version >= 5.6, you don't need to do anything
|
||||
uname -r
|
||||
|
||||
# Install the official Ubuntu hardware enablement kernel
|
||||
sudo apt install --install-recommends linux-generic-hwe-20.04
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
To install all dependencies of a project:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
|
||||
@@ -189,7 +189,7 @@ Isolated installs are conceptually similar to pnpm, so migration should be strai
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
# Remove pnpm files
|
||||
rm -rf node_modules pnpm-lock.yaml
|
||||
$ rm -rf node_modules pnpm-lock.yaml
|
||||
|
||||
# Install with Bun's isolated linker
|
||||
bun install --linker isolated
|
||||
|
||||
@@ -72,7 +72,6 @@ The following options are supported:
|
||||
- `username`
|
||||
- `_password` (base64 encoded password)
|
||||
- `_auth` (base64 encoded username:password, e.g. `btoa(username + ":" + password)`)
|
||||
- `email`
|
||||
|
||||
The equivalent `bunfig.toml` option is to add a key in [`install.scopes`](/runtime/bunfig#install-registry):
|
||||
|
||||
@@ -110,136 +109,3 @@ The equivalent `bunfig.toml` option is [`install.exact`](/runtime/bunfig#install
|
||||
[install]
|
||||
exact = true
|
||||
```
|
||||
|
||||
### `ignore-scripts`: Skip lifecycle scripts
|
||||
|
||||
Prevents running lifecycle scripts during installation:
|
||||
|
||||
```ini .npmrc icon="npm"
|
||||
ignore-scripts=true
|
||||
```
|
||||
|
||||
This is equivalent to using the `--ignore-scripts` flag with `bun install`.
|
||||
|
||||
### `dry-run`: Preview changes without installing
|
||||
|
||||
Shows what would be installed without actually installing:
|
||||
|
||||
```ini .npmrc icon="npm"
|
||||
dry-run=true
|
||||
```
|
||||
|
||||
The equivalent `bunfig.toml` option is [`install.dryRun`](/runtime/bunfig#install-dryrun):
|
||||
|
||||
```toml bunfig.toml icon="settings"
|
||||
[install]
|
||||
dryRun = true
|
||||
```
|
||||
|
||||
### `cache`: Configure cache directory
|
||||
|
||||
Set the cache directory path, or disable caching:
|
||||
|
||||
```ini .npmrc icon="npm"
|
||||
# set a custom cache directory
|
||||
cache=/path/to/cache
|
||||
|
||||
# or disable caching
|
||||
cache=false
|
||||
```
|
||||
|
||||
The equivalent `bunfig.toml` option is [`install.cache`](/runtime/bunfig#install-cache):
|
||||
|
||||
```toml bunfig.toml icon="settings"
|
||||
[install.cache]
|
||||
# set a custom cache directory
|
||||
dir = "/path/to/cache"
|
||||
|
||||
# or disable caching
|
||||
disable = true
|
||||
```
|
||||
|
||||
### `ca` and `cafile`: Configure CA certificates
|
||||
|
||||
Configure custom CA certificates for registry connections:
|
||||
|
||||
```ini .npmrc icon="npm"
|
||||
# single CA certificate
|
||||
ca="-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
|
||||
|
||||
# multiple CA certificates
|
||||
ca[]="-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
|
||||
ca[]="-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
|
||||
|
||||
# or specify a path to a CA file
|
||||
cafile=/path/to/ca-bundle.crt
|
||||
```
|
||||
|
||||
### `omit` and `include`: Control dependency types
|
||||
|
||||
Control which dependency types are installed:
|
||||
|
||||
```ini .npmrc icon="npm"
|
||||
# omit dev dependencies
|
||||
omit=dev
|
||||
|
||||
# omit multiple types
|
||||
omit[]=dev
|
||||
omit[]=optional
|
||||
|
||||
# include specific types (overrides omit)
|
||||
include=dev
|
||||
```
|
||||
|
||||
Valid values: `dev`, `peer`, `optional`
|
||||
|
||||
### `install-strategy` and `node-linker`: Installation strategy
|
||||
|
||||
Control how packages are installed in `node_modules`. Bun supports two different configuration options for compatibility with different package managers.
|
||||
|
||||
**npm's `install-strategy`:**
|
||||
|
||||
```ini .npmrc icon="npm"
|
||||
# flat node_modules structure (default)
|
||||
install-strategy=hoisted
|
||||
|
||||
# symlinked structure
|
||||
install-strategy=linked
|
||||
```
|
||||
|
||||
**pnpm/yarn's `node-linker`:**
|
||||
|
||||
The `node-linker` option controls the installation mode. Bun supports values from both pnpm and yarn:
|
||||
|
||||
| Value | Description | Accepted by |
|
||||
| -------------- | ----------------------------------------------- | ----------- |
|
||||
| `isolated` | Symlinked structure with isolated dependencies | pnpm |
|
||||
| `hoisted` | Flat node_modules structure | pnpm |
|
||||
| `pnpm` | Symlinked structure (same as `isolated`) | yarn |
|
||||
| `node-modules` | Flat node_modules structure (same as `hoisted`) | yarn |
|
||||
|
||||
```ini .npmrc icon="npm"
|
||||
# symlinked/isolated mode
|
||||
node-linker=isolated
|
||||
node-linker=pnpm
|
||||
|
||||
# flat/hoisted mode
|
||||
node-linker=hoisted
|
||||
node-linker=node-modules
|
||||
```
|
||||
|
||||
### `public-hoist-pattern` and `hoist-pattern`: Control hoisting
|
||||
|
||||
Control which packages are hoisted to the root `node_modules`:
|
||||
|
||||
```ini .npmrc icon="npm"
|
||||
# packages matching this pattern will be hoisted to the root
|
||||
public-hoist-pattern=*eslint*
|
||||
|
||||
# multiple patterns
|
||||
public-hoist-pattern[]=*eslint*
|
||||
public-hoist-pattern[]=*prettier*
|
||||
|
||||
# control general hoisting behavior
|
||||
hoist-pattern=*
|
||||
```
|
||||
|
||||
@@ -14,7 +14,7 @@ It is strongly recommended to use [PowerShell 7 (`pwsh.exe`)](https://learn.micr
|
||||
By default, running unverified scripts are blocked.
|
||||
|
||||
```ps1
|
||||
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted
|
||||
> Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted
|
||||
```
|
||||
|
||||
### System Dependencies
|
||||
@@ -22,7 +22,7 @@ Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Unrestricted
|
||||
Bun v1.1 or later. We use Bun to run it's own code generators.
|
||||
|
||||
```ps1
|
||||
irm bun.sh/install.ps1 | iex
|
||||
> irm bun.sh/install.ps1 | iex
|
||||
```
|
||||
|
||||
[Visual Studio](https://visualstudio.microsoft.com) with the "Desktop Development with C++" workload. While installing, make sure to install Git as well, if Git for Windows is not already installed.
|
||||
@@ -30,12 +30,12 @@ irm bun.sh/install.ps1 | iex
|
||||
Visual Studio can be installed graphically using the wizard or through WinGet:
|
||||
|
||||
```ps1
|
||||
winget install "Visual Studio Community 2022" --override "--add Microsoft.VisualStudio.Workload.NativeDesktop Microsoft.VisualStudio.Component.Git " -s msstore
|
||||
> winget install "Visual Studio Community 2022" --override "--add Microsoft.VisualStudio.Workload.NativeDesktop Microsoft.VisualStudio.Component.Git " -s msstore
|
||||
```
|
||||
|
||||
After Visual Studio, you need the following:
|
||||
|
||||
- LLVM (19.1.7 for x64, 21.1.8 for ARM64)
|
||||
- LLVM 19.1.7
|
||||
- Go
|
||||
- Rust
|
||||
- NASM
|
||||
@@ -47,19 +47,11 @@ After Visual Studio, you need the following:
|
||||
|
||||
[Scoop](https://scoop.sh) can be used to install these remaining tools easily.
|
||||
|
||||
```ps1 Scoop (x64)
|
||||
irm https://get.scoop.sh | iex
|
||||
scoop install nodejs-lts go rust nasm ruby perl ccache
|
||||
```ps1 Scoop
|
||||
> irm https://get.scoop.sh | iex
|
||||
> scoop install nodejs-lts go rust nasm ruby perl ccache
|
||||
# scoop seems to be buggy if you install llvm and the rest at the same time
|
||||
scoop install llvm@19.1.7
|
||||
```
|
||||
|
||||
For Windows ARM64, download LLVM 21.1.8 directly from GitHub releases (first version with ARM64 Windows builds):
|
||||
|
||||
```ps1 ARM64
|
||||
# Download and install LLVM for ARM64
|
||||
Invoke-WebRequest -Uri "https://github.com/llvm/llvm-project/releases/download/llvmorg-21.1.8/LLVM-21.1.8-woa64.exe" -OutFile "$env:TEMP\LLVM-21.1.8-woa64.exe"
|
||||
Start-Process -FilePath "$env:TEMP\LLVM-21.1.8-woa64.exe" -ArgumentList "/S" -Wait
|
||||
> scoop install llvm@19.1.7
|
||||
```
|
||||
|
||||
<Note>
|
||||
@@ -68,24 +60,22 @@ Start-Process -FilePath "$env:TEMP\LLVM-21.1.8-woa64.exe" -ArgumentList "/S" -Wa
|
||||
will conflict with MSVC and break the build.
|
||||
</Note>
|
||||
|
||||
If you intend on building WebKit locally (optional, x64 only), you should install these packages:
|
||||
If you intend on building WebKit locally (optional), you should install these packages:
|
||||
|
||||
```ps1 Scoop
|
||||
scoop install make cygwin python
|
||||
> scoop install make cygwin python
|
||||
```
|
||||
|
||||
<Note>Cygwin is not required for ARM64 builds as WebKit is provided as a pre-built binary.</Note>
|
||||
|
||||
From here on out, it is **expected you use a PowerShell Terminal with `.\scripts\vs-shell.ps1` sourced**. This script is available in the Bun repository and can be loaded by executing it:
|
||||
|
||||
```ps1
|
||||
.\scripts\vs-shell.ps1
|
||||
> .\scripts\vs-shell.ps1
|
||||
```
|
||||
|
||||
To verify, you can check for an MSVC-only command line such as `mt.exe`
|
||||
|
||||
```ps1
|
||||
Get-Command mt
|
||||
> Get-Command mt
|
||||
```
|
||||
|
||||
<Note>
|
||||
@@ -96,16 +86,16 @@ Get-Command mt
|
||||
## Building
|
||||
|
||||
```ps1
|
||||
bun run build
|
||||
> bun run build
|
||||
|
||||
# after the initial `bun run build` you can use the following to build
|
||||
ninja -Cbuild/debug
|
||||
> ninja -Cbuild/debug
|
||||
```
|
||||
|
||||
If this was successful, you should have a `bun-debug.exe` in the `build/debug` folder.
|
||||
|
||||
```ps1
|
||||
.\build\debug\bun-debug.exe --revision
|
||||
> .\build\debug\bun-debug.exe --revision
|
||||
```
|
||||
|
||||
You should add this to `$Env:PATH`. The simplest way to do so is to open the start menu, type "Path", and then navigate the environment variables menu to add `C:\.....\bun\build\debug` to the user environment variable `PATH`. You should then restart your editor (if it does not update still, log out and log back in).
|
||||
@@ -121,15 +111,15 @@ You can run the test suite either using `bun test <path>` or by using the wrappe
|
||||
|
||||
```ps1
|
||||
# Setup
|
||||
bun i --cwd packages\bun-internal-test
|
||||
> bun i --cwd packages\bun-internal-test
|
||||
|
||||
# Run the entire test suite with reporter
|
||||
# the package.json script "test" uses "build/debug/bun-debug.exe" by default
|
||||
bun run test
|
||||
> bun run test
|
||||
|
||||
# Run an individual test file:
|
||||
bun-debug test node\fs
|
||||
bun-debug test "C:\bun\test\js\bun\resolve\import-meta.test.js"
|
||||
> bun-debug test node\fs
|
||||
> bun-debug test "C:\bun\test\js\bun\resolve\import-meta.test.js"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -28,23 +28,23 @@ Using your system's package manager, install Bun's dependencies:
|
||||
<CodeGroup>
|
||||
|
||||
```bash macOS (Homebrew)
|
||||
brew install automake ccache cmake coreutils gnu-sed go icu4c libiconv libtool ninja pkg-config rust ruby
|
||||
$ brew install automake ccache cmake coreutils gnu-sed go icu4c libiconv libtool ninja pkg-config rust ruby
|
||||
```
|
||||
|
||||
```bash Ubuntu/Debian
|
||||
sudo apt install curl wget lsb-release software-properties-common cargo cmake git golang libtool ninja-build pkg-config rustc ruby-full xz-utils
|
||||
$ sudo apt install curl wget lsb-release software-properties-common cargo cmake git golang libtool ninja-build pkg-config rustc ruby-full xz-utils
|
||||
```
|
||||
|
||||
```bash Arch
|
||||
sudo pacman -S base-devel cmake git go libiconv libtool make ninja pkg-config python rust sed unzip ruby
|
||||
$ sudo pacman -S base-devel cmake git go libiconv libtool make ninja pkg-config python rust sed unzip ruby
|
||||
```
|
||||
|
||||
```bash Fedora
|
||||
sudo dnf install cargo clang19 llvm19 lld19 cmake git golang libtool ninja-build pkg-config rustc ruby libatomic-static libstdc++-static sed unzip which libicu-devel 'perl(Math::BigInt)'
|
||||
$ sudo dnf install cargo clang19 llvm19 lld19 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
|
||||
sudo zypper install go cmake ninja automake git icu rustup && rustup toolchain install stable
|
||||
$ sudo zypper install go cmake ninja automake git icu rustup && rustup toolchain install stable
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
@@ -56,16 +56,16 @@ Before starting, you will need to already have a release build of Bun installed,
|
||||
<CodeGroup>
|
||||
|
||||
```bash Native
|
||||
curl -fsSL https://bun.com/install | bash
|
||||
$ curl -fsSL https://bun.com/install | bash
|
||||
```
|
||||
|
||||
```bash npm
|
||||
npm install -g bun
|
||||
$ npm install -g bun
|
||||
```
|
||||
|
||||
```bash Homebrew
|
||||
brew tap oven-sh/bun
|
||||
brew install bun
|
||||
$ brew tap oven-sh/bun
|
||||
$ brew install bun
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
@@ -76,19 +76,19 @@ ccache is used to cache compilation artifacts, significantly speeding up builds:
|
||||
|
||||
```bash
|
||||
# For macOS
|
||||
brew install ccache
|
||||
$ brew install ccache
|
||||
|
||||
# For Ubuntu/Debian
|
||||
sudo apt install ccache
|
||||
$ sudo apt install ccache
|
||||
|
||||
# For Arch
|
||||
sudo pacman -S ccache
|
||||
$ sudo pacman -S ccache
|
||||
|
||||
# For Fedora
|
||||
sudo dnf install ccache
|
||||
$ sudo dnf install ccache
|
||||
|
||||
# For openSUSE
|
||||
sudo zypper install ccache
|
||||
$ sudo zypper install ccache
|
||||
```
|
||||
|
||||
Our build scripts will automatically detect and use `ccache` if available. You can check cache statistics with `ccache --show-stats`.
|
||||
@@ -100,24 +100,24 @@ Bun requires LLVM 19 (`clang` is part of LLVM). This version requirement is to m
|
||||
<CodeGroup>
|
||||
|
||||
```bash macOS (Homebrew)
|
||||
brew install llvm@19
|
||||
$ brew install llvm@19
|
||||
```
|
||||
|
||||
```bash Ubuntu/Debian
|
||||
# LLVM has an automatic installation script that is compatible with all versions of Ubuntu
|
||||
wget https://apt.llvm.org/llvm.sh -O - | sudo bash -s -- 19 all
|
||||
$ # LLVM has an automatic installation script that is compatible with all versions of Ubuntu
|
||||
$ wget https://apt.llvm.org/llvm.sh -O - | sudo bash -s -- 19 all
|
||||
```
|
||||
|
||||
```bash Arch
|
||||
sudo pacman -S llvm clang lld
|
||||
$ sudo pacman -S llvm clang lld
|
||||
```
|
||||
|
||||
```bash Fedora
|
||||
sudo dnf install llvm clang lld-devel
|
||||
$ sudo dnf install llvm clang lld-devel
|
||||
```
|
||||
|
||||
```bash openSUSE Tumbleweed
|
||||
sudo zypper install clang19 lld19 llvm19
|
||||
$ sudo zypper install clang19 lld19 llvm19
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
@@ -127,7 +127,7 @@ If none of the above solutions apply, you will have to install it [manually](htt
|
||||
Make sure Clang/LLVM 19 is in your path:
|
||||
|
||||
```bash
|
||||
which clang-19
|
||||
$ which clang-19
|
||||
```
|
||||
|
||||
If not, run this to manually add it:
|
||||
@@ -137,12 +137,12 @@ If not, run this to manually add it:
|
||||
```bash macOS (Homebrew)
|
||||
# use fish_add_path if you're using fish
|
||||
# use path+="$(brew --prefix llvm@19)/bin" if you are using zsh
|
||||
export PATH="$(brew --prefix llvm@19)/bin:$PATH"
|
||||
$ export PATH="$(brew --prefix llvm@19)/bin:$PATH"
|
||||
```
|
||||
|
||||
```bash Arch
|
||||
# use fish_add_path if you're using fish
|
||||
export PATH="$PATH:/usr/lib/llvm19/bin"
|
||||
$ export PATH="$PATH:/usr/lib/llvm19/bin"
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
@@ -162,7 +162,7 @@ bun run build
|
||||
The binary will be located at `./build/debug/bun-debug`. It is recommended to add this to your `$PATH`. To verify the build worked, let's print the version number on the development build of Bun.
|
||||
|
||||
```bash
|
||||
build/debug/bun-debug --version
|
||||
$ build/debug/bun-debug --version
|
||||
x.y.z_debug
|
||||
```
|
||||
|
||||
@@ -261,17 +261,17 @@ WebKit is not cloned by default (to save time and disk space). To clone and buil
|
||||
|
||||
```bash
|
||||
# Clone WebKit into ./vendor/WebKit
|
||||
git clone https://github.com/oven-sh/WebKit vendor/WebKit
|
||||
$ git clone https://github.com/oven-sh/WebKit vendor/WebKit
|
||||
|
||||
# Check out the commit hash specified in `set(WEBKIT_VERSION <commit_hash>)` in cmake/tools/SetupWebKit.cmake
|
||||
git -C vendor/WebKit checkout <commit_hash>
|
||||
$ git -C vendor/WebKit checkout <commit_hash>
|
||||
|
||||
# Make a debug build of JSC. This will output build artifacts in ./vendor/WebKit/WebKitBuild/Debug
|
||||
# Optionally, you can use `bun run jsc:build` for a release build
|
||||
bun run jsc:build:debug && rm vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/DerivedSources/inspector/InspectorProtocolObjects.h
|
||||
|
||||
# After an initial run of `make jsc-debug`, you can rebuild JSC with:
|
||||
cmake --build vendor/WebKit/WebKitBuild/Debug --target jsc && rm vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/DerivedSources/inspector/InspectorProtocolObjects.h
|
||||
$ cmake --build vendor/WebKit/WebKitBuild/Debug --target jsc && rm vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/DerivedSources/inspector/InspectorProtocolObjects.h
|
||||
|
||||
# Build bun with the local JSC build
|
||||
bun run build:local
|
||||
@@ -322,20 +322,20 @@ is not able to compile a simple test program.
|
||||
To fix the error, we need to update the GCC version to 11. To do this, we'll need to check if the latest version is available in the distribution's official repositories or use a third-party repository that provides GCC 11 packages. Here are general steps:
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install gcc-11 g++-11
|
||||
$ sudo apt update
|
||||
$ sudo apt install gcc-11 g++-11
|
||||
# If the above command fails with `Unable to locate package gcc-11` we need
|
||||
# to add the APT repository
|
||||
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
$ sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
# Now run `apt install` again
|
||||
sudo apt install gcc-11 g++-11
|
||||
$ sudo apt install gcc-11 g++-11
|
||||
```
|
||||
|
||||
Now, we need to set GCC 11 as the default compiler:
|
||||
|
||||
```bash
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100
|
||||
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 100
|
||||
$ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100
|
||||
$ sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 100
|
||||
```
|
||||
|
||||
### libarchive
|
||||
@@ -343,7 +343,7 @@ sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 100
|
||||
If you see an error on macOS when compiling `libarchive`, run:
|
||||
|
||||
```bash
|
||||
brew install pkg-config
|
||||
$ brew install pkg-config
|
||||
```
|
||||
|
||||
### macOS `library not found for -lSystem`
|
||||
@@ -351,7 +351,7 @@ brew install pkg-config
|
||||
If you see this error when compiling, run:
|
||||
|
||||
```bash
|
||||
xcode-select --install
|
||||
$ xcode-select --install
|
||||
```
|
||||
|
||||
### Cannot find `libatomic.a`
|
||||
|
||||
@@ -1,452 +0,0 @@
|
||||
---
|
||||
title: Archive
|
||||
description: Create and extract tar archives with Bun's fast native implementation
|
||||
---
|
||||
|
||||
Bun provides a fast, native implementation for working with tar archives through `Bun.Archive`. It supports creating archives from in-memory data, extracting archives to disk, and reading archive contents without extraction.
|
||||
|
||||
## Quickstart
|
||||
|
||||
**Create an archive from files:**
|
||||
|
||||
```ts
|
||||
const archive = new Bun.Archive({
|
||||
"hello.txt": "Hello, World!",
|
||||
"data.json": JSON.stringify({ foo: "bar" }),
|
||||
"nested/file.txt": "Nested content",
|
||||
});
|
||||
|
||||
// Write to disk
|
||||
await Bun.write("bundle.tar", archive);
|
||||
```
|
||||
|
||||
**Extract an archive:**
|
||||
|
||||
```ts
|
||||
const tarball = await Bun.file("package.tar.gz").bytes();
|
||||
const archive = new Bun.Archive(tarball);
|
||||
const entryCount = await archive.extract("./output");
|
||||
console.log(`Extracted ${entryCount} entries`);
|
||||
```
|
||||
|
||||
**Read archive contents without extracting:**
|
||||
|
||||
```ts
|
||||
const tarball = await Bun.file("package.tar.gz").bytes();
|
||||
const archive = new Bun.Archive(tarball);
|
||||
const files = await archive.files();
|
||||
|
||||
for (const [path, file] of files) {
|
||||
console.log(`${path}: ${await file.text()}`);
|
||||
}
|
||||
```
|
||||
|
||||
## Creating Archives
|
||||
|
||||
Use `new Bun.Archive()` to create an archive from an object where keys are file paths and values are file contents. By default, archives are uncompressed:
|
||||
|
||||
```ts
|
||||
// Creates an uncompressed tar archive (default)
|
||||
const archive = new Bun.Archive({
|
||||
"README.md": "# My Project",
|
||||
"src/index.ts": "console.log('Hello');",
|
||||
"package.json": JSON.stringify({ name: "my-project" }),
|
||||
});
|
||||
```
|
||||
|
||||
File contents can be:
|
||||
|
||||
- **Strings** - Text content
|
||||
- **Blobs** - Binary data
|
||||
- **ArrayBufferViews** (e.g., `Uint8Array`) - Raw bytes
|
||||
- **ArrayBuffers** - Raw binary data
|
||||
|
||||
```ts
|
||||
const data = "binary data";
|
||||
const arrayBuffer = new ArrayBuffer(8);
|
||||
|
||||
const archive = new Bun.Archive({
|
||||
"text.txt": "Plain text",
|
||||
"blob.bin": new Blob([data]),
|
||||
"bytes.bin": new Uint8Array([1, 2, 3, 4]),
|
||||
"buffer.bin": arrayBuffer,
|
||||
});
|
||||
```
|
||||
|
||||
### Writing Archives to Disk
|
||||
|
||||
Use `Bun.write()` to write an archive to disk:
|
||||
|
||||
```ts
|
||||
// Write uncompressed tar (default)
|
||||
const archive = new Bun.Archive({
|
||||
"file1.txt": "content1",
|
||||
"file2.txt": "content2",
|
||||
});
|
||||
await Bun.write("output.tar", archive);
|
||||
|
||||
// Write gzipped tar
|
||||
const compressed = new Bun.Archive({ "src/index.ts": "console.log('Hello');" }, { compress: "gzip" });
|
||||
await Bun.write("output.tar.gz", compressed);
|
||||
```
|
||||
|
||||
### Getting Archive Bytes
|
||||
|
||||
Get the archive data as bytes or a Blob:
|
||||
|
||||
```ts
|
||||
const archive = new Bun.Archive({ "hello.txt": "Hello, World!" });
|
||||
|
||||
// As Uint8Array
|
||||
const bytes = await archive.bytes();
|
||||
|
||||
// As Blob
|
||||
const blob = await archive.blob();
|
||||
|
||||
// With gzip compression (set at construction)
|
||||
const gzipped = new Bun.Archive({ "hello.txt": "Hello, World!" }, { compress: "gzip" });
|
||||
const gzippedBytes = await gzipped.bytes();
|
||||
const gzippedBlob = await gzipped.blob();
|
||||
```
|
||||
|
||||
## Extracting Archives
|
||||
|
||||
### From Existing Archive Data
|
||||
|
||||
Create an archive from existing tar/tar.gz data:
|
||||
|
||||
```ts
|
||||
// From a file
|
||||
const tarball = await Bun.file("package.tar.gz").bytes();
|
||||
const archiveFromFile = new Bun.Archive(tarball);
|
||||
```
|
||||
|
||||
```ts
|
||||
// From a fetch response
|
||||
const response = await fetch("https://example.com/archive.tar.gz");
|
||||
const archiveFromFetch = new Bun.Archive(await response.blob());
|
||||
```
|
||||
|
||||
### Extracting to Disk
|
||||
|
||||
Use `.extract()` to write all files to a directory:
|
||||
|
||||
```ts
|
||||
const tarball = await Bun.file("package.tar.gz").bytes();
|
||||
const archive = new Bun.Archive(tarball);
|
||||
const count = await archive.extract("./extracted");
|
||||
console.log(`Extracted ${count} entries`);
|
||||
```
|
||||
|
||||
The target directory is created automatically if it doesn't exist. Existing files are overwritten. The returned count includes files, directories, and symlinks (on POSIX systems).
|
||||
|
||||
**Note**: On Windows, symbolic links in archives are always skipped during extraction. Bun does not attempt to create them regardless of privilege level. On Linux and macOS, symlinks are extracted normally.
|
||||
|
||||
**Security note**: Bun.Archive validates paths during extraction, rejecting absolute paths (POSIX `/`, Windows drive letters like `C:\` or `C:/`, and UNC paths like `\\server\share`). Path traversal components (`..`) are normalized away (e.g., `dir/sub/../file` becomes `dir/file`) to prevent directory escape attacks.
|
||||
|
||||
### Filtering Extracted Files
|
||||
|
||||
Use glob patterns to extract only specific files. Patterns are matched against archive entry paths normalized to use forward slashes (`/`). Positive patterns specify what to include, and negative patterns (prefixed with `!`) specify what to exclude. Negative patterns are applied after positive patterns, so **using only negative patterns will match nothing** (you must include a positive pattern like `**` first):
|
||||
|
||||
```ts
|
||||
const tarball = await Bun.file("package.tar.gz").bytes();
|
||||
const archive = new Bun.Archive(tarball);
|
||||
|
||||
// Extract only TypeScript files
|
||||
const tsCount = await archive.extract("./extracted", { glob: "**/*.ts" });
|
||||
|
||||
// Extract files from multiple directories
|
||||
const multiCount = await archive.extract("./extracted", {
|
||||
glob: ["src/**", "lib/**"],
|
||||
});
|
||||
```
|
||||
|
||||
Use negative patterns (prefixed with `!`) to exclude files. When mixing positive and negative patterns, entries must match at least one positive pattern and not match any negative pattern:
|
||||
|
||||
```ts
|
||||
// Extract everything except node_modules
|
||||
const distCount = await archive.extract("./extracted", {
|
||||
glob: ["**", "!node_modules/**"],
|
||||
});
|
||||
|
||||
// Extract source files but exclude tests
|
||||
const srcCount = await archive.extract("./extracted", {
|
||||
glob: ["src/**", "!**/*.test.ts", "!**/__tests__/**"],
|
||||
});
|
||||
```
|
||||
|
||||
## Reading Archive Contents
|
||||
|
||||
### Get All Files
|
||||
|
||||
Use `.files()` to get archive contents as a `Map` of `File` objects without extracting to disk. Unlike `extract()` which processes all entry types, `files()` returns only regular files (no directories):
|
||||
|
||||
```ts
|
||||
const tarball = await Bun.file("package.tar.gz").bytes();
|
||||
const archive = new Bun.Archive(tarball);
|
||||
const files = await archive.files();
|
||||
|
||||
for (const [path, file] of files) {
|
||||
console.log(`${path}: ${file.size} bytes`);
|
||||
console.log(await file.text());
|
||||
}
|
||||
```
|
||||
|
||||
Each `File` object includes:
|
||||
|
||||
- `name` - The file path within the archive (always uses forward slashes `/` as separators)
|
||||
- `size` - File size in bytes
|
||||
- `lastModified` - Modification timestamp
|
||||
- Standard `Blob` methods: `text()`, `arrayBuffer()`, `stream()`, etc.
|
||||
|
||||
**Note**: `files()` loads file contents into memory. For large archives, consider using `extract()` to write directly to disk instead.
|
||||
|
||||
### Error Handling
|
||||
|
||||
Archive operations can fail due to corrupted data, I/O errors, or invalid paths. Use try/catch to handle these cases:
|
||||
|
||||
```ts
|
||||
try {
|
||||
const tarball = await Bun.file("package.tar.gz").bytes();
|
||||
const archive = new Bun.Archive(tarball);
|
||||
const count = await archive.extract("./output");
|
||||
console.log(`Extracted ${count} entries`);
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof Error) {
|
||||
const error = e as Error & { code?: string };
|
||||
if (error.code === "EACCES") {
|
||||
console.error("Permission denied");
|
||||
} else if (error.code === "ENOSPC") {
|
||||
console.error("Disk full");
|
||||
} else {
|
||||
console.error("Archive error:", error.message);
|
||||
}
|
||||
} else {
|
||||
console.error("Archive error:", String(e));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Common error scenarios:
|
||||
|
||||
- **Corrupted/truncated archives** - `new Archive()` loads the archive data; errors may be deferred until read/extract operations
|
||||
- **Permission denied** - `extract()` throws if the target directory is not writable
|
||||
- **Disk full** - `extract()` throws if there's insufficient space
|
||||
- **Invalid paths** - Operations throw for malformed file paths
|
||||
|
||||
The count returned by `extract()` includes all successfully written entries (files, directories, and symlinks on POSIX systems).
|
||||
|
||||
**Security note**: Bun.Archive automatically validates paths during extraction. Absolute paths (POSIX `/`, Windows drive letters, UNC paths) and unsafe symlink targets are rejected. Path traversal components (`..`) are normalized away to prevent directory escape.
|
||||
|
||||
For additional security with untrusted archives, you can enumerate and validate paths before extraction:
|
||||
|
||||
```ts
|
||||
const archive = new Bun.Archive(untrustedData);
|
||||
const files = await archive.files();
|
||||
|
||||
// Optional: Custom validation for additional checks
|
||||
for (const [path] of files) {
|
||||
// Example: Reject hidden files
|
||||
if (path.startsWith(".") || path.includes("/.")) {
|
||||
throw new Error(`Hidden file rejected: ${path}`);
|
||||
}
|
||||
// Example: Whitelist specific directories
|
||||
if (!path.startsWith("src/") && !path.startsWith("lib/")) {
|
||||
throw new Error(`Unexpected path: ${path}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Extract to a controlled destination
|
||||
await archive.extract("./safe-output");
|
||||
```
|
||||
|
||||
When using `files()` with a glob pattern, an empty `Map` is returned if no files match:
|
||||
|
||||
```ts
|
||||
const matches = await archive.files("*.nonexistent");
|
||||
if (matches.size === 0) {
|
||||
console.log("No matching files found");
|
||||
}
|
||||
```
|
||||
|
||||
### Filtering with Glob Patterns
|
||||
|
||||
Pass a glob pattern to filter which files are returned:
|
||||
|
||||
```ts
|
||||
// Get only TypeScript files
|
||||
const tsFiles = await archive.files("**/*.ts");
|
||||
|
||||
// Get files in src directory
|
||||
const srcFiles = await archive.files("src/*");
|
||||
|
||||
// Get all JSON files (recursive)
|
||||
const jsonFiles = await archive.files("**/*.json");
|
||||
|
||||
// Get multiple file types with array of patterns
|
||||
const codeFiles = await archive.files(["**/*.ts", "**/*.js"]);
|
||||
```
|
||||
|
||||
Supported glob patterns (subset of [Bun.Glob](/docs/api/glob) syntax):
|
||||
|
||||
- `*` - Match any characters except `/`
|
||||
- `**` - Match any characters including `/`
|
||||
- `?` - Match single character
|
||||
- `[abc]` - Match character set
|
||||
- `{a,b}` - Match alternatives
|
||||
- `!pattern` - Exclude files matching pattern (negation). Must be combined with positive patterns; using only negative patterns matches nothing.
|
||||
|
||||
See [Bun.Glob](/docs/api/glob) for the full glob syntax including escaping and advanced patterns.
|
||||
|
||||
## Compression
|
||||
|
||||
Bun.Archive creates uncompressed tar archives by default. Use `{ compress: "gzip" }` to enable gzip compression:
|
||||
|
||||
```ts
|
||||
// Default: uncompressed tar
|
||||
const archive = new Bun.Archive({ "hello.txt": "Hello, World!" });
|
||||
|
||||
// Reading: automatically detects gzip
|
||||
const gzippedTarball = await Bun.file("archive.tar.gz").bytes();
|
||||
const readArchive = new Bun.Archive(gzippedTarball);
|
||||
|
||||
// Enable gzip compression
|
||||
const compressed = new Bun.Archive({ "hello.txt": "Hello, World!" }, { compress: "gzip" });
|
||||
|
||||
// Gzip with custom level (1-12)
|
||||
const maxCompression = new Bun.Archive({ "hello.txt": "Hello, World!" }, { compress: "gzip", level: 12 });
|
||||
```
|
||||
|
||||
The options accept:
|
||||
|
||||
- No options or `undefined` - Uncompressed tar (default)
|
||||
- `{ compress: "gzip" }` - Enable gzip compression at level 6
|
||||
- `{ compress: "gzip", level: number }` - Gzip with custom level 1-12 (1 = fastest, 12 = smallest)
|
||||
|
||||
## Examples
|
||||
|
||||
### Bundle Project Files
|
||||
|
||||
```ts
|
||||
import { Glob } from "bun";
|
||||
|
||||
// Collect source files
|
||||
const files: Record<string, string> = {};
|
||||
const glob = new Glob("src/**/*.ts");
|
||||
|
||||
for await (const path of glob.scan(".")) {
|
||||
// Normalize path separators to forward slashes for cross-platform compatibility
|
||||
const archivePath = path.replaceAll("\\", "/");
|
||||
files[archivePath] = await Bun.file(path).text();
|
||||
}
|
||||
|
||||
// Add package.json
|
||||
files["package.json"] = await Bun.file("package.json").text();
|
||||
|
||||
// Create compressed archive and write to disk
|
||||
const archive = new Bun.Archive(files, { compress: "gzip" });
|
||||
await Bun.write("bundle.tar.gz", archive);
|
||||
```
|
||||
|
||||
### Extract and Process npm Package
|
||||
|
||||
```ts
|
||||
const response = await fetch("https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz");
|
||||
const archive = new Bun.Archive(await response.blob());
|
||||
|
||||
// Get package.json
|
||||
const files = await archive.files("package/package.json");
|
||||
const packageJson = files.get("package/package.json");
|
||||
|
||||
if (packageJson) {
|
||||
const pkg = JSON.parse(await packageJson.text());
|
||||
console.log(`Package: ${pkg.name}@${pkg.version}`);
|
||||
}
|
||||
```
|
||||
|
||||
### Create Archive from Directory
|
||||
|
||||
```ts
|
||||
import { readdir } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
|
||||
async function archiveDirectory(dir: string, compress = false): Promise<Bun.Archive> {
|
||||
const files: Record<string, Blob> = {};
|
||||
|
||||
async function walk(currentDir: string, prefix: string = "") {
|
||||
const entries = await readdir(currentDir, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = join(currentDir, entry.name);
|
||||
const archivePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
await walk(fullPath, archivePath);
|
||||
} else {
|
||||
files[archivePath] = Bun.file(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await walk(dir);
|
||||
return new Bun.Archive(files, compress ? { compress: "gzip" } : undefined);
|
||||
}
|
||||
|
||||
const archive = await archiveDirectory("./my-project", true);
|
||||
await Bun.write("my-project.tar.gz", archive);
|
||||
```
|
||||
|
||||
## Reference
|
||||
|
||||
> **Note**: The following type signatures are simplified for documentation purposes. See [`packages/bun-types/bun.d.ts`](https://github.com/oven-sh/bun/blob/main/packages/bun-types/bun.d.ts) for the full type definitions.
|
||||
|
||||
```ts
|
||||
type ArchiveInput =
|
||||
| Record<string, string | Blob | Bun.ArrayBufferView | ArrayBufferLike>
|
||||
| Blob
|
||||
| Bun.ArrayBufferView
|
||||
| ArrayBufferLike;
|
||||
|
||||
type ArchiveOptions = {
|
||||
/** Compression algorithm. Currently only "gzip" is supported. */
|
||||
compress?: "gzip";
|
||||
/** Compression level 1-12 (default 6 when gzip is enabled). */
|
||||
level?: number;
|
||||
};
|
||||
|
||||
interface ArchiveExtractOptions {
|
||||
/** Glob pattern(s) to filter extraction. Supports negative patterns with "!" prefix. */
|
||||
glob?: string | readonly string[];
|
||||
}
|
||||
|
||||
class Archive {
|
||||
/**
|
||||
* Create an Archive from input data
|
||||
* @param data - Files to archive (as object) or existing archive data (as bytes/blob)
|
||||
* @param options - Compression options. Uncompressed by default.
|
||||
* Pass { compress: "gzip" } to enable compression.
|
||||
*/
|
||||
constructor(data: ArchiveInput, options?: ArchiveOptions);
|
||||
|
||||
/**
|
||||
* Extract archive to a directory
|
||||
* @returns Number of entries extracted (files, directories, and symlinks)
|
||||
*/
|
||||
extract(path: string, options?: ArchiveExtractOptions): Promise<number>;
|
||||
|
||||
/**
|
||||
* Get archive as a Blob (uses compression setting from constructor)
|
||||
*/
|
||||
blob(): Promise<Blob>;
|
||||
|
||||
/**
|
||||
* Get archive as a Uint8Array (uses compression setting from constructor)
|
||||
*/
|
||||
bytes(): Promise<Uint8Array<ArrayBuffer>>;
|
||||
|
||||
/**
|
||||
* Get archive contents as File objects (regular files only, no directories)
|
||||
*/
|
||||
files(glob?: string | readonly string[]): Promise<Map<string, File>>;
|
||||
}
|
||||
```
|
||||
@@ -115,6 +115,20 @@ Currently we do not collect telemetry and this setting is only used for enabling
|
||||
telemetry = false
|
||||
```
|
||||
|
||||
### `macros`
|
||||
|
||||
Configure Bun's macro behavior. Macros allow compile-time code execution. Set to `false` to disable macros entirely, or configure specific macro mappings.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
# Disable macros
|
||||
macros = false
|
||||
|
||||
# Or configure macro mappings
|
||||
[macros]
|
||||
# Map imports from a package to replacement paths
|
||||
"react-relay" = { "graphql" = "./my-graphql-macro.ts" }
|
||||
```
|
||||
|
||||
### `env`
|
||||
|
||||
Configure automatic `.env` file loading. By default, Bun automatically loads `.env` files. To disable this behavior:
|
||||
@@ -150,6 +164,94 @@ depth = 3
|
||||
|
||||
This controls how deeply nested objects are displayed in console output. Higher values show more nested properties but may produce verbose output for complex objects. This setting can be overridden by the `--console-depth` CLI flag.
|
||||
|
||||
## Serve
|
||||
|
||||
Configure the behavior of `bun --serve` under the `[serve]` section.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[serve]
|
||||
# configuration goes here
|
||||
```
|
||||
|
||||
### `serve.port`
|
||||
|
||||
Set the port number for the dev server. Default `3000`.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[serve]
|
||||
port = 8080
|
||||
```
|
||||
|
||||
### `serve.static`
|
||||
|
||||
Configure settings for static file serving and the dev server.
|
||||
|
||||
#### `serve.static.plugins`
|
||||
|
||||
Specify plugins to load for the dev server.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[serve.static]
|
||||
plugins = ["./my-plugin.ts"]
|
||||
```
|
||||
|
||||
#### `serve.static.hmr`
|
||||
|
||||
Enable or disable Hot Module Replacement. Default `true`.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[serve.static]
|
||||
hmr = true
|
||||
```
|
||||
|
||||
#### `serve.static.minify`
|
||||
|
||||
Configure minification settings. This can be a boolean to enable or disable all minification, or an object for fine-grained control.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[serve.static]
|
||||
# Enable all minification
|
||||
minify = true
|
||||
|
||||
# Or configure specific types
|
||||
[serve.static.minify]
|
||||
syntax = true
|
||||
whitespace = true
|
||||
identifiers = false
|
||||
```
|
||||
|
||||
#### `serve.static.define`
|
||||
|
||||
Define constants for the dev server bundling.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[serve.static.define]
|
||||
"process.env.API_URL" = "'https://api.example.com'"
|
||||
```
|
||||
|
||||
#### `serve.static.publicPath`
|
||||
|
||||
Set the public path for assets.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[serve.static]
|
||||
publicPath = "/assets/"
|
||||
```
|
||||
|
||||
#### `serve.static.env`
|
||||
|
||||
Configure environment variable handling for the dev server. Values can be:
|
||||
|
||||
- `true` or `"inline"` - Load all environment variables
|
||||
- `false`, `null`, or `"disable"` - Disable environment variable loading
|
||||
- A prefix pattern with `*` (e.g., `"PUBLIC_*"`) - Only load variables matching the prefix
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[serve.static]
|
||||
# Only expose env vars starting with PUBLIC_
|
||||
env = "PUBLIC_*"
|
||||
```
|
||||
|
||||
## Test runner
|
||||
|
||||
The test runner is configured under the `[test]` section of your bunfig.toml.
|
||||
@@ -258,6 +360,15 @@ Set path where coverage reports will be saved. Please notice, that it works only
|
||||
coverageDir = "path/to/somewhere" # default "coverage"
|
||||
```
|
||||
|
||||
### `test.coverageIgnoreSourcemaps`
|
||||
|
||||
Ignore sourcemaps when computing coverage statistics. This is primarily useful for debugging coverage issues. Default `false`.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[test]
|
||||
coverageIgnoreSourcemaps = true
|
||||
```
|
||||
|
||||
### `test.randomize`
|
||||
|
||||
Run tests in random order. Default `false`.
|
||||
@@ -455,6 +566,50 @@ Whether `bun install` will actually install dependencies. Default `false`. When
|
||||
dryRun = false
|
||||
```
|
||||
|
||||
### `install.concurrentScripts`
|
||||
|
||||
Set the number of lifecycle scripts that can run concurrently during installation. This controls parallelism for `postinstall`, `preinstall`, and other scripts.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[install]
|
||||
concurrentScripts = 4
|
||||
```
|
||||
|
||||
### `install.ignoreScripts`
|
||||
|
||||
When `true`, skip running lifecycle scripts (`preinstall`, `postinstall`, etc.) during installation. Default `false`.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[install]
|
||||
ignoreScripts = true
|
||||
```
|
||||
|
||||
### `install.prefer`
|
||||
|
||||
Configure how Bun resolves package versions. Default behavior depends on network availability.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[install]
|
||||
prefer = "online"
|
||||
```
|
||||
|
||||
Valid values are:
|
||||
|
||||
| Value | Description |
|
||||
| ----------- | ------------------------------------------------------------- |
|
||||
| `"online"` | Always fetch the latest versions from the registry. |
|
||||
| `"offline"` | Use cached versions when available, without network requests. |
|
||||
| `"latest"` | Prefer the latest versions, updating the lockfile as needed. |
|
||||
|
||||
### `install.logLevel`
|
||||
|
||||
Set the log level for package installation. This can be one of `"debug"`, `"info"`, `"warn"`, or `"error"`.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[install]
|
||||
logLevel = "warn"
|
||||
```
|
||||
|
||||
### `install.globalDir`
|
||||
|
||||
To configure the directory where Bun puts globally installed packages.
|
||||
@@ -570,6 +725,20 @@ Whether to generate a non-Bun lockfile alongside `bun.lock`. (A `bun.lock` will
|
||||
print = "yarn"
|
||||
```
|
||||
|
||||
Specify a custom path to read the lockfile from:
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[install.lockfile]
|
||||
path = "./custom/bun.lock"
|
||||
```
|
||||
|
||||
Specify a custom path to save the lockfile to:
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[install.lockfile]
|
||||
savePath = "./custom/bun.lock"
|
||||
```
|
||||
|
||||
### `install.linker`
|
||||
|
||||
Configure the linker strategy for installing dependencies. Defaults to `"isolated"` for new workspaces, `"hoisted"` for new single-package projects and existing projects (made pre-v1.3.2).
|
||||
@@ -588,6 +757,28 @@ Valid values are:
|
||||
| `"hoisted"` | Link dependencies in a shared `node_modules` directory. |
|
||||
| `"isolated"` | Link dependencies inside each package installation. |
|
||||
|
||||
### `install.hoistPattern`
|
||||
|
||||
Specify glob patterns for packages that should be hoisted to the root `node_modules`. This is similar to pnpm's `hoist-pattern` option. Only applies when using `linker = "isolated"`.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[install]
|
||||
hoistPattern = ["*"]
|
||||
```
|
||||
|
||||
### `install.publicHoistPattern`
|
||||
|
||||
Specify glob patterns for packages that should be publicly hoisted (accessible to all packages). This is similar to pnpm's `public-hoist-pattern` option. Only applies when using `linker = "isolated"`.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[install]
|
||||
publicHoistPattern = ["*eslint*", "*prettier*"]
|
||||
```
|
||||
|
||||
## Debug
|
||||
|
||||
Configure debugging options under the `[debug]` section.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[debug]
|
||||
# When navigating to a blob: or src: link, open the file in your editor
|
||||
@@ -741,3 +932,60 @@ bun --silent run dev
|
||||
bun --silent dev
|
||||
bun run --silent dev
|
||||
```
|
||||
|
||||
### `run.elide-lines`
|
||||
|
||||
Set the number of lines to elide (hide) in error output. This can help reduce noise in stack traces by truncating very long output.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[run]
|
||||
elide-lines = 10
|
||||
```
|
||||
|
||||
## Bundle
|
||||
|
||||
Configure bundler settings under the `[bundle]` section. These options apply to `bun build`.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[bundle]
|
||||
# configuration goes here
|
||||
```
|
||||
|
||||
### `bundle.entryPoints`
|
||||
|
||||
Specify entry points for the bundler.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[bundle]
|
||||
entryPoints = ["./src/index.ts", "./src/worker.ts"]
|
||||
```
|
||||
|
||||
### `bundle.outdir`
|
||||
|
||||
Set the output directory for bundled files.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[bundle]
|
||||
outdir = "./dist"
|
||||
```
|
||||
|
||||
### `bundle.logLevel`
|
||||
|
||||
Set the log level for the bundler. This can be one of `"debug"`, `"info"`, `"warn"`, or `"error"`.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[bundle]
|
||||
logLevel = "warn"
|
||||
```
|
||||
|
||||
### `bundle.packages`
|
||||
|
||||
Configure how specific packages should be handled during bundling. Set a package to `true` to always bundle it, or `false` to always mark it as external.
|
||||
|
||||
```toml title="bunfig.toml" icon="settings"
|
||||
[bundle.packages]
|
||||
# Always bundle lodash, even if it's in node_modules
|
||||
lodash = true
|
||||
# Never bundle aws-sdk, always treat as external
|
||||
aws-sdk = false
|
||||
```
|
||||
|
||||
@@ -358,8 +358,6 @@ Bun represents [pointers](<https://en.wikipedia.org/wiki/Pointer_(computer_progr
|
||||
|
||||
**Why not `BigInt`?** `BigInt` is slower. JavaScript engines allocate a separate `BigInt` which means they can't fit into a regular JavaScript value. If you pass a `BigInt` to a function, it will be converted to a `number`
|
||||
|
||||
**Windows Note**: The Windows API type HANDLE does not represent a virtual address, and using `ptr` for it will _not_ work as expected. Use `u64` to safely represent HANDLE values.
|
||||
|
||||
</Accordion>
|
||||
|
||||
To convert from a `TypedArray` to a pointer:
|
||||
|
||||
@@ -152,31 +152,6 @@ const response = await fetch("https://bun.com");
|
||||
await Bun.write("index.html", response);
|
||||
```
|
||||
|
||||
## Renaming files (`Bun.rename()`)
|
||||
|
||||
`Bun.rename(from, to, conflict?): Promise<void>`
|
||||
|
||||
Atomically rename or move files and directories. Similar to Node.js `fs.promises.rename()` but with an optional third parameter for conflict resolution.
|
||||
|
||||
```ts
|
||||
// Basic rename (replaces destination if exists)
|
||||
await Bun.rename("old.txt", "new.txt");
|
||||
|
||||
// Atomic swap (Linux/macOS only, falls back to replace on Windows)
|
||||
await Bun.rename("file1.txt", "file2.txt", "swap");
|
||||
|
||||
// Fail if destination exists
|
||||
await Bun.rename("source.txt", "dest.txt", "no-replace");
|
||||
```
|
||||
|
||||
The `conflict` parameter controls behavior when the destination already exists:
|
||||
|
||||
| Mode | Description |
|
||||
| -------------- | -------------------------------------------------------------------------------------- |
|
||||
| `"replace"` | Replace destination if it exists (default) |
|
||||
| `"swap"` | Atomically swap the two files (Linux/macOS only, falls back to `"replace"` on Windows) |
|
||||
| `"no-replace"` | Fail with an error if destination exists |
|
||||
|
||||
---
|
||||
|
||||
## Incremental writing with `FileSink`
|
||||
|
||||
@@ -5,7 +5,7 @@ description: "File types and loaders supported by Bun's bundler and runtime"
|
||||
|
||||
The Bun bundler implements a set of default loaders out of the box. As a rule of thumb, the bundler and the runtime both support the same set of file types out of the box.
|
||||
|
||||
`.js` `.cjs` `.mjs` `.mts` `.cts` `.ts` `.tsx` `.jsx` `.css` `.json` `.jsonc` `.json5` `.toml` `.yaml` `.yml` `.txt` `.wasm` `.node` `.html` `.sh`
|
||||
`.js` `.cjs` `.mjs` `.mts` `.cts` `.ts` `.tsx` `.jsx` `.css` `.json` `.jsonc` `.toml` `.yaml` `.yml` `.txt` `.wasm` `.node` `.html` `.sh`
|
||||
|
||||
Bun uses the file extension to determine which built-in _loader_ should be used to parse the file. Every loader has a name, such as `js`, `tsx`, or `json`. These names are used when building [plugins](/bundler/plugins) that extend Bun with custom loaders.
|
||||
|
||||
@@ -197,53 +197,6 @@ export default {
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
### `json5`
|
||||
|
||||
**JSON5 loader**. Default for `.json5`.
|
||||
|
||||
JSON5 files can be directly imported. Bun will parse them with its fast native JSON5 parser. JSON5 is a superset of JSON that supports comments, trailing commas, unquoted keys, single-quoted strings, and more.
|
||||
|
||||
```ts
|
||||
import config from "./config.json5";
|
||||
console.log(config);
|
||||
|
||||
// via import attribute:
|
||||
import data from "./data.txt" with { type: "json5" };
|
||||
```
|
||||
|
||||
During bundling, the parsed JSON5 is inlined into the bundle as a JavaScript object.
|
||||
|
||||
```ts
|
||||
var config = {
|
||||
name: "my-app",
|
||||
version: "1.0.0",
|
||||
// ...other fields
|
||||
};
|
||||
```
|
||||
|
||||
If a `.json5` file is passed as an entrypoint, it will be converted to a `.js` module that `export default`s the parsed object.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```json5 Input
|
||||
{
|
||||
// Configuration
|
||||
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`.
|
||||
|
||||
@@ -1,271 +0,0 @@
|
||||
---
|
||||
title: JSON5
|
||||
description: Use Bun's built-in support for JSON5 files through both runtime APIs and bundler integration
|
||||
---
|
||||
|
||||
In Bun, JSON5 is a first-class citizen alongside JSON, TOML, and YAML. You can:
|
||||
|
||||
- Parse and stringify JSON5 with `Bun.JSON5.parse` and `Bun.JSON5.stringify`
|
||||
- `import` & `require` JSON5 files as modules at runtime (including hot reloading & watch mode support)
|
||||
- `import` & `require` JSON5 files in frontend apps via Bun's bundler
|
||||
|
||||
---
|
||||
|
||||
## Conformance
|
||||
|
||||
Bun's JSON5 parser passes 100% of the [official JSON5 test suite](https://github.com/json5/json5-tests). The parser is written in Zig for optimal performance. You can view our [translated test suite](https://github.com/oven-sh/bun/blob/main/test/js/bun/json5/json5-test-suite.test.ts) to see every test case.
|
||||
|
||||
---
|
||||
|
||||
## Runtime API
|
||||
|
||||
### `Bun.JSON5.parse()`
|
||||
|
||||
Parse a JSON5 string into a JavaScript value.
|
||||
|
||||
```ts
|
||||
import { JSON5 } from "bun";
|
||||
|
||||
const data = JSON5.parse(`{
|
||||
// JSON5 supports comments
|
||||
name: 'my-app',
|
||||
version: '1.0.0',
|
||||
debug: true,
|
||||
|
||||
// trailing commas are allowed
|
||||
tags: ['web', 'api',],
|
||||
}`);
|
||||
|
||||
console.log(data);
|
||||
// {
|
||||
// name: "my-app",
|
||||
// version: "1.0.0",
|
||||
// debug: true,
|
||||
// tags: ["web", "api"]
|
||||
// }
|
||||
```
|
||||
|
||||
#### Supported JSON5 Features
|
||||
|
||||
JSON5 is a superset of JSON based on ECMAScript 5.1 syntax. It supports:
|
||||
|
||||
- **Comments**: single-line (`//`) and multi-line (`/* */`)
|
||||
- **Trailing commas**: in objects and arrays
|
||||
- **Unquoted keys**: valid ECMAScript 5.1 identifiers can be used as keys
|
||||
- **Single-quoted strings**: in addition to double-quoted strings
|
||||
- **Multi-line strings**: using backslash line continuations
|
||||
- **Hex numbers**: `0xFF`
|
||||
- **Leading & trailing decimal points**: `.5` and `5.`
|
||||
- **Infinity and NaN**: positive and negative
|
||||
- **Explicit plus sign**: `+42`
|
||||
|
||||
```ts
|
||||
const data = JSON5.parse(`{
|
||||
// Unquoted keys
|
||||
unquoted: 'keys work',
|
||||
|
||||
// Single and double quotes
|
||||
single: 'single-quoted',
|
||||
double: "double-quoted",
|
||||
|
||||
// Trailing commas
|
||||
trailing: 'comma',
|
||||
|
||||
// Special numbers
|
||||
hex: 0xDEADbeef,
|
||||
half: .5,
|
||||
to: Infinity,
|
||||
nan: NaN,
|
||||
|
||||
// Multi-line strings
|
||||
multiline: 'line 1 \
|
||||
line 2',
|
||||
}`);
|
||||
```
|
||||
|
||||
#### Error Handling
|
||||
|
||||
`Bun.JSON5.parse()` throws a `SyntaxError` if the input is invalid JSON5:
|
||||
|
||||
```ts
|
||||
try {
|
||||
JSON5.parse("{invalid}");
|
||||
} catch (error) {
|
||||
console.error("Failed to parse JSON5:", error.message);
|
||||
}
|
||||
```
|
||||
|
||||
### `Bun.JSON5.stringify()`
|
||||
|
||||
Stringify a JavaScript value to a JSON5 string.
|
||||
|
||||
```ts
|
||||
import { JSON5 } from "bun";
|
||||
|
||||
const str = JSON5.stringify({ name: "my-app", version: "1.0.0" });
|
||||
console.log(str);
|
||||
// {name:'my-app',version:'1.0.0'}
|
||||
```
|
||||
|
||||
#### Pretty Printing
|
||||
|
||||
Pass a `space` argument to format the output with indentation:
|
||||
|
||||
```ts
|
||||
const pretty = JSON5.stringify(
|
||||
{
|
||||
name: "my-app",
|
||||
debug: true,
|
||||
tags: ["web", "api"],
|
||||
},
|
||||
null,
|
||||
2,
|
||||
);
|
||||
|
||||
console.log(pretty);
|
||||
// {
|
||||
// name: 'my-app',
|
||||
// debug: true,
|
||||
// tags: [
|
||||
// 'web',
|
||||
// 'api',
|
||||
// ],
|
||||
// }
|
||||
```
|
||||
|
||||
The `space` argument can be a number (number of spaces) or a string (used as the indent character):
|
||||
|
||||
```ts
|
||||
// Tab indentation
|
||||
JSON5.stringify(data, null, "\t");
|
||||
```
|
||||
|
||||
#### Special Values
|
||||
|
||||
Unlike `JSON.stringify`, `JSON5.stringify` preserves special numeric values:
|
||||
|
||||
```ts
|
||||
JSON5.stringify({ inf: Infinity, ninf: -Infinity, nan: NaN });
|
||||
// {inf:Infinity,ninf:-Infinity,nan:NaN}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Module Import
|
||||
|
||||
### ES Modules
|
||||
|
||||
You can import JSON5 files directly as ES modules:
|
||||
|
||||
```json5 config.json5
|
||||
{
|
||||
// Database configuration
|
||||
database: {
|
||||
host: "localhost",
|
||||
port: 5432,
|
||||
name: "myapp",
|
||||
},
|
||||
|
||||
features: {
|
||||
auth: true,
|
||||
rateLimit: true,
|
||||
analytics: false,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
#### Default Import
|
||||
|
||||
```ts app.ts icon="/icons/typescript.svg"
|
||||
import config from "./config.json5";
|
||||
|
||||
console.log(config.database.host); // "localhost"
|
||||
console.log(config.features.auth); // true
|
||||
```
|
||||
|
||||
#### Named Imports
|
||||
|
||||
You can destructure top-level properties as named imports:
|
||||
|
||||
```ts app.ts icon="/icons/typescript.svg"
|
||||
import { database, features } from "./config.json5";
|
||||
|
||||
console.log(database.host); // "localhost"
|
||||
console.log(features.rateLimit); // true
|
||||
```
|
||||
|
||||
### CommonJS
|
||||
|
||||
JSON5 files can also be required in CommonJS:
|
||||
|
||||
```ts app.ts icon="/icons/typescript.svg"
|
||||
const config = require("./config.json5");
|
||||
console.log(config.database.name); // "myapp"
|
||||
|
||||
// Destructuring also works
|
||||
const { database, features } = require("./config.json5");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hot Reloading with JSON5
|
||||
|
||||
When you run your application with `bun --hot`, changes to JSON5 files are automatically detected and reloaded:
|
||||
|
||||
```json5 config.json5
|
||||
{
|
||||
server: {
|
||||
port: 3000,
|
||||
host: "localhost",
|
||||
},
|
||||
features: {
|
||||
debug: true,
|
||||
verbose: false,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
import { server, features } from "./config.json5";
|
||||
|
||||
Bun.serve({
|
||||
port: server.port,
|
||||
hostname: server.host,
|
||||
fetch(req) {
|
||||
if (features.verbose) {
|
||||
console.log(`${req.method} ${req.url}`);
|
||||
}
|
||||
return new Response("Hello World");
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Run with hot reloading:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun --hot server.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bundler Integration
|
||||
|
||||
When you import JSON5 files and bundle with Bun, the JSON5 is parsed at build time and included as a JavaScript module:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build app.ts --outdir=dist
|
||||
```
|
||||
|
||||
This means:
|
||||
|
||||
- Zero runtime JSON5 parsing overhead in production
|
||||
- Smaller bundle sizes
|
||||
- Tree-shaking support for unused properties (named imports)
|
||||
|
||||
### Dynamic Imports
|
||||
|
||||
JSON5 files can be dynamically imported:
|
||||
|
||||
```ts
|
||||
const config = await import("./config.json5");
|
||||
```
|
||||
@@ -1,188 +0,0 @@
|
||||
---
|
||||
title: JSONL
|
||||
description: Parse newline-delimited JSON (JSONL) with Bun's built-in streaming parser
|
||||
---
|
||||
|
||||
Bun has built-in support for parsing [JSONL](https://jsonlines.org/) (newline-delimited JSON), where each line is a separate JSON value. The parser is implemented in C++ using JavaScriptCore's optimized JSON parser and supports streaming use cases.
|
||||
|
||||
```ts
|
||||
const results = Bun.JSONL.parse('{"name":"Alice"}\n{"name":"Bob"}\n');
|
||||
// [{ name: "Alice" }, { name: "Bob" }]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `Bun.JSONL.parse()`
|
||||
|
||||
Parse a complete JSONL input and return an array of all parsed values.
|
||||
|
||||
```ts
|
||||
import { JSONL } from "bun";
|
||||
|
||||
const input = '{"id":1,"name":"Alice"}\n{"id":2,"name":"Bob"}\n{"id":3,"name":"Charlie"}\n';
|
||||
const records = JSONL.parse(input);
|
||||
console.log(records);
|
||||
// [
|
||||
// { id: 1, name: "Alice" },
|
||||
// { id: 2, name: "Bob" },
|
||||
// { id: 3, name: "Charlie" }
|
||||
// ]
|
||||
```
|
||||
|
||||
Input can be a string or a `Uint8Array`:
|
||||
|
||||
```ts
|
||||
const buffer = new TextEncoder().encode('{"a":1}\n{"b":2}\n');
|
||||
const results = Bun.JSONL.parse(buffer);
|
||||
// [{ a: 1 }, { b: 2 }]
|
||||
```
|
||||
|
||||
When passed a `Uint8Array`, a UTF-8 BOM at the start of the buffer is automatically skipped.
|
||||
|
||||
### Error handling
|
||||
|
||||
If the input contains invalid JSON, `Bun.JSONL.parse()` throws a `SyntaxError`:
|
||||
|
||||
```ts
|
||||
try {
|
||||
Bun.JSONL.parse('{"valid":true}\n{invalid}\n');
|
||||
} catch (error) {
|
||||
console.error(error); // SyntaxError: Failed to parse JSONL
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `Bun.JSONL.parseChunk()`
|
||||
|
||||
For streaming scenarios, `parseChunk` parses as many complete values as possible from the input and reports how far it got. This is useful when receiving data incrementally (e.g., from a network stream) and you need to know where to resume parsing.
|
||||
|
||||
```ts
|
||||
const chunk = '{"id":1}\n{"id":2}\n{"id":3';
|
||||
|
||||
const result = Bun.JSONL.parseChunk(chunk);
|
||||
console.log(result.values); // [{ id: 1 }, { id: 2 }]
|
||||
console.log(result.read); // 17 — characters consumed
|
||||
console.log(result.done); // false — incomplete value remains
|
||||
console.log(result.error); // null — no parse error
|
||||
```
|
||||
|
||||
### Return value
|
||||
|
||||
`parseChunk` returns an object with four properties:
|
||||
|
||||
| Property | Type | Description |
|
||||
| -------- | --------------------- | ----------------------------------------------------------------------- |
|
||||
| `values` | `any[]` | Array of successfully parsed JSON values |
|
||||
| `read` | `number` | Number of bytes (for `Uint8Array`) or characters (for strings) consumed |
|
||||
| `done` | `boolean` | `true` if the entire input was consumed with no remaining data |
|
||||
| `error` | `SyntaxError \| null` | Parse error, or `null` if no error occurred |
|
||||
|
||||
### Streaming example
|
||||
|
||||
Use `read` to slice off consumed input and carry forward the remainder:
|
||||
|
||||
```ts
|
||||
let buffer = "";
|
||||
|
||||
async function processStream(stream: ReadableStream<string>) {
|
||||
for await (const chunk of stream) {
|
||||
buffer += chunk;
|
||||
const result = Bun.JSONL.parseChunk(buffer);
|
||||
|
||||
for (const value of result.values) {
|
||||
handleRecord(value);
|
||||
}
|
||||
|
||||
// Keep only the unconsumed portion
|
||||
buffer = buffer.slice(result.read);
|
||||
}
|
||||
|
||||
// Handle any remaining data
|
||||
if (buffer.length > 0) {
|
||||
const final = Bun.JSONL.parseChunk(buffer);
|
||||
for (const value of final.values) {
|
||||
handleRecord(value);
|
||||
}
|
||||
if (final.error) {
|
||||
console.error("Parse error in final chunk:", final.error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Byte offsets with `Uint8Array`
|
||||
|
||||
When the input is a `Uint8Array`, you can pass optional `start` and `end` byte offsets:
|
||||
|
||||
```ts
|
||||
const buf = new TextEncoder().encode('{"a":1}\n{"b":2}\n{"c":3}\n');
|
||||
|
||||
// Parse starting from byte 8
|
||||
const result = Bun.JSONL.parseChunk(buf, 8);
|
||||
console.log(result.values); // [{ b: 2 }, { c: 3 }]
|
||||
console.log(result.read); // 24
|
||||
|
||||
// Parse a specific range
|
||||
const partial = Bun.JSONL.parseChunk(buf, 0, 8);
|
||||
console.log(partial.values); // [{ a: 1 }]
|
||||
```
|
||||
|
||||
The `read` value is always a byte offset into the original buffer, making it easy to use with `TypedArray.subarray()` for zero-copy streaming:
|
||||
|
||||
```ts
|
||||
let buf = new Uint8Array(0);
|
||||
|
||||
async function processBinaryStream(stream: ReadableStream<Uint8Array>) {
|
||||
for await (const chunk of stream) {
|
||||
// Append chunk to buffer
|
||||
const newBuf = new Uint8Array(buf.length + chunk.length);
|
||||
newBuf.set(buf);
|
||||
newBuf.set(chunk, buf.length);
|
||||
buf = newBuf;
|
||||
|
||||
const result = Bun.JSONL.parseChunk(buf);
|
||||
|
||||
for (const value of result.values) {
|
||||
handleRecord(value);
|
||||
}
|
||||
|
||||
// Keep unconsumed bytes
|
||||
buf = buf.slice(result.read);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Error recovery
|
||||
|
||||
Unlike `parse()`, `parseChunk()` does not throw on invalid JSON. Instead, it returns the error in the `error` property, along with any values that were successfully parsed before the error:
|
||||
|
||||
```ts
|
||||
const input = '{"a":1}\n{invalid}\n{"b":2}\n';
|
||||
const result = Bun.JSONL.parseChunk(input);
|
||||
|
||||
console.log(result.values); // [{ a: 1 }] — values parsed before the error
|
||||
console.log(result.error); // SyntaxError
|
||||
console.log(result.read); // 7 — position up to last successful parse
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Supported value types
|
||||
|
||||
Each line can be any valid JSON value, not just objects:
|
||||
|
||||
```ts
|
||||
const input = '42\n"hello"\ntrue\nnull\n[1,2,3]\n{"key":"value"}\n';
|
||||
const values = Bun.JSONL.parse(input);
|
||||
// [42, "hello", true, null, [1, 2, 3], { key: "value" }]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance notes
|
||||
|
||||
- **ASCII fast path**: Pure ASCII input is parsed directly without copying, using a zero-allocation `StringView`.
|
||||
- **UTF-8 support**: Non-ASCII `Uint8Array` input is decoded to UTF-16 using SIMD-accelerated conversion.
|
||||
- **BOM handling**: UTF-8 BOM (`0xEF 0xBB 0xBF`) at the start of a `Uint8Array` is automatically skipped.
|
||||
- **Pre-built object shape**: The result object from `parseChunk` uses a cached structure for fast property access.
|
||||
@@ -127,54 +127,3 @@ const socket = await Bun.udpSocket({
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Socket options
|
||||
|
||||
UDP sockets support setting various socket options:
|
||||
|
||||
```ts
|
||||
const socket = await Bun.udpSocket({});
|
||||
|
||||
// Enable broadcasting to send packets to a broadcast address
|
||||
socket.setBroadcast(true);
|
||||
|
||||
// Set the IP TTL (time to live) for outgoing packets
|
||||
socket.setTTL(64);
|
||||
```
|
||||
|
||||
### Multicast
|
||||
|
||||
Bun supports multicast operations for UDP sockets. Use `addMembership` and `dropMembership` to join and leave multicast groups:
|
||||
|
||||
```ts
|
||||
const socket = await Bun.udpSocket({});
|
||||
|
||||
// Join a multicast group
|
||||
socket.addMembership("224.0.0.1");
|
||||
|
||||
// Join with a specific interface
|
||||
socket.addMembership("224.0.0.1", "192.168.1.100");
|
||||
|
||||
// Leave a multicast group
|
||||
socket.dropMembership("224.0.0.1");
|
||||
```
|
||||
|
||||
Additional multicast options:
|
||||
|
||||
```ts
|
||||
// Set TTL for multicast packets (number of network hops)
|
||||
socket.setMulticastTTL(2);
|
||||
|
||||
// Control whether multicast packets loop back to the local socket
|
||||
socket.setMulticastLoopback(true);
|
||||
|
||||
// Specify which interface to use for outgoing multicast packets
|
||||
socket.setMulticastInterface("192.168.1.100");
|
||||
```
|
||||
|
||||
For source-specific multicast (SSM), use `addSourceSpecificMembership` and `dropSourceSpecificMembership`:
|
||||
|
||||
```ts
|
||||
socket.addSourceSpecificMembership("10.0.0.1", "232.0.0.1");
|
||||
socket.dropSourceSpecificMembership("10.0.0.1", "232.0.0.1");
|
||||
```
|
||||
|
||||
@@ -19,7 +19,7 @@ If you're looking to create a brand new empty project, use [`bun init`](/runtime
|
||||
`bun create ./MyComponent.tsx` turns an existing React component into a complete dev environment with hot reload and production builds in one command.
|
||||
|
||||
```bash
|
||||
bun create ./MyComponent.jsx # .tsx also supported
|
||||
$ bun create ./MyComponent.jsx # .tsx also supported
|
||||
```
|
||||
|
||||
<Frame>
|
||||
|
||||
@@ -149,25 +149,25 @@ div.callout .code-block {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
[language="shellscript"] code span.line:not(:empty):has(span)::before {
|
||||
.code-block[language="shellscript"] code span.line:not(:empty):has(span)::before {
|
||||
content: "$ ";
|
||||
color: #6272a4;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[language="shellscript"] code span.line:has(> span:first-child[style*="color: rgb(98, 114, 164)"])::before,
|
||||
[language="shellscript"] code span.line:has(> span:first-child[style*="#6272A4"])::before {
|
||||
.code-block[language="shellscript"] code span.line:has(> span:first-child[style*="color: rgb(98, 114, 164)"])::before,
|
||||
.code-block[language="shellscript"] code span.line:has(> span:first-child[style*="#6272A4"])::before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
[language="powershell"] code span.line:not(:empty):has(span)::before {
|
||||
.code-block[language="powershell"] code span.line:not(:empty):has(span)::before {
|
||||
content: "> ";
|
||||
color: #6272a4;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
[language="powershell"] code span.line:has(> span:first-child[style*="color: rgb(98, 114, 164)"])::before,
|
||||
[language="powershell"] code span.line:has(> span:first-child[style*="#6272A4"])::before {
|
||||
.code-block[language="powershell"] code span.line:has(> span:first-child[style*="color: rgb(98, 114, 164)"])::before,
|
||||
.code-block[language="powershell"] code span.line:has(> span:first-child[style*="#6272A4"])::before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +131,6 @@
|
||||
stdenv = pkgs.clangStdenv;
|
||||
}) {
|
||||
inherit packages;
|
||||
hardeningDisable = [ "fortify" ];
|
||||
|
||||
shellHook = ''
|
||||
# Set up build environment
|
||||
|
||||
@@ -35,7 +35,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionWrite, (JSC::JSGlobalObject * globalObject,
|
||||
JSValue arg1 = callframe->argument(0);
|
||||
JSValue toWriteArg = callframe->argument(1);
|
||||
auto &vm = globalObject->vm();
|
||||
auto scope = DECLARE_TOP_EXCEPTION_SCOPE(vm);
|
||||
auto scope = DECLARE_CATCH_SCOPE(vm);
|
||||
|
||||
int32_t fd = STDOUT_FILENO;
|
||||
if (callframe->argumentCount() > 1) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "bun",
|
||||
"version": "1.3.8",
|
||||
"version": "1.3.6",
|
||||
"workspaces": [
|
||||
"./packages/bun-types",
|
||||
"./packages/@types/bun"
|
||||
|
||||
@@ -71,25 +71,8 @@ async function buildRootModule(dryRun?: boolean) {
|
||||
js: "// Source code: https://github.com/oven-sh/bun/blob/main/packages/bun-release/scripts/npm-postinstall.ts",
|
||||
},
|
||||
});
|
||||
// Create placeholder scripts that print an error message if postinstall hasn't run.
|
||||
// On Unix, these are executed as shell scripts despite the .exe extension.
|
||||
// Do NOT add a shebang (#!/bin/sh) here — npm's cmd-shim reads shebangs to generate
|
||||
// .ps1/.cmd wrappers BEFORE postinstall runs, and bakes the interpreter path in.
|
||||
// A #!/bin/sh shebang breaks Windows because the wrappers reference /bin/sh which
|
||||
// doesn't exist, even after postinstall replaces the placeholder with the real binary.
|
||||
const placeholderScript = `echo "Error: Bun's postinstall script was not run." >&2
|
||||
echo "" >&2
|
||||
echo "This occurs when using --ignore-scripts during installation, or when using a" >&2
|
||||
echo "package manager like pnpm that does not run postinstall scripts by default." >&2
|
||||
echo "" >&2
|
||||
echo "To fix this, run the postinstall script manually:" >&2
|
||||
echo " cd node_modules/bun && node install.js" >&2
|
||||
echo "" >&2
|
||||
echo "Or reinstall bun without the --ignore-scripts flag." >&2
|
||||
exit 1
|
||||
`;
|
||||
write(join(cwd, "bin", "bun.exe"), placeholderScript);
|
||||
write(join(cwd, "bin", "bunx.exe"), placeholderScript);
|
||||
write(join(cwd, "bin", "bun.exe"), "");
|
||||
write(join(cwd, "bin", "bunx.exe"), "");
|
||||
write(
|
||||
join(cwd, "bin", "README.txt"),
|
||||
`The 'bun.exe' file is a placeholder for the binary file, which
|
||||
|
||||
1048
packages/bun-types/bun.d.ts
vendored
1048
packages/bun-types/bun.d.ts
vendored
File diff suppressed because it is too large
Load Diff
5
packages/bun-types/extensions.d.ts
vendored
5
packages/bun-types/extensions.d.ts
vendored
@@ -23,11 +23,6 @@ declare module "*.jsonc" {
|
||||
export = contents;
|
||||
}
|
||||
|
||||
declare module "*.json5" {
|
||||
var contents: any;
|
||||
export = contents;
|
||||
}
|
||||
|
||||
declare module "*/bun.lock" {
|
||||
var contents: import("bun").BunLockFile;
|
||||
export = contents;
|
||||
|
||||
50
packages/bun-types/s3.d.ts
vendored
50
packages/bun-types/s3.d.ts
vendored
@@ -11,9 +11,9 @@ declare module "bun" {
|
||||
* If the file descriptor is not writable yet, the data is buffered.
|
||||
*
|
||||
* @param chunk The data to write
|
||||
* @returns Number of bytes written or, if the write is pending, a Promise resolving to the number of bytes
|
||||
* @returns Number of bytes written
|
||||
*/
|
||||
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number | Promise<number>;
|
||||
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
|
||||
/**
|
||||
* Flush the internal buffer, committing the data to disk or the pipe.
|
||||
*
|
||||
@@ -78,9 +78,9 @@ declare module "bun" {
|
||||
* If the network is not writable yet, the data is buffered.
|
||||
*
|
||||
* @param chunk The data to write
|
||||
* @returns Number of bytes written or, if the write is pending, a Promise resolving to the number of bytes
|
||||
* @returns Number of bytes written
|
||||
*/
|
||||
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number | Promise<number>;
|
||||
write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
|
||||
/**
|
||||
* Flush the internal buffer, committing the data to the network.
|
||||
*
|
||||
@@ -321,30 +321,6 @@ declare module "bun" {
|
||||
| "SNOW"
|
||||
| "STANDARD_IA";
|
||||
|
||||
/**
|
||||
* When set to `true`, confirms that the requester knows they will be charged
|
||||
* for the request and data transfer costs. Required for accessing objects
|
||||
* in Requester Pays buckets.
|
||||
*
|
||||
* @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/RequesterPaysBuckets.html
|
||||
*
|
||||
* @example
|
||||
* // Accessing a file in a Requester Pays bucket
|
||||
* const file = s3.file("data.csv", {
|
||||
* bucket: "requester-pays-bucket",
|
||||
* requestPayer: true
|
||||
* });
|
||||
* const content = await file.text();
|
||||
*
|
||||
* @example
|
||||
* // Uploading to a Requester Pays bucket
|
||||
* await s3.write("output.json", data, {
|
||||
* bucket: "requester-pays-bucket",
|
||||
* requestPayer: true
|
||||
* });
|
||||
*/
|
||||
requestPayer?: boolean;
|
||||
|
||||
/**
|
||||
* @deprecated The size of the internal buffer in bytes. Defaults to 5 MiB. use `partSize` and `queueSize` instead.
|
||||
*/
|
||||
@@ -609,17 +585,7 @@ declare module "bun" {
|
||||
* });
|
||||
*/
|
||||
write(
|
||||
data:
|
||||
| string
|
||||
| ArrayBufferView
|
||||
| ArrayBuffer
|
||||
| SharedArrayBuffer
|
||||
| Request
|
||||
| Response
|
||||
| BunFile
|
||||
| S3File
|
||||
| Blob
|
||||
| Archive,
|
||||
data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer | Request | Response | BunFile | S3File | Blob,
|
||||
options?: S3Options,
|
||||
): Promise<number>;
|
||||
|
||||
@@ -930,8 +896,7 @@ declare module "bun" {
|
||||
| BunFile
|
||||
| S3File
|
||||
| Blob
|
||||
| File
|
||||
| Archive,
|
||||
| File,
|
||||
options?: S3Options,
|
||||
): Promise<number>;
|
||||
|
||||
@@ -981,8 +946,7 @@ declare module "bun" {
|
||||
| BunFile
|
||||
| S3File
|
||||
| Blob
|
||||
| File
|
||||
| Archive,
|
||||
| File,
|
||||
options?: S3Options,
|
||||
): Promise<number>;
|
||||
|
||||
|
||||
@@ -315,15 +315,6 @@ int us_internal_ssl_socket_is_closed(struct us_internal_ssl_socket_t *s) {
|
||||
return us_socket_is_closed(0, &s->s);
|
||||
}
|
||||
|
||||
int us_internal_ssl_socket_is_handshake_finished(struct us_internal_ssl_socket_t *s) {
|
||||
if (!s || !s->ssl) return 0;
|
||||
return SSL_is_init_finished(s->ssl);
|
||||
}
|
||||
|
||||
int us_internal_ssl_socket_handshake_callback_has_fired(struct us_internal_ssl_socket_t *s) {
|
||||
if (!s) return 0;
|
||||
return s->handshake_state == HANDSHAKE_COMPLETED;
|
||||
}
|
||||
|
||||
void us_internal_trigger_handshake_callback_econnreset(struct us_internal_ssl_socket_t *s) {
|
||||
struct us_internal_ssl_socket_context_t *context =
|
||||
|
||||
@@ -439,8 +439,6 @@ void *us_internal_ssl_socket_ext(us_internal_ssl_socket_r s);
|
||||
void *us_internal_connecting_ssl_socket_ext(struct us_connecting_socket_t *c);
|
||||
int us_internal_ssl_socket_is_shut_down(us_internal_ssl_socket_r s);
|
||||
int us_internal_ssl_socket_is_closed(us_internal_ssl_socket_r s);
|
||||
int us_internal_ssl_socket_is_handshake_finished(us_internal_ssl_socket_r s);
|
||||
int us_internal_ssl_socket_handshake_callback_has_fired(us_internal_ssl_socket_r s);
|
||||
void us_internal_ssl_socket_shutdown(us_internal_ssl_socket_r s);
|
||||
|
||||
struct us_internal_ssl_socket_t *us_internal_ssl_socket_context_adopt_socket(
|
||||
|
||||
@@ -457,12 +457,6 @@ int us_socket_is_shut_down(int ssl, us_socket_r s) nonnull_fn_decl;
|
||||
/* Returns whether this socket has been closed. Only valid if memory has not yet been released. */
|
||||
int us_socket_is_closed(int ssl, us_socket_r s) nonnull_fn_decl;
|
||||
|
||||
/* Returns 1 if the TLS handshake has completed, 0 otherwise. For non-SSL sockets, always returns 1. */
|
||||
int us_socket_is_ssl_handshake_finished(int ssl, us_socket_r s) nonnull_fn_decl;
|
||||
|
||||
/* Returns 1 if the TLS handshake callback has been invoked, 0 otherwise. For non-SSL sockets, always returns 1. */
|
||||
int us_socket_ssl_handshake_callback_has_fired(int ssl, us_socket_r s) nonnull_fn_decl;
|
||||
|
||||
/* Immediately closes the socket */
|
||||
struct us_socket_t *us_socket_close(int ssl, us_socket_r s, int code, void *reason) __attribute__((nonnull(2)));
|
||||
|
||||
|
||||
@@ -128,26 +128,6 @@ int us_socket_is_closed(int ssl, struct us_socket_t *s) {
|
||||
return s->flags.is_closed;
|
||||
}
|
||||
|
||||
int us_socket_is_ssl_handshake_finished(int ssl, struct us_socket_t *s) {
|
||||
#ifndef LIBUS_NO_SSL
|
||||
if(ssl) {
|
||||
return us_internal_ssl_socket_is_handshake_finished((struct us_internal_ssl_socket_t *) s);
|
||||
}
|
||||
#endif
|
||||
// Non-SSL sockets are always "handshake finished"
|
||||
return 1;
|
||||
}
|
||||
|
||||
int us_socket_ssl_handshake_callback_has_fired(int ssl, struct us_socket_t *s) {
|
||||
#ifndef LIBUS_NO_SSL
|
||||
if(ssl) {
|
||||
return us_internal_ssl_socket_handshake_callback_has_fired((struct us_internal_ssl_socket_t *) s);
|
||||
}
|
||||
#endif
|
||||
// Non-SSL sockets are always "callback fired"
|
||||
return 1;
|
||||
}
|
||||
|
||||
int us_connecting_socket_is_closed(int ssl, struct us_connecting_socket_t *c) {
|
||||
return c->closed;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef UWS_HTTP_MAX_HEADERS_COUNT
|
||||
#define UWS_HTTP_MAX_HEADERS_COUNT 200
|
||||
#define UWS_HTTP_MAX_HEADERS_COUNT 100
|
||||
#endif
|
||||
|
||||
// todo: HttpParser is in need of a few clean-ups and refactorings
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <string_view>
|
||||
#include <span>
|
||||
#include <map>
|
||||
#include "MoveOnlyFunction.h"
|
||||
#include "ChunkedEncoding.h"
|
||||
@@ -161,13 +160,6 @@ namespace uWS
|
||||
std::map<std::string, unsigned short, std::less<>> *currentParameterOffsets = nullptr;
|
||||
|
||||
public:
|
||||
/* Any data pipelined after the HTTP headers (before response).
|
||||
* Used for Node.js compatibility: 'connect' and 'upgrade' events
|
||||
* pass this as the 'head' Buffer parameter.
|
||||
* WARNING: This points to data in the receive buffer and may be stack-allocated.
|
||||
* Must be cloned before the request handler returns. */
|
||||
std::span<const char> head;
|
||||
|
||||
bool isAncient()
|
||||
{
|
||||
return ancientHttp;
|
||||
@@ -891,8 +883,6 @@ namespace uWS
|
||||
/* If returned socket is not what we put in we need
|
||||
* to break here as we either have upgraded to
|
||||
* WebSockets or otherwise closed the socket. */
|
||||
/* Store any remaining data as head for Node.js compat (connect/upgrade events) */
|
||||
req->head = std::span<const char>(data, length);
|
||||
void *returnedUser = requestHandler(user, req);
|
||||
if (returnedUser != user) {
|
||||
/* We are upgraded to WebSocket or otherwise broken */
|
||||
@@ -938,13 +928,9 @@ namespace uWS
|
||||
consumedTotal += emittable;
|
||||
}
|
||||
} else if(isConnectRequest) {
|
||||
// This only serves to mark that the connect request read all headers
|
||||
// and can start emitting data. Don't try to parse remaining data as HTTP -
|
||||
// it's pipelined data that we've already captured in req->head.
|
||||
// This only server to mark that the connect request read all headers
|
||||
// and can starting emitting data
|
||||
remainingStreamingBytes = STATE_IS_CHUNKED;
|
||||
// Mark remaining data as consumed and break - it's not HTTP
|
||||
consumedTotal += length;
|
||||
break;
|
||||
} else {
|
||||
/* If we came here without a body; emit an empty data chunk to signal no data */
|
||||
dataHandler(user, {}, true);
|
||||
|
||||
@@ -39,7 +39,6 @@ add_compile_definitions(
|
||||
CONFIG_TCC_PREDEFS
|
||||
ONE_SOURCE=0
|
||||
TCC_LIBTCC1="\\0"
|
||||
CONFIG_TCC_BACKTRACE=0
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
|
||||
@@ -23,10 +23,7 @@ const OS_NAME = platform().toLowerCase();
|
||||
const ARCH_NAME_RAW = arch();
|
||||
const IS_MAC = OS_NAME === "darwin";
|
||||
const IS_LINUX = OS_NAME === "linux";
|
||||
const IS_WINDOWS = OS_NAME === "win32";
|
||||
// On Windows, use PROCESSOR_ARCHITECTURE env var to get native arch (Bun may run under x64 emulation)
|
||||
const NATIVE_ARCH = IS_WINDOWS ? (process.env.PROCESSOR_ARCHITECTURE || ARCH_NAME_RAW).toUpperCase() : ARCH_NAME_RAW;
|
||||
const IS_ARM64 = NATIVE_ARCH === "ARM64" || NATIVE_ARCH === "AARCH64" || ARCH_NAME_RAW === "arm64";
|
||||
const IS_ARM64 = ARCH_NAME_RAW === "arm64" || ARCH_NAME_RAW === "aarch64";
|
||||
|
||||
// Paths
|
||||
const ROOT_DIR = resolve(import.meta.dir, "..");
|
||||
@@ -36,54 +33,22 @@ const WEBKIT_RELEASE_DIR = join(WEBKIT_BUILD_DIR, "Release");
|
||||
const WEBKIT_DEBUG_DIR = join(WEBKIT_BUILD_DIR, "Debug");
|
||||
const WEBKIT_RELEASE_DIR_LTO = join(WEBKIT_BUILD_DIR, "ReleaseLTO");
|
||||
|
||||
// Windows ICU paths - use vcpkg static build
|
||||
// Auto-detect triplet: prefer arm64 if it exists, otherwise x64
|
||||
const VCPKG_ARM64_PATH = join(WEBKIT_DIR, "vcpkg_installed", "arm64-windows-static");
|
||||
const VCPKG_X64_PATH = join(WEBKIT_DIR, "vcpkg_installed", "x64-windows-static");
|
||||
const VCPKG_ROOT = existsSync(VCPKG_ARM64_PATH) ? VCPKG_ARM64_PATH : VCPKG_X64_PATH;
|
||||
const ICU_INCLUDE_DIR = join(VCPKG_ROOT, "include");
|
||||
|
||||
// Get ICU library paths based on build config (debug uses 'd' suffix libraries)
|
||||
function getICULibraryPaths(config: BuildConfig) {
|
||||
const isDebug = config === "debug";
|
||||
// vcpkg static ICU libraries: release in lib/, debug in debug/lib/ with 'd' suffix
|
||||
const libDir = isDebug ? join(VCPKG_ROOT, "debug", "lib") : join(VCPKG_ROOT, "lib");
|
||||
const suffix = isDebug ? "d" : "";
|
||||
return {
|
||||
ICU_LIBRARY: libDir,
|
||||
ICU_DATA_LIBRARY: join(libDir, `sicudt${suffix}.lib`),
|
||||
ICU_I18N_LIBRARY: join(libDir, `sicuin${suffix}.lib`),
|
||||
ICU_UC_LIBRARY: join(libDir, `sicuuc${suffix}.lib`),
|
||||
};
|
||||
}
|
||||
|
||||
// Homebrew prefix detection
|
||||
const HOMEBREW_PREFIX = IS_ARM64 ? "/opt/homebrew/" : "/usr/local/";
|
||||
|
||||
// Compiler detection
|
||||
function findExecutable(names: string[]): string | null {
|
||||
for (const name of names) {
|
||||
const path = Bun.which(name);
|
||||
if (path) return path;
|
||||
const result = spawnSync("which", [name], { encoding: "utf8" });
|
||||
if (result.status === 0) {
|
||||
return result.stdout.trim();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Detect ccache
|
||||
const CCACHE = findExecutable(["ccache"]);
|
||||
const HAS_CCACHE = CCACHE !== null;
|
||||
|
||||
// Configure compilers with ccache if available
|
||||
// On Windows, use clang-cl for MSVC compatibility
|
||||
const CC_BASE = IS_WINDOWS
|
||||
? findExecutable(["clang-cl.exe", "clang-cl"]) || "clang-cl"
|
||||
: findExecutable(["clang-19", "clang"]) || "clang";
|
||||
const CXX_BASE = IS_WINDOWS
|
||||
? findExecutable(["clang-cl.exe", "clang-cl"]) || "clang-cl"
|
||||
: findExecutable(["clang++-19", "clang++"]) || "clang++";
|
||||
|
||||
const CC = HAS_CCACHE ? CCACHE : CC_BASE;
|
||||
const CXX = HAS_CCACHE ? CCACHE : CXX_BASE;
|
||||
const CC = findExecutable(["clang-19", "clang"]) || "clang";
|
||||
const CXX = findExecutable(["clang++-19", "clang++"]) || "clang++";
|
||||
|
||||
// Build directory based on config
|
||||
const getBuildDir = (config: BuildConfig) => {
|
||||
@@ -98,7 +63,7 @@ const getBuildDir = (config: BuildConfig) => {
|
||||
};
|
||||
|
||||
// Common CMake flags
|
||||
const getCommonFlags = (config: BuildConfig) => {
|
||||
const getCommonFlags = () => {
|
||||
const flags = [
|
||||
"-DPORT=JSCOnly",
|
||||
"-DENABLE_STATIC_JSC=ON",
|
||||
@@ -109,27 +74,16 @@ const getCommonFlags = (config: BuildConfig) => {
|
||||
"-DENABLE_FTL_JIT=ON",
|
||||
"-G",
|
||||
"Ninja",
|
||||
`-DCMAKE_C_COMPILER=${CC}`,
|
||||
`-DCMAKE_CXX_COMPILER=${CXX}`,
|
||||
];
|
||||
|
||||
// Configure compiler with ccache if available
|
||||
if (HAS_CCACHE) {
|
||||
flags.push(
|
||||
`-DCMAKE_C_COMPILER_LAUNCHER=${CCACHE}`,
|
||||
`-DCMAKE_CXX_COMPILER_LAUNCHER=${CCACHE}`,
|
||||
`-DCMAKE_C_COMPILER=${CC_BASE}`,
|
||||
`-DCMAKE_CXX_COMPILER=${CXX_BASE}`,
|
||||
);
|
||||
} else {
|
||||
flags.push(`-DCMAKE_C_COMPILER=${CC}`, `-DCMAKE_CXX_COMPILER=${CXX}`);
|
||||
}
|
||||
|
||||
if (IS_MAC) {
|
||||
flags.push(
|
||||
"-DENABLE_SINGLE_THREADED_VM_ENTRY_SCOPE=ON",
|
||||
"-DBUN_FAST_TLS=ON",
|
||||
"-DPTHREAD_JIT_PERMISSIONS_API=1",
|
||||
"-DUSE_PTHREAD_JIT_PERMISSIONS_API=ON",
|
||||
"-DENABLE_REMOTE_INSPECTOR=ON",
|
||||
);
|
||||
} else if (IS_LINUX) {
|
||||
flags.push(
|
||||
@@ -137,27 +91,6 @@ const getCommonFlags = (config: BuildConfig) => {
|
||||
"-DUSE_VISIBILITY_ATTRIBUTE=1",
|
||||
"-DENABLE_REMOTE_INSPECTOR=ON",
|
||||
);
|
||||
} else if (IS_WINDOWS) {
|
||||
// Find lld-link for Windows builds
|
||||
const lldLink = findExecutable(["lld-link.exe", "lld-link"]) || "lld-link";
|
||||
// Get ICU library paths for this build config (debug uses 'd' suffix libraries)
|
||||
const icuPaths = getICULibraryPaths(config);
|
||||
|
||||
flags.push(
|
||||
"-DENABLE_REMOTE_INSPECTOR=ON",
|
||||
"-DUSE_VISIBILITY_ATTRIBUTE=1",
|
||||
"-DUSE_SYSTEM_MALLOC=ON",
|
||||
`-DCMAKE_LINKER=${lldLink}`,
|
||||
`-DICU_ROOT=${VCPKG_ROOT}`,
|
||||
`-DICU_LIBRARY=${icuPaths.ICU_LIBRARY}`,
|
||||
`-DICU_INCLUDE_DIR=${ICU_INCLUDE_DIR}`,
|
||||
// Explicitly set ICU library paths to use vcpkg static libs (debug has 'd' suffix)
|
||||
`-DICU_DATA_LIBRARY_RELEASE=${icuPaths.ICU_DATA_LIBRARY}`,
|
||||
`-DICU_I18N_LIBRARY_RELEASE=${icuPaths.ICU_I18N_LIBRARY}`,
|
||||
`-DICU_UC_LIBRARY_RELEASE=${icuPaths.ICU_UC_LIBRARY}`,
|
||||
"-DCMAKE_C_FLAGS=/DU_STATIC_IMPLEMENTATION",
|
||||
"-DCMAKE_CXX_FLAGS=/DU_STATIC_IMPLEMENTATION /clang:-fno-c++-static-destructors",
|
||||
);
|
||||
}
|
||||
|
||||
return flags;
|
||||
@@ -165,7 +98,7 @@ const getCommonFlags = (config: BuildConfig) => {
|
||||
|
||||
// Build-specific CMake flags
|
||||
const getBuildFlags = (config: BuildConfig) => {
|
||||
const flags = [...getCommonFlags(config)];
|
||||
const flags = [...getCommonFlags()];
|
||||
|
||||
switch (config) {
|
||||
case "debug":
|
||||
@@ -173,40 +106,24 @@ const getBuildFlags = (config: BuildConfig) => {
|
||||
"-DCMAKE_BUILD_TYPE=Debug",
|
||||
"-DENABLE_BUN_SKIP_FAILING_ASSERTIONS=ON",
|
||||
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
|
||||
"-DENABLE_REMOTE_INSPECTOR=ON",
|
||||
"-DUSE_VISIBILITY_ATTRIBUTE=1",
|
||||
);
|
||||
|
||||
if (IS_MAC || IS_LINUX) {
|
||||
// Enable address sanitizer by default on Mac/Linux debug builds
|
||||
if (IS_MAC) {
|
||||
// Enable address sanitizer by default on Mac debug builds
|
||||
flags.push("-DENABLE_SANITIZERS=address");
|
||||
// To disable asan, comment the line above and uncomment:
|
||||
// flags.push("-DENABLE_MALLOC_HEAP_BREAKDOWN=ON");
|
||||
}
|
||||
|
||||
if (IS_WINDOWS) {
|
||||
flags.push("-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebug");
|
||||
}
|
||||
break;
|
||||
|
||||
case "lto":
|
||||
flags.push("-DCMAKE_BUILD_TYPE=Release");
|
||||
if (IS_WINDOWS) {
|
||||
// On Windows, append LTO flags to existing Windows-specific flags
|
||||
flags.push(
|
||||
"-DCMAKE_C_FLAGS=/DU_STATIC_IMPLEMENTATION -flto=full",
|
||||
"-DCMAKE_CXX_FLAGS=/DU_STATIC_IMPLEMENTATION /clang:-fno-c++-static-destructors -flto=full",
|
||||
"-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded",
|
||||
);
|
||||
} else {
|
||||
flags.push("-DCMAKE_C_FLAGS=-flto=full", "-DCMAKE_CXX_FLAGS=-flto=full");
|
||||
}
|
||||
flags.push("-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_C_FLAGS=-flto=full", "-DCMAKE_CXX_FLAGS=-flto=full");
|
||||
break;
|
||||
|
||||
default: // release
|
||||
flags.push("-DCMAKE_BUILD_TYPE=RelWithDebInfo");
|
||||
if (IS_WINDOWS) {
|
||||
flags.push("-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -217,6 +134,17 @@ const getBuildFlags = (config: BuildConfig) => {
|
||||
const getBuildEnv = () => {
|
||||
const env = { ...process.env };
|
||||
|
||||
const cflags = ["-ffat-lto-objects"];
|
||||
const cxxflags = ["-ffat-lto-objects"];
|
||||
|
||||
if (IS_LINUX && buildConfig !== "lto") {
|
||||
cflags.push("-Wl,--whole-archive");
|
||||
cxxflags.push("-Wl,--whole-archive", "-DUSE_BUN_JSC_ADDITIONS=ON", "-DUSE_BUN_EVENT_LOOP=ON");
|
||||
}
|
||||
|
||||
env.CFLAGS = (env.CFLAGS || "") + " " + cflags.join(" ");
|
||||
env.CXXFLAGS = (env.CXXFLAGS || "") + " " + cxxflags.join(" ");
|
||||
|
||||
if (IS_MAC) {
|
||||
env.ICU_INCLUDE_DIRS = `${HOMEBREW_PREFIX}opt/icu4c/include`;
|
||||
}
|
||||
@@ -251,9 +179,6 @@ function buildJSC() {
|
||||
|
||||
console.log(`Building JSC with configuration: ${buildConfig}`);
|
||||
console.log(`Build directory: ${buildDir}`);
|
||||
if (HAS_CCACHE) {
|
||||
console.log(`Using ccache for faster builds: ${CCACHE}`);
|
||||
}
|
||||
|
||||
// Create build directories
|
||||
if (!existsSync(buildDir)) {
|
||||
|
||||
@@ -14,15 +14,6 @@ import {
|
||||
startGroup,
|
||||
} from "./utils.mjs";
|
||||
|
||||
// Detect Windows ARM64 - bun may run under x64 emulation (WoW64), so check multiple indicators
|
||||
const isWindowsARM64 =
|
||||
isWindows &&
|
||||
(process.env.PROCESSOR_ARCHITECTURE === "ARM64" ||
|
||||
process.env.VSCMD_ARG_HOST_ARCH === "arm64" ||
|
||||
process.env.MSYSTEM_CARCH === "aarch64" ||
|
||||
(process.env.PROCESSOR_IDENTIFIER || "").includes("ARMv8") ||
|
||||
process.arch === "arm64");
|
||||
|
||||
if (globalThis.Bun) {
|
||||
await import("./glob-sources.mjs");
|
||||
}
|
||||
@@ -92,23 +83,6 @@ async function build(args) {
|
||||
generateOptions["--toolchain"] = toolchainPath;
|
||||
}
|
||||
|
||||
// Windows ARM64: automatically set required options
|
||||
if (isWindowsARM64) {
|
||||
// Use clang-cl instead of MSVC cl.exe for proper ARM64 flag support
|
||||
if (!generateOptions["-DCMAKE_C_COMPILER"]) {
|
||||
generateOptions["-DCMAKE_C_COMPILER"] = "clang-cl";
|
||||
}
|
||||
if (!generateOptions["-DCMAKE_CXX_COMPILER"]) {
|
||||
generateOptions["-DCMAKE_CXX_COMPILER"] = "clang-cl";
|
||||
}
|
||||
// Skip codegen by default since x64 bun crashes under WoW64 emulation
|
||||
// Can be overridden with -DSKIP_CODEGEN=OFF once ARM64 bun is available
|
||||
if (!generateOptions["-DSKIP_CODEGEN"]) {
|
||||
generateOptions["-DSKIP_CODEGEN"] = "ON";
|
||||
}
|
||||
console.log("Windows ARM64 detected: using clang-cl and SKIP_CODEGEN=ON");
|
||||
}
|
||||
|
||||
const generateArgs = Object.entries(generateOptions).flatMap(([flag, value]) =>
|
||||
flag.startsWith("-D") ? [`${flag}=${value}`] : [flag, value],
|
||||
);
|
||||
|
||||
@@ -49,42 +49,9 @@ const colors = {
|
||||
|
||||
// Parse command line arguments
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
// Show help
|
||||
if (args.includes("--help") || args.includes("-h")) {
|
||||
console.log(`Usage: bun run scripts/buildkite-failures.ts [options] [build-id|branch|pr-url|buildkite-url]
|
||||
|
||||
Shows detailed error information from BuildKite build failures.
|
||||
Full logs are saved to /tmp/bun-build-{number}-{platform}-{step}.log
|
||||
|
||||
Arguments:
|
||||
build-id BuildKite build number (e.g., 35051)
|
||||
branch Git branch name (e.g., main, claude/fix-bug)
|
||||
pr-url GitHub PR URL (e.g., https://github.com/oven-sh/bun/pull/26173)
|
||||
buildkite-url BuildKite build URL
|
||||
#number GitHub PR number (e.g., #26173)
|
||||
(none) Uses current git branch
|
||||
|
||||
Options:
|
||||
--flaky, -f Include flaky test annotations
|
||||
--warnings, -w Include warning annotations
|
||||
--wait Poll continuously until build completes or fails
|
||||
--help, -h Show this help message
|
||||
|
||||
Examples:
|
||||
bun run scripts/buildkite-failures.ts # Current branch
|
||||
bun run scripts/buildkite-failures.ts main # Main branch
|
||||
bun run scripts/buildkite-failures.ts 35051 # Build #35051
|
||||
bun run scripts/buildkite-failures.ts #26173 # PR #26173
|
||||
bun run scripts/buildkite-failures.ts --wait # Wait for current branch build to complete
|
||||
`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const showWarnings = args.includes("--warnings") || args.includes("-w");
|
||||
const showFlaky = args.includes("--flaky") || args.includes("-f");
|
||||
const waitMode = args.includes("--wait");
|
||||
const inputArg = args.find(arg => !arg.startsWith("-") && !arg.startsWith("--"));
|
||||
const inputArg = args.find(arg => !arg.startsWith("-"));
|
||||
|
||||
// Determine what type of input we have
|
||||
let buildNumber = null;
|
||||
@@ -147,138 +114,38 @@ if (!buildNumber) {
|
||||
buildNumber = match[1];
|
||||
}
|
||||
|
||||
// Helper to format time ago
|
||||
function formatTimeAgo(dateStr: string | null): string {
|
||||
if (!dateStr) return "not started";
|
||||
const date = new Date(dateStr);
|
||||
const now = new Date();
|
||||
const diffMs = now.getTime() - date.getTime();
|
||||
const diffSecs = Math.floor(diffMs / 1000);
|
||||
const diffMins = Math.floor(diffSecs / 60);
|
||||
const diffHours = Math.floor(diffMins / 60);
|
||||
const diffDays = Math.floor(diffHours / 24);
|
||||
// Fetch build JSON
|
||||
const buildResponse = await fetch(`https://buildkite.com/bun/bun/builds/${buildNumber}.json`);
|
||||
const build = await buildResponse.json();
|
||||
|
||||
if (diffDays > 0) return `${diffDays} day${diffDays !== 1 ? "s" : ""} ago`;
|
||||
if (diffHours > 0) return `${diffHours} hour${diffHours !== 1 ? "s" : ""} ago`;
|
||||
if (diffMins > 0) return `${diffMins} minute${diffMins !== 1 ? "s" : ""} ago`;
|
||||
return `${diffSecs} second${diffSecs !== 1 ? "s" : ""} ago`;
|
||||
// Calculate time ago
|
||||
const buildTime = new Date(build.started_at);
|
||||
const now = new Date();
|
||||
const diffMs = now.getTime() - buildTime.getTime();
|
||||
const diffSecs = Math.floor(diffMs / 1000);
|
||||
const diffMins = Math.floor(diffSecs / 60);
|
||||
const diffHours = Math.floor(diffMins / 60);
|
||||
const diffDays = Math.floor(diffHours / 24);
|
||||
|
||||
let timeAgo;
|
||||
if (diffDays > 0) {
|
||||
timeAgo = `${diffDays} day${diffDays !== 1 ? "s" : ""} ago`;
|
||||
} else if (diffHours > 0) {
|
||||
timeAgo = `${diffHours} hour${diffHours !== 1 ? "s" : ""} ago`;
|
||||
} else if (diffMins > 0) {
|
||||
timeAgo = `${diffMins} minute${diffMins !== 1 ? "s" : ""} ago`;
|
||||
} else {
|
||||
timeAgo = `${diffSecs} second${diffSecs !== 1 ? "s" : ""} ago`;
|
||||
}
|
||||
|
||||
// Helper to clear line for updates
|
||||
const clearLine = isTTY ? "\x1b[2K\r" : "";
|
||||
|
||||
// Poll for build status
|
||||
let build: any;
|
||||
let pollCount = 0;
|
||||
const pollInterval = 10000; // 10 seconds
|
||||
|
||||
while (true) {
|
||||
// Fetch build JSON
|
||||
const buildResponse = await fetch(`https://buildkite.com/bun/bun/builds/${buildNumber}.json`);
|
||||
build = await buildResponse.json();
|
||||
|
||||
// Check for failed jobs first (even if build is still running)
|
||||
const failedJobsEarly =
|
||||
build.jobs?.filter(
|
||||
(job: any) => job.exit_status && job.exit_status > 0 && !job.soft_failed && job.type === "script",
|
||||
) || [];
|
||||
|
||||
// In wait mode with failures, stop polling and show failures
|
||||
if (waitMode && failedJobsEarly.length > 0) {
|
||||
if (pollCount > 0) {
|
||||
process.stdout.write(clearLine);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Calculate time ago (use created_at as fallback for scheduled/pending builds)
|
||||
const timeAgo = formatTimeAgo(build.started_at || build.created_at);
|
||||
|
||||
// Check if build passed
|
||||
if (build.state === "passed") {
|
||||
if (pollCount > 0) {
|
||||
process.stdout.write(clearLine);
|
||||
}
|
||||
console.log(`${timeAgo} - build #${buildNumber} https://buildkite.com/bun/bun/builds/${buildNumber}\n`);
|
||||
console.log(`${colors.green}✅ Passed!${colors.reset}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Check if build was canceled
|
||||
if (build.state === "canceled" || build.state === "canceling") {
|
||||
if (pollCount > 0) {
|
||||
process.stdout.write(clearLine);
|
||||
}
|
||||
console.log(`${timeAgo} - build #${buildNumber} https://buildkite.com/bun/bun/builds/${buildNumber}\n`);
|
||||
console.log(`${colors.dim}🚫 Build was canceled${colors.reset}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Check if build is pending/running/scheduled
|
||||
if (
|
||||
build.state === "scheduled" ||
|
||||
build.state === "running" ||
|
||||
build.state === "creating" ||
|
||||
build.state === "started"
|
||||
) {
|
||||
const runningJobs = build.jobs?.filter((job: any) => job.state === "running") || [];
|
||||
const pendingJobs = build.jobs?.filter((job: any) => job.state === "scheduled" || job.state === "waiting") || [];
|
||||
const passedJobs = build.jobs?.filter((job: any) => job.state === "passed") || [];
|
||||
const totalJobs = build.jobs?.filter((job: any) => job.type === "script")?.length || 0;
|
||||
|
||||
if (waitMode) {
|
||||
// In wait mode, show a single updating line
|
||||
let statusMsg = "";
|
||||
if (build.state === "scheduled" || build.state === "creating") {
|
||||
statusMsg = `⏳ Waiting... (scheduled ${formatTimeAgo(build.created_at)})`;
|
||||
} else {
|
||||
statusMsg = `🔄 Running... ${passedJobs.length}/${totalJobs} passed, ${runningJobs.length} running`;
|
||||
}
|
||||
process.stdout.write(`${clearLine}${colors.dim}${statusMsg}${colors.reset}`);
|
||||
pollCount++;
|
||||
await Bun.sleep(pollInterval);
|
||||
continue;
|
||||
} else {
|
||||
// Not in wait mode, show full status and exit
|
||||
console.log(`${timeAgo} - build #${buildNumber} https://buildkite.com/bun/bun/builds/${buildNumber}\n`);
|
||||
|
||||
if (build.state === "scheduled" || build.state === "creating") {
|
||||
console.log(`${colors.dim}⏳ Build is scheduled/pending${colors.reset}`);
|
||||
if (build.created_at) {
|
||||
console.log(`${colors.dim} Created: ${formatTimeAgo(build.created_at)}${colors.reset}`);
|
||||
}
|
||||
} else {
|
||||
console.log(`${colors.dim}🔄 Build is running${colors.reset}`);
|
||||
if (build.started_at) {
|
||||
console.log(`${colors.dim} Started: ${formatTimeAgo(build.started_at)}${colors.reset}`);
|
||||
}
|
||||
console.log(
|
||||
`${colors.dim} Progress: ${passedJobs.length}/${totalJobs} jobs passed, ${runningJobs.length} running, ${pendingJobs.length} pending${colors.reset}`,
|
||||
);
|
||||
|
||||
if (runningJobs.length > 0) {
|
||||
console.log(`\n${colors.dim}Running jobs:${colors.reset}`);
|
||||
for (const job of runningJobs.slice(0, 5)) {
|
||||
const name = job.name || job.label || "Unknown";
|
||||
console.log(` ${colors.dim}• ${name}${colors.reset}`);
|
||||
}
|
||||
if (runningJobs.length > 5) {
|
||||
console.log(` ${colors.dim}... and ${runningJobs.length - 5} more${colors.reset}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Build is in a terminal state (failed, etc.) - break out of loop
|
||||
break;
|
||||
}
|
||||
|
||||
// Print header for failed build
|
||||
const timeAgo = formatTimeAgo(build.started_at || build.created_at);
|
||||
console.log(`${timeAgo} - build #${buildNumber} https://buildkite.com/bun/bun/builds/${buildNumber}\n`);
|
||||
|
||||
// Check if build passed
|
||||
if (build.state === "passed") {
|
||||
console.log(`${colors.green}✅ Passed!${colors.reset}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Get failed jobs
|
||||
const failedJobs =
|
||||
build.jobs?.filter(job => job.exit_status && job.exit_status > 0 && !job.soft_failed && job.type === "script") || [];
|
||||
@@ -867,212 +734,7 @@ if (registerRequestIndex !== -1) {
|
||||
console.log(` https://buildkite.com/bun/bun/builds/${buildNumber}#annotations`);
|
||||
}
|
||||
} else {
|
||||
// No annotations found - show detailed job failure information
|
||||
if (failedJobs.length > 0) {
|
||||
console.log(`\n${colors.red}${colors.bold}${failedJobs.length} job failures${colors.reset}\n`);
|
||||
|
||||
// Show annotation counts if available
|
||||
const annotationCounts = build.annotation_counts_by_style;
|
||||
if (annotationCounts) {
|
||||
const errors = annotationCounts.error || 0;
|
||||
const warnings = annotationCounts.warning || 0;
|
||||
if (errors > 0 || warnings > 0) {
|
||||
const parts = [];
|
||||
if (errors > 0) parts.push(`${errors} error${errors !== 1 ? "s" : ""}`);
|
||||
if (warnings > 0) parts.push(`${warnings} warning${warnings !== 1 ? "s" : ""}`);
|
||||
console.log(
|
||||
`${colors.dim}Annotations: ${parts.join(", ")} - view at https://buildkite.com/bun/bun/builds/${buildNumber}#annotations${colors.reset}\n`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Group jobs by type
|
||||
const buildJobs = failedJobs.filter(job => (job.name || job.label || "").includes("build-"));
|
||||
const testJobs = failedJobs.filter(job => (job.name || job.label || "").includes("test"));
|
||||
const otherJobs = failedJobs.filter(
|
||||
job => !(job.name || job.label || "").includes("build-") && !(job.name || job.label || "").includes("test"),
|
||||
);
|
||||
|
||||
// Display build failures
|
||||
if (buildJobs.length > 0) {
|
||||
console.log(
|
||||
`${colors.bgRed}${colors.white}${colors.bold} Build Failures (${buildJobs.length}) ${colors.reset}\n`,
|
||||
);
|
||||
for (const job of buildJobs) {
|
||||
const name = (job.name || job.label || "Unknown").replace(/^:([^:]+):/, (_, emoji) => {
|
||||
const platform = emoji.toLowerCase();
|
||||
return platformMap[platform] || `:${emoji}:`;
|
||||
});
|
||||
const duration =
|
||||
job.started_at && job.finished_at
|
||||
? `${((new Date(job.finished_at).getTime() - new Date(job.started_at).getTime()) / 1000).toFixed(0)}s`
|
||||
: "N/A";
|
||||
console.log(` ${colors.red}✗${colors.reset} ${name}`);
|
||||
console.log(` ${colors.dim}Duration: ${duration} | Exit: ${job.exit_status}${colors.reset}`);
|
||||
console.log(` ${colors.dim}https://buildkite.com${job.path}${colors.reset}`);
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
|
||||
// Display test failures
|
||||
if (testJobs.length > 0) {
|
||||
console.log(`${colors.bgBlue}${colors.white}${colors.bold} Test Failures (${testJobs.length}) ${colors.reset}\n`);
|
||||
for (const job of testJobs) {
|
||||
const name = (job.name || job.label || "Unknown").replace(/^:([^:]+):/, (_, emoji) => {
|
||||
const platform = emoji.toLowerCase();
|
||||
return platformMap[platform] || `:${emoji}:`;
|
||||
});
|
||||
const duration =
|
||||
job.started_at && job.finished_at
|
||||
? `${((new Date(job.finished_at).getTime() - new Date(job.started_at).getTime()) / 1000).toFixed(0)}s`
|
||||
: "N/A";
|
||||
console.log(` ${colors.red}✗${colors.reset} ${name}`);
|
||||
console.log(` ${colors.dim}Duration: ${duration} | Exit: ${job.exit_status}${colors.reset}`);
|
||||
console.log(` ${colors.dim}https://buildkite.com${job.path}${colors.reset}`);
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
|
||||
// Display other failures
|
||||
if (otherJobs.length > 0) {
|
||||
console.log(
|
||||
`${colors.bgBlue}${colors.white}${colors.bold} Other Failures (${otherJobs.length}) ${colors.reset}\n`,
|
||||
);
|
||||
for (const job of otherJobs) {
|
||||
const name = (job.name || job.label || "Unknown").replace(/^:([^:]+):/, (_, emoji) => {
|
||||
const platform = emoji.toLowerCase();
|
||||
return platformMap[platform] || `:${emoji}:`;
|
||||
});
|
||||
const duration =
|
||||
job.started_at && job.finished_at
|
||||
? `${((new Date(job.finished_at).getTime() - new Date(job.started_at).getTime()) / 1000).toFixed(0)}s`
|
||||
: "N/A";
|
||||
console.log(` ${colors.red}✗${colors.reset} ${name}`);
|
||||
console.log(` ${colors.dim}Duration: ${duration} | Exit: ${job.exit_status}${colors.reset}`);
|
||||
console.log(` ${colors.dim}https://buildkite.com${job.path}${colors.reset}`);
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch and display logs for all failed jobs
|
||||
// Use the public BuildKite log endpoint
|
||||
console.log(`${colors.dim}Fetching logs for ${failedJobs.length} failed jobs...${colors.reset}\n`);
|
||||
|
||||
for (const job of failedJobs) {
|
||||
const name = (job.name || job.label || "Unknown").replace(/^:([^:]+):/, (_, emoji) => {
|
||||
const platform = emoji.toLowerCase();
|
||||
return platformMap[platform] || `:${emoji}:`;
|
||||
});
|
||||
|
||||
// Create a sanitized filename from the job name
|
||||
// e.g., ":darwin: aarch64 - build-cpp" -> "darwin-aarch64-build-cpp"
|
||||
const sanitizedName = (job.name || job.label || "unknown")
|
||||
.replace(/^:([^:]+):\s*/, "$1-") // :darwin: -> darwin-
|
||||
.replace(/\s+-\s+/g, "-") // " - " -> "-"
|
||||
.replace(/[^a-zA-Z0-9-]/g, "-") // Replace other chars with -
|
||||
.replace(/-+/g, "-") // Collapse multiple -
|
||||
.replace(/^-|-$/g, "") // Remove leading/trailing -
|
||||
.toLowerCase();
|
||||
|
||||
const logFilePath = `/tmp/bun-build-${buildNumber}-${sanitizedName}.log`;
|
||||
|
||||
try {
|
||||
const logResponse = await fetch(
|
||||
`https://buildkite.com/organizations/bun/pipelines/bun/builds/${buildNumber}/jobs/${job.id}/log`,
|
||||
);
|
||||
|
||||
if (logResponse.ok) {
|
||||
const logData = await logResponse.json();
|
||||
let output = logData.output || "";
|
||||
|
||||
// Convert HTML to readable text (without ANSI codes for file output)
|
||||
const plainOutput = output
|
||||
// Remove timestamp tags
|
||||
.replace(/<time[^>]*>[^<]*<\/time>/g, "")
|
||||
// Remove all span tags
|
||||
.replace(/<span[^>]*>([^<]*)<\/span>/g, "$1")
|
||||
// Remove remaining HTML tags
|
||||
.replace(/<[^>]+>/g, "")
|
||||
// Decode HTML entities
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'")
|
||||
.replace(///g, "/")
|
||||
.replace(/ /g, " ");
|
||||
|
||||
// Write the full log to a file
|
||||
await Bun.write(logFilePath, plainOutput);
|
||||
|
||||
// Extract unique error messages for display
|
||||
const lines = plainOutput.split("\n");
|
||||
const uniqueErrors = new Set<string>();
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
|
||||
// Look for actual error messages
|
||||
const isError =
|
||||
(line.includes("error:") && !line.includes('error: script "') && !line.includes("error: exit")) ||
|
||||
line.includes("fatal error:") ||
|
||||
line.includes("panic:") ||
|
||||
line.includes("undefined reference");
|
||||
|
||||
if (isError) {
|
||||
// Extract just the error message part (remove path prefixes and timestamps)
|
||||
const errorMsg = line
|
||||
.replace(/^.*?\d{4}-\d{2}-\d{2}T[\d:.]+Z/, "") // Remove timestamps
|
||||
.replace(/^.*?\/[^\s]*:\d+:\d+:\s*/, "") // Remove file paths
|
||||
.trim();
|
||||
|
||||
if (errorMsg && !uniqueErrors.has(errorMsg)) {
|
||||
uniqueErrors.add(errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Display job info with log file path
|
||||
console.log(`${colors.bgBlue}${colors.white}${colors.bold} ${name} ${colors.reset}`);
|
||||
console.log(` ${colors.dim}Log: ${logFilePath}${colors.reset}`);
|
||||
|
||||
if (uniqueErrors.size > 0) {
|
||||
console.log(` ${colors.red}Errors (${uniqueErrors.size}):${colors.reset}`);
|
||||
let count = 0;
|
||||
for (const err of uniqueErrors) {
|
||||
if (count >= 5) {
|
||||
console.log(` ${colors.dim}... and ${uniqueErrors.size - 5} more${colors.reset}`);
|
||||
break;
|
||||
}
|
||||
console.log(` ${colors.red}•${colors.reset} ${err.slice(0, 120)}${err.length > 120 ? "..." : ""}`);
|
||||
count++;
|
||||
}
|
||||
} else {
|
||||
// Show last few lines as a preview
|
||||
const lastLines = lines.slice(-5).filter(l => l.trim());
|
||||
if (lastLines.length > 0) {
|
||||
console.log(` ${colors.dim}Last output:${colors.reset}`);
|
||||
for (const line of lastLines) {
|
||||
console.log(` ${colors.dim}${line.slice(0, 100)}${line.length > 100 ? "..." : ""}${colors.reset}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (logData.truncated) {
|
||||
console.log(` ${colors.dim}(Log was truncated by BuildKite)${colors.reset}`);
|
||||
}
|
||||
} else {
|
||||
console.log(`${colors.bgBlue}${colors.white}${colors.bold} ${name} ${colors.reset}`);
|
||||
console.log(` ${colors.dim}Failed to fetch log: ${logResponse.status}${colors.reset}`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`${colors.bgBlue}${colors.white}${colors.bold} ${name} ${colors.reset}`);
|
||||
console.log(` ${colors.dim}Error fetching log: ${e.message}${colors.reset}`);
|
||||
}
|
||||
console.log();
|
||||
}
|
||||
} else {
|
||||
console.log("View detailed results at:");
|
||||
console.log(` https://buildkite.com/bun/bun/builds/${buildNumber}#annotations`);
|
||||
}
|
||||
console.log(`\n${colors.red}${colors.bold}${failedJobs.length} job failures${colors.reset}\n`);
|
||||
console.log("View detailed results at:");
|
||||
console.log(` https://buildkite.com/bun/bun/builds/${buildNumber}#annotations`);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ console.log("Reading linker files...");
|
||||
const linkerLds = await Bun.file(join(repoRoot, "src", "linker.lds")).text();
|
||||
const symbolsDyn = await Bun.file(join(repoRoot, "src", "symbols.dyn")).text();
|
||||
const symbolsTxt = await Bun.file(join(repoRoot, "src", "symbols.txt")).text();
|
||||
const symbolsDef = await Bun.file(join(repoRoot, "src", "symbols.def")).text();
|
||||
|
||||
// Create metadata JSON with link command included
|
||||
const metadata = {
|
||||
@@ -42,7 +41,6 @@ const metadata = {
|
||||
linker_lds: linkerLds,
|
||||
symbols_dyn: symbolsDyn,
|
||||
symbols_txt: symbolsTxt,
|
||||
symbols_def: symbolsDef,
|
||||
};
|
||||
|
||||
const metadataPath = join(buildPath, "link-metadata.json");
|
||||
|
||||
@@ -1128,7 +1128,6 @@ async function spawnBun(execPath, { args, cwd, timeout, env, stdout, stderr }) {
|
||||
...process.env,
|
||||
PATH: path,
|
||||
TMPDIR: tmpdirPath,
|
||||
BUN_TMPDIR: tmpdirPath,
|
||||
USER: username,
|
||||
HOME: homedir,
|
||||
SHELL: shellPath,
|
||||
@@ -1585,9 +1584,6 @@ function isNodeTest(path) {
|
||||
if (isCI && isMacOS && isX64) {
|
||||
return false;
|
||||
}
|
||||
if (!isJavaScript(path)) {
|
||||
return false;
|
||||
}
|
||||
const unixPath = path.replaceAll(sep, "/");
|
||||
return (
|
||||
unixPath.includes("js/node/test/parallel/") ||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Updates the vendored uucode library and regenerates grapheme tables.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/update-uucode.sh # update from default URL
|
||||
# ./scripts/update-uucode.sh /path/to/uucode # update from local directory
|
||||
# ./scripts/update-uucode.sh https://url.tar.gz # update from URL
|
||||
#
|
||||
# After running, verify with:
|
||||
# bun bd test test/js/bun/util/stringWidth.test.ts
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BUN_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
UUCODE_DIR="$BUN_ROOT/src/deps/uucode"
|
||||
ZIG="$BUN_ROOT/vendor/zig/zig"
|
||||
|
||||
if [ ! -x "$ZIG" ]; then
|
||||
echo "error: zig not found at $ZIG"
|
||||
echo " run scripts/bootstrap.sh first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
update_from_dir() {
|
||||
local src="$1"
|
||||
echo "Updating uucode from: $src"
|
||||
rm -rf "$UUCODE_DIR"
|
||||
mkdir -p "$UUCODE_DIR"
|
||||
cp -r "$src"/* "$UUCODE_DIR/"
|
||||
}
|
||||
|
||||
update_from_url() {
|
||||
local url="$1"
|
||||
local tmp
|
||||
tmp=$(mktemp -d)
|
||||
trap "rm -rf $tmp" EXIT
|
||||
|
||||
echo "Downloading uucode from: $url"
|
||||
curl -fsSL "$url" | tar -xz -C "$tmp" --strip-components=1
|
||||
|
||||
update_from_dir "$tmp"
|
||||
}
|
||||
|
||||
# Handle source argument
|
||||
if [ $# -ge 1 ]; then
|
||||
SOURCE="$1"
|
||||
if [ -d "$SOURCE" ]; then
|
||||
update_from_dir "$SOURCE"
|
||||
elif [[ "$SOURCE" == http* ]]; then
|
||||
update_from_url "$SOURCE"
|
||||
else
|
||||
echo "error: argument must be a directory or URL"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Default: use the zig global cache if available
|
||||
CACHED=$(find "$HOME/.cache/zig/p" -maxdepth 1 -name "uucode-*" -type d 2>/dev/null | sort -V | tail -1)
|
||||
if [ -n "$CACHED" ]; then
|
||||
update_from_dir "$CACHED"
|
||||
else
|
||||
echo "error: no uucode source specified and none found in zig cache"
|
||||
echo ""
|
||||
echo "usage: $0 <path-to-uucode-dir-or-url>"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Regenerating grapheme tables..."
|
||||
cd "$BUN_ROOT"
|
||||
"$ZIG" build generate-grapheme-tables
|
||||
|
||||
echo ""
|
||||
echo "Done. Updated files:"
|
||||
echo " src/deps/uucode/ (vendored library)"
|
||||
echo " src/string/immutable/grapheme_tables.zig (regenerated)"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. bun bd test test/js/bun/util/stringWidth.test.ts"
|
||||
echo " 2. git add src/deps/uucode src/string/immutable/grapheme_tables.zig"
|
||||
echo " 3. git commit -m 'Update uucode to <version>'"
|
||||
@@ -3,10 +3,6 @@
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# Detect system architecture
|
||||
$script:IsARM64 = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq [System.Runtime.InteropServices.Architecture]::Arm64
|
||||
$script:VsArch = if ($script:IsARM64) { "arm64" } else { "amd64" }
|
||||
|
||||
if($env:VSINSTALLDIR -eq $null) {
|
||||
Write-Host "Loading Visual Studio environment, this may take a second..."
|
||||
|
||||
@@ -27,14 +23,14 @@ if($env:VSINSTALLDIR -eq $null) {
|
||||
Push-Location $vsDir
|
||||
try {
|
||||
$vsShell = (Join-Path -Path $vsDir -ChildPath "Common7\Tools\Launch-VsDevShell.ps1")
|
||||
. $vsShell -Arch $script:VsArch -HostArch $script:VsArch
|
||||
. $vsShell -Arch amd64 -HostArch amd64
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
}
|
||||
|
||||
if($env:VSCMD_ARG_TGT_ARCH -eq "x86") {
|
||||
throw "Visual Studio environment is targeting 32 bit x86, but only 64-bit architectures (x64/arm64) are supported."
|
||||
throw "Visual Studio environment is targeting 32 bit, but only 64 bit is supported."
|
||||
}
|
||||
|
||||
if ($args.Count -gt 0) {
|
||||
|
||||
@@ -8,5 +8,4 @@ Syntax reminders:
|
||||
Conventions:
|
||||
|
||||
- Prefer `@import` at the **bottom** of the file, but the auto formatter will move them so you don't need to worry about it.
|
||||
- **Never** use `@import()` inline inside of functions. **Always** put them at the bottom of the file or containing struct. Imports in Zig are free of side-effects, so there's no such thing as a "dynamic" import.
|
||||
- You must be patient with the build.
|
||||
|
||||
@@ -74,7 +74,7 @@ pub const FileOperation = struct {
|
||||
|
||||
pub fn getPathname(file: *const FileOperation) string {
|
||||
if (file.is_tmpdir) {
|
||||
return resolve_path.joinAbs(Fs.FileSystem.RealFS.tmpdirPath(), .auto, file.pathname);
|
||||
return resolve_path.joinAbs(@TypeOf(Fs.FileSystem.instance.fs).tmpdir_path, .auto, file.pathname);
|
||||
} else {
|
||||
return file.pathname;
|
||||
}
|
||||
|
||||
@@ -429,49 +429,16 @@ pub const StandaloneModuleGraph = struct {
|
||||
|
||||
const bytecode: StringPointer = brk: {
|
||||
if (output_file.bytecode_index != std.math.maxInt(u32)) {
|
||||
// Bytecode alignment for JSC bytecode cache deserialization.
|
||||
// Not aligning correctly causes a runtime assertion error or segfault.
|
||||
//
|
||||
// PLATFORM-SPECIFIC ALIGNMENT:
|
||||
// - PE (Windows) and Mach-O (macOS): The module graph data is embedded in
|
||||
// a dedicated section with an 8-byte size header. At runtime, the section
|
||||
// is memory-mapped at a page-aligned address (hence 128-byte aligned).
|
||||
// The data buffer starts 8 bytes after the section start.
|
||||
// For bytecode at offset O to be 128-byte aligned:
|
||||
// (section_va + 8 + O) % 128 == 0
|
||||
// => O % 128 == 120
|
||||
//
|
||||
// - ELF (Linux): The module graph data is appended to the executable and
|
||||
// read into a heap-allocated buffer at runtime. The allocator provides
|
||||
// natural alignment, and there's no 8-byte section header offset.
|
||||
// However, using target_mod=120 is still safe because:
|
||||
// - If the buffer is 128-aligned: bytecode at offset 120 is at (128n + 120),
|
||||
// which when loaded at a 128-aligned address gives proper alignment.
|
||||
// - The extra 120 bytes of padding is acceptable overhead.
|
||||
//
|
||||
// This alignment strategy (target_mod=120) works for all platforms because
|
||||
// it's the worst-case offset needed for the 8-byte header scenario.
|
||||
// Use up to 256 byte alignment for bytecode
|
||||
// Not aligning it correctly will cause a runtime assertion error, or a segfault.
|
||||
const bytecode = output_files[output_file.bytecode_index].value.buffer.bytes;
|
||||
const current_offset = string_builder.len;
|
||||
// Calculate padding so that (current_offset + padding) % 128 == 120
|
||||
// This accounts for the 8-byte section header on PE/Mach-O platforms.
|
||||
const target_mod: usize = 128 - @sizeOf(u64); // 120 = accounts for 8-byte header
|
||||
const current_mod = current_offset % 128;
|
||||
const padding = if (current_mod <= target_mod)
|
||||
target_mod - current_mod
|
||||
else
|
||||
128 - current_mod + target_mod;
|
||||
// Zero the padding bytes to ensure deterministic output
|
||||
const writable = string_builder.writable();
|
||||
@memset(writable[0..padding], 0);
|
||||
string_builder.len += padding;
|
||||
const aligned_offset = string_builder.len;
|
||||
const writable_after_padding = string_builder.writable();
|
||||
@memcpy(writable_after_padding[0..bytecode.len], bytecode[0..bytecode.len]);
|
||||
const unaligned_space = writable_after_padding[bytecode.len..];
|
||||
const aligned = std.mem.alignInSlice(string_builder.writable(), 128).?;
|
||||
@memcpy(aligned[0..bytecode.len], bytecode[0..bytecode.len]);
|
||||
const unaligned_space = aligned[bytecode.len..];
|
||||
const offset = @intFromPtr(aligned.ptr) - @intFromPtr(string_builder.ptr.?);
|
||||
const len = bytecode.len + @min(unaligned_space.len, 128);
|
||||
string_builder.len += len;
|
||||
break :brk StringPointer{ .offset = @truncate(aligned_offset), .length = @truncate(len) };
|
||||
break :brk StringPointer{ .offset = @truncate(offset), .length = @truncate(len) };
|
||||
} else {
|
||||
break :brk .{};
|
||||
}
|
||||
@@ -703,7 +670,7 @@ pub const StandaloneModuleGraph = struct {
|
||||
if (!tried_changing_abs_dir) {
|
||||
tried_changing_abs_dir = true;
|
||||
const zname_z = bun.strings.concat(bun.default_allocator, &.{
|
||||
bun.fs.FileSystem.RealFS.tmpdirPath(),
|
||||
bun.fs.FileSystem.instance.fs.tmpdirPath(),
|
||||
std.fs.path.sep_str,
|
||||
zname,
|
||||
&.{0},
|
||||
@@ -1192,9 +1159,7 @@ pub const StandaloneModuleGraph = struct {
|
||||
return .success;
|
||||
}
|
||||
|
||||
/// Loads the standalone module graph from the executable, allocates it on the heap,
|
||||
/// sets it globally, and returns the pointer.
|
||||
pub fn fromExecutable(allocator: std.mem.Allocator) !?*StandaloneModuleGraph {
|
||||
pub fn fromExecutable(allocator: std.mem.Allocator) !?StandaloneModuleGraph {
|
||||
if (comptime Environment.isMac) {
|
||||
const macho_bytes = Macho.getData() orelse return null;
|
||||
if (macho_bytes.len < @sizeOf(Offsets) + trailer.len) {
|
||||
@@ -1208,7 +1173,7 @@ pub const StandaloneModuleGraph = struct {
|
||||
return null;
|
||||
}
|
||||
const offsets = std.mem.bytesAsValue(Offsets, macho_bytes_slice).*;
|
||||
return try fromBytesAlloc(allocator, @constCast(macho_bytes), offsets);
|
||||
return try StandaloneModuleGraph.fromBytes(allocator, @constCast(macho_bytes), offsets);
|
||||
}
|
||||
|
||||
if (comptime Environment.isWindows) {
|
||||
@@ -1224,7 +1189,7 @@ pub const StandaloneModuleGraph = struct {
|
||||
return null;
|
||||
}
|
||||
const offsets = std.mem.bytesAsValue(Offsets, pe_bytes_slice).*;
|
||||
return try fromBytesAlloc(allocator, @constCast(pe_bytes), offsets);
|
||||
return try StandaloneModuleGraph.fromBytes(allocator, @constCast(pe_bytes), offsets);
|
||||
}
|
||||
|
||||
// Do not invoke libuv here.
|
||||
@@ -1248,7 +1213,7 @@ pub const StandaloneModuleGraph = struct {
|
||||
}
|
||||
}
|
||||
|
||||
if (read_amount < trailer.len + @sizeOf(usize) + @sizeOf(Offsets))
|
||||
if (read_amount < trailer.len + @sizeOf(usize) + 32)
|
||||
// definitely missing data
|
||||
return null;
|
||||
|
||||
@@ -1319,15 +1284,7 @@ pub const StandaloneModuleGraph = struct {
|
||||
}
|
||||
}
|
||||
|
||||
return try fromBytesAlloc(allocator, to_read, offsets);
|
||||
}
|
||||
|
||||
/// Allocates a StandaloneModuleGraph on the heap, populates it from bytes, sets it globally, and returns the pointer.
|
||||
fn fromBytesAlloc(allocator: std.mem.Allocator, raw_bytes: []u8, offsets: Offsets) !*StandaloneModuleGraph {
|
||||
const graph_ptr = try allocator.create(StandaloneModuleGraph);
|
||||
graph_ptr.* = try StandaloneModuleGraph.fromBytes(allocator, raw_bytes, offsets);
|
||||
graph_ptr.set();
|
||||
return graph_ptr;
|
||||
return try StandaloneModuleGraph.fromBytes(allocator, to_read, offsets);
|
||||
}
|
||||
|
||||
/// heuristic: `bun build --compile` won't be supported if the name is "bun", "bunx", or "node".
|
||||
|
||||
@@ -166,7 +166,12 @@ pub const WatchEvent = struct {
|
||||
|
||||
pub fn merge(this: *WatchEvent, other: WatchEvent) void {
|
||||
this.name_len += other.name_len;
|
||||
this.op = Op.merge(this.op, other.op);
|
||||
this.op = Op{
|
||||
.delete = this.op.delete or other.op.delete,
|
||||
.metadata = this.op.metadata or other.op.metadata,
|
||||
.rename = this.op.rename or other.op.rename,
|
||||
.write = this.op.write or other.op.write,
|
||||
};
|
||||
}
|
||||
|
||||
pub const Op = packed struct(u8) {
|
||||
@@ -175,8 +180,7 @@ pub const WatchEvent = struct {
|
||||
rename: bool = false,
|
||||
write: bool = false,
|
||||
move_to: bool = false,
|
||||
create: bool = false,
|
||||
_padding: u2 = 0,
|
||||
_padding: u3 = 0,
|
||||
|
||||
pub fn merge(before: Op, after: Op) Op {
|
||||
return .{
|
||||
@@ -185,7 +189,6 @@ pub const WatchEvent = struct {
|
||||
.metadata = before.metadata or after.metadata,
|
||||
.rename = before.rename or after.rename,
|
||||
.move_to = before.move_to or after.move_to,
|
||||
.create = before.create or after.create,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ pub const AllocationScopeIn = allocation_scope.AllocationScopeIn;
|
||||
pub const NullableAllocator = @import("./allocators/NullableAllocator.zig");
|
||||
pub const MaxHeapAllocator = @import("./allocators/MaxHeapAllocator.zig");
|
||||
pub const LinuxMemFdAllocator = @import("./allocators/LinuxMemFdAllocator.zig");
|
||||
pub const BufferFallbackAllocator = @import("./allocators/BufferFallbackAllocator.zig");
|
||||
pub const MaybeOwned = @import("./allocators/maybe_owned.zig").MaybeOwned;
|
||||
|
||||
pub fn isSliceInBufferT(comptime T: type, slice: []const T, buffer: []const T) bool {
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
/// An allocator that attempts to allocate from a provided buffer first,
|
||||
/// falling back to another allocator when the buffer is exhausted.
|
||||
/// Unlike `std.heap.StackFallbackAllocator`, this does not own the buffer.
|
||||
const BufferFallbackAllocator = @This();
|
||||
|
||||
#fallback_allocator: Allocator,
|
||||
#fixed_buffer_allocator: FixedBufferAllocator,
|
||||
|
||||
pub fn init(buffer: []u8, fallback_allocator: Allocator) BufferFallbackAllocator {
|
||||
return .{
|
||||
.#fallback_allocator = fallback_allocator,
|
||||
.#fixed_buffer_allocator = FixedBufferAllocator.init(buffer),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn allocator(self: *BufferFallbackAllocator) Allocator {
|
||||
return .{
|
||||
.ptr = self,
|
||||
.vtable = &.{
|
||||
.alloc = alloc,
|
||||
.resize = resize,
|
||||
.remap = remap,
|
||||
.free = free,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn alloc(ctx: *anyopaque, len: usize, alignment: std.mem.Alignment, ra: usize) ?[*]u8 {
|
||||
const self: *BufferFallbackAllocator = @ptrCast(@alignCast(ctx));
|
||||
return FixedBufferAllocator.alloc(
|
||||
&self.#fixed_buffer_allocator,
|
||||
len,
|
||||
alignment,
|
||||
ra,
|
||||
) orelse self.#fallback_allocator.rawAlloc(len, alignment, ra);
|
||||
}
|
||||
|
||||
fn resize(ctx: *anyopaque, buf: []u8, alignment: std.mem.Alignment, new_len: usize, ra: usize) bool {
|
||||
const self: *BufferFallbackAllocator = @ptrCast(@alignCast(ctx));
|
||||
if (self.#fixed_buffer_allocator.ownsPtr(buf.ptr)) {
|
||||
return FixedBufferAllocator.resize(
|
||||
&self.#fixed_buffer_allocator,
|
||||
buf,
|
||||
alignment,
|
||||
new_len,
|
||||
ra,
|
||||
);
|
||||
}
|
||||
return self.#fallback_allocator.rawResize(buf, alignment, new_len, ra);
|
||||
}
|
||||
|
||||
fn remap(ctx: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ra: usize) ?[*]u8 {
|
||||
const self: *BufferFallbackAllocator = @ptrCast(@alignCast(ctx));
|
||||
if (self.#fixed_buffer_allocator.ownsPtr(memory.ptr)) {
|
||||
return FixedBufferAllocator.remap(
|
||||
&self.#fixed_buffer_allocator,
|
||||
memory,
|
||||
alignment,
|
||||
new_len,
|
||||
ra,
|
||||
);
|
||||
}
|
||||
return self.#fallback_allocator.rawRemap(memory, alignment, new_len, ra);
|
||||
}
|
||||
|
||||
fn free(ctx: *anyopaque, buf: []u8, alignment: std.mem.Alignment, ra: usize) void {
|
||||
const self: *BufferFallbackAllocator = @ptrCast(@alignCast(ctx));
|
||||
if (self.#fixed_buffer_allocator.ownsPtr(buf.ptr)) {
|
||||
return FixedBufferAllocator.free(
|
||||
&self.#fixed_buffer_allocator,
|
||||
buf,
|
||||
alignment,
|
||||
ra,
|
||||
);
|
||||
}
|
||||
return self.#fallback_allocator.rawFree(buf, alignment, ra);
|
||||
}
|
||||
|
||||
pub fn reset(self: *BufferFallbackAllocator) void {
|
||||
self.#fixed_buffer_allocator.reset();
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const FixedBufferAllocator = std.heap.FixedBufferAllocator;
|
||||
@@ -2,10 +2,7 @@
|
||||
|
||||
const Self = @This();
|
||||
|
||||
const safety_checks = bun.Environment.isDebug or bun.Environment.enable_asan;
|
||||
|
||||
#heap: *mimalloc.Heap,
|
||||
thread_id: if (safety_checks) std.Thread.Id else void,
|
||||
#heap: if (safety_checks) Owned(*DebugHeap) else *mimalloc.Heap,
|
||||
|
||||
/// Uses the default thread-local heap. This type is zero-sized.
|
||||
///
|
||||
@@ -23,18 +20,18 @@ pub const Default = struct {
|
||||
///
|
||||
/// This type is a `GenericAllocator`; see `src/allocators.zig`.
|
||||
pub const Borrowed = struct {
|
||||
#heap: *mimalloc.Heap,
|
||||
#heap: BorrowedHeap,
|
||||
|
||||
pub fn allocator(self: Borrowed) std.mem.Allocator {
|
||||
return .{ .ptr = self.#heap, .vtable = c_allocator_vtable };
|
||||
return .{ .ptr = self.#heap, .vtable = &c_allocator_vtable };
|
||||
}
|
||||
|
||||
pub fn getDefault() Borrowed {
|
||||
return .{ .#heap = mimalloc.mi_heap_main() };
|
||||
return .{ .#heap = getThreadHeap() };
|
||||
}
|
||||
|
||||
pub fn gc(self: Borrowed) void {
|
||||
mimalloc.mi_heap_collect(self.#heap, false);
|
||||
mimalloc.mi_heap_collect(self.getMimallocHeap(), false);
|
||||
}
|
||||
|
||||
pub fn helpCatchMemoryIssues(self: Borrowed) void {
|
||||
@@ -44,17 +41,30 @@ pub const Borrowed = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ownsPtr(self: Borrowed, ptr: *const anyopaque) bool {
|
||||
return mimalloc.mi_heap_check_owned(self.getMimallocHeap(), ptr);
|
||||
}
|
||||
|
||||
fn fromOpaque(ptr: *anyopaque) Borrowed {
|
||||
return .{ .#heap = @ptrCast(@alignCast(ptr)) };
|
||||
}
|
||||
|
||||
fn getMimallocHeap(self: Borrowed) *mimalloc.Heap {
|
||||
return if (comptime safety_checks) self.#heap.inner else self.#heap;
|
||||
}
|
||||
|
||||
fn assertThreadLock(self: Borrowed) void {
|
||||
if (comptime safety_checks) self.#heap.thread_lock.assertLocked();
|
||||
}
|
||||
|
||||
fn alignedAlloc(self: Borrowed, len: usize, alignment: Alignment) ?[*]u8 {
|
||||
log("Malloc: {d}\n", .{len});
|
||||
|
||||
const heap = self.getMimallocHeap();
|
||||
const ptr: ?*anyopaque = if (mimalloc.mustUseAlignedAlloc(alignment))
|
||||
mimalloc.mi_heap_malloc_aligned(self.#heap, len, alignment.toByteUnits())
|
||||
mimalloc.mi_heap_malloc_aligned(heap, len, alignment.toByteUnits())
|
||||
else
|
||||
mimalloc.mi_heap_malloc(self.#heap, len);
|
||||
mimalloc.mi_heap_malloc(heap, len);
|
||||
|
||||
if (comptime bun.Environment.isDebug) {
|
||||
const usable = mimalloc.mi_malloc_usable_size(ptr);
|
||||
@@ -79,17 +89,42 @@ pub const Borrowed = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const BorrowedHeap = if (safety_checks) *DebugHeap else *mimalloc.Heap;
|
||||
|
||||
const DebugHeap = struct {
|
||||
inner: *mimalloc.Heap,
|
||||
thread_lock: bun.safety.ThreadLock,
|
||||
|
||||
pub const deinit = void;
|
||||
};
|
||||
|
||||
threadlocal var thread_heap: if (safety_checks) ?DebugHeap else void = if (safety_checks) null;
|
||||
|
||||
fn getThreadHeap() BorrowedHeap {
|
||||
if (comptime !safety_checks) return mimalloc.mi_heap_get_default();
|
||||
if (thread_heap == null) {
|
||||
thread_heap = .{
|
||||
.inner = mimalloc.mi_heap_get_default(),
|
||||
.thread_lock = .initLocked(),
|
||||
};
|
||||
}
|
||||
return &thread_heap.?;
|
||||
}
|
||||
|
||||
const log = bun.Output.scoped(.mimalloc, .hidden);
|
||||
|
||||
pub fn allocator(self: Self) std.mem.Allocator {
|
||||
self.assertThreadOwnership();
|
||||
return self.borrow().allocator();
|
||||
}
|
||||
|
||||
pub fn borrow(self: Self) Borrowed {
|
||||
return .{ .#heap = self.#heap };
|
||||
return .{ .#heap = if (comptime safety_checks) self.#heap.get() else self.#heap };
|
||||
}
|
||||
|
||||
/// Internally, mimalloc calls mi_heap_get_default()
|
||||
/// to get the default heap.
|
||||
/// It uses pthread_getspecific to do that.
|
||||
/// We can save those extra calls if we just do it once in here
|
||||
pub fn getThreadLocalDefault() std.mem.Allocator {
|
||||
if (bun.Environment.enable_asan) return bun.default_allocator;
|
||||
return Borrowed.getDefault().allocator();
|
||||
@@ -122,15 +157,22 @@ pub fn dumpStats(_: Self) void {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
mimalloc.mi_heap_destroy(self.#heap);
|
||||
const mimalloc_heap = self.borrow().getMimallocHeap();
|
||||
if (comptime safety_checks) {
|
||||
self.#heap.deinit();
|
||||
}
|
||||
mimalloc.mi_heap_destroy(mimalloc_heap);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn init() Self {
|
||||
return .{
|
||||
.#heap = mimalloc.mi_heap_new() orelse bun.outOfMemory(),
|
||||
.thread_id = if (safety_checks) std.Thread.getCurrentId() else {},
|
||||
};
|
||||
const mimalloc_heap = mimalloc.mi_heap_new() orelse bun.outOfMemory();
|
||||
if (comptime !safety_checks) return .{ .#heap = mimalloc_heap };
|
||||
const heap: Owned(*DebugHeap) = .new(.{
|
||||
.inner = mimalloc_heap,
|
||||
.thread_lock = .initLocked(),
|
||||
});
|
||||
return .{ .#heap = heap };
|
||||
}
|
||||
|
||||
pub fn gc(self: Self) void {
|
||||
@@ -141,16 +183,8 @@ pub fn helpCatchMemoryIssues(self: Self) void {
|
||||
self.borrow().helpCatchMemoryIssues();
|
||||
}
|
||||
|
||||
fn assertThreadOwnership(self: Self) void {
|
||||
if (comptime safety_checks) {
|
||||
const current_thread = std.Thread.getCurrentId();
|
||||
if (current_thread != self.thread_id) {
|
||||
std.debug.panic(
|
||||
"MimallocArena used from wrong thread: arena belongs to thread {d}, but current thread is {d}",
|
||||
.{ self.thread_id, current_thread },
|
||||
);
|
||||
}
|
||||
}
|
||||
pub fn ownsPtr(self: Self, ptr: *const anyopaque) bool {
|
||||
return self.borrow().ownsPtr(ptr);
|
||||
}
|
||||
|
||||
fn alignedAllocSize(ptr: [*]u8) usize {
|
||||
@@ -159,10 +193,13 @@ fn alignedAllocSize(ptr: [*]u8) usize {
|
||||
|
||||
fn vtable_alloc(ptr: *anyopaque, len: usize, alignment: Alignment, _: usize) ?[*]u8 {
|
||||
const self: Borrowed = .fromOpaque(ptr);
|
||||
self.assertThreadLock();
|
||||
return self.alignedAlloc(len, alignment);
|
||||
}
|
||||
|
||||
fn vtable_resize(_: *anyopaque, buf: []u8, _: Alignment, new_len: usize, _: usize) bool {
|
||||
fn vtable_resize(ptr: *anyopaque, buf: []u8, _: Alignment, new_len: usize, _: usize) bool {
|
||||
const self: Borrowed = .fromOpaque(ptr);
|
||||
self.assertThreadLock();
|
||||
return mimalloc.mi_expand(buf.ptr, new_len) != null;
|
||||
}
|
||||
|
||||
@@ -186,17 +223,39 @@ fn vtable_free(
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to expand or shrink memory, allowing relocation.
|
||||
///
|
||||
/// `memory.len` must equal the length requested from the most recent
|
||||
/// successful call to `alloc`, `resize`, or `remap`. `alignment` must
|
||||
/// equal the same value that was passed as the `alignment` parameter to
|
||||
/// the original `alloc` call.
|
||||
///
|
||||
/// A non-`null` return value indicates the resize was successful. The
|
||||
/// allocation may have same address, or may have been relocated. In either
|
||||
/// case, the allocation now has size of `new_len`. A `null` return value
|
||||
/// indicates that the resize would be equivalent to allocating new memory,
|
||||
/// copying the bytes from the old memory, and then freeing the old memory.
|
||||
/// In such case, it is more efficient for the caller to perform the copy.
|
||||
///
|
||||
/// `new_len` must be greater than zero.
|
||||
///
|
||||
/// `ret_addr` is optionally provided as the first return address of the
|
||||
/// allocation call stack. If the value is `0` it means no return address
|
||||
/// has been provided.
|
||||
fn vtable_remap(ptr: *anyopaque, buf: []u8, alignment: Alignment, new_len: usize, _: usize) ?[*]u8 {
|
||||
const self: Borrowed = .fromOpaque(ptr);
|
||||
const value = mimalloc.mi_heap_realloc_aligned(self.#heap, buf.ptr, new_len, alignment.toByteUnits());
|
||||
self.assertThreadLock();
|
||||
const heap = self.getMimallocHeap();
|
||||
const aligned_size = alignment.toByteUnits();
|
||||
const value = mimalloc.mi_heap_realloc_aligned(heap, buf.ptr, new_len, aligned_size);
|
||||
return @ptrCast(value);
|
||||
}
|
||||
|
||||
pub fn isInstance(alloc: std.mem.Allocator) bool {
|
||||
return alloc.vtable == c_allocator_vtable;
|
||||
return alloc.vtable == &c_allocator_vtable;
|
||||
}
|
||||
|
||||
const c_allocator_vtable = &std.mem.Allocator.VTable{
|
||||
const c_allocator_vtable = std.mem.Allocator.VTable{
|
||||
.alloc = vtable_alloc,
|
||||
.resize = vtable_resize,
|
||||
.remap = vtable_remap,
|
||||
@@ -209,3 +268,5 @@ const Alignment = std.mem.Alignment;
|
||||
const bun = @import("bun");
|
||||
const assert = bun.assert;
|
||||
const mimalloc = bun.mimalloc;
|
||||
const Owned = bun.ptr.Owned;
|
||||
const safety_checks = bun.Environment.ci_assert;
|
||||
|
||||
@@ -60,29 +60,17 @@ pub const Heap = opaque {
|
||||
return mi_heap_realloc(self, p, newsize);
|
||||
}
|
||||
|
||||
pub fn isOwned(self: *Heap, p: ?*const anyopaque) bool {
|
||||
return mi_heap_contains(self, p);
|
||||
pub fn isOwned(self: *Heap, p: ?*anyopaque) bool {
|
||||
return mi_heap_check_owned(self, p);
|
||||
}
|
||||
};
|
||||
pub extern fn mi_heap_new() ?*Heap;
|
||||
pub extern fn mi_heap_delete(heap: *Heap) void;
|
||||
pub extern fn mi_heap_destroy(heap: *Heap) void;
|
||||
pub extern fn mi_heap_set_default(heap: *Heap) *Heap;
|
||||
pub extern fn mi_heap_get_default() *Heap;
|
||||
pub extern fn mi_heap_get_backing() *Heap;
|
||||
pub extern fn mi_heap_collect(heap: *Heap, force: bool) void;
|
||||
pub extern fn mi_heap_main() *Heap;
|
||||
|
||||
// Thread-local heap (theap) API - new in mimalloc v3
|
||||
pub const THeap = opaque {};
|
||||
pub extern fn mi_theap_get_default() *THeap;
|
||||
pub extern fn mi_theap_set_default(theap: *THeap) *THeap;
|
||||
pub extern fn mi_theap_collect(theap: *THeap, force: bool) void;
|
||||
pub extern fn mi_theap_malloc(theap: *THeap, size: usize) ?*anyopaque;
|
||||
pub extern fn mi_theap_zalloc(theap: *THeap, size: usize) ?*anyopaque;
|
||||
pub extern fn mi_theap_calloc(theap: *THeap, count: usize, size: usize) ?*anyopaque;
|
||||
pub extern fn mi_theap_malloc_small(theap: *THeap, size: usize) ?*anyopaque;
|
||||
pub extern fn mi_theap_malloc_aligned(theap: *THeap, size: usize, alignment: usize) ?*anyopaque;
|
||||
pub extern fn mi_theap_realloc(theap: *THeap, p: ?*anyopaque, newsize: usize) ?*anyopaque;
|
||||
pub extern fn mi_theap_destroy(theap: *THeap) void;
|
||||
pub extern fn mi_heap_theap(heap: *Heap) *THeap;
|
||||
pub extern fn mi_heap_malloc(heap: *Heap, size: usize) ?*anyopaque;
|
||||
pub extern fn mi_heap_zalloc(heap: *Heap, size: usize) ?*anyopaque;
|
||||
pub extern fn mi_heap_calloc(heap: *Heap, count: usize, size: usize) ?*anyopaque;
|
||||
@@ -114,7 +102,8 @@ pub extern fn mi_heap_rezalloc_aligned(heap: *Heap, p: ?*anyopaque, newsize: usi
|
||||
pub extern fn mi_heap_rezalloc_aligned_at(heap: *Heap, p: ?*anyopaque, newsize: usize, alignment: usize, offset: usize) ?*anyopaque;
|
||||
pub extern fn mi_heap_recalloc_aligned(heap: *Heap, p: ?*anyopaque, newcount: usize, size: usize, alignment: usize) ?*anyopaque;
|
||||
pub extern fn mi_heap_recalloc_aligned_at(heap: *Heap, p: ?*anyopaque, newcount: usize, size: usize, alignment: usize, offset: usize) ?*anyopaque;
|
||||
pub extern fn mi_heap_contains(heap: *const Heap, p: ?*const anyopaque) bool;
|
||||
pub extern fn mi_heap_contains_block(heap: *Heap, p: *const anyopaque) bool;
|
||||
pub extern fn mi_heap_check_owned(heap: *Heap, p: *const anyopaque) bool;
|
||||
pub extern fn mi_check_owned(p: ?*const anyopaque) bool;
|
||||
pub const struct_mi_heap_area_s = extern struct {
|
||||
blocks: ?*anyopaque,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user