mirror of
https://github.com/oven-sh/bun
synced 2026-02-25 11:07:19 +01:00
Compare commits
1 Commits
ciro/fix-h
...
dylan/npmr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a01fd06d44 |
1486
.buildkite/ci.mjs
Executable file → Normal file
1486
.buildkite/ci.mjs
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,4 +8,4 @@ function run_command() {
|
||||
{ set +x; } 2>/dev/null
|
||||
}
|
||||
|
||||
run_command node ".buildkite/ci.mjs" "$@"
|
||||
run_command node ".buildkite/ci.mjs"
|
||||
|
||||
@@ -164,9 +164,7 @@ function upload_s3_file() {
|
||||
|
||||
function send_bench_webhook() {
|
||||
if [ -z "$BENCHMARK_URL" ]; then
|
||||
echo "error: \$BENCHMARK_URL is not set"
|
||||
# exit 1 # TODO: this isn't live yet
|
||||
return
|
||||
return 1
|
||||
fi
|
||||
|
||||
local tag="$1"
|
||||
@@ -202,12 +200,6 @@ function create_release() {
|
||||
bun-linux-x64-profile.zip
|
||||
bun-linux-x64-baseline.zip
|
||||
bun-linux-x64-baseline-profile.zip
|
||||
bun-linux-aarch64-musl.zip
|
||||
bun-linux-aarch64-musl-profile.zip
|
||||
bun-linux-x64-musl.zip
|
||||
bun-linux-x64-musl-profile.zip
|
||||
bun-linux-x64-musl-baseline.zip
|
||||
bun-linux-x64-musl-baseline-profile.zip
|
||||
bun-windows-x64.zip
|
||||
bun-windows-x64-profile.zip
|
||||
bun-windows-x64-baseline.zip
|
||||
|
||||
92
.github/workflows/update-cares.yml
vendored
92
.github/workflows/update-cares.yml
vendored
@@ -1,92 +0,0 @@
|
||||
name: Update c-ares
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 4 * * 0"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check-update:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Check c-ares version
|
||||
id: check-version
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Extract the commit hash from the line after COMMIT
|
||||
CURRENT_VERSION=$(awk '/[[:space:]]*COMMIT[[:space:]]*$/{getline; gsub(/^[[:space:]]+|[[:space:]]+$/,"",$0); print}' cmake/targets/BuildCares.cmake)
|
||||
|
||||
if [ -z "$CURRENT_VERSION" ]; then
|
||||
echo "Error: Could not find COMMIT line in BuildCares.cmake"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate that it looks like a git hash
|
||||
if ! [[ $CURRENT_VERSION =~ ^[0-9a-f]{40}$ ]]; then
|
||||
echo "Error: Invalid git hash format in BuildCares.cmake"
|
||||
echo "Found: $CURRENT_VERSION"
|
||||
echo "Expected: 40 character hexadecimal string"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "current=$CURRENT_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
LATEST_RELEASE=$(curl -sL https://api.github.com/repos/c-ares/c-ares/releases/latest)
|
||||
if [ -z "$LATEST_RELEASE" ]; then
|
||||
echo "Error: Failed to fetch latest release from GitHub API"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LATEST_TAG=$(echo "$LATEST_RELEASE" | jq -r '.tag_name')
|
||||
if [ -z "$LATEST_TAG" ] || [ "$LATEST_TAG" = "null" ]; then
|
||||
echo "Error: Could not extract tag name from GitHub API response"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LATEST_SHA=$(curl -sL "https://api.github.com/repos/c-ares/c-ares/git/ref/tags/$LATEST_TAG" | jq -r '.object.sha')
|
||||
if [ -z "$LATEST_SHA" ] || [ "$LATEST_SHA" = "null" ]; then
|
||||
echo "Error: Could not fetch SHA for tag $LATEST_TAG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ $LATEST_SHA =~ ^[0-9a-f]{40}$ ]]; then
|
||||
echo "Error: Invalid SHA format received from GitHub"
|
||||
echo "Found: $LATEST_SHA"
|
||||
echo "Expected: 40 character hexadecimal string"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "latest=$LATEST_SHA" >> $GITHUB_OUTPUT
|
||||
echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update version if needed
|
||||
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Handle multi-line format where COMMIT and its value are on separate lines
|
||||
sed -i -E '/[[:space:]]*COMMIT[[:space:]]*$/{n;s/[[:space:]]*([0-9a-f]+)[[:space:]]*$/ ${{ steps.check-version.outputs.latest }}/}' cmake/targets/BuildCares.cmake
|
||||
|
||||
- name: Create Pull Request
|
||||
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
add-paths: |
|
||||
cmake/targets/BuildCares.cmake
|
||||
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-${{ github.run_number }}
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
Updates c-ares to version ${{ steps.check-version.outputs.tag }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-cares.yml)
|
||||
92
.github/workflows/update-libarchive.yml
vendored
92
.github/workflows/update-libarchive.yml
vendored
@@ -1,92 +0,0 @@
|
||||
name: Update libarchive
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 3 * * 0"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check-update:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Check libarchive version
|
||||
id: check-version
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Extract the commit hash from the line after COMMIT
|
||||
CURRENT_VERSION=$(awk '/[[:space:]]*COMMIT[[:space:]]*$/{getline; gsub(/^[[:space:]]+|[[:space:]]+$/,"",$0); print}' cmake/targets/BuildLibArchive.cmake)
|
||||
|
||||
if [ -z "$CURRENT_VERSION" ]; then
|
||||
echo "Error: Could not find COMMIT line in BuildLibArchive.cmake"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate that it looks like a git hash
|
||||
if ! [[ $CURRENT_VERSION =~ ^[0-9a-f]{40}$ ]]; then
|
||||
echo "Error: Invalid git hash format in BuildLibArchive.cmake"
|
||||
echo "Found: $CURRENT_VERSION"
|
||||
echo "Expected: 40 character hexadecimal string"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "current=$CURRENT_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
LATEST_RELEASE=$(curl -sL https://api.github.com/repos/libarchive/libarchive/releases/latest)
|
||||
if [ -z "$LATEST_RELEASE" ]; then
|
||||
echo "Error: Failed to fetch latest release from GitHub API"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LATEST_TAG=$(echo "$LATEST_RELEASE" | jq -r '.tag_name')
|
||||
if [ -z "$LATEST_TAG" ] || [ "$LATEST_TAG" = "null" ]; then
|
||||
echo "Error: Could not extract tag name from GitHub API response"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LATEST_SHA=$(curl -sL "https://api.github.com/repos/libarchive/libarchive/git/ref/tags/$LATEST_TAG" | jq -r '.object.sha')
|
||||
if [ -z "$LATEST_SHA" ] || [ "$LATEST_SHA" = "null" ]; then
|
||||
echo "Error: Could not fetch SHA for tag $LATEST_TAG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ $LATEST_SHA =~ ^[0-9a-f]{40}$ ]]; then
|
||||
echo "Error: Invalid SHA format received from GitHub"
|
||||
echo "Found: $LATEST_SHA"
|
||||
echo "Expected: 40 character hexadecimal string"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "latest=$LATEST_SHA" >> $GITHUB_OUTPUT
|
||||
echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update version if needed
|
||||
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Handle multi-line format where COMMIT and its value are on separate lines
|
||||
sed -i -E '/[[:space:]]*COMMIT[[:space:]]*$/{n;s/[[:space:]]*([0-9a-f]+)[[:space:]]*$/ ${{ steps.check-version.outputs.latest }}/}' cmake/targets/BuildLibArchive.cmake
|
||||
|
||||
- name: Create Pull Request
|
||||
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
add-paths: |
|
||||
cmake/targets/BuildLibArchive.cmake
|
||||
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-${{ github.run_number }}
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
Updates libarchive to version ${{ steps.check-version.outputs.tag }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-libarchive.yml)
|
||||
92
.github/workflows/update-libdeflate.yml
vendored
92
.github/workflows/update-libdeflate.yml
vendored
@@ -1,92 +0,0 @@
|
||||
name: Update libdeflate
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 2 * * 0"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check-update:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Check libdeflate version
|
||||
id: check-version
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Extract the commit hash from the line after COMMIT
|
||||
CURRENT_VERSION=$(awk '/[[:space:]]*COMMIT[[:space:]]*$/{getline; gsub(/^[[:space:]]+|[[:space:]]+$/,"",$0); print}' cmake/targets/BuildLibDeflate.cmake)
|
||||
|
||||
if [ -z "$CURRENT_VERSION" ]; then
|
||||
echo "Error: Could not find COMMIT line in BuildLibDeflate.cmake"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate that it looks like a git hash
|
||||
if ! [[ $CURRENT_VERSION =~ ^[0-9a-f]{40}$ ]]; then
|
||||
echo "Error: Invalid git hash format in BuildLibDeflate.cmake"
|
||||
echo "Found: $CURRENT_VERSION"
|
||||
echo "Expected: 40 character hexadecimal string"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "current=$CURRENT_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
LATEST_RELEASE=$(curl -sL https://api.github.com/repos/ebiggers/libdeflate/releases/latest)
|
||||
if [ -z "$LATEST_RELEASE" ]; then
|
||||
echo "Error: Failed to fetch latest release from GitHub API"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LATEST_TAG=$(echo "$LATEST_RELEASE" | jq -r '.tag_name')
|
||||
if [ -z "$LATEST_TAG" ] || [ "$LATEST_TAG" = "null" ]; then
|
||||
echo "Error: Could not extract tag name from GitHub API response"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LATEST_SHA=$(curl -sL "https://api.github.com/repos/ebiggers/libdeflate/git/ref/tags/$LATEST_TAG" | jq -r '.object.sha')
|
||||
if [ -z "$LATEST_SHA" ] || [ "$LATEST_SHA" = "null" ]; then
|
||||
echo "Error: Could not fetch SHA for tag $LATEST_TAG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ $LATEST_SHA =~ ^[0-9a-f]{40}$ ]]; then
|
||||
echo "Error: Invalid SHA format received from GitHub"
|
||||
echo "Found: $LATEST_SHA"
|
||||
echo "Expected: 40 character hexadecimal string"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "latest=$LATEST_SHA" >> $GITHUB_OUTPUT
|
||||
echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update version if needed
|
||||
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Handle multi-line format where COMMIT and its value are on separate lines
|
||||
sed -i -E '/[[:space:]]*COMMIT[[:space:]]*$/{n;s/[[:space:]]*([0-9a-f]+)[[:space:]]*$/ ${{ steps.check-version.outputs.latest }}/}' cmake/targets/BuildLibDeflate.cmake
|
||||
|
||||
- name: Create Pull Request
|
||||
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
add-paths: |
|
||||
cmake/targets/BuildLibDeflate.cmake
|
||||
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-${{ github.run_number }}
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
Updates libdeflate to version ${{ steps.check-version.outputs.tag }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-libdeflate.yml)
|
||||
92
.github/workflows/update-lolhtml.yml
vendored
92
.github/workflows/update-lolhtml.yml
vendored
@@ -1,92 +0,0 @@
|
||||
name: Update lolhtml
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 1 * * 0"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check-update:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Check lolhtml version
|
||||
id: check-version
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Extract the commit hash from the line after COMMIT
|
||||
CURRENT_VERSION=$(awk '/[[:space:]]*COMMIT[[:space:]]*$/{getline; gsub(/^[[:space:]]+|[[:space:]]+$/,"",$0); print}' cmake/targets/BuildLolHtml.cmake)
|
||||
|
||||
if [ -z "$CURRENT_VERSION" ]; then
|
||||
echo "Error: Could not find COMMIT line in BuildLolHtml.cmake"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate that it looks like a git hash
|
||||
if ! [[ $CURRENT_VERSION =~ ^[0-9a-f]{40}$ ]]; then
|
||||
echo "Error: Invalid git hash format in BuildLolHtml.cmake"
|
||||
echo "Found: $CURRENT_VERSION"
|
||||
echo "Expected: 40 character hexadecimal string"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "current=$CURRENT_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
LATEST_RELEASE=$(curl -sL https://api.github.com/repos/cloudflare/lol-html/releases/latest)
|
||||
if [ -z "$LATEST_RELEASE" ]; then
|
||||
echo "Error: Failed to fetch latest release from GitHub API"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LATEST_TAG=$(echo "$LATEST_RELEASE" | jq -r '.tag_name')
|
||||
if [ -z "$LATEST_TAG" ] || [ "$LATEST_TAG" = "null" ]; then
|
||||
echo "Error: Could not extract tag name from GitHub API response"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LATEST_SHA=$(curl -sL "https://api.github.com/repos/cloudflare/lol-html/git/ref/tags/$LATEST_TAG" | jq -r '.object.sha')
|
||||
if [ -z "$LATEST_SHA" ] || [ "$LATEST_SHA" = "null" ]; then
|
||||
echo "Error: Could not fetch SHA for tag $LATEST_TAG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ $LATEST_SHA =~ ^[0-9a-f]{40}$ ]]; then
|
||||
echo "Error: Invalid SHA format received from GitHub"
|
||||
echo "Found: $LATEST_SHA"
|
||||
echo "Expected: 40 character hexadecimal string"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "latest=$LATEST_SHA" >> $GITHUB_OUTPUT
|
||||
echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update version if needed
|
||||
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Handle multi-line format where COMMIT and its value are on separate lines
|
||||
sed -i -E '/[[:space:]]*COMMIT[[:space:]]*$/{n;s/[[:space:]]*([0-9a-f]+)[[:space:]]*$/ ${{ steps.check-version.outputs.latest }}/}' cmake/targets/BuildLolHtml.cmake
|
||||
|
||||
- name: Create Pull Request
|
||||
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
add-paths: |
|
||||
cmake/targets/BuildLolHtml.cmake
|
||||
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-${{ github.run_number }}
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
Updates lolhtml to version ${{ steps.check-version.outputs.tag }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-lolhtml.yml)
|
||||
92
.github/workflows/update-lshpack.yml
vendored
92
.github/workflows/update-lshpack.yml
vendored
@@ -1,92 +0,0 @@
|
||||
name: Update lshpack
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 5 * * 0"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check-update:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Check lshpack version
|
||||
id: check-version
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Extract the commit hash from the line after COMMIT
|
||||
CURRENT_VERSION=$(awk '/[[:space:]]*COMMIT[[:space:]]*$/{getline; gsub(/^[[:space:]]+|[[:space:]]+$/,"",$0); print}' cmake/targets/BuildLshpack.cmake)
|
||||
|
||||
if [ -z "$CURRENT_VERSION" ]; then
|
||||
echo "Error: Could not find COMMIT line in BuildLshpack.cmake"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate that it looks like a git hash
|
||||
if ! [[ $CURRENT_VERSION =~ ^[0-9a-f]{40}$ ]]; then
|
||||
echo "Error: Invalid git hash format in BuildLshpack.cmake"
|
||||
echo "Found: $CURRENT_VERSION"
|
||||
echo "Expected: 40 character hexadecimal string"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "current=$CURRENT_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
LATEST_RELEASE=$(curl -sL https://api.github.com/repos/litespeedtech/ls-hpack/releases/latest)
|
||||
if [ -z "$LATEST_RELEASE" ]; then
|
||||
echo "Error: Failed to fetch latest release from GitHub API"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LATEST_TAG=$(echo "$LATEST_RELEASE" | jq -r '.tag_name')
|
||||
if [ -z "$LATEST_TAG" ] || [ "$LATEST_TAG" = "null" ]; then
|
||||
echo "Error: Could not extract tag name from GitHub API response"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LATEST_SHA=$(curl -sL "https://api.github.com/repos/litespeedtech/ls-hpack/git/ref/tags/$LATEST_TAG" | jq -r '.object.sha')
|
||||
if [ -z "$LATEST_SHA" ] || [ "$LATEST_SHA" = "null" ]; then
|
||||
echo "Error: Could not fetch SHA for tag $LATEST_TAG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ $LATEST_SHA =~ ^[0-9a-f]{40}$ ]]; then
|
||||
echo "Error: Invalid SHA format received from GitHub"
|
||||
echo "Found: $LATEST_SHA"
|
||||
echo "Expected: 40 character hexadecimal string"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "latest=$LATEST_SHA" >> $GITHUB_OUTPUT
|
||||
echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update version if needed
|
||||
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Handle multi-line format where COMMIT and its value are on separate lines
|
||||
sed -i -E '/[[:space:]]*COMMIT[[:space:]]*$/{n;s/[[:space:]]*([0-9a-f]+)[[:space:]]*$/ ${{ steps.check-version.outputs.latest }}/}' cmake/targets/BuildLshpack.cmake
|
||||
|
||||
- name: Create Pull Request
|
||||
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
add-paths: |
|
||||
cmake/targets/BuildLshpack.cmake
|
||||
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-${{ github.run_number }}
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
Updates lshpack to version ${{ steps.check-version.outputs.tag }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-lshpack.yml)
|
||||
109
.github/workflows/update-sqlite3.yml
vendored
109
.github/workflows/update-sqlite3.yml
vendored
@@ -1,109 +0,0 @@
|
||||
name: Update SQLite3
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 6 * * 0" # Run weekly
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check-update:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Check SQLite version
|
||||
id: check-version
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Get current version from the header file using SQLITE_VERSION_NUMBER
|
||||
CURRENT_VERSION_NUM=$(grep -o '#define SQLITE_VERSION_NUMBER [0-9]\+' src/bun.js/bindings/sqlite/sqlite3_local.h | awk '{print $3}' | tr -d '\n\r')
|
||||
if [ -z "$CURRENT_VERSION_NUM" ]; then
|
||||
echo "Error: Could not find SQLITE_VERSION_NUMBER in sqlite3_local.h"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Convert numeric version to semantic version for display
|
||||
CURRENT_MAJOR=$((CURRENT_VERSION_NUM / 1000000))
|
||||
CURRENT_MINOR=$((($CURRENT_VERSION_NUM / 1000) % 1000))
|
||||
CURRENT_PATCH=$((CURRENT_VERSION_NUM % 1000))
|
||||
CURRENT_VERSION="$CURRENT_MAJOR.$CURRENT_MINOR.$CURRENT_PATCH"
|
||||
|
||||
echo "current=$CURRENT_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "current_num=$CURRENT_VERSION_NUM" >> $GITHUB_OUTPUT
|
||||
|
||||
# Fetch SQLite download page
|
||||
DOWNLOAD_PAGE=$(curl -sL https://sqlite.org/download.html)
|
||||
if [ -z "$DOWNLOAD_PAGE" ]; then
|
||||
echo "Error: Failed to fetch SQLite download page"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract latest version and year from the amalgamation link
|
||||
LATEST_INFO=$(echo "$DOWNLOAD_PAGE" | grep -o 'sqlite-amalgamation-[0-9]\{7\}.zip' | head -n1)
|
||||
LATEST_YEAR=$(echo "$DOWNLOAD_PAGE" | grep -o '[0-9]\{4\}/sqlite-amalgamation-[0-9]\{7\}.zip' | head -n1 | cut -d'/' -f1 | tr -d '\n\r')
|
||||
LATEST_VERSION_NUM=$(echo "$LATEST_INFO" | grep -o '[0-9]\{7\}' | tr -d '\n\r')
|
||||
|
||||
if [ -z "$LATEST_VERSION_NUM" ] || [ -z "$LATEST_YEAR" ]; then
|
||||
echo "Error: Could not extract latest version info"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Convert numeric version to semantic version for display
|
||||
LATEST_MAJOR=$((10#$LATEST_VERSION_NUM / 1000000))
|
||||
LATEST_MINOR=$((($LATEST_VERSION_NUM / 1000) % 1000))
|
||||
LATEST_PATCH=$((10#$LATEST_VERSION_NUM % 1000))
|
||||
LATEST_VERSION="$LATEST_MAJOR.$LATEST_MINOR.$LATEST_PATCH"
|
||||
|
||||
echo "latest=$LATEST_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "latest_year=$LATEST_YEAR" >> $GITHUB_OUTPUT
|
||||
echo "latest_num=$LATEST_VERSION_NUM" >> $GITHUB_OUTPUT
|
||||
|
||||
# Debug output
|
||||
echo "Current version: $CURRENT_VERSION ($CURRENT_VERSION_NUM)"
|
||||
echo "Latest version: $LATEST_VERSION ($LATEST_VERSION_NUM)"
|
||||
|
||||
- name: Update SQLite if needed
|
||||
if: success() && steps.check-version.outputs.current_num < steps.check-version.outputs.latest_num
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
cd $TEMP_DIR
|
||||
|
||||
echo "Downloading from: https://sqlite.org/${{ steps.check-version.outputs.latest_year }}/sqlite-amalgamation-${{ steps.check-version.outputs.latest_num }}.zip"
|
||||
|
||||
# Download and extract latest version
|
||||
wget "https://sqlite.org/${{ steps.check-version.outputs.latest_year }}/sqlite-amalgamation-${{ steps.check-version.outputs.latest_num }}.zip"
|
||||
unzip "sqlite-amalgamation-${{ steps.check-version.outputs.latest_num }}.zip"
|
||||
cd "sqlite-amalgamation-${{ steps.check-version.outputs.latest_num }}"
|
||||
|
||||
# Add header comment and copy files
|
||||
echo "// clang-format off" > $GITHUB_WORKSPACE/src/bun.js/bindings/sqlite/sqlite3.c
|
||||
cat sqlite3.c >> $GITHUB_WORKSPACE/src/bun.js/bindings/sqlite/sqlite3.c
|
||||
|
||||
echo "// clang-format off" > $GITHUB_WORKSPACE/src/bun.js/bindings/sqlite/sqlite3_local.h
|
||||
cat sqlite3.h >> $GITHUB_WORKSPACE/src/bun.js/bindings/sqlite/sqlite3_local.h
|
||||
|
||||
- name: Create Pull Request
|
||||
if: success() && steps.check-version.outputs.current_num < steps.check-version.outputs.latest_num
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
add-paths: |
|
||||
src/bun.js/bindings/sqlite/sqlite3.c
|
||||
src/bun.js/bindings/sqlite/sqlite3_local.h
|
||||
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-${{ steps.check-version.outputs.latest }}
|
||||
body: |
|
||||
## What does this PR do?
|
||||
|
||||
Updates SQLite to version ${{ steps.check-version.outputs.latest }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-sqlite3.yml)
|
||||
19
.gitignore
vendored
19
.gitignore
vendored
@@ -26,7 +26,6 @@
|
||||
*.db
|
||||
*.dmg
|
||||
*.dSYM
|
||||
*.generated.ts
|
||||
*.jsb
|
||||
*.lib
|
||||
*.log
|
||||
@@ -54,8 +53,8 @@
|
||||
/test-report.md
|
||||
/test.js
|
||||
/test.ts
|
||||
/test.zig
|
||||
/testdir
|
||||
/test.zig
|
||||
build
|
||||
build.ninja
|
||||
bun-binary
|
||||
@@ -112,10 +111,8 @@ pnpm-lock.yaml
|
||||
profile.json
|
||||
README.md.template
|
||||
release/
|
||||
scripts/env.local
|
||||
sign.*.json
|
||||
sign.json
|
||||
src/bake/generated.ts
|
||||
src/bun.js/bindings-obj
|
||||
src/bun.js/bindings/GeneratedJS2Native.zig
|
||||
src/bun.js/debug-bindings-obj
|
||||
@@ -134,7 +131,6 @@ src/runtime.version
|
||||
src/tests.zig
|
||||
test.txt
|
||||
test/js/bun/glob/fixtures
|
||||
test/node.js/upstream
|
||||
tsconfig.tsbuildinfo
|
||||
txt.js
|
||||
x64
|
||||
@@ -146,9 +142,6 @@ test/node.js/upstream
|
||||
scripts/env.local
|
||||
*.generated.ts
|
||||
src/bake/generated.ts
|
||||
test/cli/install/registry/packages/publish-pkg-*
|
||||
test/cli/install/registry/packages/@secret/publish-pkg-8
|
||||
test/js/third_party/prisma/prisma/sqlite/dev.db-journal
|
||||
|
||||
# Dependencies
|
||||
/vendor
|
||||
@@ -156,24 +149,22 @@ test/js/third_party/prisma/prisma/sqlite/dev.db-journal
|
||||
# Dependencies (before CMake)
|
||||
# These can be removed in the far future
|
||||
/src/bun.js/WebKit
|
||||
/src/deps/WebKit
|
||||
/src/deps/boringssl
|
||||
/src/deps/brotli
|
||||
/src/deps/c*ares
|
||||
/src/deps/lol*html
|
||||
/src/deps/libarchive
|
||||
/src/deps/libdeflate
|
||||
/src/deps/libuv
|
||||
/src/deps/lol*html
|
||||
/src/deps/ls*hpack
|
||||
/src/deps/mimalloc
|
||||
/src/deps/picohttpparser
|
||||
/src/deps/tinycc
|
||||
/src/deps/WebKit
|
||||
/src/deps/zig
|
||||
/src/deps/zlib
|
||||
/src/deps/zstd
|
||||
/src/deps/zlib
|
||||
/src/deps/zig
|
||||
|
||||
# Generated files
|
||||
|
||||
.buildkite/ci.yml
|
||||
*.sock
|
||||
scratch*.{js,ts,tsx,cjs,mjs}
|
||||
@@ -5,5 +5,6 @@ test/js/deno
|
||||
test/node.js
|
||||
src/react-refresh.js
|
||||
*.min.js
|
||||
test/js/node/test/fixtures
|
||||
test/js/node/test/common
|
||||
test/snippets
|
||||
test/js/node/test
|
||||
|
||||
5
.vscode/launch.json
generated
vendored
5
.vscode/launch.json
generated
vendored
@@ -224,11 +224,8 @@
|
||||
"cwd": "${fileDirname}",
|
||||
"env": {
|
||||
"FORCE_COLOR": "1",
|
||||
// "BUN_DEBUG_DEBUGGER": "1",
|
||||
// "BUN_DEBUG_INTERNAL_DEBUGGER": "1",
|
||||
"BUN_DEBUG_QUIET_LOGS": "1",
|
||||
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
|
||||
// "BUN_INSPECT": "ws+unix:///var/folders/jk/8fzl9l5119598vsqrmphsw7m0000gn/T/tl15npi7qtf.sock?report=1",
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
@@ -1257,4 +1254,4 @@
|
||||
"description": "Usage: bun test [...]",
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -78,7 +78,7 @@
|
||||
"prettier.prettierPath": "./node_modules/prettier",
|
||||
|
||||
// TypeScript
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"typescript.tsdk": "${workspaceFolder}/node_modules/typescript/lib",
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
},
|
||||
|
||||
93
.vscode/tasks.json
vendored
93
.vscode/tasks.json
vendored
@@ -2,57 +2,50 @@
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build Bun",
|
||||
"type": "shell",
|
||||
"command": "bun run build",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true,
|
||||
"type": "process",
|
||||
"label": "Install Dependencies",
|
||||
"command": "scripts/all-dependencies.sh",
|
||||
"windows": {
|
||||
"command": "scripts/all-dependencies.ps1",
|
||||
},
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "zig",
|
||||
"fileLocation": ["relative", "${workspaceFolder}"],
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^(.+?):(\\d+):(\\d+): (error|warning|note): (.+)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"severity": 4,
|
||||
"message": 5,
|
||||
},
|
||||
{
|
||||
"regexp": "^\\s+(.+)$",
|
||||
"message": 1,
|
||||
"loop": true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"owner": "clang",
|
||||
"fileLocation": ["relative", "${workspaceFolder}"],
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^([^:]+):(\\d+):(\\d+):\\s+(warning|error|note|remark):\\s+(.*)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"severity": 4,
|
||||
"message": 5,
|
||||
},
|
||||
{
|
||||
"regexp": "^\\s*(.*)$",
|
||||
"message": 1,
|
||||
"loop": true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "shared",
|
||||
"clear": true,
|
||||
"icon": {
|
||||
"id": "arrow-down",
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}",
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "process",
|
||||
"label": "Setup Environment",
|
||||
"dependsOn": ["Install Dependencies"],
|
||||
"command": "scripts/setup.sh",
|
||||
"windows": {
|
||||
"command": "scripts/setup.ps1",
|
||||
},
|
||||
"icon": {
|
||||
"id": "check",
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}",
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "process",
|
||||
"label": "Build Bun",
|
||||
"dependsOn": ["Setup Environment"],
|
||||
"command": "bun",
|
||||
"args": ["run", "build"],
|
||||
"icon": {
|
||||
"id": "gear",
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}",
|
||||
},
|
||||
"isBuildCommand": true,
|
||||
"runOptions": {
|
||||
"instanceLimit": 1,
|
||||
"reevaluateOnRerun": true,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Configuring a development environment for Bun can take 10-30 minutes depending on your internet connection and computer speed. You will need ~10GB of free disk space for the repository and build artifacts.
|
||||
|
||||
If you are using Windows, please refer to [this guide](/docs/project/building-windows.md)
|
||||
If you are using Windows, please refer to [this guide](/docs/project/building-windows)
|
||||
|
||||
{% details summary="For Ubuntu users" %}
|
||||
TL;DR: Ubuntu 22.04 is suggested.
|
||||
@@ -11,7 +11,7 @@ Bun currently requires `glibc >=2.32` in development which means if you're on Ub
|
||||
|
||||
Using your system's package manager, install Bun's dependencies:
|
||||
|
||||
{% codetabs group="os" %}
|
||||
{% codetabs %}
|
||||
|
||||
```bash#macOS (Homebrew)
|
||||
$ brew install automake ccache cmake coreutils gnu-sed go icu4c libiconv libtool ninja pkg-config rust ruby
|
||||
@@ -60,7 +60,7 @@ $ brew install bun
|
||||
|
||||
Bun requires LLVM 16 (`clang` is part of LLVM). This version requirement is to match WebKit (precompiled), as mismatching versions will cause memory allocation failures at runtime. In most cases, you can install LLVM through your system package manager:
|
||||
|
||||
{% codetabs group="os" %}
|
||||
{% codetabs %}
|
||||
|
||||
```bash#macOS (Homebrew)
|
||||
$ brew install llvm@18
|
||||
@@ -97,7 +97,7 @@ $ which clang-16
|
||||
|
||||
If not, run this to manually add it:
|
||||
|
||||
{% codetabs group="os" %}
|
||||
{% codetabs %}
|
||||
|
||||
```bash#macOS (Homebrew)
|
||||
# use fish_add_path if you're using fish
|
||||
|
||||
@@ -414,15 +414,6 @@ pub fn addBunObject(b: *Build, opts: *BunBuildOptions) *Compile {
|
||||
}
|
||||
addInternalPackages(b, obj, opts);
|
||||
obj.root_module.addImport("build_options", opts.buildOptionsModule(b));
|
||||
|
||||
const translate_plugin_api = b.addTranslateC(.{
|
||||
.root_source_file = b.path("./packages/bun-native-bundler-plugin-api/bundler_plugin.h"),
|
||||
.target = opts.target,
|
||||
.optimize = opts.optimize,
|
||||
.link_libc = true,
|
||||
});
|
||||
obj.root_module.addImport("bun-native-bundler-plugin-api", translate_plugin_api.createModule());
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
ARG IMAGE=debian:11
|
||||
FROM $IMAGE
|
||||
COPY ./scripts/bootstrap.sh /tmp/bootstrap.sh
|
||||
ENV CI=true
|
||||
RUN sh /tmp/bootstrap.sh && rm -rf /tmp/*
|
||||
WORKDIR /workspace/bun
|
||||
COPY bunfig.toml bunfig.toml
|
||||
COPY package.json package.json
|
||||
COPY CMakeLists.txt CMakeLists.txt
|
||||
COPY cmake/ cmake/
|
||||
COPY scripts/ scripts/
|
||||
COPY patches/ patches/
|
||||
COPY *.zig ./
|
||||
COPY src/ src/
|
||||
COPY packages/ packages/
|
||||
COPY test/ test/
|
||||
RUN bun i
|
||||
RUN bun run build:ci
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script sets the hostname of the current machine.
|
||||
|
||||
execute() {
|
||||
echo "$ $@" >&2
|
||||
if ! "$@"; then
|
||||
echo "Command failed: $@" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <hostname>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f "$(which hostnamectl)" ]; then
|
||||
execute hostnamectl set-hostname "$1"
|
||||
else
|
||||
echo "Error: hostnamectl is not installed." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script starts tailscale on the current machine.
|
||||
|
||||
execute() {
|
||||
echo "$ $@" >&2
|
||||
if ! "$@"; then
|
||||
echo "Command failed: $@" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <auth-key>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
execute tailscale up --reset --ssh --accept-risk=lose-ssh --auth-key="$1"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -2,7 +2,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"bootstrap": "brew install gh jq cirruslabs/cli/tart cirruslabs/cli/sshpass hashicorp/tap/packer && packer init darwin",
|
||||
"login": "token=$(gh auth token); username=$(gh api user --jq .login); echo \"Login as $username...\"; echo \"$token\" | tart login ghcr.io --username \"$username\" --password-stdin; echo \"$token\" | docker login ghcr.io --username \"$username\" --password-stdin",
|
||||
"login": "gh auth token | tart login ghcr.io --username $(gh api user --jq .login) --password-stdin",
|
||||
"fetch:image-name": "echo ghcr.io/oven-sh/bun-vm",
|
||||
"fetch:darwin-version": "echo 1",
|
||||
"fetch:macos-version": "sw_vers -productVersion | cut -d. -f1",
|
||||
|
||||
@@ -265,7 +265,7 @@ if(ENABLE_LTO)
|
||||
endif()
|
||||
|
||||
# --- Remapping ---
|
||||
if(UNIX AND CI)
|
||||
if(UNIX)
|
||||
register_compiler_flags(
|
||||
DESCRIPTION "Remap source files"
|
||||
-ffile-prefix-map=${CWD}=.
|
||||
|
||||
@@ -105,6 +105,14 @@ else()
|
||||
unsupported(CMAKE_HOST_SYSTEM_NAME)
|
||||
endif()
|
||||
|
||||
if(EXISTS "/lib/ld-musl-aarch64.so.1")
|
||||
set(IS_MUSL ON)
|
||||
elseif(EXISTS "/lib/ld-musl-x86_64.so.1")
|
||||
set(IS_MUSL ON)
|
||||
else()
|
||||
set(IS_MUSL OFF)
|
||||
endif()
|
||||
|
||||
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "arm64|ARM64|aarch64|AARCH64")
|
||||
set(HOST_OS "aarch64")
|
||||
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "x86_64|X86_64|x64|X64|amd64|AMD64")
|
||||
|
||||
@@ -10,6 +10,7 @@ optionx(GITHUB_ACTIONS BOOL "If GitHub Actions is enabled" DEFAULT OFF)
|
||||
|
||||
if(BUILDKITE)
|
||||
optionx(BUILDKITE_COMMIT STRING "The commit hash")
|
||||
optionx(BUILDKITE_MESSAGE STRING "The commit message")
|
||||
endif()
|
||||
|
||||
optionx(CMAKE_BUILD_TYPE "Debug|Release|RelWithDebInfo|MinSizeRel" "The build type to use" REQUIRED)
|
||||
@@ -20,7 +21,7 @@ else()
|
||||
setx(RELEASE OFF)
|
||||
endif()
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
if(CMAKE_BUILD_TYPE MATCHES "Debug|RelWithDebInfo")
|
||||
setx(DEBUG ON)
|
||||
else()
|
||||
setx(DEBUG OFF)
|
||||
@@ -48,16 +49,6 @@ else()
|
||||
message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
|
||||
if(LINUX)
|
||||
if(EXISTS "/etc/alpine-release")
|
||||
set(DEFAULT_ABI "musl")
|
||||
else()
|
||||
set(DEFAULT_ABI "gnu")
|
||||
endif()
|
||||
|
||||
optionx(ABI "musl|gnu" "The ABI to use (e.g. musl, gnu)" DEFAULT ${DEFAULT_ABI})
|
||||
endif()
|
||||
|
||||
if(ARCH STREQUAL "x64")
|
||||
optionx(ENABLE_BASELINE BOOL "If baseline features should be used for older CPUs (e.g. disables AVX, AVX2)" DEFAULT OFF)
|
||||
endif()
|
||||
@@ -65,7 +56,14 @@ endif()
|
||||
optionx(ENABLE_LOGS BOOL "If debug logs should be enabled" DEFAULT ${DEBUG})
|
||||
optionx(ENABLE_ASSERTIONS BOOL "If debug assertions should be enabled" DEFAULT ${DEBUG})
|
||||
|
||||
optionx(ENABLE_CANARY BOOL "If canary features should be enabled" DEFAULT ON)
|
||||
if(BUILDKITE_MESSAGE AND BUILDKITE_MESSAGE MATCHES "\\[release build\\]")
|
||||
message(STATUS "Switched to release build, since commit message contains: \"[release build]\"")
|
||||
set(DEFAULT_CANARY OFF)
|
||||
else()
|
||||
set(DEFAULT_CANARY ON)
|
||||
endif()
|
||||
|
||||
optionx(ENABLE_CANARY BOOL "If canary features should be enabled" DEFAULT ${DEFAULT_CANARY})
|
||||
|
||||
if(ENABLE_CANARY AND BUILDKITE)
|
||||
execute_process(
|
||||
|
||||
@@ -484,12 +484,14 @@ set(BUN_ZIG_OUTPUT ${BUILD_PATH}/bun-zig.o)
|
||||
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|ARM|arm64|ARM64|aarch64|AARCH64")
|
||||
set(IS_ARM64 ON)
|
||||
if(APPLE)
|
||||
set(ZIG_CPU "apple_m1")
|
||||
else()
|
||||
set(ZIG_CPU "native")
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|X86_64|x64|X64|amd64|AMD64")
|
||||
set(IS_X86_64 ON)
|
||||
if(ENABLE_BASELINE)
|
||||
set(ZIG_CPU "nehalem")
|
||||
else()
|
||||
@@ -526,7 +528,6 @@ register_command(
|
||||
-Dcanary=${CANARY_REVISION}
|
||||
-Dcodegen_path=${CODEGEN_PATH}
|
||||
-Dcodegen_embed=$<IF:$<BOOL:${CODEGEN_EMBED}>,true,false>
|
||||
--prominent-compile-errors
|
||||
${ZIG_FLAGS_BUN}
|
||||
ARTIFACTS
|
||||
${BUN_ZIG_OUTPUT}
|
||||
@@ -759,8 +760,8 @@ if(NOT WIN32)
|
||||
)
|
||||
if(DEBUG)
|
||||
# TODO: this shouldn't be necessary long term
|
||||
if (NOT ABI STREQUAL "musl")
|
||||
target_compile_options(${bun} PUBLIC
|
||||
if (NOT IS_MUSL)
|
||||
set(ABI_PUBLIC_FLAGS
|
||||
-fsanitize=null
|
||||
-fsanitize-recover=all
|
||||
-fsanitize=bounds
|
||||
@@ -771,9 +772,14 @@ if(NOT WIN32)
|
||||
-fsanitize=returns-nonnull-attribute
|
||||
-fsanitize=unreachable
|
||||
)
|
||||
target_link_libraries(${bun} PRIVATE
|
||||
set(ABI_PRIVATE_FLAGS
|
||||
-fsanitize=null
|
||||
)
|
||||
else()
|
||||
set(ABI_PUBLIC_FLAGS
|
||||
)
|
||||
set(ABI_PRIVATE_FLAGS
|
||||
)
|
||||
endif()
|
||||
|
||||
target_compile_options(${bun} PUBLIC
|
||||
@@ -791,6 +797,10 @@ if(NOT WIN32)
|
||||
-Wno-unused-function
|
||||
-Wno-nullability-completeness
|
||||
-Werror
|
||||
${ABI_PUBLIC_FLAGS}
|
||||
)
|
||||
target_link_libraries(${bun} PRIVATE
|
||||
${ABI_PRIVATE_FLAGS}
|
||||
)
|
||||
else()
|
||||
# Leave -Werror=unused off in release builds so we avoid errors from being used in ASSERT
|
||||
@@ -835,48 +845,70 @@ if(WIN32)
|
||||
/delayload:IPHLPAPI.dll
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
elseif(APPLE)
|
||||
target_link_options(${bun} PUBLIC
|
||||
-dead_strip
|
||||
-dead_strip_dylibs
|
||||
-Wl,-ld_new
|
||||
-Wl,-no_compact_unwind
|
||||
-Wl,-stack_size,0x1200000
|
||||
-fno-keep-static-consts
|
||||
-Wl,-map,${bun}.linker-map
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
# Try to use lld-16 if available, otherwise fallback to lld
|
||||
# Cache it so we don't have to re-run CMake to pick it up
|
||||
if((NOT DEFINED LLD_NAME) AND (NOT CI OR BUN_LINK_ONLY))
|
||||
find_program(LLD_EXECUTABLE_NAME lld-${LLVM_VERSION_MAJOR})
|
||||
|
||||
if(LINUX)
|
||||
if(NOT ABI STREQUAL "musl")
|
||||
if(ARCH STREQUAL "aarch64")
|
||||
target_link_options(${bun} PUBLIC
|
||||
-Wl,--wrap=fcntl64
|
||||
-Wl,--wrap=statx
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ARCH STREQUAL "x64")
|
||||
target_link_options(${bun} PUBLIC
|
||||
-Wl,--wrap=fcntl
|
||||
-Wl,--wrap=fcntl64
|
||||
-Wl,--wrap=fstat
|
||||
-Wl,--wrap=fstat64
|
||||
-Wl,--wrap=fstatat
|
||||
-Wl,--wrap=fstatat64
|
||||
-Wl,--wrap=lstat
|
||||
-Wl,--wrap=lstat64
|
||||
-Wl,--wrap=mknod
|
||||
-Wl,--wrap=mknodat
|
||||
-Wl,--wrap=stat
|
||||
-Wl,--wrap=stat64
|
||||
-Wl,--wrap=statx
|
||||
)
|
||||
if(NOT LLD_EXECUTABLE_NAME)
|
||||
if(CI)
|
||||
# Ensure we don't use a differing version of lld in CI vs clang
|
||||
message(FATAL_ERROR "lld-${LLVM_VERSION_MAJOR} not found. Please make sure you have LLVM ${LLVM_VERSION_MAJOR}.x installed and set to lld-${LLVM_VERSION_MAJOR}")
|
||||
endif()
|
||||
|
||||
# To make it easier for contributors, allow differing versions of lld vs clang/cmake
|
||||
find_program(LLD_EXECUTABLE_NAME lld)
|
||||
endif()
|
||||
|
||||
target_link_options(${bun} PUBLIC
|
||||
if(NOT LLD_EXECUTABLE_NAME)
|
||||
message(FATAL_ERROR "LLD not found. Please make sure you have LLVM ${LLVM_VERSION_MAJOR}.x installed and lld is available in your PATH as lld-${LLVM_VERSION_MAJOR}")
|
||||
endif()
|
||||
|
||||
# normalize to basename so it can be used with -fuse-ld
|
||||
get_filename_component(LLD_NAME ${LLD_EXECUTABLE_NAME} NAME CACHE)
|
||||
message(STATUS "Using linker: ${LLD_NAME} (${LLD_EXECUTABLE_NAME})")
|
||||
elseif(NOT DEFINED LLD_NAME)
|
||||
set(LLD_NAME lld-${LLVM_VERSION_MAJOR})
|
||||
endif()
|
||||
|
||||
if (NOT IS_MUSL)
|
||||
if (IS_ARM64)
|
||||
set(ARCH_WRAP_FLAGS
|
||||
-Wl,--wrap=fcntl64
|
||||
-Wl,--wrap=statx
|
||||
)
|
||||
elseif(IS_X86_64)
|
||||
set(ARCH_WRAP_FLAGS
|
||||
-Wl,--wrap=fcntl
|
||||
-Wl,--wrap=fcntl64
|
||||
-Wl,--wrap=fstat
|
||||
-Wl,--wrap=fstat64
|
||||
-Wl,--wrap=fstatat
|
||||
-Wl,--wrap=fstatat64
|
||||
-Wl,--wrap=lstat
|
||||
-Wl,--wrap=lstat64
|
||||
-Wl,--wrap=mknod
|
||||
-Wl,--wrap=mknodat
|
||||
-Wl,--wrap=stat
|
||||
-Wl,--wrap=stat64
|
||||
-Wl,--wrap=statx
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
set(ARCH_WRAP_FLAGS
|
||||
)
|
||||
endif()
|
||||
|
||||
if (NOT IS_MUSL)
|
||||
set(ABI_WRAP_FLAGS
|
||||
-Wl,--wrap=cosf
|
||||
-Wl,--wrap=exp
|
||||
-Wl,--wrap=expf
|
||||
@@ -893,37 +925,26 @@ if(LINUX)
|
||||
-Wl,--wrap=sinf
|
||||
-Wl,--wrap=tanf
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT ABI STREQUAL "musl")
|
||||
target_link_options(${bun} PUBLIC
|
||||
-static-libstdc++
|
||||
-static-libgcc
|
||||
)
|
||||
else()
|
||||
target_link_options(${bun} PUBLIC
|
||||
-lstdc++
|
||||
-lgcc
|
||||
set(ABI_WRAP_FLAGS
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_options(${bun} PUBLIC
|
||||
--ld-path=${LLD_PROGRAM}
|
||||
-fuse-ld=${LLD_NAME}
|
||||
-fno-pic
|
||||
-static-libstdc++
|
||||
-static-libgcc
|
||||
-Wl,-no-pie
|
||||
-Wl,-icf=safe
|
||||
-Wl,--as-needed
|
||||
-Wl,--gc-sections
|
||||
-Wl,-z,stack-size=12800000
|
||||
${ARCH_WRAP_FLAGS}
|
||||
${ABI_WRAP_FLAGS}
|
||||
-Wl,--compress-debug-sections=zlib
|
||||
-Wl,-z,lazy
|
||||
-Wl,-z,norelro
|
||||
-Wl,-z,combreloc
|
||||
-Wl,--no-eh-frame-hdr
|
||||
-Wl,--sort-section=name
|
||||
-Wl,--hash-style=gnu
|
||||
-Wl,--build-id=sha1 # Better for debugging than default
|
||||
-Wl,-Map=${bun}.linker-map
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -1063,18 +1084,6 @@ endif()
|
||||
# --- Packaging ---
|
||||
|
||||
if(NOT BUN_CPP_ONLY)
|
||||
set(CMAKE_STRIP_FLAGS "")
|
||||
if(APPLE)
|
||||
# We do not build with exceptions enabled. These are generated by lolhtml
|
||||
# and other dependencies. We build lolhtml with abort on panic, so it
|
||||
# shouldn't be including these in the first place.
|
||||
set(CMAKE_STRIP_FLAGS --remove-section=__TEXT,__eh_frame --remove-section=__TEXT,__unwind_info --remove-section=__TEXT,__gcc_except_tab)
|
||||
elseif(LINUX AND NOT ABI STREQUAL "musl")
|
||||
# When you use llvm-strip to do this, it doesn't delete it from the binary and instead keeps it as [LOAD #2 [R]]
|
||||
# So, we must use GNU strip to do this.
|
||||
set(CMAKE_STRIP_FLAGS -R .eh_frame -R .gcc_except_table)
|
||||
endif()
|
||||
|
||||
if(bunStrip)
|
||||
register_command(
|
||||
TARGET
|
||||
@@ -1086,7 +1095,6 @@ if(NOT BUN_CPP_ONLY)
|
||||
COMMAND
|
||||
${CMAKE_STRIP}
|
||||
${bunExe}
|
||||
${CMAKE_STRIP_FLAGS}
|
||||
--strip-all
|
||||
--strip-debug
|
||||
--discard-all
|
||||
@@ -1162,12 +1170,10 @@ if(NOT BUN_CPP_ONLY)
|
||||
endif()
|
||||
|
||||
if(CI)
|
||||
set(bunTriplet bun-${OS}-${ARCH})
|
||||
if(LINUX AND ABI STREQUAL "musl")
|
||||
set(bunTriplet ${bunTriplet}-musl)
|
||||
endif()
|
||||
if(ENABLE_BASELINE)
|
||||
set(bunTriplet ${bunTriplet}-baseline)
|
||||
set(bunTriplet bun-${OS}-${ARCH}-baseline)
|
||||
else()
|
||||
set(bunTriplet bun-${OS}-${ARCH})
|
||||
endif()
|
||||
string(REPLACE bun ${bunTriplet} bunPath ${bun})
|
||||
set(bunFiles ${bunExe} features.json)
|
||||
@@ -1176,12 +1182,6 @@ if(NOT BUN_CPP_ONLY)
|
||||
elseif(APPLE)
|
||||
list(APPEND bunFiles ${bun}.dSYM)
|
||||
endif()
|
||||
|
||||
if(APPLE OR LINUX)
|
||||
list(APPEND bunFiles ${bun}.linker-map)
|
||||
endif()
|
||||
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
${bun}
|
||||
|
||||
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
c-ares/c-ares
|
||||
COMMIT
|
||||
41ee334af3e3d0027dca5e477855d0244936bd49
|
||||
d1722e6e8acaf10eb73fa995798a9cd421d9f85e
|
||||
)
|
||||
|
||||
register_cmake_command(
|
||||
|
||||
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
ebiggers/libdeflate
|
||||
COMMIT
|
||||
9d624d1d8ba82c690d6d6be1d0a961fc5a983ea4
|
||||
dc76454a39e7e83b68c3704b6e3784654f8d5ac5
|
||||
)
|
||||
|
||||
register_cmake_command(
|
||||
|
||||
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
cloudflare/lol-html
|
||||
COMMIT
|
||||
4f8becea13a0021c8b71abd2dcc5899384973b66
|
||||
8d4c273ded322193d017042d1f48df2766b0f88b
|
||||
)
|
||||
|
||||
set(LOLHTML_CWD ${VENDOR_PATH}/lolhtml/c-api)
|
||||
@@ -26,13 +26,6 @@ if(RELEASE)
|
||||
list(APPEND LOLHTML_BUILD_ARGS --release)
|
||||
endif()
|
||||
|
||||
# Windows requires unwind tables, apparently.
|
||||
if (NOT WIN32)
|
||||
# The encoded escape sequences are intentional. They're how you delimit multiple arguments in a single environment variable.
|
||||
# Also add rust optimization flag for smaller binary size, but not huge speed penalty.
|
||||
set(RUSTFLAGS "-Cpanic=abort-Cdebuginfo=0-Cforce-unwind-tables=no-Copt-level=s")
|
||||
endif()
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
lolhtml
|
||||
@@ -44,13 +37,6 @@ register_command(
|
||||
${LOLHTML_BUILD_ARGS}
|
||||
ARTIFACTS
|
||||
${LOLHTML_LIBRARY}
|
||||
ENVIRONMENT
|
||||
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
|
||||
litespeedtech/ls-hpack
|
||||
COMMIT
|
||||
32e96f10593c7cb8553cd8c9c12721100ae9e924
|
||||
3d0f1fc1d6e66a642e7a98c55deb38aa986eb4b0
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||
set(ABI musl)
|
||||
|
||||
set(CMAKE_C_COMPILER_WORKS ON)
|
||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||
@@ -1,6 +1,5 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||
set(ABI gnu)
|
||||
|
||||
set(CMAKE_C_COMPILER_WORKS ON)
|
||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||
@@ -1,7 +1,6 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x64)
|
||||
set(ENABLE_BASELINE ON)
|
||||
set(ABI gnu)
|
||||
|
||||
set(CMAKE_C_COMPILER_WORKS ON)
|
||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||
@@ -1,7 +0,0 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x64)
|
||||
set(ENABLE_BASELINE ON)
|
||||
set(ABI musl)
|
||||
|
||||
set(CMAKE_C_COMPILER_WORKS ON)
|
||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||
@@ -1,6 +0,0 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x64)
|
||||
set(ABI musl)
|
||||
|
||||
set(CMAKE_C_COMPILER_WORKS ON)
|
||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||
@@ -1,6 +1,5 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x64)
|
||||
set(ABI gnu)
|
||||
|
||||
set(CMAKE_C_COMPILER_WORKS ON)
|
||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||
|
||||
@@ -29,7 +29,7 @@ execute_process(
|
||||
)
|
||||
|
||||
if(NOT GIT_DIFF_RESULT EQUAL 0)
|
||||
message(WARNING "Command failed: ${GIT_DIFF_COMMAND} ${GIT_DIFF_ERROR}")
|
||||
message(${WARNING} "Command failed: ${GIT_DIFF_COMMAND} ${GIT_DIFF_ERROR}")
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ if(NOT ENABLE_LLVM)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(CMAKE_HOST_WIN32 OR CMAKE_HOST_APPLE OR EXISTS "/etc/alpine-release")
|
||||
if(CMAKE_HOST_WIN32 OR CMAKE_HOST_APPLE OR IS_MUSL)
|
||||
set(DEFAULT_LLVM_VERSION "18.1.8")
|
||||
else()
|
||||
set(DEFAULT_LLVM_VERSION "16.0.6")
|
||||
@@ -52,7 +52,6 @@ if(UNIX)
|
||||
/usr/lib/llvm-${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}/bin
|
||||
/usr/lib/llvm-${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}/bin
|
||||
/usr/lib/llvm-${LLVM_VERSION_MAJOR}/bin
|
||||
/usr/lib/llvm${LLVM_VERSION_MAJOR}/bin
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
@@ -109,23 +108,8 @@ else()
|
||||
find_llvm_command(CMAKE_CXX_COMPILER clang++)
|
||||
find_llvm_command(CMAKE_LINKER llvm-link)
|
||||
find_llvm_command(CMAKE_AR llvm-ar)
|
||||
if (LINUX)
|
||||
# On Linux, strip ends up being more useful for us.
|
||||
find_command(
|
||||
VARIABLE
|
||||
CMAKE_STRIP
|
||||
COMMAND
|
||||
strip
|
||||
REQUIRED
|
||||
ON
|
||||
)
|
||||
else()
|
||||
find_llvm_command(CMAKE_STRIP llvm-strip)
|
||||
endif()
|
||||
find_llvm_command(CMAKE_STRIP llvm-strip)
|
||||
find_llvm_command(CMAKE_RANLIB llvm-ranlib)
|
||||
if(LINUX)
|
||||
find_llvm_command(LLD_PROGRAM ld.lld)
|
||||
endif()
|
||||
if(APPLE)
|
||||
find_llvm_command(CMAKE_DSYMUTIL dsymutil)
|
||||
endif()
|
||||
|
||||
@@ -1,42 +1,15 @@
|
||||
if(DEFINED ENV{CARGO_HOME})
|
||||
set(CARGO_HOME $ENV{CARGO_HOME})
|
||||
elseif(CMAKE_HOST_WIN32)
|
||||
set(CARGO_HOME $ENV{USERPROFILE}/.cargo)
|
||||
if(NOT EXISTS ${CARGO_HOME})
|
||||
set(CARGO_HOME $ENV{PROGRAMFILES}/Rust/cargo)
|
||||
endif()
|
||||
else()
|
||||
set(CARGO_HOME $ENV{HOME}/.cargo)
|
||||
endif()
|
||||
|
||||
if(DEFINED ENV{RUSTUP_HOME})
|
||||
set(RUSTUP_HOME $ENV{RUSTUP_HOME})
|
||||
elseif(CMAKE_HOST_WIN32)
|
||||
set(RUSTUP_HOME $ENV{USERPROFILE}/.rustup)
|
||||
if(NOT EXISTS ${RUSTUP_HOME})
|
||||
set(RUSTUP_HOME $ENV{PROGRAMFILES}/Rust/rustup)
|
||||
endif()
|
||||
else()
|
||||
set(RUSTUP_HOME $ENV{HOME}/.rustup)
|
||||
endif()
|
||||
|
||||
find_command(
|
||||
VARIABLE
|
||||
CARGO_EXECUTABLE
|
||||
COMMAND
|
||||
cargo
|
||||
PATHS
|
||||
${CARGO_HOME}/bin
|
||||
$ENV{HOME}/.cargo/bin
|
||||
REQUIRED
|
||||
OFF
|
||||
)
|
||||
|
||||
if(EXISTS ${CARGO_EXECUTABLE})
|
||||
if(CARGO_EXECUTABLE MATCHES "^${CARGO_HOME}")
|
||||
setx(CARGO_HOME ${CARGO_HOME})
|
||||
setx(RUSTUP_HOME ${RUSTUP_HOME})
|
||||
endif()
|
||||
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ 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 8f9ae4f01a047c666ef548864294e01df731d4ea)
|
||||
set(WEBKIT_VERSION 3bc4abf2d5875baf500b4687ef869987f6d19e00)
|
||||
endif()
|
||||
|
||||
if(WEBKIT_LOCAL)
|
||||
@@ -63,7 +63,7 @@ else()
|
||||
message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
|
||||
if(LINUX AND ABI STREQUAL "musl")
|
||||
if(IS_MUSL)
|
||||
set(WEBKIT_SUFFIX "-musl")
|
||||
endif()
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ if(APPLE)
|
||||
elseif(WIN32)
|
||||
set(DEFAULT_ZIG_TARGET ${DEFAULT_ZIG_ARCH}-windows-msvc)
|
||||
elseif(LINUX)
|
||||
if(ABI STREQUAL "musl")
|
||||
if(IS_MUSL)
|
||||
set(DEFAULT_ZIG_TARGET ${DEFAULT_ZIG_ARCH}-linux-musl)
|
||||
else()
|
||||
set(DEFAULT_ZIG_TARGET ${DEFAULT_ZIG_ARCH}-linux-gnu)
|
||||
|
||||
@@ -1,13 +1,30 @@
|
||||
FROM alpine:3.20 AS build
|
||||
FROM alpine:3.18 AS build
|
||||
|
||||
# https://github.com/oven-sh/bun/releases
|
||||
ARG BUN_VERSION=latest
|
||||
|
||||
RUN apk --no-cache add ca-certificates curl dirmngr gpg gpg-agent unzip \
|
||||
# TODO: Instead of downloading glibc from a third-party source, we should
|
||||
# build it from source. This is a temporary solution.
|
||||
# See: https://github.com/sgerrand/alpine-pkg-glibc
|
||||
|
||||
# https://github.com/sgerrand/alpine-pkg-glibc/releases
|
||||
# https://github.com/sgerrand/alpine-pkg-glibc/issues/176
|
||||
ARG GLIBC_VERSION=2.34-r0
|
||||
|
||||
# https://github.com/oven-sh/bun/issues/5545#issuecomment-1722461083
|
||||
ARG GLIBC_VERSION_AARCH64=2.26-r1
|
||||
|
||||
RUN apk --no-cache add \
|
||||
ca-certificates \
|
||||
curl \
|
||||
dirmngr \
|
||||
gpg \
|
||||
gpg-agent \
|
||||
unzip \
|
||||
&& arch="$(apk --print-arch)" \
|
||||
&& case "${arch##*-}" in \
|
||||
x86_64) build="x64-musl-baseline";; \
|
||||
aarch64) build="aarch64-musl";; \
|
||||
x86_64) build="x64-baseline";; \
|
||||
aarch64) build="aarch64";; \
|
||||
*) echo "error: unsupported architecture: $arch"; exit 1 ;; \
|
||||
esac \
|
||||
&& version="$BUN_VERSION" \
|
||||
@@ -42,9 +59,37 @@ RUN apk --no-cache add ca-certificates curl dirmngr gpg gpg-agent unzip \
|
||||
&& unzip "bun-linux-$build.zip" \
|
||||
&& mv "bun-linux-$build/bun" /usr/local/bin/bun \
|
||||
&& rm -f "bun-linux-$build.zip" SHASUMS256.txt.asc SHASUMS256.txt \
|
||||
&& chmod +x /usr/local/bin/bun
|
||||
&& chmod +x /usr/local/bin/bun \
|
||||
&& cd /tmp \
|
||||
&& case "${arch##*-}" in \
|
||||
x86_64) curl "https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VERSION}/glibc-${GLIBC_VERSION}.apk" \
|
||||
-fsSLO \
|
||||
--compressed \
|
||||
--retry 5 \
|
||||
|| (echo "error: failed to download: glibc v${GLIBC_VERSION}" && exit 1) \
|
||||
&& mv "glibc-${GLIBC_VERSION}.apk" glibc.apk \
|
||||
&& curl "https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VERSION}/glibc-bin-${GLIBC_VERSION}.apk" \
|
||||
-fsSLO \
|
||||
--compressed \
|
||||
--retry 5 \
|
||||
|| (echo "error: failed to download: glibc-bin v${GLIBC_VERSION}" && exit 1) \
|
||||
&& mv "glibc-bin-${GLIBC_VERSION}.apk" glibc-bin.apk ;; \
|
||||
aarch64) curl "https://raw.githubusercontent.com/squishyu/alpine-pkg-glibc-aarch64-bin/master/glibc-${GLIBC_VERSION_AARCH64}.apk" \
|
||||
-fsSLO \
|
||||
--compressed \
|
||||
--retry 5 \
|
||||
|| (echo "error: failed to download: glibc v${GLIBC_VERSION_AARCH64}" && exit 1) \
|
||||
&& mv "glibc-${GLIBC_VERSION_AARCH64}.apk" glibc.apk \
|
||||
&& curl "https://raw.githubusercontent.com/squishyu/alpine-pkg-glibc-aarch64-bin/master/glibc-bin-${GLIBC_VERSION_AARCH64}.apk" \
|
||||
-fsSLO \
|
||||
--compressed \
|
||||
--retry 5 \
|
||||
|| (echo "error: failed to download: glibc-bin v${GLIBC_VERSION_AARCH64}" && exit 1) \
|
||||
&& mv "glibc-bin-${GLIBC_VERSION_AARCH64}.apk" glibc-bin.apk ;; \
|
||||
*) echo "error: unsupported architecture '$arch'"; exit 1 ;; \
|
||||
esac
|
||||
|
||||
FROM alpine:3.20
|
||||
FROM alpine:3.18
|
||||
|
||||
# Disable the runtime transpiler cache by default inside Docker containers.
|
||||
# On ephemeral containers, the cache is not useful
|
||||
@@ -62,8 +107,10 @@ COPY docker-entrypoint.sh /usr/local/bin/
|
||||
RUN --mount=type=bind,from=build,source=/tmp,target=/tmp \
|
||||
addgroup -g 1000 bun \
|
||||
&& adduser -u 1000 -G bun -s /bin/sh -D bun \
|
||||
&& apk --no-cache --force-overwrite --allow-untrusted add \
|
||||
/tmp/glibc.apk \
|
||||
/tmp/glibc-bin.apk \
|
||||
&& ln -s /usr/local/bin/bun /usr/local/bin/bunx \
|
||||
&& apk add libgcc libstdc++ \
|
||||
&& which bun \
|
||||
&& which bunx \
|
||||
&& bun --version
|
||||
|
||||
@@ -75,7 +75,7 @@ To instead throw an error when a parameter is missing and allow binding without
|
||||
import { Database } from "bun:sqlite";
|
||||
|
||||
const strict = new Database(
|
||||
":memory:",
|
||||
":memory:",
|
||||
{ strict: true }
|
||||
);
|
||||
|
||||
@@ -177,7 +177,7 @@ const query = db.prepare("SELECT * FROM foo WHERE bar = ?");
|
||||
|
||||
## WAL mode
|
||||
|
||||
SQLite supports [write-ahead log mode](https://www.sqlite.org/wal.html) (WAL) which dramatically improves performance, especially in situations with many concurrent readers and a single writer. It's broadly recommended to enable WAL mode for most typical applications.
|
||||
SQLite supports [write-ahead log mode](https://www.sqlite.org/wal.html) (WAL) which dramatically improves performance, especially in situations with many concurrent writes. It's broadly recommended to enable WAL mode for most typical applications.
|
||||
|
||||
To enable WAL mode, run this pragma query at the beginning of your application:
|
||||
|
||||
|
||||
@@ -55,51 +55,6 @@ $ bun test ./test/specific-file.test.ts
|
||||
|
||||
The test runner runs all tests in a single process. It loads all `--preload` scripts (see [Lifecycle](https://bun.sh/docs/test/lifecycle) for details), then runs all tests. If a test fails, the test runner will exit with a non-zero exit code.
|
||||
|
||||
## CI/CD integration
|
||||
|
||||
`bun test` supports a variety of CI/CD integrations.
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
`bun test` automatically detects if it's running inside GitHub Actions and will emit GitHub Actions annotations to the console directly.
|
||||
|
||||
No configuration is needed, other than installing `bun` in the workflow and running `bun test`.
|
||||
|
||||
#### How to install `bun` in a GitHub Actions workflow
|
||||
|
||||
To use `bun test` in a GitHub Actions workflow, add the following step:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
build:
|
||||
name: build-app
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
- name: Install dependencies # (assuming your project has dependencies)
|
||||
run: bun install # You can use npm/yarn/pnpm instead if you prefer
|
||||
- name: Run tests
|
||||
run: bun test
|
||||
```
|
||||
|
||||
From there, you'll get GitHub Actions annotations.
|
||||
|
||||
### JUnit XML reports (GitLab, etc.)
|
||||
|
||||
To use `bun test` with a JUnit XML reporter, you can use the `--reporter=junit` in combination with `--reporter-outfile`.
|
||||
|
||||
```sh
|
||||
$ bun test --reporter=junit --reporter-outfile=./bun.xml
|
||||
```
|
||||
|
||||
This will continue to output to stdout/stderr as usual, and also write a JUnit
|
||||
XML report to the given path at the very end of the test run.
|
||||
|
||||
JUnit XML is a popular format for reporting test results in CI/CD pipelines.
|
||||
|
||||
## Timeouts
|
||||
|
||||
Use the `--timeout` flag to specify a _per-test_ timeout in milliseconds. If a test times out, it will be marked as failed. The default value is `5000`.
|
||||
@@ -126,7 +81,7 @@ Use the `--bail` flag to abort the test run early after a pre-determined number
|
||||
$ bun test --bail
|
||||
|
||||
# bail after 10 failure
|
||||
$ bun test --bail=10
|
||||
$ bun test --bail 10
|
||||
```
|
||||
|
||||
## Watch mode
|
||||
|
||||
11
docs/dev/bundev.md
Normal file
11
docs/dev/bundev.md
Normal file
@@ -0,0 +1,11 @@
|
||||
- pages
|
||||
- auto-bundle dependencies
|
||||
- pages is function that returns a list of pages?
|
||||
- plugins for svelte and vue
|
||||
- custom loaders
|
||||
- HMR
|
||||
- server endpoints
|
||||
|
||||
```ts
|
||||
Bun.serve({});
|
||||
```
|
||||
31
docs/dev/cra.md
Normal file
31
docs/dev/cra.md
Normal file
@@ -0,0 +1,31 @@
|
||||
To create a new React app:
|
||||
|
||||
```bash
|
||||
$ bun create react ./app
|
||||
$ cd app
|
||||
$ bun dev # start dev server
|
||||
```
|
||||
|
||||
To use an existing React app:
|
||||
|
||||
```bash
|
||||
$ bun add -d react-refresh # install React Fast Refresh
|
||||
$ bun bun ./src/index.js # generate a bundle for your entry point(s)
|
||||
$ bun dev # start the dev server
|
||||
```
|
||||
|
||||
From there, Bun relies on the filesystem for mapping dev server paths to source files. All URL paths are relative to the project root (where `package.json` is located).
|
||||
|
||||
Here are examples of routing source code file paths:
|
||||
|
||||
| Dev Server URL | File Path (relative to cwd) |
|
||||
| -------------------------- | --------------------------- |
|
||||
| /src/components/Button.tsx | src/components/Button.tsx |
|
||||
| /src/index.tsx | src/index.tsx |
|
||||
| /pages/index.js | pages/index.js |
|
||||
|
||||
You do not need to include file extensions in `import` paths. CommonJS-style import paths without the file extension work.
|
||||
|
||||
You can override the public directory by passing `--public-dir="path-to-folder"`.
|
||||
|
||||
If no directory is specified and `./public/` doesn’t exist, Bun will try `./static/`. If `./static/` does not exist, but won’t serve from a public directory. If you pass `--public-dir=./` Bun will serve from the current directory, but it will check the current directory last instead of first.
|
||||
77
docs/dev/css.md
Normal file
77
docs/dev/css.md
Normal file
@@ -0,0 +1,77 @@
|
||||
## With `bun dev`
|
||||
|
||||
When importing CSS in JavaScript-like loaders, CSS is treated special.
|
||||
|
||||
By default, Bun will transform a statement like this:
|
||||
|
||||
```js
|
||||
import "../styles/global.css";
|
||||
```
|
||||
|
||||
### When `platform` is `browser`
|
||||
|
||||
```js
|
||||
globalThis.document?.dispatchEvent(
|
||||
new CustomEvent("onimportcss", {
|
||||
detail: "http://localhost:3000/styles/globals.css",
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
An event handler for turning that into a `<link>` is automatically registered when HMR is enabled. That event handler can be turned off either in a framework’s `package.json` or by setting `globalThis["Bun_disableCSSImports"] = true;` in client-side code. Additionally, you can get a list of every .css file imported this way via `globalThis["__BUN"].allImportedStyles`.
|
||||
|
||||
### When `platform` is `bun`
|
||||
|
||||
```js
|
||||
//@import url("http://localhost:3000/styles/globals.css");
|
||||
```
|
||||
|
||||
Additionally, Bun exposes an API for SSR/SSG that returns a flat list of URLs to css files imported. That function is `Bun.getImportedStyles()`.
|
||||
|
||||
```ts
|
||||
// This specifically is for "framework" in package.json when loaded via `bun dev`
|
||||
// This API needs to be changed somewhat to work more generally with Bun.js
|
||||
// Initially, you could only use Bun.js through `bun dev`
|
||||
// and this API was created at that time
|
||||
addEventListener("fetch", async (event: FetchEvent) => {
|
||||
let route = Bun.match(event);
|
||||
const App = await import("pages/_app");
|
||||
|
||||
// This returns all .css files that were imported in the line above.
|
||||
// It’s recursive, so any file that imports a CSS file will be included.
|
||||
const appStylesheets = bun.getImportedStyles();
|
||||
|
||||
// ...rest of code
|
||||
});
|
||||
```
|
||||
|
||||
This is useful for preventing flash of unstyled content.
|
||||
|
||||
## With `bun bun`
|
||||
|
||||
Bun bundles `.css` files imported via `@import` into a single file. It doesn’t auto-prefix or minify CSS today. Multiple `.css` files imported in one JavaScript file will _not_ be bundled into one file. You’ll have to import those from a `.css` file.
|
||||
|
||||
This input:
|
||||
|
||||
```css
|
||||
@import url("./hi.css");
|
||||
@import url("./hello.css");
|
||||
@import url("./yo.css");
|
||||
```
|
||||
|
||||
Becomes:
|
||||
|
||||
```css
|
||||
/* hi.css */
|
||||
/* ...contents of hi.css */
|
||||
/* hello.css */
|
||||
/* ...contents of hello.css */
|
||||
/* yo.css */
|
||||
/* ...contents of yo.css */
|
||||
```
|
||||
|
||||
## CSS runtime
|
||||
|
||||
To support hot CSS reloading, Bun inserts `@supports` annotations into CSS that tag which files a stylesheet is composed of. Browsers ignore this, so it doesn’t impact styles.
|
||||
|
||||
By default, Bun’s runtime code automatically listens to `onimportcss` and will insert the `event.detail` into a `<link rel="stylesheet" href={${event.detail}}>` if there is no existing `link` tag with that stylesheet. That’s how Bun’s equivalent of `style-loader` works.
|
||||
26
docs/dev/discord.md
Normal file
26
docs/dev/discord.md
Normal file
@@ -0,0 +1,26 @@
|
||||
## Creating a Discord bot with Bun
|
||||
|
||||
Discord bots perform actions in response to _application commands_. There are 3 types of commands accessible in different interfaces: the chat input, a message's context menu (top-right menu or right-clicking in a message), and a user's context menu (right-clicking on a user).
|
||||
|
||||
To get started you can use the interactions template:
|
||||
|
||||
```bash
|
||||
bun create discord-interactions my-interactions-bot
|
||||
cd my-interactions-bot
|
||||
```
|
||||
|
||||
If you don't have a Discord bot/application yet, you can create one [here (https://discord.com/developers/applications/me)](https://discord.com/developers/applications/me).
|
||||
|
||||
Invite bot to your server by visiting `https://discord.com/api/oauth2/authorize?client_id=<your_application_id>&scope=bot%20applications.commands`
|
||||
|
||||
Afterwards you will need to get your bot's token, public key, and application id from the application page and put them into `.env.example` file
|
||||
|
||||
Then you can run the http server that will handle your interactions:
|
||||
|
||||
```bash
|
||||
$ bun install
|
||||
$ mv .env.example .env
|
||||
$ bun run.js # listening on port 1337
|
||||
```
|
||||
|
||||
Discord does not accept an insecure HTTP server, so you will need to provide an SSL certificate or put the interactions server behind a secure reverse proxy. For development, you can use ngrok/cloudflare tunnel to expose local ports as secure URL.
|
||||
@@ -63,4 +63,4 @@ process.on("exit", kill);
|
||||
|
||||
---
|
||||
|
||||
Bun has also implemented the `node:cluster` module, but this is a faster, simple, and limited alternative.
|
||||
At the time of writing, Bun hasn't implemented the `node:cluster` module yet, but this is a faster, simple, and limited alternative. We will also implement `node:cluster` in the future.
|
||||
|
||||
@@ -16,7 +16,7 @@ Set these variables in a `.env` file.
|
||||
Bun reads the following files automatically (listed in order of increasing precedence).
|
||||
|
||||
- `.env`
|
||||
- `.env.production`, `.env.development`, `.env.test` (depending on value of `NODE_ENV`)
|
||||
- `.env.production` or `.env.development` (depending on value of `NODE_ENV`)
|
||||
- `.env.local`
|
||||
|
||||
```txt#.env
|
||||
|
||||
@@ -14,7 +14,7 @@ To bail after a certain threshold of failures, optionally specify a number after
|
||||
|
||||
```sh
|
||||
# bail after 10 failures
|
||||
$ bun test --bail=10
|
||||
$ bun test --bail 10
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -30,6 +30,7 @@ Bun implements the vast majority of Jest's matchers, but compatibility isn't 100
|
||||
|
||||
Some notable missing features:
|
||||
|
||||
- `expect().toMatchInlineSnapshot()`
|
||||
- `expect().toHaveReturned()`
|
||||
|
||||
---
|
||||
@@ -56,7 +57,7 @@ Replace `bail` in your Jest config with the `--bail` CLI flag.
|
||||
``` -->
|
||||
|
||||
```sh
|
||||
$ bun test --bail=3
|
||||
$ bun test --bail 3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -4,6 +4,10 @@ name: Use snapshot testing in `bun test`
|
||||
|
||||
Bun's test runner supports Jest-style snapshot testing via `.toMatchSnapshot()`.
|
||||
|
||||
{% callout %}
|
||||
The `.toMatchInlineSnapshot()` method is not yet supported.
|
||||
{% /callout %}
|
||||
|
||||
```ts#snap.test.ts
|
||||
import { test, expect } from "bun:test";
|
||||
|
||||
@@ -92,4 +96,4 @@ Ran 1 tests across 1 files. [102.00ms]
|
||||
|
||||
---
|
||||
|
||||
See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/snapshots) for complete documentation on snapshots with the Bun test runner.
|
||||
See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/mocks) for complete documentation on mocking with the Bun test runner.
|
||||
|
||||
@@ -44,17 +44,10 @@ test.todo("unimplemented feature", () => {
|
||||
|
||||
---
|
||||
|
||||
If an implementation is provided, it will not be run unless the `--todo` flag is passed. If the `--todo` flag is passed, the test will be executed and _expected to fail_ by test runner! If a todo test passes, the `bun test` run will return a non-zero exit code to signal the failure.
|
||||
If an implementation is provided, it will be executed and _expected to fail_ by test runner! If a todo test passes, the `bun test` run will return a non-zero exit code to signal the failure.
|
||||
|
||||
```sh
|
||||
$ bun test --todo
|
||||
my.test.ts:
|
||||
✗ unimplemented feature
|
||||
^ this test is marked as todo but passes. Remove `.todo` or check that test is correct.
|
||||
|
||||
0 pass
|
||||
1 fail
|
||||
1 expect() calls
|
||||
$ bun test
|
||||
$ echo $?
|
||||
1 # this is the exit code of the previous command
|
||||
```
|
||||
|
||||
@@ -4,6 +4,10 @@ name: Update snapshots in `bun test`
|
||||
|
||||
Bun's test runner supports Jest-style snapshot testing via `.toMatchSnapshot()`.
|
||||
|
||||
{% callout %}
|
||||
The `.toMatchInlineSnapshot()` method is not yet supported.
|
||||
{% /callout %}
|
||||
|
||||
```ts#snap.test.ts
|
||||
import { test, expect } from "bun:test";
|
||||
|
||||
@@ -43,4 +47,4 @@ Ran 1 tests across 1 files. [102.00ms]
|
||||
|
||||
---
|
||||
|
||||
See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/snapshots) for complete documentation on snapshots with the Bun test runner.
|
||||
See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/mocks) for complete documentation on mocking with the Bun test runner.
|
||||
|
||||
@@ -73,7 +73,8 @@ There are also image variants for different operating systems.
|
||||
$ docker pull oven/bun:debian
|
||||
$ docker pull oven/bun:slim
|
||||
$ docker pull oven/bun:distroless
|
||||
$ docker pull oven/bun:alpine
|
||||
# alpine not recommended until #918 is fixed
|
||||
# $ docker pull oven/bun:alpine
|
||||
```
|
||||
|
||||
## Checking installation
|
||||
@@ -189,19 +190,14 @@ For convenience, here are download links for the latest version:
|
||||
|
||||
- [`bun-linux-x64.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64.zip)
|
||||
- [`bun-linux-x64-baseline.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64-baseline.zip)
|
||||
- [`bun-linux-x64-musl.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64-musl.zip)
|
||||
- [`bun-linux-x64-musl-baseline.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-x64-musl-baseline.zip)
|
||||
- [`bun-windows-x64.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-windows-x64.zip)
|
||||
- [`bun-windows-x64-baseline.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-windows-x64-baseline.zip)
|
||||
- [`bun-darwin-aarch64.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-darwin-aarch64.zip)
|
||||
- [`bun-linux-aarch64.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-aarch64.zip)
|
||||
- [`bun-linux-aarch64-musl.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-linux-aarch64-musl.zip)
|
||||
- [`bun-darwin-x64.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-darwin-x64.zip)
|
||||
- [`bun-darwin-x64-baseline.zip`](https://github.com/oven-sh/bun/releases/latest/download/bun-darwin-x64-baseline.zip)
|
||||
|
||||
The `musl` binaries are built for distributions that do not ship with the glibc libraries by default, instead relying on musl. The two most popular distros are Void Linux and Alpine Linux, with the latter is used heavily in Docker containers. If you encounter an error like the following: `bun: /lib/x86_64-linux-gnu/libm.so.6: version GLIBC_2.29' not found (required by bun)`, try using the musl binary. Bun's install script automatically chooses the correct binary for your system.
|
||||
|
||||
The `baseline` binaries are built for older CPUs which may not support AVX2 instructions. If you run into an "Illegal Instruction" error when running Bun, try using the `baseline` binaries instead. Bun's install scripts automatically chooses the correct binary for your system which helps avoid this issue. Baseline builds are slower than regular builds, so use them only if necessary.
|
||||
The `baseline` binaries are built for older CPUs which may not support AVX2 instructions. If you run into an "Illegal Instruction" error when running Bun, try using the `baseline` binaries instead. Bun's install scripts automatically choose the correct binary for your system which helps avoid this issue. Baseline builds are slower than regular builds, so use them only if necessary.
|
||||
|
||||
<!--
|
||||
## Native
|
||||
|
||||
@@ -73,10 +73,15 @@ After Visual Studio, you need the following:
|
||||
**Note** – The Zig compiler is automatically downloaded, installed, and updated by the building process.
|
||||
{% /callout %}
|
||||
|
||||
[Scoop](https://scoop.sh) can be used to install these remaining tools easily.
|
||||
[WinGet](https://learn.microsoft.com/windows/package-manager/winget) or [Scoop](https://scoop.sh) can be used to install these remaining tools easily:
|
||||
|
||||
{% codetabs group="a" %}
|
||||
|
||||
```ps1#WinGet
|
||||
## Select "Add LLVM to the system PATH for all users" in the LLVM installer
|
||||
> winget install -i LLVM.LLVM -v 18.1.8 && winget install GoLang.Go Rustlang.Rustup NASM.NASM StrawberryPerl.StrawberryPerl RubyInstallerTeam.Ruby.3.2 OpenJS.NodeJS.LTS Ccache.Ccache
|
||||
```
|
||||
|
||||
```ps1#Scoop
|
||||
> irm https://get.scoop.sh | iex
|
||||
> scoop install nodejs-lts go rust nasm ruby perl ccache
|
||||
@@ -86,16 +91,20 @@ After Visual Studio, you need the following:
|
||||
|
||||
{% /codetabs %}
|
||||
|
||||
{% callout %}
|
||||
Please do not use WinGet/other package manager for these, as you will likely install Strawberry Perl instead of a more minimal installation of Perl. Strawberry Perl includes many other utilities that get installed into `$Env:PATH` that will conflict with MSVC and break the build.
|
||||
{% /callout %}
|
||||
|
||||
If you intend on building WebKit locally (optional), you should install these packages:
|
||||
|
||||
{% codetabs group="a" %}
|
||||
|
||||
```ps1#WinGet
|
||||
> winget install ezwinports.make Cygwin.Cygwin Python.Python.3.12
|
||||
```
|
||||
|
||||
```ps1#Scoop
|
||||
> scoop install make cygwin python
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
|
||||
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
|
||||
|
||||
4
docs/rfcs/README.md
Normal file
4
docs/rfcs/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# RFCs
|
||||
|
||||
| Number | Name | Issue |
|
||||
| ------ | ---- | ----- |
|
||||
@@ -355,7 +355,7 @@ Bun.build({
|
||||
|
||||
{% /callout %}
|
||||
|
||||
## Lifecycle hooks
|
||||
## Lifecycle callbacks
|
||||
|
||||
Plugins can register callbacks to be run at various points in the lifecycle of a bundle:
|
||||
|
||||
@@ -363,8 +363,6 @@ Plugins can register callbacks to be run at various points in the lifecycle of a
|
||||
- [`onResolve()`](#onresolve): Run before a module is resolved
|
||||
- [`onLoad()`](#onload): Run before a module is loaded.
|
||||
|
||||
### Reference
|
||||
|
||||
A rough overview of the types (please refer to Bun's `bun.d.ts` for the full type definitions):
|
||||
|
||||
```ts
|
||||
@@ -605,98 +603,3 @@ plugin({
|
||||
```
|
||||
|
||||
Note that the `.defer()` function currently has the limitation that it can only be called once per `onLoad` callback.
|
||||
|
||||
## Native plugins
|
||||
|
||||
{% callout %}
|
||||
**NOTE** — This is an advanced and experiemental API recommended for plugin developers who are familiar with systems programming and the C ABI. Use with caution.
|
||||
{% /callout %}
|
||||
|
||||
One of the reasons why Bun's bundler is so fast is that it is written in native code and leverages multi-threading to load and parse modules in parallel.
|
||||
|
||||
However, one limitation of plugins written in JavaScript is that JavaScript itself is single-threaded.
|
||||
|
||||
Native plugins are written as [NAPI](/docs/node-api) modules and can be run on multiple threads. This allows native plugins to run much faster than JavaScript plugins.
|
||||
|
||||
In addition, native plugins can skip unnecessary work such as the UTF-8 -> UTF-16 conversion needed to pass strings to JavaScript.
|
||||
|
||||
These are the following lifecycle hooks which are available to native plugins:
|
||||
|
||||
- [`onBeforeParse()`](#onbeforeparse): Called on any thread before a file is parsed by Bun's bundler.
|
||||
|
||||
### Creating a native plugin
|
||||
|
||||
Native plugins are NAPI modules which expose lifecycle hooks as C ABI functions.
|
||||
|
||||
To create a native plugin, you must export a C ABI function which matches the signature of the native lifecycle hook you want to implement.
|
||||
|
||||
#### Example: Rust with napi-rs
|
||||
|
||||
First initialize a napi project (see [here](https://napi.rs/docs/introduction/getting-started) for a more comprehensive guide).
|
||||
|
||||
Then install Bun's official safe plugin wrapper crate:
|
||||
|
||||
```bash
|
||||
cargo add bun-native-plugin
|
||||
```
|
||||
|
||||
Now you can export an `extern "C" fn` which is the implementation of your plugin:
|
||||
|
||||
```rust
|
||||
#[no_mangle]
|
||||
extern "C" fn on_before_parse_impl(
|
||||
args: *const bun_native_plugin::sys::OnBeforeParseArguments,
|
||||
result: *mut bun_native_plugin::sys::OnBeforeParseResult,
|
||||
) {
|
||||
let args = unsafe { &*args };
|
||||
let result = unsafe { &mut *result };
|
||||
|
||||
let mut handle = match bun_native_plugin::OnBeforeParse::from_raw(args, result) {
|
||||
Ok(handle) => handle,
|
||||
Err(_) => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let source_code = match handle.input_source_code() {
|
||||
Ok(source_code) => source_code,
|
||||
Err(_) => {
|
||||
handle.log_error("Fetching source code failed!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let loader = handle.output_loader();
|
||||
handle.set_output_source_code(source_code.replace("foo", "bar"), loader);
|
||||
```
|
||||
|
||||
Use napi-rs to compile the plugin to a `.node` file, then you can `require()` it from JS and use it:
|
||||
|
||||
```js
|
||||
await Bun.build({
|
||||
entrypoints: ["index.ts"],
|
||||
setup(build) {
|
||||
const myNativePlugin = require("./path/to/plugin.node");
|
||||
|
||||
build.onBeforeParse(
|
||||
{ filter: /\.ts/ },
|
||||
{ napiModule: myNativePlugin, symbol: "on_before_parse_impl" },
|
||||
);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### `onBeforeParse`
|
||||
|
||||
```ts
|
||||
onBeforeParse(
|
||||
args: { filter: RegExp; namespace?: string },
|
||||
callback: { napiModule: NapiModule; symbol: string; external?: unknown },
|
||||
): void;
|
||||
```
|
||||
|
||||
This lifecycle callback is run immediately before a file is parsed by Bun's bundler.
|
||||
|
||||
As input, it receives the file's contents and can optionally return new source code.
|
||||
|
||||
This callback can be called from any thread and so the napi module implementation must be thread-safe.
|
||||
|
||||
@@ -97,7 +97,7 @@ test.skip("wat", () => {
|
||||
|
||||
## `test.todo`
|
||||
|
||||
Mark a test as a todo with `test.todo`. These tests will not be run.
|
||||
Mark a test as a todo with `test.todo`. These tests _will_ be run, and the test runner will expect them to fail. If they pass, you will be prompted to mark it as a regular test.
|
||||
|
||||
```ts
|
||||
import { expect, test } from "bun:test";
|
||||
@@ -107,22 +107,12 @@ test.todo("fix this", () => {
|
||||
});
|
||||
```
|
||||
|
||||
To run todo tests and find any which are passing, use `bun test --todo`.
|
||||
To exclusively run tests marked as _todo_, use `bun test --todo`.
|
||||
|
||||
```sh
|
||||
$ bun test --todo
|
||||
my.test.ts:
|
||||
✗ unimplemented feature
|
||||
^ this test is marked as todo but passes. Remove `.todo` or check that test is correct.
|
||||
|
||||
0 pass
|
||||
1 fail
|
||||
1 expect() calls
|
||||
```
|
||||
|
||||
With this flag, failing todo tests will not cause an error, but todo tests which pass will be marked as failing so you can remove the todo mark or
|
||||
fix the test.
|
||||
|
||||
## `test.only`
|
||||
|
||||
To run a particular test or suite of tests use `test.only()` or `describe.only()`. Once declared, running `bun test --only` will only execute tests/suites that have been marked with `.only()`. Running `bun test` without the `--only` option with `test.only()` declared will result in all tests in the given suite being executed _up to_ the test with `.only()`. `describe.only()` functions the same in both execution scenarios.
|
||||
@@ -531,7 +521,7 @@ Bun implements the following matchers. Full Jest compatibility is on the roadmap
|
||||
|
||||
---
|
||||
|
||||
- ✅
|
||||
- ❌
|
||||
- [`.toMatchInlineSnapshot()`](https://jestjs.io/docs/expect#tomatchinlinesnapshotpropertymatchers-inlinesnapshot)
|
||||
|
||||
---
|
||||
|
||||
67
docs/troubleshooting.md
Normal file
67
docs/troubleshooting.md
Normal file
@@ -0,0 +1,67 @@
|
||||
## Troubleshooting
|
||||
|
||||
### Bun not running on an M1 (or Apple Silicon)
|
||||
|
||||
If you see a message like this
|
||||
|
||||
> [1] 28447 killed bun create next ./test
|
||||
|
||||
It most likely means you’re running Bun’s x64 version on Apple Silicon. This happens if Bun is running via Rosetta. Rosetta is unable to emulate AVX2 instructions, which Bun indirectly uses.
|
||||
|
||||
The fix is to ensure you installed a version of Bun built for Apple Silicon.
|
||||
|
||||
### error: Unexpected
|
||||
|
||||
If you see an error like this:
|
||||
|
||||

|
||||
|
||||
It usually means the max number of open file descriptors is being explicitly set to a low number. By default, Bun requests the max number of file descriptors available (which on macOS, is something like 32,000). But, if you previously ran into ulimit issues with, e.g., Chokidar, someone on The Internet may have advised you to run `ulimit -n 8192`.
|
||||
|
||||
That advice unfortunately **lowers** the hard limit to `8192`. This can be a problem in large repositories or projects with lots of dependencies. Chokidar (and other watchers) don’t seem to call `setrlimit`, which means they’re reliant on the (much lower) soft limit.
|
||||
|
||||
To fix this issue:
|
||||
|
||||
1. Remove any scripts that call `ulimit -n` and restart your shell.
|
||||
2. Try again, and if the error still occurs, try setting `ulimit -n` to an absurdly high number, such as `ulimit -n 2147483646`
|
||||
3. Try again, and if that still doesn’t fix it, open an issue
|
||||
|
||||
### Unzip is required
|
||||
|
||||
Unzip is required to install Bun on Linux. You can use one of the following commands to install `unzip`:
|
||||
|
||||
#### Debian / Ubuntu / Mint
|
||||
|
||||
```sh
|
||||
$ sudo apt install unzip
|
||||
```
|
||||
|
||||
#### RedHat / CentOS / Fedora
|
||||
|
||||
```sh
|
||||
$ sudo dnf install unzip
|
||||
```
|
||||
|
||||
#### Arch / Manjaro
|
||||
|
||||
```sh
|
||||
$ sudo pacman -S unzip
|
||||
```
|
||||
|
||||
#### OpenSUSE
|
||||
|
||||
```sh
|
||||
$ sudo zypper install unzip
|
||||
```
|
||||
|
||||
### bun install is stuck
|
||||
|
||||
Please run `bun install --verbose 2> logs.txt` and send them to me in Bun's discord. If you're on Linux, it would also be helpful if you run `sudo perf trace bun install --silent` and attach the logs.
|
||||
|
||||
### Uninstalling
|
||||
|
||||
Bun's binary and install cache is located in `~/.bun` by default. To uninstall bun, delete this directory and edit your shell config (`.bashrc`, `.zshrc`, or similar) to remove `~/.bun/bin` from the `$PATH` variable.
|
||||
|
||||
```sh
|
||||
$ rm -rf ~/.bun # make sure to remove ~/.bun/bin from $PATH
|
||||
```
|
||||
10
package.json
10
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "bun",
|
||||
"version": "1.1.39",
|
||||
"version": "1.1.35",
|
||||
"workspaces": [
|
||||
"./packages/bun-types"
|
||||
],
|
||||
@@ -30,8 +30,8 @@
|
||||
"bun-types": "workspace:packages/bun-types"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "bun run build:debug",
|
||||
"build:debug": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -B build/debug",
|
||||
"build": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -B build/debug",
|
||||
"build:debug": "bun run build",
|
||||
"build:valgrind": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -DENABLE_BASELINE=ON -ENABLE_VALGRIND=ON -B build/debug-valgrind",
|
||||
"build:release": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -B build/release",
|
||||
"build:ci": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=ON -DCI=true -B build/release-ci --verbose --fresh",
|
||||
@@ -39,8 +39,8 @@
|
||||
"build:logs": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -DENABLE_LOGS=ON -B build/release-logs",
|
||||
"build:safe": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -DZIG_OPTIMIZE=ReleaseSafe -B build/release-safe",
|
||||
"build:smol": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -B build/release-smol",
|
||||
"build:local": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -DWEBKIT_LOCAL=ON -B build/debug-local",
|
||||
"build:release:local": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -DWEBKIT_LOCAL=ON -B build/release-local",
|
||||
"build:local": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -DWEBKIT_LOCAL=ON -B build/debug",
|
||||
"build:release:local": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -DWEBKIT_LOCAL=ON -B build/release",
|
||||
"build:release:with_logs": "cmake . -DCMAKE_BUILD_TYPE=Release -DENABLE_LOGS=true -GNinja -Bbuild-release && ninja -Cbuild-release",
|
||||
"build:debug-zig-release": "cmake . -DCMAKE_BUILD_TYPE=Release -DZIG_OPTIMIZE=Debug -GNinja -Bbuild-debug-zig-release && ninja -Cbuild-debug-zig-release",
|
||||
"css-properties": "bun run src/css/properties/generate_properties.ts",
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
[target.aarch64-unknown-linux-musl]
|
||||
linker = "aarch64-linux-musl-gcc"
|
||||
rustflags = ["-C", "target-feature=-crt-static"]
|
||||
[target.x86_64-pc-windows-msvc]
|
||||
rustflags = ["-C", "target-feature=+crt-static"]
|
||||
202
packages/bun-build-mdx-rs/.gitignore
vendored
202
packages/bun-build-mdx-rs/.gitignore
vendored
@@ -1,202 +0,0 @@
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/macos
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=macos
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### macOS Patch ###
|
||||
# iCloud generated files
|
||||
*.icloud
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/macos
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/windows
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=windows
|
||||
|
||||
### Windows ###
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/windows
|
||||
|
||||
#Added by cargo
|
||||
|
||||
/target
|
||||
Cargo.lock
|
||||
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
|
||||
*.node
|
||||
|
||||
dist/
|
||||
|
||||
index.js
|
||||
index.d.ts
|
||||
@@ -1,13 +0,0 @@
|
||||
target
|
||||
Cargo.lock
|
||||
.cargo
|
||||
.github
|
||||
npm
|
||||
.eslintrc
|
||||
.prettierignore
|
||||
rustfmt.toml
|
||||
yarn.lock
|
||||
*.node
|
||||
.yarn
|
||||
__test__
|
||||
renovate.json
|
||||
@@ -1,21 +0,0 @@
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "bun-mdx-rs"
|
||||
version = "0.0.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
|
||||
napi = { version = "2.12.2", default-features = false, features = ["napi4"] }
|
||||
napi-derive = "2.12.2"
|
||||
mdxjs = "0.2.11"
|
||||
bun-native-plugin = { path = "../bun-native-plugin-rs" }
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = "2.0.1"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
strip = "symbols"
|
||||
@@ -1,34 +0,0 @@
|
||||
# bun-build-mdx-rs
|
||||
|
||||
This is a proof of concept for using a third-party native addon in `Bun.build()`.
|
||||
|
||||
This uses `mdxjs-rs` to convert MDX to JSX.
|
||||
|
||||
TODO: **This needs to be built & published to npm.**
|
||||
|
||||
## Building locally:
|
||||
|
||||
```sh
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
```js
|
||||
import { build } from "bun";
|
||||
import mdx from "./index.js";
|
||||
|
||||
// TODO: This needs to be prebuilt for the current platform
|
||||
// Probably use a napi-rs template for this
|
||||
import addon from "./target/release/libmdx_bun.dylib" with { type: "file" };
|
||||
|
||||
const results = await build({
|
||||
entrypoints: ["./hello.jsx"],
|
||||
plugins: [mdx({ addon })],
|
||||
minify: true,
|
||||
outdir: "./dist",
|
||||
define: {
|
||||
"process.env.NODE_ENV": JSON.stringify("production"),
|
||||
},
|
||||
});
|
||||
|
||||
console.log(results);
|
||||
```
|
||||
@@ -1,7 +0,0 @@
|
||||
import test from 'ava'
|
||||
|
||||
import { sum } from '../index.js'
|
||||
|
||||
test('sum from native', (t) => {
|
||||
t.is(sum(1, 2), 3)
|
||||
})
|
||||
@@ -1,5 +0,0 @@
|
||||
extern crate napi_build;
|
||||
|
||||
fn main() {
|
||||
napi_build::setup();
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import page1 from "./page1.mdx";
|
||||
import page2 from "./page2.mdx";
|
||||
import page3 from "./page3.mdx";
|
||||
import page4 from "./page4.mdx";
|
||||
|
||||
console.log(page1, page2, page3, page4);
|
||||
@@ -1,11 +0,0 @@
|
||||
# Hello World
|
||||
|
||||
This is a sample MDX file that demonstrates various MDX features.
|
||||
|
||||
## Components
|
||||
|
||||
You can use JSX components directly in MDX:
|
||||
|
||||
<Button onClick={() => alert("Hello!")}>Click me</Button>
|
||||
|
||||
## Code Blocks
|
||||
@@ -1,11 +0,0 @@
|
||||
# Hello World
|
||||
|
||||
This is a sample MDX file that demonstrates various MDX features.
|
||||
|
||||
## Components
|
||||
|
||||
You can use JSX components directly in MDX:
|
||||
|
||||
<Button onClick={() => alert("Hello!")}>Click me</Button>
|
||||
|
||||
## Code Blocks
|
||||
@@ -1,11 +0,0 @@
|
||||
# Hello World
|
||||
|
||||
This is a sample MDX file that demonstrates various MDX features.
|
||||
|
||||
## Components
|
||||
|
||||
You can use JSX components directly in MDX:
|
||||
|
||||
<Button onClick={() => alert("Hello!")}>Click me</Button>
|
||||
|
||||
## Code Blocks
|
||||
@@ -1,11 +0,0 @@
|
||||
# Hello World
|
||||
|
||||
This is a sample MDX file that demonstrates various MDX features.
|
||||
|
||||
## Components
|
||||
|
||||
You can use JSX components directly in MDX:
|
||||
|
||||
<Button onClick={() => alert("Hello!")}>Click me</Button>
|
||||
|
||||
## Code Blocks
|
||||
@@ -1,3 +0,0 @@
|
||||
# `bun-mdx-rs-darwin-arm64`
|
||||
|
||||
This is the **aarch64-apple-darwin** binary for `bun-mdx-rs`
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"name": "bun-mdx-rs-darwin-arm64",
|
||||
"version": "0.0.0",
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"main": "bun-mdx-rs.darwin-arm64.node",
|
||||
"files": [
|
||||
"bun-mdx-rs.darwin-arm64.node"
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# `bun-mdx-rs-darwin-x64`
|
||||
|
||||
This is the **x86_64-apple-darwin** binary for `bun-mdx-rs`
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"name": "bun-mdx-rs-darwin-x64",
|
||||
"version": "0.0.0",
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"main": "bun-mdx-rs.darwin-x64.node",
|
||||
"files": [
|
||||
"bun-mdx-rs.darwin-x64.node"
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# `bun-mdx-rs-linux-arm64-gnu`
|
||||
|
||||
This is the **aarch64-unknown-linux-gnu** binary for `bun-mdx-rs`
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "bun-mdx-rs-linux-arm64-gnu",
|
||||
"version": "0.0.0",
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"main": "bun-mdx-rs.linux-arm64-gnu.node",
|
||||
"files": [
|
||||
"bun-mdx-rs.linux-arm64-gnu.node"
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"libc": [
|
||||
"glibc"
|
||||
]
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# `bun-mdx-rs-linux-arm64-musl`
|
||||
|
||||
This is the **aarch64-unknown-linux-musl** binary for `bun-mdx-rs`
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "bun-mdx-rs-linux-arm64-musl",
|
||||
"version": "0.0.0",
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"main": "bun-mdx-rs.linux-arm64-musl.node",
|
||||
"files": [
|
||||
"bun-mdx-rs.linux-arm64-musl.node"
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"libc": [
|
||||
"musl"
|
||||
]
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# `bun-mdx-rs-linux-x64-gnu`
|
||||
|
||||
This is the **x86_64-unknown-linux-gnu** binary for `bun-mdx-rs`
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "bun-mdx-rs-linux-x64-gnu",
|
||||
"version": "0.0.0",
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"main": "bun-mdx-rs.linux-x64-gnu.node",
|
||||
"files": [
|
||||
"bun-mdx-rs.linux-x64-gnu.node"
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"libc": [
|
||||
"glibc"
|
||||
]
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# `bun-mdx-rs-linux-x64-musl`
|
||||
|
||||
This is the **x86_64-unknown-linux-musl** binary for `bun-mdx-rs`
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "bun-mdx-rs-linux-x64-musl",
|
||||
"version": "0.0.0",
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"main": "bun-mdx-rs.linux-x64-musl.node",
|
||||
"files": [
|
||||
"bun-mdx-rs.linux-x64-musl.node"
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"libc": [
|
||||
"musl"
|
||||
]
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# `bun-mdx-rs-win32-x64-msvc`
|
||||
|
||||
This is the **x86_64-pc-windows-msvc** binary for `bun-mdx-rs`
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"name": "bun-mdx-rs-win32-x64-msvc",
|
||||
"version": "0.0.0",
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"main": "bun-mdx-rs.win32-x64-msvc.node",
|
||||
"files": [
|
||||
"bun-mdx-rs.win32-x64-msvc.node"
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
{
|
||||
"name": "bun-mdx-rs",
|
||||
"version": "0.0.0",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"napi": {
|
||||
"name": "bun-mdx-rs",
|
||||
"triples": {
|
||||
"additional": [
|
||||
"aarch64-apple-darwin",
|
||||
"aarch64-unknown-linux-gnu",
|
||||
"aarch64-unknown-linux-musl",
|
||||
"x86_64-unknown-linux-musl"
|
||||
]
|
||||
}
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@napi-rs/cli": "^2.18.4",
|
||||
"ava": "^6.0.1"
|
||||
},
|
||||
"ava": {
|
||||
"timeout": "3m"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"scripts": {
|
||||
"artifacts": "napi artifacts",
|
||||
"build": "napi build --platform --release",
|
||||
"build:debug": "napi build --platform",
|
||||
"prepublishOnly": "napi prepublish -t npm",
|
||||
"test": "ava",
|
||||
"universal": "napi universal",
|
||||
"version": "napi version"
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
tab_spaces = 2
|
||||
edition = "2021"
|
||||
@@ -1,55 +0,0 @@
|
||||
use bun_native_plugin::{define_bun_plugin, BunLoader, OnBeforeParse};
|
||||
use mdxjs::{compile, Options as CompileOptions};
|
||||
use napi_derive::napi;
|
||||
|
||||
#[macro_use]
|
||||
extern crate napi;
|
||||
|
||||
define_bun_plugin!("bun-mdx-rs");
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn bun_mdx_rs(
|
||||
args: *const bun_native_plugin::sys::OnBeforeParseArguments,
|
||||
result: *mut bun_native_plugin::sys::OnBeforeParseResult,
|
||||
) {
|
||||
let args = unsafe { &*args };
|
||||
|
||||
let mut handle = match OnBeforeParse::from_raw(args, result) {
|
||||
Ok(handle) => handle,
|
||||
Err(_) => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let source_str = match handle.input_source_code() {
|
||||
Ok(source_str) => source_str,
|
||||
Err(_) => {
|
||||
handle.log_error("Failed to fetch source code");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut options = CompileOptions::gfm();
|
||||
|
||||
// Leave it as JSX for Bun to handle
|
||||
options.jsx = true;
|
||||
|
||||
let path = match handle.path() {
|
||||
Ok(path) => path,
|
||||
Err(e) => {
|
||||
handle.log_error(&format!("Failed to get path: {:?}", e));
|
||||
return;
|
||||
}
|
||||
};
|
||||
options.filepath = Some(path.to_string());
|
||||
|
||||
match compile(&source_str, &options) {
|
||||
Ok(compiled) => {
|
||||
handle.set_output_source_code(compiled, BunLoader::BUN_LOADER_JSX);
|
||||
}
|
||||
Err(_) => {
|
||||
handle.log_error("Failed to compile MDX");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"name": "bun-debug-adapter-protocol",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"semver": "^7.5.4",
|
||||
"source-map-js": "^1.0.2"
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import type { InspectorEventMap } from "../../../bun-inspector-protocol/src/inspector";
|
||||
import type { JSC } from "../../../bun-inspector-protocol/src/protocol";
|
||||
import type { DAP } from "../protocol";
|
||||
// @ts-ignore
|
||||
import { ChildProcess, spawn } from "node:child_process";
|
||||
import { EventEmitter } from "node:events";
|
||||
import { AddressInfo, createServer, Socket } from "node:net";
|
||||
import { AddressInfo, createServer } from "node:net";
|
||||
import * as path from "node:path";
|
||||
import { remoteObjectToString, WebSocketInspector } from "../../../bun-inspector-protocol/index.ts";
|
||||
import type { Inspector, InspectorEventMap } from "../../../bun-inspector-protocol/src/inspector/index.d.ts";
|
||||
import { NodeSocketInspector } from "../../../bun-inspector-protocol/src/inspector/node-socket.ts";
|
||||
import type { JSC } from "../../../bun-inspector-protocol/src/protocol/index.d.ts";
|
||||
import type { DAP } from "../protocol/index.d.ts";
|
||||
import { randomUnixPath, TCPSocketSignal, UnixSignal } from "./signal.ts";
|
||||
import { Location, SourceMap } from "./sourcemap.ts";
|
||||
import { remoteObjectToString, WebSocketInspector } from "../../../bun-inspector-protocol/index";
|
||||
import { randomUnixPath, TCPSocketSignal, UnixSignal } from "./signal";
|
||||
import { Location, SourceMap } from "./sourcemap";
|
||||
|
||||
export async function getAvailablePort(): Promise<number> {
|
||||
const server = createServer();
|
||||
server.listen(0);
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve, reject) => {
|
||||
server.on("listening", () => {
|
||||
const { port } = server.address() as AddressInfo;
|
||||
server.close(() => {
|
||||
@@ -105,18 +105,7 @@ const capabilities: DAP.Capabilities = {
|
||||
|
||||
type InitializeRequest = DAP.InitializeRequest & {
|
||||
supportsConfigurationDoneRequest?: boolean;
|
||||
enableControlFlowProfiler?: boolean;
|
||||
enableDebugger?: boolean;
|
||||
} & (
|
||||
| {
|
||||
enableLifecycleAgentReporter?: false;
|
||||
sendImmediatePreventExit?: false;
|
||||
}
|
||||
| {
|
||||
enableLifecycleAgentReporter: true;
|
||||
sendImmediatePreventExit?: boolean;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
type LaunchRequest = DAP.LaunchRequest & {
|
||||
runtime?: string;
|
||||
@@ -242,14 +231,10 @@ function normalizeSourcePath(sourcePath: string, untitledDocPath?: string, bunEv
|
||||
return path.normalize(sourcePath);
|
||||
}
|
||||
|
||||
export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
extends EventEmitter<DebugAdapterEventMap>
|
||||
implements IDebugAdapter
|
||||
{
|
||||
protected readonly inspector: T;
|
||||
protected options?: DebuggerOptions;
|
||||
|
||||
export class DebugAdapter extends EventEmitter<DebugAdapterEventMap> implements IDebugAdapter {
|
||||
#threadId: number;
|
||||
#inspector: WebSocketInspector;
|
||||
#process?: ChildProcess;
|
||||
#sourceId: number;
|
||||
#pendingSources: Map<string, ((source: Source) => void)[]>;
|
||||
#sources: Map<string | number, Source>;
|
||||
@@ -262,21 +247,20 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
#targets: Map<number, Target>;
|
||||
#variableId: number;
|
||||
#variables: Map<number, Variable>;
|
||||
#initialized?: InitializeRequest;
|
||||
#options?: DebuggerOptions;
|
||||
#untitledDocPath?: string;
|
||||
#bunEvalPath?: string;
|
||||
#initialized?: InitializeRequest;
|
||||
|
||||
protected constructor(inspector: T, untitledDocPath?: string, bunEvalPath?: string) {
|
||||
constructor(url?: string | URL, untitledDocPath?: string, bunEvalPath?: string) {
|
||||
super();
|
||||
this.#untitledDocPath = untitledDocPath;
|
||||
this.#bunEvalPath = bunEvalPath;
|
||||
this.#threadId = threadId++;
|
||||
this.inspector = inspector;
|
||||
const emit = this.inspector.emit.bind(this.inspector);
|
||||
this.inspector.emit = (event, ...args) => {
|
||||
this.#inspector = new WebSocketInspector(url);
|
||||
const emit = this.#inspector.emit.bind(this.#inspector);
|
||||
this.#inspector.emit = (event, ...args) => {
|
||||
let sent = false;
|
||||
sent ||= emit(event, ...args);
|
||||
sent ||= this.emit(event as keyof JSC.EventMap, ...(args as any));
|
||||
sent ||= this.emit(event, ...(args as any));
|
||||
return sent;
|
||||
};
|
||||
this.#sourceId = 1;
|
||||
@@ -290,27 +274,26 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
this.#targets = new Map();
|
||||
this.#variableId = 1;
|
||||
this.#variables = new Map();
|
||||
this.#untitledDocPath = untitledDocPath;
|
||||
this.#bunEvalPath = bunEvalPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the inspector url. This is deprecated and exists for compat.
|
||||
* @deprecated You should get the inspector directly (with .getInspector()), and if it's a WebSocketInspector you can access `.url` direclty.
|
||||
* Gets the inspector url.
|
||||
*/
|
||||
get url(): string {
|
||||
// This code has been migrated from a time when the inspector was always a WebSocketInspector.
|
||||
if (this.inspector instanceof WebSocketInspector) {
|
||||
return this.inspector.url;
|
||||
}
|
||||
|
||||
throw new Error("Inspector does not offer a URL");
|
||||
return this.#inspector.url;
|
||||
}
|
||||
|
||||
public getInspector() {
|
||||
return this.inspector;
|
||||
/**
|
||||
* Starts the inspector.
|
||||
* @param url the inspector url
|
||||
* @returns if the inspector was able to connect
|
||||
*/
|
||||
start(url?: string): Promise<boolean> {
|
||||
return this.#attach({ url });
|
||||
}
|
||||
|
||||
abstract start(...args: unknown[]): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Sends a request to the JavaScript inspector.
|
||||
* @param method the method name
|
||||
@@ -323,7 +306,7 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
* console.log(result.value); // 2
|
||||
*/
|
||||
async send<M extends keyof JSC.ResponseMap>(method: M, params?: JSC.RequestMap[M]): Promise<JSC.ResponseMap[M]> {
|
||||
return this.inspector.send(method, params);
|
||||
return this.#inspector.send(method, params);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,7 +347,7 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
return sent;
|
||||
}
|
||||
|
||||
protected emitAdapterEvent<E extends keyof DAP.EventMap>(event: E, body?: DAP.EventMap[E]): void {
|
||||
#emit<E extends keyof DAP.EventMap>(event: E, body?: DAP.EventMap[E]): void {
|
||||
this.emit("Adapter.event", {
|
||||
type: "event",
|
||||
seq: 0,
|
||||
@@ -376,7 +359,7 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
#emitAfterResponse<E extends keyof DAP.EventMap>(event: E, body?: DAP.EventMap[E]): void {
|
||||
this.once("Adapter.response", () => {
|
||||
process.nextTick(() => {
|
||||
this.emitAdapterEvent(event, body);
|
||||
this.#emit(event, body);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -454,37 +437,19 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
this.emit(`Adapter.${name}` as keyof DebugAdapterEventMap, body);
|
||||
}
|
||||
|
||||
public initialize(request: InitializeRequest): DAP.InitializeResponse {
|
||||
initialize(request: InitializeRequest): DAP.InitializeResponse {
|
||||
this.#initialized = request;
|
||||
|
||||
this.send("Inspector.enable");
|
||||
this.send("Runtime.enable");
|
||||
this.send("Console.enable");
|
||||
|
||||
if (request.enableControlFlowProfiler) {
|
||||
this.send("Runtime.enableControlFlowProfiler");
|
||||
}
|
||||
|
||||
if (request.enableLifecycleAgentReporter) {
|
||||
this.send("LifecycleReporter.enable");
|
||||
|
||||
if (request.sendImmediatePreventExit) {
|
||||
this.send("LifecycleReporter.preventExit");
|
||||
this.send("Debugger.enable").catch(error => {
|
||||
const { message } = unknownToError(error);
|
||||
if (message !== "Debugger domain already enabled") {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// use !== false because by default if unspecified we want to enable the debugger
|
||||
// and this option didn't exist beforehand, so we can't make it non-optional
|
||||
if (request.enableDebugger !== false) {
|
||||
this.send("Debugger.enable").catch(error => {
|
||||
const { message } = unknownToError(error);
|
||||
if (message !== "Debugger domain already enabled") {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
this.send("Debugger.setAsyncStackTraceDepth", { depth: 200 });
|
||||
}
|
||||
});
|
||||
this.send("Debugger.setAsyncStackTraceDepth", { depth: 200 });
|
||||
|
||||
const { clientID, supportsConfigurationDoneRequest } = request;
|
||||
if (!supportsConfigurationDoneRequest && clientID !== "vscode") {
|
||||
@@ -498,20 +463,248 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
configurationDone(): void {
|
||||
// If the client requested that `noDebug` mode be enabled,
|
||||
// then we need to disable all breakpoints and pause on statements.
|
||||
const active = !this.options?.noDebug;
|
||||
const active = !this.#options?.noDebug;
|
||||
this.send("Debugger.setBreakpointsActive", { active });
|
||||
|
||||
// Tell the debugger that its ready to start execution.
|
||||
this.send("Inspector.initialized");
|
||||
}
|
||||
|
||||
// Required so all implementations have a method that .terminate() always calls.
|
||||
// This is useful because we don't want any implementors to forget
|
||||
protected abstract exitJSProcess(): void;
|
||||
async launch(request: DAP.LaunchRequest): Promise<void> {
|
||||
this.#options = { ...request, type: "launch" };
|
||||
|
||||
try {
|
||||
await this.#launch(request);
|
||||
} catch (error) {
|
||||
// Some clients, like VSCode, will show a system-level popup when a `launch` request fails.
|
||||
// Instead, we want to show the error as a sidebar notification.
|
||||
const { message } = unknownToError(error);
|
||||
this.#emit("output", {
|
||||
category: "stderr",
|
||||
output: `Failed to start debugger.\n${message}`,
|
||||
});
|
||||
this.terminate();
|
||||
}
|
||||
}
|
||||
|
||||
async #launch(request: LaunchRequest): Promise<void> {
|
||||
const {
|
||||
runtime = "bun",
|
||||
runtimeArgs = [],
|
||||
program,
|
||||
args = [],
|
||||
cwd,
|
||||
env = {},
|
||||
strictEnv = false,
|
||||
watchMode = false,
|
||||
stopOnEntry = false,
|
||||
__skipValidation = false,
|
||||
stdin,
|
||||
} = request;
|
||||
|
||||
if (!__skipValidation && !program) {
|
||||
throw new Error("No program specified");
|
||||
}
|
||||
|
||||
const processArgs = [...runtimeArgs];
|
||||
|
||||
if (program === "-" && stdin) {
|
||||
processArgs.push("--eval", stdin);
|
||||
} else if (program) {
|
||||
processArgs.push(program);
|
||||
}
|
||||
|
||||
processArgs.push(...args);
|
||||
|
||||
if (program && isTestJavaScript(program) && !runtimeArgs.includes("test")) {
|
||||
processArgs.unshift("test");
|
||||
}
|
||||
|
||||
if (watchMode && !runtimeArgs.includes("--watch") && !runtimeArgs.includes("--hot")) {
|
||||
processArgs.unshift(watchMode === "hot" ? "--hot" : "--watch");
|
||||
}
|
||||
|
||||
const processEnv = strictEnv
|
||||
? {
|
||||
...env,
|
||||
}
|
||||
: {
|
||||
...process.env,
|
||||
...env,
|
||||
};
|
||||
|
||||
if (process.platform !== "win32") {
|
||||
// we're on unix
|
||||
const url = `ws+unix://${randomUnixPath()}`;
|
||||
const signal = new UnixSignal();
|
||||
|
||||
signal.on("Signal.received", () => {
|
||||
this.#attach({ url });
|
||||
});
|
||||
|
||||
this.once("Adapter.terminated", () => {
|
||||
signal.close();
|
||||
});
|
||||
|
||||
const query = stopOnEntry ? "break=1" : "wait=1";
|
||||
processEnv["BUN_INSPECT"] = `${url}?${query}`;
|
||||
processEnv["BUN_INSPECT_NOTIFY"] = signal.url;
|
||||
|
||||
// This is probably not correct, but it's the best we can do for now.
|
||||
processEnv["FORCE_COLOR"] = "1";
|
||||
processEnv["BUN_QUIET_DEBUG_LOGS"] = "1";
|
||||
processEnv["BUN_DEBUG_QUIET_LOGS"] = "1";
|
||||
|
||||
const started = await this.#spawn({
|
||||
command: runtime,
|
||||
args: processArgs,
|
||||
env: processEnv,
|
||||
cwd,
|
||||
isDebugee: true,
|
||||
});
|
||||
|
||||
if (!started) {
|
||||
throw new Error("Program could not be started.");
|
||||
}
|
||||
} else {
|
||||
// we're on windows
|
||||
// Create TCPSocketSignal
|
||||
const url = `ws://127.0.0.1:${await getAvailablePort()}/${getRandomId()}`; // 127.0.0.1 so it resolves correctly on windows
|
||||
const signal = new TCPSocketSignal(await getAvailablePort());
|
||||
|
||||
signal.on("Signal.received", async () => {
|
||||
this.#attach({ url });
|
||||
});
|
||||
|
||||
this.once("Adapter.terminated", () => {
|
||||
signal.close();
|
||||
});
|
||||
|
||||
const query = stopOnEntry ? "break=1" : "wait=1";
|
||||
processEnv["BUN_INSPECT"] = `${url}?${query}`;
|
||||
processEnv["BUN_INSPECT_NOTIFY"] = signal.url; // 127.0.0.1 so it resolves correctly on windows
|
||||
|
||||
// This is probably not correct, but it's the best we can do for now.
|
||||
processEnv["FORCE_COLOR"] = "1";
|
||||
processEnv["BUN_QUIET_DEBUG_LOGS"] = "1";
|
||||
processEnv["BUN_DEBUG_QUIET_LOGS"] = "1";
|
||||
|
||||
const started = await this.#spawn({
|
||||
command: runtime,
|
||||
args: processArgs,
|
||||
env: processEnv,
|
||||
cwd,
|
||||
isDebugee: true,
|
||||
});
|
||||
|
||||
if (!started) {
|
||||
throw new Error("Program could not be started.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async #spawn(options: {
|
||||
command: string;
|
||||
args?: string[];
|
||||
cwd?: string;
|
||||
env?: Record<string, string | undefined>;
|
||||
isDebugee?: boolean;
|
||||
}): Promise<boolean> {
|
||||
const { command, args = [], cwd, env, isDebugee } = options;
|
||||
const request = { command, args, cwd, env };
|
||||
this.emit("Process.requested", request);
|
||||
|
||||
let subprocess: ChildProcess;
|
||||
try {
|
||||
subprocess = spawn(command, args, {
|
||||
...request,
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
} catch (cause) {
|
||||
this.emit("Process.exited", new Error("Failed to spawn process", { cause }), null);
|
||||
return false;
|
||||
}
|
||||
|
||||
subprocess.on("spawn", () => {
|
||||
this.emit("Process.spawned", subprocess);
|
||||
|
||||
if (isDebugee) {
|
||||
this.#process = subprocess;
|
||||
this.#emit("process", {
|
||||
name: `${command} ${args.join(" ")}`,
|
||||
systemProcessId: subprocess.pid,
|
||||
isLocalProcess: true,
|
||||
startMethod: "launch",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
subprocess.on("exit", (code, signal) => {
|
||||
this.emit("Process.exited", code, signal);
|
||||
|
||||
if (isDebugee) {
|
||||
this.#process = undefined;
|
||||
this.#emit("exited", {
|
||||
exitCode: code ?? -1,
|
||||
});
|
||||
this.#emit("terminated");
|
||||
}
|
||||
});
|
||||
|
||||
subprocess.stdout?.on("data", data => {
|
||||
this.emit("Process.stdout", data.toString());
|
||||
});
|
||||
|
||||
subprocess.stderr?.on("data", data => {
|
||||
this.emit("Process.stderr", data.toString());
|
||||
});
|
||||
|
||||
return new Promise(resolve => {
|
||||
subprocess.on("spawn", () => resolve(true));
|
||||
subprocess.on("exit", () => resolve(false));
|
||||
subprocess.on("error", () => resolve(false));
|
||||
});
|
||||
}
|
||||
|
||||
async attach(request: AttachRequest): Promise<void> {
|
||||
this.#options = { ...request, type: "attach" };
|
||||
|
||||
try {
|
||||
await this.#attach(request);
|
||||
} catch (error) {
|
||||
// Some clients, like VSCode, will show a system-level popup when a `launch` request fails.
|
||||
// Instead, we want to show the error as a sidebar notification.
|
||||
const { message } = unknownToError(error);
|
||||
this.#emit("output", {
|
||||
category: "stderr",
|
||||
output: `Failed to start debugger.\n${message}`,
|
||||
});
|
||||
this.terminate();
|
||||
}
|
||||
}
|
||||
|
||||
async #attach(request: AttachRequest): Promise<boolean> {
|
||||
const { url } = request;
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const ok = await this.#inspector.start(url);
|
||||
if (ok) {
|
||||
return true;
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 100 * i));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
terminate(): void {
|
||||
this.exitJSProcess();
|
||||
this.emitAdapterEvent("terminated");
|
||||
if (!this.#process?.kill()) {
|
||||
this.#evaluate({
|
||||
expression: "process.exit(0)",
|
||||
});
|
||||
}
|
||||
|
||||
this.#emit("terminated");
|
||||
}
|
||||
|
||||
disconnect(request: DAP.DisconnectRequest): void {
|
||||
@@ -884,7 +1077,7 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
}
|
||||
|
||||
for (const breakpoint of breakpoints) {
|
||||
this.emitAdapterEvent("breakpoint", {
|
||||
this.#emit("breakpoint", {
|
||||
reason: "removed",
|
||||
breakpoint,
|
||||
});
|
||||
@@ -1123,7 +1316,7 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
const callFrameId = this.#getCallFrameId(frameId);
|
||||
const objectGroup = callFrameId ? "debugger" : context;
|
||||
|
||||
const { result, wasThrown } = await this.evaluateInternal({
|
||||
const { result, wasThrown } = await this.#evaluate({
|
||||
expression,
|
||||
objectGroup,
|
||||
callFrameId,
|
||||
@@ -1144,7 +1337,7 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
};
|
||||
}
|
||||
|
||||
protected async evaluateInternal(options: {
|
||||
async #evaluate(options: {
|
||||
expression: string;
|
||||
objectGroup?: string;
|
||||
callFrameId?: string;
|
||||
@@ -1168,7 +1361,7 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
const callFrameId = this.#getCallFrameId(frameId);
|
||||
|
||||
const { expression, hint } = completionToExpression(text);
|
||||
const { result, wasThrown } = await this.evaluateInternal({
|
||||
const { result, wasThrown } = await this.#evaluate({
|
||||
expression: expression || "this",
|
||||
callFrameId,
|
||||
objectGroup: "repl",
|
||||
@@ -1200,29 +1393,33 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
}
|
||||
|
||||
["Inspector.connected"](): void {
|
||||
this.emitAdapterEvent("output", {
|
||||
this.#emit("output", {
|
||||
category: "debug console",
|
||||
output: "Debugger attached.\n",
|
||||
});
|
||||
|
||||
this.emitAdapterEvent("initialized");
|
||||
this.#emit("initialized");
|
||||
}
|
||||
|
||||
async ["Inspector.disconnected"](error?: Error): Promise<void> {
|
||||
this.emitAdapterEvent("output", {
|
||||
this.#emit("output", {
|
||||
category: "debug console",
|
||||
output: "Debugger detached.\n",
|
||||
});
|
||||
|
||||
if (error) {
|
||||
const { message } = error;
|
||||
this.emitAdapterEvent("output", {
|
||||
this.#emit("output", {
|
||||
category: "stderr",
|
||||
output: `${message}\n`,
|
||||
});
|
||||
}
|
||||
|
||||
this.resetInternal();
|
||||
this.#reset();
|
||||
|
||||
if (this.#process?.exitCode !== null) {
|
||||
this.#emit("terminated");
|
||||
}
|
||||
}
|
||||
|
||||
async ["Debugger.scriptParsed"](event: JSC.Debugger.ScriptParsedEvent): Promise<void> {
|
||||
@@ -1273,7 +1470,7 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
return;
|
||||
}
|
||||
|
||||
this.emitAdapterEvent("output", {
|
||||
this.#emit("output", {
|
||||
category: "stderr",
|
||||
output: errorMessage,
|
||||
line: this.#lineFrom0BasedLine(errorLine),
|
||||
@@ -1301,7 +1498,7 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
const breakpoint = breakpoints[i];
|
||||
const oldBreakpoint = oldBreakpoints[i];
|
||||
|
||||
this.emitAdapterEvent("breakpoint", {
|
||||
this.#emit("breakpoint", {
|
||||
reason: "changed",
|
||||
breakpoint: {
|
||||
...breakpoint,
|
||||
@@ -1384,7 +1581,7 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
}
|
||||
}
|
||||
|
||||
this.emitAdapterEvent("stopped", {
|
||||
this.#emit("stopped", {
|
||||
threadId: this.#threadId,
|
||||
reason: this.#stopped,
|
||||
hitBreakpointIds,
|
||||
@@ -1401,20 +1598,20 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
}
|
||||
}
|
||||
|
||||
this.emitAdapterEvent("continued", {
|
||||
this.#emit("continued", {
|
||||
threadId: this.#threadId,
|
||||
});
|
||||
}
|
||||
|
||||
["Process.stdout"](output: string): void {
|
||||
this.emitAdapterEvent("output", {
|
||||
this.#emit("output", {
|
||||
category: "debug console",
|
||||
output,
|
||||
});
|
||||
}
|
||||
|
||||
["Process.stderr"](output: string): void {
|
||||
this.emitAdapterEvent("output", {
|
||||
this.#emit("output", {
|
||||
category: "debug console",
|
||||
output,
|
||||
});
|
||||
@@ -1498,8 +1695,8 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
|
||||
// If the path changed or the source has a source reference,
|
||||
// the old source should be marked as removed.
|
||||
if (path !== oldPath /*|| sourceReference*/) {
|
||||
this.emitAdapterEvent("loadedSource", {
|
||||
if (path !== oldPath || sourceReference) {
|
||||
this.#emit("loadedSource", {
|
||||
reason: "removed",
|
||||
source: oldSource,
|
||||
});
|
||||
@@ -1509,7 +1706,7 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
this.#sources.set(sourceId, source);
|
||||
this.#sources.set(scriptId, source);
|
||||
|
||||
this.emitAdapterEvent("loadedSource", {
|
||||
this.#emit("loadedSource", {
|
||||
// If the reason is "changed", the source will be retrieved using
|
||||
// the `source` command, which is why it cannot be set when `path` is present.
|
||||
reason: oldSource && !path ? "changed" : "new",
|
||||
@@ -1565,9 +1762,9 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
}
|
||||
|
||||
// If the source is not present, it may not have been loaded yet.
|
||||
let resolves = this.#pendingSources.get(sourceId.toString());
|
||||
let resolves = this.#pendingSources.get(sourceId);
|
||||
if (!resolves) {
|
||||
this.#pendingSources.set(sourceId.toString(), (resolves = []));
|
||||
this.#pendingSources.set(sourceId, (resolves = []));
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
@@ -1819,7 +2016,7 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
const callFrameId = this.#getCallFrameId(frameId);
|
||||
const objectGroup = callFrameId ? "debugger" : "repl";
|
||||
|
||||
const { result, wasThrown } = await this.evaluateInternal({
|
||||
const { result, wasThrown } = await this.#evaluate({
|
||||
expression: `${expression} = (${value});`,
|
||||
objectGroup: "repl",
|
||||
callFrameId,
|
||||
@@ -2019,11 +2216,12 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this.inspector.close();
|
||||
this.resetInternal();
|
||||
this.#process?.kill();
|
||||
this.#inspector.close();
|
||||
this.#reset();
|
||||
}
|
||||
|
||||
protected resetInternal(): void {
|
||||
#reset(): void {
|
||||
this.#pendingSources.clear();
|
||||
this.#sources.clear();
|
||||
this.#stackFrames.length = 0;
|
||||
@@ -2034,309 +2232,10 @@ export abstract class BaseDebugAdapter<T extends Inspector = Inspector>
|
||||
this.#functionBreakpoints.clear();
|
||||
this.#targets.clear();
|
||||
this.#variables.clear();
|
||||
this.options = undefined;
|
||||
this.#options = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a debug adapter that connects over a unix/tcp socket. Usually
|
||||
* in the case of a reverse connection. This is used by the vscode extension.
|
||||
*
|
||||
* @warning This will gracefully handle socket closure, you don't need to add extra handling.
|
||||
*/
|
||||
export class NodeSocketDebugAdapter extends BaseDebugAdapter<NodeSocketInspector> {
|
||||
public constructor(socket: Socket, untitledDocPath?: string, bunEvalPath?: string) {
|
||||
super(new NodeSocketInspector(socket), untitledDocPath, bunEvalPath);
|
||||
|
||||
socket.once("close", () => {
|
||||
this.resetInternal();
|
||||
});
|
||||
}
|
||||
|
||||
protected exitJSProcess(): void {
|
||||
this.evaluateInternal({
|
||||
expression: "process.exit(0)",
|
||||
});
|
||||
}
|
||||
|
||||
public async start() {
|
||||
const ok = await this.inspector.start();
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The default debug adapter. Connects via WebSocket
|
||||
*/
|
||||
export class WebSocketDebugAdapter extends BaseDebugAdapter<WebSocketInspector> {
|
||||
#process?: ChildProcess;
|
||||
|
||||
public constructor(url?: string | URL, untitledDocPath?: string, bunEvalPath?: string) {
|
||||
super(new WebSocketInspector(url), untitledDocPath, bunEvalPath);
|
||||
}
|
||||
|
||||
async ["Inspector.disconnected"](error?: Error): Promise<void> {
|
||||
await super["Inspector.disconnected"](error);
|
||||
|
||||
if (this.#process?.exitCode !== null) {
|
||||
this.emitAdapterEvent("terminated");
|
||||
}
|
||||
}
|
||||
|
||||
protected exitJSProcess() {
|
||||
if (!this.#process?.kill()) {
|
||||
this.evaluateInternal({
|
||||
expression: "process.exit(0)",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the inspector.
|
||||
* @param url the inspector url, will default to the one provided in the constructor (if any). If none
|
||||
* @returns if the inspector was able to connect
|
||||
*/
|
||||
start(url?: string): Promise<boolean> {
|
||||
return this.#attach({ url });
|
||||
}
|
||||
|
||||
close() {
|
||||
this.#process?.kill();
|
||||
super.close();
|
||||
}
|
||||
|
||||
async launch(request: DAP.LaunchRequest): Promise<void> {
|
||||
this.options = { ...request, type: "launch" };
|
||||
|
||||
try {
|
||||
await this.#launch(request);
|
||||
} catch (error) {
|
||||
// Some clients, like VSCode, will show a system-level popup when a `launch` request fails.
|
||||
// Instead, we want to show the error as a sidebar notification.
|
||||
const { message } = unknownToError(error);
|
||||
|
||||
this.emitAdapterEvent("output", {
|
||||
category: "stderr",
|
||||
output: `Failed to start debugger.\n${message}`,
|
||||
});
|
||||
|
||||
this.terminate();
|
||||
}
|
||||
}
|
||||
|
||||
async #launch(request: LaunchRequest): Promise<void> {
|
||||
const {
|
||||
runtime = "bun",
|
||||
runtimeArgs = [],
|
||||
program,
|
||||
args = [],
|
||||
cwd,
|
||||
env = {},
|
||||
strictEnv = false,
|
||||
watchMode = false,
|
||||
stopOnEntry = false,
|
||||
__skipValidation = false,
|
||||
stdin,
|
||||
} = request;
|
||||
|
||||
if (!__skipValidation && !program) {
|
||||
throw new Error("No program specified");
|
||||
}
|
||||
|
||||
const processArgs = [...runtimeArgs];
|
||||
|
||||
if (program === "-" && stdin) {
|
||||
processArgs.push("--eval", stdin);
|
||||
} else if (program) {
|
||||
processArgs.push(program);
|
||||
}
|
||||
|
||||
processArgs.push(...args);
|
||||
|
||||
if (program && isTestJavaScript(program) && !runtimeArgs.includes("test")) {
|
||||
processArgs.unshift("test");
|
||||
}
|
||||
|
||||
if (watchMode && !runtimeArgs.includes("--watch") && !runtimeArgs.includes("--hot")) {
|
||||
processArgs.unshift(watchMode === "hot" ? "--hot" : "--watch");
|
||||
}
|
||||
|
||||
const processEnv = strictEnv
|
||||
? {
|
||||
...env,
|
||||
}
|
||||
: {
|
||||
...process.env,
|
||||
...env,
|
||||
};
|
||||
|
||||
if (process.platform !== "win32") {
|
||||
// we're on unix
|
||||
const url = `ws+unix://${randomUnixPath()}`;
|
||||
const signal = new UnixSignal();
|
||||
|
||||
signal.on("Signal.received", () => {
|
||||
this.#attach({ url });
|
||||
});
|
||||
|
||||
this.once("Adapter.terminated", () => {
|
||||
signal.close();
|
||||
});
|
||||
|
||||
const query = stopOnEntry ? "break=1" : "wait=1";
|
||||
processEnv["BUN_INSPECT"] = `${url}?${query}`;
|
||||
processEnv["BUN_INSPECT_NOTIFY"] = signal.url;
|
||||
|
||||
// This is probably not correct, but it's the best we can do for now.
|
||||
processEnv["FORCE_COLOR"] = "1";
|
||||
processEnv["BUN_QUIET_DEBUG_LOGS"] = "1";
|
||||
processEnv["BUN_DEBUG_QUIET_LOGS"] = "1";
|
||||
|
||||
const started = await this.#spawn({
|
||||
command: runtime,
|
||||
args: processArgs,
|
||||
env: processEnv,
|
||||
cwd,
|
||||
isDebugee: true,
|
||||
});
|
||||
|
||||
if (!started) {
|
||||
throw new Error("Program could not be started.");
|
||||
}
|
||||
} else {
|
||||
// we're on windows
|
||||
// Create TCPSocketSignal
|
||||
const url = `ws://127.0.0.1:${await getAvailablePort()}/${getRandomId()}`; // 127.0.0.1 so it resolves correctly on windows
|
||||
const signal = new TCPSocketSignal(await getAvailablePort());
|
||||
|
||||
signal.on("Signal.received", async () => {
|
||||
this.#attach({ url });
|
||||
});
|
||||
|
||||
this.once("Adapter.terminated", () => {
|
||||
signal.close();
|
||||
});
|
||||
|
||||
const query = stopOnEntry ? "break=1" : "wait=1";
|
||||
processEnv["BUN_INSPECT"] = `${url}?${query}`;
|
||||
processEnv["BUN_INSPECT_NOTIFY"] = signal.url; // 127.0.0.1 so it resolves correctly on windows
|
||||
|
||||
// This is probably not correct, but it's the best we can do for now.
|
||||
processEnv["FORCE_COLOR"] = "1";
|
||||
processEnv["BUN_QUIET_DEBUG_LOGS"] = "1";
|
||||
processEnv["BUN_DEBUG_QUIET_LOGS"] = "1";
|
||||
|
||||
const started = await this.#spawn({
|
||||
command: runtime,
|
||||
args: processArgs,
|
||||
env: processEnv,
|
||||
cwd,
|
||||
isDebugee: true,
|
||||
});
|
||||
|
||||
if (!started) {
|
||||
throw new Error("Program could not be started.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async #spawn(options: {
|
||||
command: string;
|
||||
args?: string[];
|
||||
cwd?: string;
|
||||
env?: Record<string, string | undefined>;
|
||||
isDebugee?: boolean;
|
||||
}): Promise<boolean> {
|
||||
const { command, args = [], cwd, env, isDebugee } = options;
|
||||
const request = { command, args, cwd, env };
|
||||
this.emit("Process.requested", request);
|
||||
|
||||
let subprocess: ChildProcess;
|
||||
try {
|
||||
subprocess = spawn(command, args, {
|
||||
...request,
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
} catch (cause) {
|
||||
this.emit("Process.exited", new Error("Failed to spawn process", { cause }), null);
|
||||
return false;
|
||||
}
|
||||
|
||||
subprocess.on("spawn", () => {
|
||||
this.emit("Process.spawned", subprocess);
|
||||
|
||||
if (isDebugee) {
|
||||
this.#process = subprocess;
|
||||
this.emitAdapterEvent("process", {
|
||||
name: `${command} ${args.join(" ")}`,
|
||||
systemProcessId: subprocess.pid,
|
||||
isLocalProcess: true,
|
||||
startMethod: "launch",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
subprocess.on("exit", (code, signal) => {
|
||||
this.emit("Process.exited", code, signal);
|
||||
|
||||
if (isDebugee) {
|
||||
this.#process = undefined;
|
||||
this.emitAdapterEvent("exited", {
|
||||
exitCode: code ?? -1,
|
||||
});
|
||||
this.emitAdapterEvent("terminated");
|
||||
}
|
||||
});
|
||||
|
||||
subprocess.stdout?.on("data", data => {
|
||||
this.emit("Process.stdout", data.toString());
|
||||
});
|
||||
|
||||
subprocess.stderr?.on("data", data => {
|
||||
this.emit("Process.stderr", data.toString());
|
||||
});
|
||||
|
||||
return new Promise(resolve => {
|
||||
subprocess.on("spawn", () => resolve(true));
|
||||
subprocess.on("exit", () => resolve(false));
|
||||
subprocess.on("error", () => resolve(false));
|
||||
});
|
||||
}
|
||||
|
||||
async attach(request: AttachRequest): Promise<void> {
|
||||
this.options = { ...request, type: "attach" };
|
||||
|
||||
try {
|
||||
await this.#attach(request);
|
||||
} catch (error) {
|
||||
// Some clients, like VSCode, will show a system-level popup when a `launch` request fails.
|
||||
// Instead, we want to show the error as a sidebar notification.
|
||||
const { message } = unknownToError(error);
|
||||
this.emitAdapterEvent("output", {
|
||||
category: "stderr",
|
||||
output: `Failed to start debugger.\n${message}`,
|
||||
});
|
||||
this.terminate();
|
||||
}
|
||||
}
|
||||
|
||||
async #attach(request: AttachRequest): Promise<boolean> {
|
||||
const { url } = request;
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const ok = await this.inspector.start(url);
|
||||
if (ok) {
|
||||
return true;
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, 100 * i));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const DebugAdapter = WebSocketDebugAdapter;
|
||||
|
||||
function stoppedReason(reason: JSC.Debugger.PausedEvent["reason"]): DAP.StoppedEvent["reason"] {
|
||||
switch (reason) {
|
||||
case "Breakpoint":
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
import type { Socket } from "node:net";
|
||||
const enum FramerState {
|
||||
WaitingForLength,
|
||||
WaitingForMessage,
|
||||
}
|
||||
|
||||
let socketFramerMessageLengthBuffer: Buffer;
|
||||
export class SocketFramer {
|
||||
state: FramerState = FramerState.WaitingForLength;
|
||||
pendingLength: number = 0;
|
||||
sizeBuffer: Buffer = Buffer.alloc(4);
|
||||
sizeBufferIndex: number = 0;
|
||||
bufferedData: Buffer = Buffer.alloc(0);
|
||||
socket: Socket;
|
||||
private onMessage: (message: string | string[]) => void;
|
||||
|
||||
constructor(socket: Socket, onMessage: (message: string | string[]) => void) {
|
||||
this.socket = socket;
|
||||
this.onMessage = onMessage;
|
||||
|
||||
if (!socketFramerMessageLengthBuffer) {
|
||||
socketFramerMessageLengthBuffer = Buffer.alloc(4);
|
||||
}
|
||||
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.state = FramerState.WaitingForLength;
|
||||
this.bufferedData = Buffer.alloc(0);
|
||||
this.sizeBufferIndex = 0;
|
||||
this.sizeBuffer = Buffer.alloc(4);
|
||||
}
|
||||
|
||||
send(data: string): void {
|
||||
socketFramerMessageLengthBuffer.writeUInt32BE(data.length, 0);
|
||||
this.socket.write(socketFramerMessageLengthBuffer);
|
||||
this.socket.write(data);
|
||||
}
|
||||
|
||||
onData(data: Buffer): void {
|
||||
this.bufferedData = this.bufferedData.length > 0 ? Buffer.concat([this.bufferedData, data]) : data;
|
||||
|
||||
let messagesToDeliver: string[] = [];
|
||||
let position = 0;
|
||||
|
||||
while (position < this.bufferedData.length) {
|
||||
// Need 4 bytes for the length
|
||||
if (this.bufferedData.length - position < 4) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Read the length prefix
|
||||
const messageLength = this.bufferedData.readUInt32BE(position);
|
||||
|
||||
// Validate message length
|
||||
if (messageLength <= 0 || messageLength > 1024 * 1024) {
|
||||
// 1MB max
|
||||
// Try to resync by looking for the next valid message
|
||||
let newPosition = position + 1;
|
||||
let found = false;
|
||||
|
||||
while (newPosition < this.bufferedData.length - 4) {
|
||||
const testLength = this.bufferedData.readUInt32BE(newPosition);
|
||||
|
||||
if (testLength > 0 && testLength <= 1024 * 1024) {
|
||||
// Verify we can read the full message
|
||||
if (this.bufferedData.length - newPosition - 4 >= testLength) {
|
||||
const testMessage = this.bufferedData.toString("utf-8", newPosition + 4, newPosition + 4 + testLength);
|
||||
|
||||
if (testMessage.startsWith('{"')) {
|
||||
position = newPosition;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newPosition++;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// Couldn't find a valid message, discard buffer up to this point
|
||||
this.bufferedData = this.bufferedData.slice(position + 4);
|
||||
return;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if we have the complete message
|
||||
if (this.bufferedData.length - position - 4 < messageLength) {
|
||||
break;
|
||||
}
|
||||
|
||||
const message = this.bufferedData.toString("utf-8", position + 4, position + 4 + messageLength);
|
||||
if (message.startsWith('{"')) {
|
||||
messagesToDeliver.push(message);
|
||||
}
|
||||
|
||||
position += 4 + messageLength;
|
||||
}
|
||||
|
||||
if (position > 0) {
|
||||
this.bufferedData =
|
||||
position < this.bufferedData.length ? this.bufferedData.slice(position) : SocketFramer.emptyBuffer;
|
||||
}
|
||||
|
||||
if (messagesToDeliver.length === 1) {
|
||||
this.onMessage(messagesToDeliver[0]);
|
||||
} else if (messagesToDeliver.length > 1) {
|
||||
this.onMessage(messagesToDeliver);
|
||||
}
|
||||
}
|
||||
|
||||
private static emptyBuffer = Buffer.from([]);
|
||||
}
|
||||
@@ -11,8 +11,6 @@ export type UnixSignalEventMap = {
|
||||
"Signal.error": [Error];
|
||||
"Signal.received": [string];
|
||||
"Signal.closed": [];
|
||||
"Signal.Socket.closed": [socket: Socket];
|
||||
"Signal.Socket.connect": [socket: Socket];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -23,7 +21,7 @@ export class UnixSignal extends EventEmitter<UnixSignalEventMap> {
|
||||
#server: Server;
|
||||
#ready: Promise<void>;
|
||||
|
||||
constructor(path?: string | URL | undefined) {
|
||||
constructor(path?: string | URL) {
|
||||
super();
|
||||
this.#path = path ? parseUnixPath(path) : randomUnixPath();
|
||||
this.#server = createServer();
|
||||
@@ -31,13 +29,9 @@ export class UnixSignal extends EventEmitter<UnixSignalEventMap> {
|
||||
this.#server.on("error", error => this.emit("Signal.error", error));
|
||||
this.#server.on("close", () => this.emit("Signal.closed"));
|
||||
this.#server.on("connection", socket => {
|
||||
this.emit("Signal.Socket.connect", socket);
|
||||
socket.on("data", data => {
|
||||
this.emit("Signal.received", data.toString());
|
||||
});
|
||||
socket.on("close", () => {
|
||||
this.emit("Signal.Socket.closed", socket);
|
||||
});
|
||||
});
|
||||
this.#ready = new Promise((resolve, reject) => {
|
||||
this.#server.on("listening", resolve);
|
||||
@@ -51,7 +45,7 @@ export class UnixSignal extends EventEmitter<UnixSignalEventMap> {
|
||||
console.log(event, ...args);
|
||||
}
|
||||
|
||||
return super.emit(event, ...(args as never));
|
||||
return super.emit(event, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,8 +91,6 @@ export type TCPSocketSignalEventMap = {
|
||||
"Signal.error": [Error];
|
||||
"Signal.closed": [];
|
||||
"Signal.received": [string];
|
||||
"Signal.Socket.closed": [socket: Socket];
|
||||
"Signal.Socket.connect": [socket: Socket];
|
||||
};
|
||||
|
||||
export class TCPSocketSignal extends EventEmitter {
|
||||
@@ -111,8 +103,6 @@ export class TCPSocketSignal extends EventEmitter {
|
||||
this.#port = port;
|
||||
|
||||
this.#server = createServer((socket: Socket) => {
|
||||
this.emit("Signal.Socket.connect", socket);
|
||||
|
||||
socket.on("data", data => {
|
||||
this.emit("Signal.received", data.toString());
|
||||
});
|
||||
@@ -122,14 +112,10 @@ export class TCPSocketSignal extends EventEmitter {
|
||||
});
|
||||
|
||||
socket.on("close", () => {
|
||||
this.emit("Signal.Socket.closed", socket);
|
||||
this.emit("Signal.closed");
|
||||
});
|
||||
});
|
||||
|
||||
this.#server.on("close", () => {
|
||||
this.emit("Signal.closed");
|
||||
});
|
||||
|
||||
this.#ready = new Promise((resolve, reject) => {
|
||||
this.#server.listen(this.#port, () => {
|
||||
this.emit("Signal.listening");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { SourceMap } from "./sourcemap.js";
|
||||
import { SourceMap } from "./sourcemap";
|
||||
|
||||
test("works without source map", () => {
|
||||
const sourceMap = getSourceMap("without-sourcemap.js");
|
||||
|
||||
@@ -21,15 +21,7 @@ export type Location = {
|
||||
);
|
||||
|
||||
export interface SourceMap {
|
||||
/**
|
||||
* Converts a location in the original source to a location in the generated source.
|
||||
* @param request A request
|
||||
*/
|
||||
generatedLocation(request: LocationRequest): Location;
|
||||
/**
|
||||
* Converts a location in the generated source to a location in the original source.
|
||||
* @param request A request
|
||||
*/
|
||||
originalLocation(request: LocationRequest): Location;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext"],
|
||||
"module": "NodeNext",
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"moduleResolution": "nodenext",
|
||||
"moduleDetection": "force",
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true,
|
||||
// "composite": true,
|
||||
"composite": true,
|
||||
"strict": true,
|
||||
"downlevelIteration": true,
|
||||
"skipLibCheck": true,
|
||||
@@ -15,7 +15,7 @@
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"inlineSourceMap": true,
|
||||
"allowJs": true,
|
||||
"outDir": "dist"
|
||||
"outDir": "dist",
|
||||
},
|
||||
"include": ["src", "scripts", "../bun-types/index.d.ts", "../bun-inspector-protocol/**/*.ts"]
|
||||
"include": ["src", "scripts", "../bun-types/index.d.ts", "../bun-inspector-protocol/src"]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export type * from "./src/inspector/index.js";
|
||||
export * from "./src/inspector/websocket.js";
|
||||
export type * from "./src/protocol/index.js";
|
||||
export * from "./src/util/preview.js";
|
||||
export type * from "./src/inspector";
|
||||
export * from "./src/inspector/websocket";
|
||||
export type * from "./src/protocol";
|
||||
export * from "./src/util/preview";
|
||||
|
||||
@@ -1,7 +1,26 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { readFileSync, writeFileSync, realpathSync } from "node:fs";
|
||||
import { readFileSync, writeFileSync } from "node:fs";
|
||||
import type { Domain, Property, Protocol } from "../src/protocol/schema";
|
||||
import path from "node:path";
|
||||
|
||||
run().catch(console.error);
|
||||
|
||||
async function run() {
|
||||
const cwd = new URL("../src/protocol/", import.meta.url);
|
||||
const runner = "Bun" in globalThis ? "bunx" : "npx";
|
||||
const write = (name: string, data: string) => {
|
||||
const path = new URL(name, cwd);
|
||||
writeFileSync(path, data);
|
||||
spawnSync(runner, ["prettier", "--write", path.pathname], { cwd, stdio: "ignore" });
|
||||
};
|
||||
const base = readFileSync(new URL("protocol.d.ts", cwd), "utf-8");
|
||||
const baseNoComments = base.replace(/\/\/.*/g, "");
|
||||
const jsc = await downloadJsc();
|
||||
write("jsc/protocol.json", JSON.stringify(jsc));
|
||||
write("jsc/index.d.ts", "// GENERATED - DO NOT EDIT\n" + formatProtocol(jsc, baseNoComments));
|
||||
const v8 = await downloadV8();
|
||||
write("v8/protocol.json", JSON.stringify(v8));
|
||||
write("v8/index.d.ts", "// GENERATED - DO NOT EDIT\n" + formatProtocol(v8, baseNoComments));
|
||||
}
|
||||
|
||||
function formatProtocol(protocol: Protocol, extraTs?: string): string {
|
||||
const { name, domains } = protocol;
|
||||
@@ -10,7 +29,6 @@ function formatProtocol(protocol: Protocol, extraTs?: string): string {
|
||||
let body = `export namespace ${name} {`;
|
||||
for (const { domain, types = [], events = [], commands = [] } of domains) {
|
||||
body += `export namespace ${domain} {`;
|
||||
|
||||
for (const type of types) {
|
||||
body += formatProperty(type);
|
||||
}
|
||||
@@ -135,12 +153,32 @@ async function downloadV8(): Promise<Protocol> {
|
||||
}));
|
||||
}
|
||||
|
||||
async function getJSC(): Promise<Protocol> {
|
||||
let bunExecutable = Bun.which("bun-debug") || process.execPath;
|
||||
if (!bunExecutable) {
|
||||
throw new Error("bun-debug not found");
|
||||
}
|
||||
bunExecutable = realpathSync(bunExecutable);
|
||||
/**
|
||||
* @link https://github.com/WebKit/WebKit/tree/main/Source/JavaScriptCore/inspector/protocol
|
||||
*/
|
||||
async function downloadJsc(): Promise<Protocol> {
|
||||
const baseUrl = "https://raw.githubusercontent.com/WebKit/WebKit/main/Source/JavaScriptCore/inspector/protocol";
|
||||
const domains = [
|
||||
"Runtime",
|
||||
"Console",
|
||||
"Debugger",
|
||||
"Heap",
|
||||
"ScriptProfiler",
|
||||
"CPUProfiler",
|
||||
"GenericTypes",
|
||||
"Network",
|
||||
"Inspector",
|
||||
];
|
||||
return {
|
||||
name: "JSC",
|
||||
version: {
|
||||
major: 1,
|
||||
minor: 3,
|
||||
},
|
||||
domains: await Promise.all(domains.map(domain => download<Domain>(`${baseUrl}/${domain}.json`))).then(domains =>
|
||||
domains.sort((a, b) => a.domain.localeCompare(b.domain)),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
async function download<V>(url: string): Promise<V> {
|
||||
@@ -162,39 +200,3 @@ function toComment(description?: string): string {
|
||||
const lines = ["/**", ...description.split("\n").map(line => ` * ${line.trim()}`), "*/"];
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
const cwd = new URL("../src/protocol/", import.meta.url);
|
||||
const runner = "Bun" in globalThis ? "bunx" : "npx";
|
||||
const write = (name: string, data: string) => {
|
||||
const filePath = path.resolve(__dirname, "..", "src", "protocol", name);
|
||||
writeFileSync(filePath, data);
|
||||
spawnSync(runner, ["prettier", "--write", filePath], { cwd, stdio: "ignore" });
|
||||
};
|
||||
const base = readFileSync(new URL("protocol.d.ts", cwd), "utf-8");
|
||||
const baseNoComments = base.replace(/\/\/.*/g, "");
|
||||
|
||||
const jscJsonFile = path.resolve(__dirname, process.argv.at(-1) ?? "");
|
||||
let jscJSONFile;
|
||||
try {
|
||||
jscJSONFile = await Bun.file(jscJsonFile).json();
|
||||
} catch (error) {
|
||||
console.warn("Failed to read CombinedDomains.json from WebKit build. Is this a WebKit build from Bun?");
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const jsc = {
|
||||
name: "JSC",
|
||||
version: {
|
||||
major: 1,
|
||||
minor: 4,
|
||||
},
|
||||
domains: jscJSONFile.domains
|
||||
.filter(a => a.debuggableTypes?.includes?.("javascript"))
|
||||
.sort((a, b) => a.domain.localeCompare(b.domain)),
|
||||
};
|
||||
write("jsc/protocol.json", JSON.stringify(jsc, null, 2));
|
||||
write("jsc/index.d.ts", "// GENERATED - DO NOT EDIT\n" + formatProtocol(jsc, baseNoComments));
|
||||
const v8 = await downloadV8();
|
||||
write("v8/protocol.json", JSON.stringify(v8));
|
||||
write("v8/index.d.ts", "// GENERATED - DO NOT EDIT\n" + formatProtocol(v8, baseNoComments));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user