mirror of
https://github.com/oven-sh/bun
synced 2026-02-22 16:51:50 +00:00
Compare commits
65 Commits
jarred/pos
...
ciro/disab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6a472d975 | ||
|
|
dd32e6b416 | ||
|
|
b453360dff | ||
|
|
1476e4c958 | ||
|
|
958133b7f5 | ||
|
|
eacf89e5bf | ||
|
|
fa6ac405a4 | ||
|
|
4c8cbecb08 | ||
|
|
00b7d6479b | ||
|
|
bcf023c829 | ||
|
|
b7b1ca8ebe | ||
|
|
784bc4e012 | ||
|
|
dd5c40dab7 | ||
|
|
3a4a9ae4e9 | ||
|
|
9d1a35b658 | ||
|
|
61cc9c3947 | ||
|
|
e904a181d8 | ||
|
|
55a0bdc68d | ||
|
|
55454f7910 | ||
|
|
e4aeb761e4 | ||
|
|
f9efe94b85 | ||
|
|
7eb8a3feae | ||
|
|
d7ed9c673e | ||
|
|
b4dce96c40 | ||
|
|
52ef8b1778 | ||
|
|
baff3c900e | ||
|
|
23299dadf6 | ||
|
|
0d5e4e162b | ||
|
|
d27594ecf4 | ||
|
|
a2e2d114e9 | ||
|
|
da3d64b1ef | ||
|
|
ce64e04b16 | ||
|
|
55473cb64a | ||
|
|
752441d911 | ||
|
|
da5d4d791c | ||
|
|
6d453be7d9 | ||
|
|
2d441d868b | ||
|
|
56ad4cc4a6 | ||
|
|
d2acb2eac0 | ||
|
|
de7eafbdd1 | ||
|
|
4114986c3e | ||
|
|
8aa451c2dc | ||
|
|
497cef9759 | ||
|
|
dd57b95546 | ||
|
|
ea7c4986d7 | ||
|
|
6c7edf2dbe | ||
|
|
bf2f153f5c | ||
|
|
f64a4c4ace | ||
|
|
0216431c98 | ||
|
|
ae289c4858 | ||
|
|
5d1609fe5c | ||
|
|
471fe7b886 | ||
|
|
08222eda71 | ||
|
|
6f8c5959d0 | ||
|
|
40d5e745c9 | ||
|
|
225bfd54fa | ||
|
|
a6ca8c40d4 | ||
|
|
b52ad226a5 | ||
|
|
5f8f805db9 | ||
|
|
37c98bebd6 | ||
|
|
bd01df19c1 | ||
|
|
7fd16ebffa | ||
|
|
1bb211df56 | ||
|
|
bdd0b89f16 | ||
|
|
992c52230d |
1754
.buildkite/ci.mjs
1754
.buildkite/ci.mjs
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" "$@"
|
||||
|
||||
79
.github/workflows/update-cares.yml
vendored
79
.github/workflows/update-cares.yml
vendored
@@ -2,7 +2,7 @@ name: Update c-ares
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * 0"
|
||||
- cron: "0 4 * * 0"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -18,44 +18,75 @@ jobs:
|
||||
- name: Check c-ares version
|
||||
id: check-version
|
||||
run: |
|
||||
CURRENT_VERSION=$(grep -oP 'CARES_VERSION\s+\K\S+' cmake/targets/BuildCares.cmake)
|
||||
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 current version in BuildCares.cmake"
|
||||
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" ]; then
|
||||
echo "Error: Could not fetch latest version from GitHub API"
|
||||
if [ -z "$LATEST_SHA" ] || [ "$LATEST_SHA" = "null" ]; then
|
||||
echo "Error: Could not fetch SHA for tag $LATEST_TAG"
|
||||
exit 1
|
||||
fi
|
||||
if [ ${#LATEST_SHA} -ne 40 ]; then
|
||||
echo "Error: Invalid SHA length"
|
||||
|
||||
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: Create PR if update needed
|
||||
- name: Update version if needed
|
||||
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
BRANCH="update-cares-${GITHUB_SHA::8}"
|
||||
git checkout -b "$BRANCH"
|
||||
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
|
||||
|
||||
sed -i "s/CARES_VERSION\s\+[0-9a-f]\+/CARES_VERSION ${{ steps.check-version.outputs.latest }}/" cmake/targets/BuildCares.cmake
|
||||
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add cmake/targets/BuildCares.cmake
|
||||
git commit -m "deps: update c-ares to ${{ steps.check-version.outputs.latest }}"
|
||||
git push origin "$BRANCH"
|
||||
- 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?
|
||||
|
||||
gh pr create \
|
||||
--title "deps: update c-ares to ${LATEST_SHA::8}" \
|
||||
--body "Updates c-ares from ${CURRENT_VERSION::8} to ${LATEST_SHA::8}" \
|
||||
--base main \
|
||||
--head "$BRANCH"
|
||||
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)
|
||||
|
||||
79
.github/workflows/update-libarchive.yml
vendored
79
.github/workflows/update-libarchive.yml
vendored
@@ -2,7 +2,7 @@ name: Update libarchive
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * 0"
|
||||
- cron: "0 3 * * 0"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -18,44 +18,75 @@ jobs:
|
||||
- name: Check libarchive version
|
||||
id: check-version
|
||||
run: |
|
||||
CURRENT_VERSION=$(grep -oP 'LIBARCHIVE_VERSION\s+\K\S+' cmake/targets/BuildLibArchive.cmake)
|
||||
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 current version in BuildLibArchive.cmake"
|
||||
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" ]; then
|
||||
echo "Error: Could not fetch latest version from GitHub API"
|
||||
if [ -z "$LATEST_SHA" ] || [ "$LATEST_SHA" = "null" ]; then
|
||||
echo "Error: Could not fetch SHA for tag $LATEST_TAG"
|
||||
exit 1
|
||||
fi
|
||||
if [ ${#LATEST_SHA} -ne 40 ]; then
|
||||
echo "Error: Invalid SHA length"
|
||||
|
||||
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: Create PR if update needed
|
||||
- name: Update version if needed
|
||||
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
BRANCH="update-libarchive-${GITHUB_SHA::8}"
|
||||
git checkout -b "$BRANCH"
|
||||
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
|
||||
|
||||
sed -i "s/LIBARCHIVE_VERSION\s\+[0-9a-f]\+/LIBARCHIVE_VERSION ${{ steps.check-version.outputs.latest }}/" cmake/targets/BuildLibArchive.cmake
|
||||
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add cmake/targets/BuildLibArchive.cmake
|
||||
git commit -m "deps: update libarchive to ${{ steps.check-version.outputs.latest }}"
|
||||
git push origin "$BRANCH"
|
||||
- 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?
|
||||
|
||||
gh pr create \
|
||||
--title "deps: update libarchive to ${LATEST_SHA::8}" \
|
||||
--body "Updates libarchive from ${CURRENT_VERSION::8} to ${LATEST_SHA::8}" \
|
||||
--base main \
|
||||
--head "$BRANCH"
|
||||
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)
|
||||
|
||||
79
.github/workflows/update-libdeflate.yml
vendored
79
.github/workflows/update-libdeflate.yml
vendored
@@ -2,7 +2,7 @@ name: Update libdeflate
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * 0"
|
||||
- cron: "0 2 * * 0"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -18,44 +18,75 @@ jobs:
|
||||
- name: Check libdeflate version
|
||||
id: check-version
|
||||
run: |
|
||||
CURRENT_VERSION=$(grep -oP 'LIBDEFLATE_VERSION\s+\K\S+' cmake/targets/BuildLibDeflate.cmake)
|
||||
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 current version in BuildLibDeflate.cmake"
|
||||
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" ]; then
|
||||
echo "Error: Could not fetch latest version from GitHub API"
|
||||
if [ -z "$LATEST_SHA" ] || [ "$LATEST_SHA" = "null" ]; then
|
||||
echo "Error: Could not fetch SHA for tag $LATEST_TAG"
|
||||
exit 1
|
||||
fi
|
||||
if [ ${#LATEST_SHA} -ne 40 ]; then
|
||||
echo "Error: Invalid SHA length"
|
||||
|
||||
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: Create PR if update needed
|
||||
- name: Update version if needed
|
||||
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
BRANCH="update-libdeflate-${GITHUB_SHA::8}"
|
||||
git checkout -b "$BRANCH"
|
||||
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
|
||||
|
||||
sed -i "s/LIBDEFLATE_VERSION\s\+[0-9a-f]\+/LIBDEFLATE_VERSION ${{ steps.check-version.outputs.latest }}/" cmake/targets/BuildLibDeflate.cmake
|
||||
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add cmake/targets/BuildLibDeflate.cmake
|
||||
git commit -m "deps: update libdeflate to ${{ steps.check-version.outputs.latest }}"
|
||||
git push origin "$BRANCH"
|
||||
- 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?
|
||||
|
||||
gh pr create \
|
||||
--title "deps: update libdeflate to ${LATEST_SHA::8}" \
|
||||
--body "Updates libdeflate from ${CURRENT_VERSION::8} to ${LATEST_SHA::8}" \
|
||||
--base main \
|
||||
--head "$BRANCH"
|
||||
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)
|
||||
|
||||
79
.github/workflows/update-lolhtml.yml
vendored
79
.github/workflows/update-lolhtml.yml
vendored
@@ -2,7 +2,7 @@ name: Update lolhtml
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * 0"
|
||||
- cron: "0 1 * * 0"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -18,44 +18,75 @@ jobs:
|
||||
- name: Check lolhtml version
|
||||
id: check-version
|
||||
run: |
|
||||
CURRENT_VERSION=$(grep -oP 'LOLHTML_VERSION\s+\K\S+' cmake/targets/BuildLolHtml.cmake)
|
||||
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 current version in BuildLolHtml.cmake"
|
||||
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" ]; then
|
||||
echo "Error: Could not fetch latest version from GitHub API"
|
||||
if [ -z "$LATEST_SHA" ] || [ "$LATEST_SHA" = "null" ]; then
|
||||
echo "Error: Could not fetch SHA for tag $LATEST_TAG"
|
||||
exit 1
|
||||
fi
|
||||
if [ ${#LATEST_SHA} -ne 40 ]; then
|
||||
echo "Error: Invalid SHA length"
|
||||
|
||||
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: Create PR if update needed
|
||||
- name: Update version if needed
|
||||
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
BRANCH="update-lolhtml-${GITHUB_SHA::8}"
|
||||
git checkout -b "$BRANCH"
|
||||
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
|
||||
|
||||
sed -i "s/LOLHTML_VERSION\s\+[0-9a-f]\+/LOLHTML_VERSION ${{ steps.check-version.outputs.latest }}/" cmake/targets/BuildLolHtml.cmake
|
||||
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add cmake/targets/BuildLolHtml.cmake
|
||||
git commit -m "deps: update lolhtml to ${{ steps.check-version.outputs.latest }}"
|
||||
git push origin "$BRANCH"
|
||||
- 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?
|
||||
|
||||
gh pr create \
|
||||
--title "deps: update lolhtml to ${LATEST_SHA::8}" \
|
||||
--body "Updates lolhtml from ${CURRENT_VERSION::8} to ${LATEST_SHA::8}" \
|
||||
--base main \
|
||||
--head "$BRANCH"
|
||||
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)
|
||||
|
||||
79
.github/workflows/update-lshpack.yml
vendored
79
.github/workflows/update-lshpack.yml
vendored
@@ -2,7 +2,7 @@ name: Update lshpack
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * 0"
|
||||
- cron: "0 5 * * 0"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -18,44 +18,75 @@ jobs:
|
||||
- name: Check lshpack version
|
||||
id: check-version
|
||||
run: |
|
||||
CURRENT_VERSION=$(grep -oP 'LSHPACK_VERSION\s+\K\S+' cmake/targets/BuildLshpack.cmake)
|
||||
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 current version in BuildLshpack.cmake"
|
||||
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" ]; then
|
||||
echo "Error: Could not fetch latest version from GitHub API"
|
||||
if [ -z "$LATEST_SHA" ] || [ "$LATEST_SHA" = "null" ]; then
|
||||
echo "Error: Could not fetch SHA for tag $LATEST_TAG"
|
||||
exit 1
|
||||
fi
|
||||
if [ ${#LATEST_SHA} -ne 40 ]; then
|
||||
echo "Error: Invalid SHA length"
|
||||
|
||||
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: Create PR if update needed
|
||||
- name: Update version if needed
|
||||
if: success() && steps.check-version.outputs.current != steps.check-version.outputs.latest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
BRANCH="update-lshpack-${GITHUB_SHA::8}"
|
||||
git checkout -b "$BRANCH"
|
||||
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
|
||||
|
||||
sed -i "s/LSHPACK_VERSION\s\+[0-9a-f]\+/LSHPACK_VERSION ${{ steps.check-version.outputs.latest }}/" cmake/targets/BuildLshpack.cmake
|
||||
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add cmake/targets/BuildLshpack.cmake
|
||||
git commit -m "deps: update lshpack to ${{ steps.check-version.outputs.latest }}"
|
||||
git push origin "$BRANCH"
|
||||
- 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?
|
||||
|
||||
gh pr create \
|
||||
--title "deps: update lshpack to ${LATEST_SHA::8}" \
|
||||
--body "Updates lshpack from ${CURRENT_VERSION::8} to ${LATEST_SHA::8}" \
|
||||
--base main \
|
||||
--head "$BRANCH"
|
||||
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
Normal file
109
.github/workflows/update-sqlite3.yml
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
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)
|
||||
@@ -5,6 +5,5 @@ 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
|
||||
|
||||
2
.vscode/launch.json
generated
vendored
2
.vscode/launch.json
generated
vendored
@@ -1257,4 +1257,4 @@
|
||||
"description": "Usage: bun test [...]",
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
If you are using Windows, please refer to [this guide](/docs/project/building-windows.md)
|
||||
|
||||
{% details summary="For Ubuntu users" %}
|
||||
TL;DR: Ubuntu 22.04 is suggested.
|
||||
|
||||
@@ -414,6 +414,15 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ else()
|
||||
setx(RELEASE OFF)
|
||||
endif()
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES "Debug|RelWithDebInfo")
|
||||
if(CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
setx(DEBUG ON)
|
||||
else()
|
||||
setx(DEBUG OFF)
|
||||
|
||||
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
c-ares/c-ares
|
||||
COMMIT
|
||||
c29e75d54c3743783d51a609980495cf553b4bca
|
||||
41ee334af3e3d0027dca5e477855d0244936bd49
|
||||
)
|
||||
|
||||
register_cmake_command(
|
||||
|
||||
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
ebiggers/libdeflate
|
||||
COMMIT
|
||||
dc76454a39e7e83b68c3704b6e3784654f8d5ac5
|
||||
9d624d1d8ba82c690d6d6be1d0a961fc5a983ea4
|
||||
)
|
||||
|
||||
register_cmake_command(
|
||||
|
||||
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
cloudflare/lol-html
|
||||
COMMIT
|
||||
8d4c273ded322193d017042d1f48df2766b0f88b
|
||||
4f8becea13a0021c8b71abd2dcc5899384973b66
|
||||
)
|
||||
|
||||
set(LOLHTML_CWD ${VENDOR_PATH}/lolhtml/c-api)
|
||||
@@ -49,6 +49,8 @@ register_command(
|
||||
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
|
||||
3d0f1fc1d6e66a642e7a98c55deb38aa986eb4b0
|
||||
32e96f10593c7cb8553cd8c9c12721100ae9e924
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
|
||||
@@ -1,15 +1,42 @@
|
||||
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
|
||||
$ENV{HOME}/.cargo/bin
|
||||
${CARGO_HOME}/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()
|
||||
|
||||
|
||||
@@ -671,7 +671,7 @@ _bun() {
|
||||
cmd)
|
||||
local -a scripts_list
|
||||
IFS=$'\n' scripts_list=($(SHELL=zsh bun getcompletes i))
|
||||
scripts="scripts:scripts:(($scripts_list))"
|
||||
scripts="scripts:scripts:((${scripts_list//:/\\\\:}))"
|
||||
IFS=$'\n' files_list=($(SHELL=zsh bun getcompletes j))
|
||||
|
||||
main_commands=(
|
||||
@@ -871,8 +871,8 @@ _bun_run_param_script_completion() {
|
||||
IFS=$'\n' scripts_list=($(SHELL=zsh bun getcompletes s))
|
||||
IFS=$'\n' bins=($(SHELL=zsh bun getcompletes b))
|
||||
|
||||
_alternative "scripts:scripts:(($scripts_list))"
|
||||
_alternative "bin:bin:(($bins))"
|
||||
_alternative "scripts:scripts:((${scripts_list//:/\\\\:}))"
|
||||
_alternative "bin:bin:((${bins//:/\\\\:}))"
|
||||
_alternative "files:file:_files -g '*.(js|ts|jsx|tsx|wasm)'"
|
||||
}
|
||||
|
||||
|
||||
@@ -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` or `.env.development` (depending on value of `NODE_ENV`)
|
||||
- `.env.production`, `.env.development`, `.env.test` (depending on value of `NODE_ENV`)
|
||||
- `.env.local`
|
||||
|
||||
```txt#.env
|
||||
|
||||
@@ -30,7 +30,6 @@ Bun implements the vast majority of Jest's matchers, but compatibility isn't 100
|
||||
|
||||
Some notable missing features:
|
||||
|
||||
- `expect().toMatchInlineSnapshot()`
|
||||
- `expect().toHaveReturned()`
|
||||
|
||||
---
|
||||
|
||||
@@ -4,10 +4,6 @@ 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";
|
||||
|
||||
@@ -96,4 +92,4 @@ Ran 1 tests across 1 files. [102.00ms]
|
||||
|
||||
---
|
||||
|
||||
See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/mocks) for complete documentation on mocking with the Bun test runner.
|
||||
See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/snapshots) for complete documentation on snapshots with the Bun test runner.
|
||||
|
||||
@@ -4,10 +4,6 @@ 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";
|
||||
|
||||
@@ -47,4 +43,4 @@ Ran 1 tests across 1 files. [102.00ms]
|
||||
|
||||
---
|
||||
|
||||
See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/mocks) for complete documentation on mocking with the Bun test runner.
|
||||
See [Docs > Test Runner > Snapshots](https://bun.sh/docs/test/snapshots) for complete documentation on snapshots with the Bun test runner.
|
||||
|
||||
@@ -189,14 +189,19 @@ 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 `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.
|
||||
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.
|
||||
|
||||
<!--
|
||||
## Native
|
||||
|
||||
@@ -355,7 +355,7 @@ Bun.build({
|
||||
|
||||
{% /callout %}
|
||||
|
||||
## Lifecycle callbacks
|
||||
## Lifecycle hooks
|
||||
|
||||
Plugins can register callbacks to be run at various points in the lifecycle of a bundle:
|
||||
|
||||
@@ -363,6 +363,8 @@ 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
|
||||
@@ -603,3 +605,98 @@ 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.
|
||||
|
||||
@@ -531,17 +531,17 @@ Bun implements the following matchers. Full Jest compatibility is on the roadmap
|
||||
|
||||
---
|
||||
|
||||
- ❌
|
||||
- ✅
|
||||
- [`.toMatchInlineSnapshot()`](https://jestjs.io/docs/expect#tomatchinlinesnapshotpropertymatchers-inlinesnapshot)
|
||||
|
||||
---
|
||||
|
||||
- ❌
|
||||
- ✅
|
||||
- [`.toThrowErrorMatchingSnapshot()`](https://jestjs.io/docs/expect#tothrowerrormatchingsnapshothint)
|
||||
|
||||
---
|
||||
|
||||
- ❌
|
||||
- ✅
|
||||
- [`.toThrowErrorMatchingInlineSnapshot()`](https://jestjs.io/docs/expect#tothrowerrormatchinginlinesnapshotinlinesnapshot)
|
||||
|
||||
{% /table %}
|
||||
|
||||
10
package.json
10
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "bun",
|
||||
"version": "1.1.38",
|
||||
"version": "1.1.39",
|
||||
"workspaces": [
|
||||
"./packages/bun-types"
|
||||
],
|
||||
@@ -30,8 +30,8 @@
|
||||
"bun-types": "workspace:packages/bun-types"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -B build/debug",
|
||||
"build:debug": "bun run build",
|
||||
"build": "bun run build:debug",
|
||||
"build:debug": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Debug -B build/debug",
|
||||
"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",
|
||||
"build:release:local": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -DWEBKIT_LOCAL=ON -B build/release",
|
||||
"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: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",
|
||||
|
||||
5
packages/bun-build-mdx-rs/.cargo/config.toml
Normal file
5
packages/bun-build-mdx-rs/.cargo/config.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
[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
Normal file
202
packages/bun-build-mdx-rs/.gitignore
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
# 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
|
||||
13
packages/bun-build-mdx-rs/.npmignore
Normal file
13
packages/bun-build-mdx-rs/.npmignore
Normal file
@@ -0,0 +1,13 @@
|
||||
target
|
||||
Cargo.lock
|
||||
.cargo
|
||||
.github
|
||||
npm
|
||||
.eslintrc
|
||||
.prettierignore
|
||||
rustfmt.toml
|
||||
yarn.lock
|
||||
*.node
|
||||
.yarn
|
||||
__test__
|
||||
renovate.json
|
||||
21
packages/bun-build-mdx-rs/Cargo.toml
Normal file
21
packages/bun-build-mdx-rs/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[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"
|
||||
34
packages/bun-build-mdx-rs/README.md
Normal file
34
packages/bun-build-mdx-rs/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# 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);
|
||||
```
|
||||
7
packages/bun-build-mdx-rs/__test__/index.spec.mjs
Normal file
7
packages/bun-build-mdx-rs/__test__/index.spec.mjs
Normal file
@@ -0,0 +1,7 @@
|
||||
import test from 'ava'
|
||||
|
||||
import { sum } from '../index.js'
|
||||
|
||||
test('sum from native', (t) => {
|
||||
t.is(sum(1, 2), 3)
|
||||
})
|
||||
5
packages/bun-build-mdx-rs/build.rs
Normal file
5
packages/bun-build-mdx-rs/build.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
extern crate napi_build;
|
||||
|
||||
fn main() {
|
||||
napi_build::setup();
|
||||
}
|
||||
6
packages/bun-build-mdx-rs/input/index.ts
Normal file
6
packages/bun-build-mdx-rs/input/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
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);
|
||||
11
packages/bun-build-mdx-rs/input/page1.mdx
Normal file
11
packages/bun-build-mdx-rs/input/page1.mdx
Normal file
@@ -0,0 +1,11 @@
|
||||
# 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
|
||||
11
packages/bun-build-mdx-rs/input/page2.mdx
Normal file
11
packages/bun-build-mdx-rs/input/page2.mdx
Normal file
@@ -0,0 +1,11 @@
|
||||
# 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
|
||||
11
packages/bun-build-mdx-rs/input/page3.mdx
Normal file
11
packages/bun-build-mdx-rs/input/page3.mdx
Normal file
@@ -0,0 +1,11 @@
|
||||
# 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
|
||||
11
packages/bun-build-mdx-rs/input/page4.mdx
Normal file
11
packages/bun-build-mdx-rs/input/page4.mdx
Normal file
@@ -0,0 +1,11 @@
|
||||
# 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
|
||||
3
packages/bun-build-mdx-rs/npm/darwin-arm64/README.md
Normal file
3
packages/bun-build-mdx-rs/npm/darwin-arm64/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# `bun-mdx-rs-darwin-arm64`
|
||||
|
||||
This is the **aarch64-apple-darwin** binary for `bun-mdx-rs`
|
||||
18
packages/bun-build-mdx-rs/npm/darwin-arm64/package.json
Normal file
18
packages/bun-build-mdx-rs/npm/darwin-arm64/package.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
3
packages/bun-build-mdx-rs/npm/darwin-x64/README.md
Normal file
3
packages/bun-build-mdx-rs/npm/darwin-x64/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# `bun-mdx-rs-darwin-x64`
|
||||
|
||||
This is the **x86_64-apple-darwin** binary for `bun-mdx-rs`
|
||||
18
packages/bun-build-mdx-rs/npm/darwin-x64/package.json
Normal file
18
packages/bun-build-mdx-rs/npm/darwin-x64/package.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
3
packages/bun-build-mdx-rs/npm/linux-arm64-gnu/README.md
Normal file
3
packages/bun-build-mdx-rs/npm/linux-arm64-gnu/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# `bun-mdx-rs-linux-arm64-gnu`
|
||||
|
||||
This is the **aarch64-unknown-linux-gnu** binary for `bun-mdx-rs`
|
||||
21
packages/bun-build-mdx-rs/npm/linux-arm64-gnu/package.json
Normal file
21
packages/bun-build-mdx-rs/npm/linux-arm64-gnu/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
3
packages/bun-build-mdx-rs/npm/linux-arm64-musl/README.md
Normal file
3
packages/bun-build-mdx-rs/npm/linux-arm64-musl/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# `bun-mdx-rs-linux-arm64-musl`
|
||||
|
||||
This is the **aarch64-unknown-linux-musl** binary for `bun-mdx-rs`
|
||||
21
packages/bun-build-mdx-rs/npm/linux-arm64-musl/package.json
Normal file
21
packages/bun-build-mdx-rs/npm/linux-arm64-musl/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
3
packages/bun-build-mdx-rs/npm/linux-x64-gnu/README.md
Normal file
3
packages/bun-build-mdx-rs/npm/linux-x64-gnu/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# `bun-mdx-rs-linux-x64-gnu`
|
||||
|
||||
This is the **x86_64-unknown-linux-gnu** binary for `bun-mdx-rs`
|
||||
21
packages/bun-build-mdx-rs/npm/linux-x64-gnu/package.json
Normal file
21
packages/bun-build-mdx-rs/npm/linux-x64-gnu/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
3
packages/bun-build-mdx-rs/npm/linux-x64-musl/README.md
Normal file
3
packages/bun-build-mdx-rs/npm/linux-x64-musl/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# `bun-mdx-rs-linux-x64-musl`
|
||||
|
||||
This is the **x86_64-unknown-linux-musl** binary for `bun-mdx-rs`
|
||||
21
packages/bun-build-mdx-rs/npm/linux-x64-musl/package.json
Normal file
21
packages/bun-build-mdx-rs/npm/linux-x64-musl/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
3
packages/bun-build-mdx-rs/npm/win32-x64-msvc/README.md
Normal file
3
packages/bun-build-mdx-rs/npm/win32-x64-msvc/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# `bun-mdx-rs-win32-x64-msvc`
|
||||
|
||||
This is the **x86_64-pc-windows-msvc** binary for `bun-mdx-rs`
|
||||
18
packages/bun-build-mdx-rs/npm/win32-x64-msvc/package.json
Normal file
18
packages/bun-build-mdx-rs/npm/win32-x64-msvc/package.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
37
packages/bun-build-mdx-rs/package.json
Normal file
37
packages/bun-build-mdx-rs/package.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
2
packages/bun-build-mdx-rs/rustfmt.toml
Normal file
2
packages/bun-build-mdx-rs/rustfmt.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
tab_spaces = 2
|
||||
edition = "2021"
|
||||
55
packages/bun-build-mdx-rs/src/lib.rs
Normal file
55
packages/bun-build-mdx-rs/src/lib.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
73
packages/bun-native-bundler-plugin-api/bundler_plugin.h
Normal file
73
packages/bun-native-bundler-plugin-api/bundler_plugin.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#ifndef BUN_NATIVE_BUNDLER_PLUGIN_API_H
|
||||
#define BUN_NATIVE_BUNDLER_PLUGIN_API_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum {
|
||||
BUN_LOADER_JSX = 0,
|
||||
BUN_LOADER_JS = 1,
|
||||
BUN_LOADER_TS = 2,
|
||||
BUN_LOADER_TSX = 3,
|
||||
BUN_LOADER_CSS = 4,
|
||||
BUN_LOADER_FILE = 5,
|
||||
BUN_LOADER_JSON = 6,
|
||||
BUN_LOADER_TOML = 7,
|
||||
BUN_LOADER_WASM = 8,
|
||||
BUN_LOADER_NAPI = 9,
|
||||
BUN_LOADER_BASE64 = 10,
|
||||
BUN_LOADER_DATAURL = 11,
|
||||
BUN_LOADER_TEXT = 12,
|
||||
} BunLoader;
|
||||
|
||||
const BunLoader BUN_LOADER_MAX = BUN_LOADER_TEXT;
|
||||
|
||||
typedef struct BunLogOptions {
|
||||
size_t __struct_size;
|
||||
const uint8_t *message_ptr;
|
||||
size_t message_len;
|
||||
const uint8_t *path_ptr;
|
||||
size_t path_len;
|
||||
const uint8_t *source_line_text_ptr;
|
||||
size_t source_line_text_len;
|
||||
int8_t level;
|
||||
int line;
|
||||
int lineEnd;
|
||||
int column;
|
||||
int columnEnd;
|
||||
} BunLogOptions;
|
||||
|
||||
typedef struct {
|
||||
size_t __struct_size;
|
||||
void *bun;
|
||||
const uint8_t *path_ptr;
|
||||
size_t path_len;
|
||||
const uint8_t *namespace_ptr;
|
||||
size_t namespace_len;
|
||||
uint8_t default_loader;
|
||||
void *external;
|
||||
} OnBeforeParseArguments;
|
||||
|
||||
typedef struct OnBeforeParseResult {
|
||||
size_t __struct_size;
|
||||
uint8_t *source_ptr;
|
||||
size_t source_len;
|
||||
uint8_t loader;
|
||||
int (*fetchSourceCode)(const OnBeforeParseArguments *args,
|
||||
struct OnBeforeParseResult *result);
|
||||
void *plugin_source_code_context;
|
||||
void (*free_plugin_source_code_context)(void *ctx);
|
||||
void (*log)(const OnBeforeParseArguments *args, BunLogOptions *options);
|
||||
} OnBeforeParseResult;
|
||||
|
||||
typedef enum {
|
||||
BUN_LOG_LEVEL_VERBOSE = 0,
|
||||
BUN_LOG_LEVEL_DEBUG = 1,
|
||||
BUN_LOG_LEVEL_INFO = 2,
|
||||
BUN_LOG_LEVEL_WARN = 3,
|
||||
BUN_LOG_LEVEL_ERROR = 4,
|
||||
} BunLogLevel;
|
||||
|
||||
const BunLogLevel BUN_LOG_MAX = BUN_LOG_LEVEL_ERROR;
|
||||
|
||||
#endif // BUN_NATIVE_BUNDLER_PLUGIN_API_H
|
||||
1
packages/bun-native-plugin-rs/.gitignore
vendored
Normal file
1
packages/bun-native-plugin-rs/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
target/
|
||||
286
packages/bun-native-plugin-rs/Cargo.lock
generated
Normal file
286
packages/bun-native-plugin-rs/Cargo.lock
generated
Normal file
@@ -0,0 +1,286 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools",
|
||||
"log",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "bun-native-plugin"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.166"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2ccc108bbc0b1331bd061864e7cd823c0cab660bbe6970e66e2c0614decde36"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
7
packages/bun-native-plugin-rs/Cargo.toml
Normal file
7
packages/bun-native-plugin-rs/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "bun-native-plugin"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.70.1"
|
||||
248
packages/bun-native-plugin-rs/README.md
Normal file
248
packages/bun-native-plugin-rs/README.md
Normal file
@@ -0,0 +1,248 @@
|
||||
> ⚠️ Note: This is an advanced and experimental API recommended only for plugin developers who are familiar with systems proramming and the C ABI. Use with caution.
|
||||
|
||||
# Bun Native Plugins
|
||||
|
||||
This crate provides a Rustified wrapper over the Bun's native bundler plugin C API.
|
||||
|
||||
Some advantages to _native_ bundler plugins as opposed to regular ones implemented in JS:
|
||||
|
||||
- Native plugins take full advantage of Bun's parallelized bundler pipeline and run on multiple threads at the same time
|
||||
- Unlike JS, native plugins don't need to do the UTF-8 <-> UTF-16 source code string conversions
|
||||
|
||||
What are native bundler plugins exactly? Precisely, they are NAPI modules which expose a C ABI function which implement a plugin lifecycle hook.
|
||||
|
||||
The currently supported lifecycle hooks are:
|
||||
|
||||
- `onBeforeParse` (called immediately before a file is parsed, allows you to modify the source code of the file)
|
||||
|
||||
## Getting started
|
||||
|
||||
Since native bundler plugins are NAPI modules, the easiest way to get started is to create a new [napi-rs](https://github.com/napi-rs/napi-rs) project:
|
||||
|
||||
```bash
|
||||
bun add -g @napi-rs/cli
|
||||
napi new
|
||||
```
|
||||
|
||||
Then install this crate:
|
||||
|
||||
```bash
|
||||
cargo add bun-native-plugin
|
||||
```
|
||||
|
||||
Now, inside the `lib.rs` file, expose a C ABI function which has the same function signature as the plugin lifecycle hook that you want to implement.
|
||||
|
||||
For example, implementing `onBeforeParse`:
|
||||
|
||||
```rs
|
||||
use bun_native_plugin::{define_bun_plugin, OnBeforeParse};
|
||||
use napi_derive::napi;
|
||||
|
||||
/// Define with the name of the plugin
|
||||
define_bun_plugin!("replace-foo-with-bar");
|
||||
|
||||
/// This is necessary for napi-rs to compile this into a proper NAPI module
|
||||
#[napi]
|
||||
pub fn register_bun_plugin() {}
|
||||
|
||||
/// Use `no_mangle` so that we can reference this symbol by name later
|
||||
/// when registering this native plugin in JS.
|
||||
///
|
||||
/// Here we'll create a dummy plugin which replaces all occurences of
|
||||
/// `foo` with `bar`
|
||||
#[no_mangle]
|
||||
pub extern "C" fn on_before_parse_plugin_impl(
|
||||
args: *const bun_native_plugin::sys::OnBeforeParseArguments,
|
||||
result: *mut bun_native_plugin::sys::OnBeforeParseResult,
|
||||
) {
|
||||
let args = unsafe { &*args };
|
||||
|
||||
// This returns a handle which is a safe wrapper over the raw
|
||||
// C API.
|
||||
let mut handle = OnBeforeParse::from_raw(args, result) {
|
||||
Ok(handle) => handle,
|
||||
Err(_) => {
|
||||
// `OnBeforeParse::from_raw` handles error logging
|
||||
// so it fine to return here.
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let input_source_code = match handle.input_source_code() {
|
||||
Ok(source_str) => source_str,
|
||||
Err(_) => {
|
||||
// If we encounter an error, we must log it so that
|
||||
// Bun knows this plugin failed.
|
||||
handle.log_error("Failed to fetch source code!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let loader = handle.output_loader();
|
||||
let output_source_code = source_str.replace("foo", "bar");
|
||||
handle.set_output_source_code(output_source_code, loader);
|
||||
}
|
||||
```
|
||||
|
||||
Then compile this NAPI module. If you using napi-rs, the `package.json` should have a `build` script you can run:
|
||||
|
||||
```bash
|
||||
bun run build
|
||||
```
|
||||
|
||||
This will produce a `.node` file in the project directory.
|
||||
|
||||
With the compiled NAPI module, you can now register the plugin from JS:
|
||||
|
||||
```js
|
||||
const result = await Bun.build({
|
||||
entrypoints: ["index.ts"],
|
||||
plugins: [
|
||||
{
|
||||
name: "replace-foo-with-bar",
|
||||
setup(build) {
|
||||
const napiModule = require("path/to/napi_module.node");
|
||||
|
||||
// Register the `onBeforeParse` hook to run on all `.ts` files.
|
||||
// We tell it to use function we implemented inside of our `lib.rs` code.
|
||||
build.onBeforeParse(
|
||||
{ filter: /\.ts/ },
|
||||
{ napiModule, symbol: "on_before_parse_plugin_impl" },
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## Very important information
|
||||
|
||||
### Error handling and panics
|
||||
|
||||
It is highly recommended to avoid panicking as this will crash the runtime. Instead, you must handle errors and log them:
|
||||
|
||||
```rs
|
||||
let input_source_code = match handle.input_source_code() {
|
||||
Ok(source_str) => source_str,
|
||||
Err(_) => {
|
||||
// If we encounter an error, we must log it so that
|
||||
// Bun knows this plugin failed.
|
||||
handle.log_error("Failed to fetch source code!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Passing state to and from JS: `External`
|
||||
|
||||
One way to communicate data from your plugin and JS and vice versa is through the NAPI's [External](https://napi.rs/docs/concepts/external) type.
|
||||
|
||||
An External in NAPI is like an opaque pointer to data that can be passed to and from JS. Inside your NAPI module, you can retrieve
|
||||
the pointer and modify the data.
|
||||
|
||||
As an example that extends our getting started example above, let's say you wanted to count the number of `foo`'s that the native plugin encounters.
|
||||
|
||||
You would expose a NAPI module function which creates this state. Recall that state in native plugins must be threadsafe. This usually means
|
||||
that your state must be `Sync`:
|
||||
|
||||
```rs
|
||||
struct PluginState {
|
||||
foo_count: std::sync::atomic::AtomicU32,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn create_plugin_state() -> External<PluginState> {
|
||||
let external = External::new(PluginState {
|
||||
foo_count: 0,
|
||||
});
|
||||
|
||||
external
|
||||
}
|
||||
|
||||
|
||||
#[napi]
|
||||
pub fn get_foo_count(plugin_state: External<PluginState>) -> u32 {
|
||||
let plugin_state: &PluginState = &plugin_state;
|
||||
plugin_state.foo_count.load(std::sync::atomic::Ordering::Relaxed)
|
||||
}
|
||||
```
|
||||
|
||||
When you register your plugin from Javascript, you call the napi module function to create the external and then pass it:
|
||||
|
||||
```js
|
||||
const napiModule = require("path/to/napi_module.node");
|
||||
const pluginState = napiModule.createPluginState();
|
||||
|
||||
const result = await Bun.build({
|
||||
entrypoints: ["index.ts"],
|
||||
plugins: [
|
||||
{
|
||||
name: "replace-foo-with-bar",
|
||||
setup(build) {
|
||||
build.onBeforeParse(
|
||||
{ filter: /\.ts/ },
|
||||
{
|
||||
napiModule,
|
||||
symbol: "on_before_parse_plugin_impl",
|
||||
// pass our NAPI external which contains our plugin state here
|
||||
external: pluginState,
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log("Total `foo`s encountered: ", pluginState.getFooCount());
|
||||
```
|
||||
|
||||
Finally, from the native implementation of your plugin, you can extract the external:
|
||||
|
||||
```rs
|
||||
pub extern "C" fn on_before_parse_plugin_impl(
|
||||
args: *const bun_native_plugin::sys::OnBeforeParseArguments,
|
||||
result: *mut bun_native_plugin::sys::OnBeforeParseResult,
|
||||
) {
|
||||
let args = unsafe { &*args };
|
||||
|
||||
let mut handle = OnBeforeParse::from_raw(args, result) {
|
||||
Ok(handle) => handle,
|
||||
Err(_) => {
|
||||
// `OnBeforeParse::from_raw` handles error logging
|
||||
// so it fine to return here.
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let plugin_state: &PluginState =
|
||||
// This operation is only safe if you pass in an external when registering the plugin.
|
||||
// If you don't, this could lead to a segfault or access of undefined memory.
|
||||
match unsafe { handle.external().and_then(|state| state.ok_or(Error::Unknown)) } {
|
||||
Ok(state) => state,
|
||||
Err(_) => {
|
||||
handle.log_error("Failed to get external!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Fetch our source code again
|
||||
let input_source_code = match handle.input_source_code() {
|
||||
Ok(source_str) => source_str,
|
||||
Err(_) => {
|
||||
handle.log_error("Failed to fetch source code!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Count the number of `foo`s and add it to our state
|
||||
let foo_count = source_code.matches("foo").count() as u32;
|
||||
plugin_state.foo_count.fetch_add(foo_count, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
```
|
||||
|
||||
### Concurrency
|
||||
|
||||
Your `extern "C"` plugin function can be called _on any thread_ at _any time_ and _multiple times at once_.
|
||||
|
||||
Therefore, you must design any state management to be threadsafe
|
||||
20
packages/bun-native-plugin-rs/build.rs
Normal file
20
packages/bun-native-plugin-rs/build.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rustc-link-search=./headers");
|
||||
|
||||
let bindings = bindgen::Builder::default()
|
||||
.header("wrapper.h")
|
||||
// Add absolute path to headers directory
|
||||
.clang_arg("-I./headers")
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
|
||||
.rustified_enum("BunLogLevel")
|
||||
.rustified_enum("BunLoader")
|
||||
.generate()
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
let out_path = PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
||||
bindings
|
||||
.write_to_file(out_path.join("bindings.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
}
|
||||
6
packages/bun-native-plugin-rs/copy_headers.ts
Normal file
6
packages/bun-native-plugin-rs/copy_headers.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { join } from "node:path";
|
||||
|
||||
const dirname = join(import.meta.dir, "../", "bun-native-bundler-plugin-api");
|
||||
await Bun.$`rm -rf headers`;
|
||||
await Bun.$`mkdir -p headers`;
|
||||
await Bun.$`cp -R ${dirname} headers/bun-native-bundler-plugin-api`;
|
||||
@@ -0,0 +1,79 @@
|
||||
#ifndef BUN_NATIVE_BUNDLER_PLUGIN_API_H
|
||||
#define BUN_NATIVE_BUNDLER_PLUGIN_API_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum {
|
||||
BUN_LOADER_JSX = 0,
|
||||
BUN_LOADER_JS = 1,
|
||||
BUN_LOADER_TS = 2,
|
||||
BUN_LOADER_TSX = 3,
|
||||
BUN_LOADER_CSS = 4,
|
||||
BUN_LOADER_FILE = 5,
|
||||
BUN_LOADER_JSON = 6,
|
||||
BUN_LOADER_TOML = 7,
|
||||
BUN_LOADER_WASM = 8,
|
||||
BUN_LOADER_NAPI = 9,
|
||||
BUN_LOADER_BASE64 = 10,
|
||||
BUN_LOADER_DATAURL = 11,
|
||||
BUN_LOADER_TEXT = 12,
|
||||
BUN_LOADER_BUNSH = 13,
|
||||
BUN_LOADER_SQLITE = 14,
|
||||
BUN_LOADER_SQLITE_EMBEDDED = 15
|
||||
} BunLoader;
|
||||
|
||||
const BunLoader BUN_LOADER_MAX = BUN_LOADER_SQLITE_EMBEDDED;
|
||||
|
||||
typedef struct BunLogOptions {
|
||||
size_t __struct_size;
|
||||
const uint8_t* message_ptr;
|
||||
size_t message_len;
|
||||
const uint8_t* path_ptr;
|
||||
size_t path_len;
|
||||
const uint8_t* source_line_text_ptr;
|
||||
size_t source_line_text_len;
|
||||
int8_t level;
|
||||
int line;
|
||||
int lineEnd;
|
||||
int column;
|
||||
int columnEnd;
|
||||
} BunLogOptions;
|
||||
|
||||
typedef struct {
|
||||
size_t __struct_size;
|
||||
void* bun;
|
||||
const uint8_t* path_ptr;
|
||||
size_t path_len;
|
||||
const uint8_t* namespace_ptr;
|
||||
size_t namespace_len;
|
||||
uint8_t default_loader;
|
||||
void *external;
|
||||
} OnBeforeParseArguments;
|
||||
|
||||
typedef struct OnBeforeParseResult {
|
||||
size_t __struct_size;
|
||||
uint8_t* source_ptr;
|
||||
size_t source_len;
|
||||
uint8_t loader;
|
||||
int (*fetchSourceCode)(
|
||||
const OnBeforeParseArguments* args,
|
||||
struct OnBeforeParseResult* result
|
||||
);
|
||||
void* plugin_source_code_context;
|
||||
void (*free_plugin_source_code_context)(void* ctx);
|
||||
void (*log)(const OnBeforeParseArguments* args, BunLogOptions* options);
|
||||
} OnBeforeParseResult;
|
||||
|
||||
|
||||
typedef enum {
|
||||
BUN_LOG_LEVEL_VERBOSE = 0,
|
||||
BUN_LOG_LEVEL_DEBUG = 1,
|
||||
BUN_LOG_LEVEL_INFO = 2,
|
||||
BUN_LOG_LEVEL_WARN = 3,
|
||||
BUN_LOG_LEVEL_ERROR = 4,
|
||||
} BunLogLevel;
|
||||
|
||||
const BunLogLevel BUN_LOG_MAX = BUN_LOG_LEVEL_ERROR;
|
||||
|
||||
#endif // BUN_NATIVE_BUNDLER_PLUGIN_API_H
|
||||
627
packages/bun-native-plugin-rs/src/lib.rs
Normal file
627
packages/bun-native-plugin-rs/src/lib.rs
Normal file
@@ -0,0 +1,627 @@
|
||||
//! > ⚠️ Note: This is an advanced and experimental API recommended only for plugin developers who are familiar with systems proramming and the C ABI. Use with caution.
|
||||
//!
|
||||
//! # Bun Native Plugins
|
||||
//!
|
||||
//! This crate provides a Rustified wrapper over the Bun's native bundler plugin C API.
|
||||
//!
|
||||
//! Some advantages to _native_ bundler plugins as opposed to regular ones implemented in JS:
|
||||
//!
|
||||
//! - Native plugins take full advantage of Bun's parallelized bundler pipeline and run on multiple threads at the same time
|
||||
//! - Unlike JS, native plugins don't need to do the UTF-8 <-> UTF-16 source code string conversions
|
||||
//!
|
||||
//! What are native bundler plugins exactly? Precisely, they are NAPI modules which expose a C ABI function which implement a plugin lifecycle hook.
|
||||
//!
|
||||
//! The currently supported lifecycle hooks are:
|
||||
//!
|
||||
//! - `onBeforeParse` (called immediately before a file is parsed, allows you to modify the source code of the file)
|
||||
//!
|
||||
//! ## Getting started
|
||||
//!
|
||||
//! Since native bundler plugins are NAPI modules, the easiest way to get started is to create a new [napi-rs](https://github.com/napi-rs/napi-rs) project:
|
||||
//!
|
||||
//! ```bash
|
||||
//! bun add -g @napi-rs/cli
|
||||
//! napi new
|
||||
//! ```
|
||||
//!
|
||||
//! Then install this crate:
|
||||
//!
|
||||
//! ```bash
|
||||
//! cargo add bun-native-plugin
|
||||
//! ```
|
||||
//!
|
||||
//! Now, inside the `lib.rs` file, expose a C ABI function which has the same function signature as the plugin lifecycle hook that you want to implement.
|
||||
//!
|
||||
//! For example, implementing `onBeforeParse`:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use bun_native_plugin::{OnBeforeParse};
|
||||
//!
|
||||
//! /// This is necessary for napi-rs to compile this into a proper NAPI module
|
||||
//! #[napi]
|
||||
//! pub fn register_bun_plugin() {}
|
||||
//!
|
||||
//! /// Use `no_mangle` so that we can reference this symbol by name later
|
||||
//! /// when registering this native plugin in JS.
|
||||
//! ///
|
||||
//! /// Here we'll create a dummy plugin which replaces all occurences of
|
||||
//! /// `foo` with `bar`
|
||||
//! #[no_mangle]
|
||||
//! pub extern "C" fn on_before_parse_plugin_impl(
|
||||
//! args: *const bun_native_plugin::sys::OnBeforeParseArguments,
|
||||
//! result: *mut bun_native_plugin::sys::OnBeforeParseResult,
|
||||
//! ) {
|
||||
//! let args = unsafe { &*args };
|
||||
//! let result = unsafe { &mut *result };
|
||||
//!
|
||||
//! // This returns a handle which is a safe wrapper over the raw
|
||||
//! // C API.
|
||||
//! let mut handle = OnBeforeParse::from_raw(args, result) {
|
||||
//! Ok(handle) => handle,
|
||||
//! Err(_) => {
|
||||
//! // `OnBeforeParse::from_raw` handles error logging
|
||||
//! // so it fine to return here.
|
||||
//! return;
|
||||
//! }
|
||||
//! };
|
||||
//!
|
||||
//! let input_source_code = match handle.input_source_code() {
|
||||
//! Ok(source_str) => source_str,
|
||||
//! Err(_) => {
|
||||
//! // If we encounter an error, we must log it so that
|
||||
//! // Bun knows this plugin failed.
|
||||
//! handle.log_error("Failed to fetch source code!");
|
||||
//! return;
|
||||
//! }
|
||||
//! };
|
||||
//!
|
||||
//! let loader = handle.output_loader();
|
||||
//! let output_source_code = source_str.replace("foo", "bar");
|
||||
//! handle.set_output_source_code(output_source_code, loader);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Then compile this NAPI module. If you using napi-rs, the `package.json` should have a `build` script you can run:
|
||||
//!
|
||||
//! ```bash
|
||||
//! bun run build
|
||||
//! ```
|
||||
//!
|
||||
//! This will produce a `.node` file in the project directory.
|
||||
//!
|
||||
//! With the compiled NAPI module, you can now register the plugin from JS:
|
||||
//!
|
||||
//! ```js
|
||||
//! const result = await Bun.build({
|
||||
//! entrypoints: ["index.ts"],
|
||||
//! plugins: [
|
||||
//! {
|
||||
//! name: "replace-foo-with-bar",
|
||||
//! setup(build) {
|
||||
//! const napiModule = require("path/to/napi_module.node");
|
||||
//!
|
||||
//! // Register the `onBeforeParse` hook to run on all `.ts` files.
|
||||
//! // We tell it to use function we implemented inside of our `lib.rs` code.
|
||||
//! build.onBeforeParse(
|
||||
//! { filter: /\.ts/ },
|
||||
//! { napiModule, symbol: "on_before_parse_plugin_impl" },
|
||||
//! );
|
||||
//! },
|
||||
//! },
|
||||
//! ],
|
||||
//! });
|
||||
//! ```
|
||||
//!
|
||||
//! ## Very important information
|
||||
//!
|
||||
//! ### Error handling and panics
|
||||
//!
|
||||
//! It is highly recommended to avoid panicking as this will crash the runtime. Instead, you must handle errors and log them:
|
||||
//!
|
||||
//! ```rust
|
||||
//! let input_source_code = match handle.input_source_code() {
|
||||
//! Ok(source_str) => source_str,
|
||||
//! Err(_) => {
|
||||
//! // If we encounter an error, we must log it so that
|
||||
//! // Bun knows this plugin failed.
|
||||
//! handle.log_error("Failed to fetch source code!");
|
||||
//! return;
|
||||
//! }
|
||||
//! };
|
||||
//! ```
|
||||
//!
|
||||
//! ### Passing state to and from JS: `External`
|
||||
//!
|
||||
//! One way to communicate data from your plugin and JS and vice versa is through the NAPI's [External](https://napi.rs/docs/concepts/external) type.
|
||||
//!
|
||||
//! An External in NAPI is like an opaque pointer to data that can be passed to and from JS. Inside your NAPI module, you can retrieve
|
||||
//! the pointer and modify the data.
|
||||
//!
|
||||
//! As an example that extends our getting started example above, let's say you wanted to count the number of `foo`'s that the native plugin encounters.
|
||||
//!
|
||||
//! You would expose a NAPI module function which creates this state. Recall that state in native plugins must be threadsafe. This usually means
|
||||
//! that your state must be `Sync`:
|
||||
//!
|
||||
//! ```rust
|
||||
//! struct PluginState {
|
||||
//! foo_count: std::sync::atomic::AtomicU32,
|
||||
//! }
|
||||
//!
|
||||
//! #[napi]
|
||||
//! pub fn create_plugin_state() -> External<PluginState> {
|
||||
//! let external = External::new(PluginState {
|
||||
//! foo_count: 0,
|
||||
//! });
|
||||
//!
|
||||
//! external
|
||||
//! }
|
||||
//!
|
||||
//!
|
||||
//! #[napi]
|
||||
//! pub fn get_foo_count(plugin_state: External<PluginState>) -> u32 {
|
||||
//! let plugin_state: &PluginState = &plugin_state;
|
||||
//! plugin_state.foo_count.load(std::sync::atomic::Ordering::Relaxed)
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! When you register your plugin from Javascript, you call the napi module function to create the external and then pass it:
|
||||
//!
|
||||
//! ```js
|
||||
//! const napiModule = require("path/to/napi_module.node");
|
||||
//! const pluginState = napiModule.createPluginState();
|
||||
//!
|
||||
//! const result = await Bun.build({
|
||||
//! entrypoints: ["index.ts"],
|
||||
//! plugins: [
|
||||
//! {
|
||||
//! name: "replace-foo-with-bar",
|
||||
//! setup(build) {
|
||||
//! build.onBeforeParse(
|
||||
//! { filter: /\.ts/ },
|
||||
//! {
|
||||
//! napiModule,
|
||||
//! symbol: "on_before_parse_plugin_impl",
|
||||
//! // pass our NAPI external which contains our plugin state here
|
||||
//! external: pluginState,
|
||||
//! },
|
||||
//! );
|
||||
//! },
|
||||
//! },
|
||||
//! ],
|
||||
//! });
|
||||
//!
|
||||
//! console.log("Total `foo`s encountered: ", pluginState.getFooCount());
|
||||
//! ```
|
||||
//!
|
||||
//! Finally, from the native implementation of your plugin, you can extract the external:
|
||||
//!
|
||||
//! ```rust
|
||||
//! pub extern "C" fn on_before_parse_plugin_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 = OnBeforeParse::from_raw(args, result) {
|
||||
//! Ok(handle) => handle,
|
||||
//! Err(_) => {
|
||||
//! // `OnBeforeParse::from_raw` handles error logging
|
||||
//! // so it fine to return here.
|
||||
//! return;
|
||||
//! }
|
||||
//! };
|
||||
//!
|
||||
//! let plugin_state: &PluginState =
|
||||
//! // This operation is only safe if you pass in an external when registering the plugin.
|
||||
//! // If you don't, this could lead to a segfault or access of undefined memory.
|
||||
//! match unsafe { handle.external().and_then(|state| state.ok_or(Error::Unknown)) } {
|
||||
//! Ok(state) => state,
|
||||
//! Err(_) => {
|
||||
//! handle.log_error("Failed to get external!");
|
||||
//! return;
|
||||
//! }
|
||||
//! };
|
||||
//!
|
||||
//!
|
||||
//! // Fetch our source code again
|
||||
//! let input_source_code = match handle.input_source_code() {
|
||||
//! Ok(source_str) => source_str,
|
||||
//! Err(_) => {
|
||||
//! handle.log_error("Failed to fetch source code!");
|
||||
//! return;
|
||||
//! }
|
||||
//! };
|
||||
//!
|
||||
//! // Count the number of `foo`s and add it to our state
|
||||
//! let foo_count = source_code.matches("foo").count() as u32;
|
||||
//! plugin_state.foo_count.fetch_add(foo_count, std::sync::atomic::Ordering::Relaxed);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Concurrency
|
||||
//!
|
||||
//! Your `extern "C"` plugin function can be called _on any thread_ at _any time_ and _multiple times at once_.
|
||||
//!
|
||||
//! Therefore, you must design any state management to be threadsafe
|
||||
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct BunPluginName(*const c_char);
|
||||
|
||||
impl BunPluginName {
|
||||
pub const fn new(ptr: *const c_char) -> Self {
|
||||
Self(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! define_bun_plugin {
|
||||
($name:expr) => {
|
||||
pub static BUN_PLUGIN_NAME_STRING: &str = $name;
|
||||
|
||||
#[no_mangle]
|
||||
pub static BUN_PLUGIN_NAME: bun_native_plugin::BunPluginName =
|
||||
bun_native_plugin::BunPluginName::new(BUN_PLUGIN_NAME_STRING.as_ptr() as *const _);
|
||||
|
||||
#[napi]
|
||||
fn bun_plugin_register() {}
|
||||
};
|
||||
}
|
||||
|
||||
unsafe impl Sync for BunPluginName {}
|
||||
|
||||
use std::{
|
||||
any::TypeId,
|
||||
borrow::Cow,
|
||||
cell::UnsafeCell,
|
||||
ffi::{c_char, c_void},
|
||||
str::Utf8Error,
|
||||
};
|
||||
|
||||
pub mod sys {
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct TaggedObject<T> {
|
||||
type_id: TypeId,
|
||||
pub(crate) object: Option<T>,
|
||||
}
|
||||
|
||||
struct SourceCodeContext {
|
||||
source_ptr: *mut u8,
|
||||
source_len: usize,
|
||||
source_cap: usize,
|
||||
}
|
||||
|
||||
extern "C" fn free_plugin_source_code_context(ctx: *mut c_void) {
|
||||
// SAFETY: The ctx pointer is a pointer to the `SourceCodeContext` struct we allocated.
|
||||
unsafe {
|
||||
drop(Box::from_raw(ctx as *mut SourceCodeContext));
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SourceCodeContext {
|
||||
fn drop(&mut self) {
|
||||
if !self.source_ptr.is_null() {
|
||||
// SAFETY: These fields come from a `String` that we allocated.
|
||||
unsafe {
|
||||
drop(String::from_raw_parts(
|
||||
self.source_ptr,
|
||||
self.source_len,
|
||||
self.source_cap,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type BunLogLevel = sys::BunLogLevel;
|
||||
pub type BunLoader = sys::BunLoader;
|
||||
|
||||
fn get_from_raw_str<'a>(ptr: *const u8, len: usize) -> Result<Cow<'a, str>> {
|
||||
let slice: &'a [u8] = unsafe { std::slice::from_raw_parts(ptr, len) };
|
||||
|
||||
// Windows allows invalid UTF-16 strings in the filesystem. These get converted to WTF-8 in Zig.
|
||||
// Meaning the string may contain invalid UTF-8, we'll have to use the safe checked version.
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
std::str::from_utf8(slice)
|
||||
.map(Into::into)
|
||||
.or_else(|_| Ok(String::from_utf8_lossy(slice)))
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
// SAFETY: The source code comes from Zig, which uses UTF-8, so this should be safe.
|
||||
|
||||
std::str::from_utf8(slice)
|
||||
.map(Into::into)
|
||||
.or_else(|_| Ok(String::from_utf8_lossy(slice)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Error {
|
||||
Utf8(Utf8Error),
|
||||
IncompatiblePluginVersion,
|
||||
ExternalTypeMismatch,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl From<Utf8Error> for Error {
|
||||
fn from(value: Utf8Error) -> Self {
|
||||
Self::Utf8(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// A safe handle for the arguments + result struct for the
|
||||
/// `OnBeforeParse` bundler lifecycle hook.
|
||||
///
|
||||
/// This struct acts as a safe wrapper around the raw C API structs
|
||||
/// (`sys::OnBeforeParseArguments`/`sys::OnBeforeParseResult`) needed to
|
||||
/// implement the `OnBeforeParse` bundler lifecycle hook.
|
||||
///
|
||||
/// To initialize this struct, see the `from_raw` method.
|
||||
pub struct OnBeforeParse<'a> {
|
||||
args_raw: &'a sys::OnBeforeParseArguments,
|
||||
result_raw: *mut sys::OnBeforeParseResult,
|
||||
compilation_context: *mut SourceCodeContext,
|
||||
}
|
||||
|
||||
impl<'a> OnBeforeParse<'a> {
|
||||
/// Initialize this struct from references to their raw counterparts.
|
||||
///
|
||||
/// This function will do a versioning check to ensure that the plugin
|
||||
/// is compatible with the current version of Bun. If the plugin is not
|
||||
/// compatible, it will log an error and return an error result.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// extern "C" fn on_before_parse_impl(args: *const sys::OnBeforeParseArguments, result: *mut sys::OnBeforeParseResult) {
|
||||
/// let args = unsafe { &*args };
|
||||
/// let result = unsafe { &mut *result };
|
||||
/// let handle = match OnBeforeParse::from_raw(args, result) {
|
||||
/// Ok(handle) => handle,
|
||||
/// Err(()) => return,
|
||||
/// };
|
||||
/// }
|
||||
/// ```
|
||||
pub fn from_raw(
|
||||
args: &'a sys::OnBeforeParseArguments,
|
||||
result: *mut sys::OnBeforeParseResult,
|
||||
) -> Result<Self> {
|
||||
if args.__struct_size < std::mem::size_of::<sys::OnBeforeParseArguments>()
|
||||
|| unsafe { (*result).__struct_size } < std::mem::size_of::<sys::OnBeforeParseResult>()
|
||||
{
|
||||
let message = "This plugin is not compatible with the current version of Bun.";
|
||||
let mut log_options = sys::BunLogOptions {
|
||||
__struct_size: std::mem::size_of::<sys::BunLogOptions>(),
|
||||
message_ptr: message.as_ptr(),
|
||||
message_len: message.len(),
|
||||
path_ptr: args.path_ptr,
|
||||
path_len: args.path_len,
|
||||
source_line_text_ptr: std::ptr::null(),
|
||||
source_line_text_len: 0,
|
||||
level: BunLogLevel::BUN_LOG_LEVEL_ERROR as i8,
|
||||
line: 0,
|
||||
lineEnd: 0,
|
||||
column: 0,
|
||||
columnEnd: 0,
|
||||
};
|
||||
// SAFETY: The `log` function pointer is guaranteed to be valid by the Bun runtime.
|
||||
unsafe {
|
||||
((*result).log.unwrap())(args, &mut log_options);
|
||||
}
|
||||
return Err(Error::IncompatiblePluginVersion);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
args_raw: args,
|
||||
result_raw: result,
|
||||
compilation_context: std::ptr::null_mut() as *mut _,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn path(&self) -> Result<Cow<'_, str>> {
|
||||
get_from_raw_str(self.args_raw.path_ptr, self.args_raw.path_len)
|
||||
}
|
||||
|
||||
pub fn namespace(&self) -> Result<Cow<'_, str>> {
|
||||
get_from_raw_str(self.args_raw.namespace_ptr, self.args_raw.namespace_len)
|
||||
}
|
||||
|
||||
/// Get the external object from the `OnBeforeParse` arguments.
|
||||
///
|
||||
/// The external object is set by the plugin definition inside of JS:
|
||||
/// ```js
|
||||
/// await Bun.build({
|
||||
/// plugins: [
|
||||
/// {
|
||||
/// name: "my-plugin",
|
||||
/// setup(builder) {
|
||||
/// const native_plugin = require("./native_plugin.node");
|
||||
/// const external = native_plugin.createExternal();
|
||||
/// builder.external({ napiModule: native_plugin, symbol: 'onBeforeParse', external });
|
||||
/// },
|
||||
/// },
|
||||
/// ],
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// The external object must be created from NAPI for this function to be safe!
|
||||
///
|
||||
/// This function will return an error if the external object is not a
|
||||
/// valid tagged object for the given type.
|
||||
///
|
||||
/// This function will return `Ok(None)` if there is no external object
|
||||
/// set.
|
||||
///
|
||||
/// # Example
|
||||
/// The code to create the external from napi-rs:
|
||||
/// ```rs
|
||||
/// #[no_mangle]
|
||||
/// #[napi]
|
||||
/// pub fn create_my_external() -> External<MyStruct> {
|
||||
/// let external = External::new(MyStruct::new());
|
||||
///
|
||||
/// external
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The code to extract the external:
|
||||
/// ```rust
|
||||
/// let external = match handle.external::<MyStruct>() {
|
||||
/// Ok(Some(external)) => external,
|
||||
/// _ => {
|
||||
/// handle.log_error("Could not get external object.");
|
||||
/// return;
|
||||
/// },
|
||||
/// };
|
||||
/// ```
|
||||
pub unsafe fn external<T: 'static + Sync>(&self) -> Result<Option<&'static T>> {
|
||||
if self.args_raw.external.is_null() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let external: *mut TaggedObject<T> = self.args_raw.external as *mut TaggedObject<T>;
|
||||
|
||||
unsafe {
|
||||
if (*external).type_id != TypeId::of::<T>() {
|
||||
return Err(Error::ExternalTypeMismatch);
|
||||
}
|
||||
|
||||
Ok((*external).object.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
/// The same as [`crate::bun_native_plugin::OnBeforeParse::external`], but returns a mutable reference.
|
||||
///
|
||||
/// This is unsafe as you must ensure that no other invocation of the plugin
|
||||
/// simultaneously holds a mutable reference to the external.
|
||||
pub unsafe fn external_mut<T: 'static + Sync>(&mut self) -> Result<Option<&mut T>> {
|
||||
if self.args_raw.external.is_null() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let external: *mut TaggedObject<T> = self.args_raw.external as *mut TaggedObject<T>;
|
||||
|
||||
unsafe {
|
||||
if (*external).type_id != TypeId::of::<T>() {
|
||||
return Err(Error::ExternalTypeMismatch);
|
||||
}
|
||||
|
||||
Ok((*external).object.as_mut())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the input source code for the current file.
|
||||
///
|
||||
/// On Windows, this function may return an `Err(Error::Utf8(...))` if the
|
||||
/// source code contains invalid UTF-8.
|
||||
pub fn input_source_code(&self) -> Result<Cow<'_, str>> {
|
||||
let fetch_result = unsafe {
|
||||
((*self.result_raw).fetchSourceCode.unwrap())(self.args_raw, self.result_raw)
|
||||
};
|
||||
|
||||
if fetch_result != 0 {
|
||||
Err(Error::Unknown)
|
||||
} else {
|
||||
// SAFETY: We don't hand out mutable references to `result_raw` so dereferencing here is safe.
|
||||
unsafe {
|
||||
get_from_raw_str((*self.result_raw).source_ptr, (*self.result_raw).source_len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the output source code for the current file.
|
||||
pub fn set_output_source_code(&mut self, source: String, loader: BunLoader) {
|
||||
let source_cap = source.capacity();
|
||||
let source = source.leak();
|
||||
let source_ptr = source.as_mut_ptr();
|
||||
let source_len = source.len();
|
||||
|
||||
if self.compilation_context.is_null() {
|
||||
self.compilation_context = Box::into_raw(Box::new(SourceCodeContext {
|
||||
source_ptr,
|
||||
source_len,
|
||||
source_cap,
|
||||
}));
|
||||
|
||||
// SAFETY: We don't hand out mutable references to `result_raw` so dereferencing it is safe.
|
||||
unsafe {
|
||||
(*self.result_raw).plugin_source_code_context =
|
||||
self.compilation_context as *mut c_void;
|
||||
(*self.result_raw).free_plugin_source_code_context =
|
||||
Some(free_plugin_source_code_context);
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
// SAFETY: If we're here we know that `compilation_context` is not null.
|
||||
let context = &mut *self.compilation_context;
|
||||
|
||||
drop(String::from_raw_parts(
|
||||
context.source_ptr,
|
||||
context.source_len,
|
||||
context.source_cap,
|
||||
));
|
||||
|
||||
context.source_ptr = source_ptr;
|
||||
context.source_len = source_len;
|
||||
context.source_cap = source_cap;
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: We don't hand out mutable references to `result_raw` so dereferencing it is safe.
|
||||
unsafe {
|
||||
(*self.result_raw).loader = loader as u8;
|
||||
(*self.result_raw).source_ptr = source_ptr;
|
||||
(*self.result_raw).source_len = source_len;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the output loader for the current file.
|
||||
pub fn set_output_loader(&self, loader: BunLogLevel) {
|
||||
// SAFETY: We don't hand out mutable references to `result_raw` so dereferencing it is safe.
|
||||
unsafe {
|
||||
(*self.result_raw).loader = loader as u8;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the output loader for the current file.
|
||||
pub fn output_loader(&self) -> BunLoader {
|
||||
unsafe { std::mem::transmute((*self.result_raw).loader as u32) }
|
||||
}
|
||||
|
||||
/// Log an error message.
|
||||
pub fn log_error(&self, message: &str) {
|
||||
self.log(message, BunLogLevel::BUN_LOG_LEVEL_ERROR)
|
||||
}
|
||||
|
||||
/// Log a message with the given level.
|
||||
pub fn log(&self, message: &str, level: BunLogLevel) {
|
||||
let mut log_options = sys::BunLogOptions {
|
||||
__struct_size: std::mem::size_of::<sys::BunLogOptions>(),
|
||||
message_ptr: message.as_ptr(),
|
||||
message_len: message.len(),
|
||||
path_ptr: self.args_raw.path_ptr,
|
||||
path_len: self.args_raw.path_len,
|
||||
source_line_text_ptr: std::ptr::null(),
|
||||
source_line_text_len: 0,
|
||||
level: level as i8,
|
||||
line: 0,
|
||||
lineEnd: 0,
|
||||
column: 0,
|
||||
columnEnd: 0,
|
||||
};
|
||||
unsafe {
|
||||
((*self.result_raw).log.unwrap())(self.args_raw, &mut log_options);
|
||||
}
|
||||
}
|
||||
}
|
||||
1
packages/bun-native-plugin-rs/wrapper.h
Normal file
1
packages/bun-native-plugin-rs/wrapper.h
Normal file
@@ -0,0 +1 @@
|
||||
#include <bun-native-bundler-plugin-api/bundler_plugin.h>
|
||||
Binary file not shown.
@@ -97,6 +97,8 @@ export async function getBuild(): Promise<number> {
|
||||
}
|
||||
|
||||
export async function getSemver(tag?: string, build?: number): Promise<string> {
|
||||
const { tag_name: latest_tag_name } = await getRelease();
|
||||
const version = latest_tag_name.replace("bun-v", "");
|
||||
const { tag_name } = await getRelease(tag);
|
||||
if (tag_name !== "canary") {
|
||||
return tag_name.replace("bun-v", "");
|
||||
@@ -106,7 +108,7 @@ export async function getSemver(tag?: string, build?: number): Promise<string> {
|
||||
}
|
||||
const sha = await getSha(tag_name, "short");
|
||||
const date = new Date().toISOString().split("T")[0].replace(/-/g, "");
|
||||
return `${Bun.version}-canary.${date}.${build}+${sha}`;
|
||||
return `${version}-canary.${date}.${build}+${sha}`;
|
||||
}
|
||||
|
||||
export function formatTag(tag: string): string {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { spawn } from "../spawn";
|
||||
import { chmod, join, rename, rm, tmp, write } from "../fs";
|
||||
import { unzipSync } from "zlib";
|
||||
import type { Platform } from "../platform";
|
||||
import { os, arch, supportedPlatforms } from "../platform";
|
||||
import { os, arch, abi, supportedPlatforms } from "../platform";
|
||||
import { debug, error } from "../console";
|
||||
|
||||
declare const version: string;
|
||||
@@ -12,7 +12,7 @@ declare const owner: string;
|
||||
|
||||
export async function importBun(): Promise<string> {
|
||||
if (!supportedPlatforms.length) {
|
||||
throw new Error(`Unsupported platform: ${os} ${arch}`);
|
||||
throw new Error(`Unsupported platform: ${os} ${arch} ${abi || ""}`);
|
||||
}
|
||||
for (const platform of supportedPlatforms) {
|
||||
try {
|
||||
@@ -121,7 +121,8 @@ async function downloadBun(platform: Platform, dst: string): Promise<void> {
|
||||
}
|
||||
|
||||
export function optimizeBun(path: string): void {
|
||||
const installScript = os === "win32" ? 'powershell -c "irm bun.sh/install.ps1 | iex"' : "curl -fsSL https://bun.sh/install | bash";
|
||||
const installScript =
|
||||
os === "win32" ? 'powershell -c "irm bun.sh/install.ps1 | iex"' : "curl -fsSL https://bun.sh/install | bash";
|
||||
try {
|
||||
rename(path, join(__dirname, "bin", "bun.exe"));
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { spawn } from "./spawn";
|
||||
import { read } from "./fs";
|
||||
import { exists, read } from "./fs";
|
||||
import { debug } from "./console";
|
||||
|
||||
export const os = process.platform;
|
||||
@@ -10,9 +10,12 @@ export const avx2 =
|
||||
arch === "x64" &&
|
||||
((os === "linux" && isLinuxAVX2()) || (os === "darwin" && isDarwinAVX2()) || (os === "win32" && isWindowsAVX2()));
|
||||
|
||||
export const abi = os === "linux" && isLinuxMusl() ? "musl" : undefined;
|
||||
|
||||
export type Platform = {
|
||||
os: string;
|
||||
arch: string;
|
||||
abi?: "musl";
|
||||
avx2?: boolean;
|
||||
bin: string;
|
||||
exe: string;
|
||||
@@ -57,6 +60,28 @@ export const platforms: Platform[] = [
|
||||
bin: "bun-linux-x64-baseline",
|
||||
exe: "bin/bun",
|
||||
},
|
||||
{
|
||||
os: "linux",
|
||||
arch: "aarch64",
|
||||
abi: "musl",
|
||||
bin: "bun-linux-aarch64-musl",
|
||||
exe: "bin/bun",
|
||||
},
|
||||
{
|
||||
os: "linux",
|
||||
arch: "x64",
|
||||
abi: "musl",
|
||||
avx2: true,
|
||||
bin: "bun-linux-x64-musl",
|
||||
exe: "bin/bun",
|
||||
},
|
||||
{
|
||||
os: "linux",
|
||||
arch: "x64",
|
||||
abi: "musl",
|
||||
bin: "bun-linux-x64-musl-baseline",
|
||||
exe: "bin/bun",
|
||||
},
|
||||
{
|
||||
os: "win32",
|
||||
arch: "x64",
|
||||
@@ -73,9 +98,24 @@ export const platforms: Platform[] = [
|
||||
];
|
||||
|
||||
export const supportedPlatforms: Platform[] = platforms
|
||||
.filter(platform => platform.os === os && platform.arch === arch && (!platform.avx2 || avx2))
|
||||
.filter(
|
||||
platform =>
|
||||
platform.os === os &&
|
||||
platform.arch === arch &&
|
||||
(!platform.avx2 || avx2) &&
|
||||
(!platform.abi || abi === platform.abi),
|
||||
)
|
||||
.sort((a, b) => (a.avx2 === b.avx2 ? 0 : a.avx2 ? -1 : 1));
|
||||
|
||||
function isLinuxMusl(): boolean {
|
||||
try {
|
||||
return exists("/etc/alpine-release");
|
||||
} catch (error) {
|
||||
debug("isLinuxMusl failed", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isLinuxAVX2(): boolean {
|
||||
try {
|
||||
return read("/proc/cpuinfo").includes("avx2");
|
||||
|
||||
45
packages/bun-types/bun.d.ts
vendored
45
packages/bun-types/bun.d.ts
vendored
@@ -14,6 +14,7 @@
|
||||
* This module aliases `globalThis.Bun`.
|
||||
*/
|
||||
declare module "bun" {
|
||||
import type { FFIFunctionCallableSymbol } from "bun:ffi";
|
||||
import type { Encoding as CryptoEncoding } from "crypto";
|
||||
import type { CipherNameAndProtocol, EphemeralKeyInfo, PeerCertificate } from "tls";
|
||||
interface Env {
|
||||
@@ -3873,7 +3874,6 @@ declare module "bun" {
|
||||
* The default loader for this file extension
|
||||
*/
|
||||
loader: Loader;
|
||||
|
||||
/**
|
||||
* Defer the execution of this callback until all other modules have been parsed.
|
||||
*
|
||||
@@ -3882,7 +3882,7 @@ declare module "bun" {
|
||||
defer: () => Promise<void>;
|
||||
}
|
||||
|
||||
type OnLoadResult = OnLoadResultSourceCode | OnLoadResultObject | undefined;
|
||||
type OnLoadResult = OnLoadResultSourceCode | OnLoadResultObject | undefined | void;
|
||||
type OnLoadCallback = (args: OnLoadArgs) => OnLoadResult | Promise<OnLoadResult>;
|
||||
type OnStartCallback = () => void | Promise<void>;
|
||||
|
||||
@@ -3899,6 +3899,10 @@ declare module "bun" {
|
||||
* The namespace of the importer.
|
||||
*/
|
||||
namespace: string;
|
||||
/**
|
||||
* The directory to perform file-based resolutions in.
|
||||
*/
|
||||
resolveDir: string;
|
||||
/**
|
||||
* The kind of import this resolve is for.
|
||||
*/
|
||||
@@ -3928,7 +3932,30 @@ declare module "bun" {
|
||||
args: OnResolveArgs,
|
||||
) => OnResolveResult | Promise<OnResolveResult | undefined | null> | undefined | null;
|
||||
|
||||
type FFIFunctionCallable = Function & {
|
||||
// Making a nominally typed function so that the user must get it from dlopen
|
||||
readonly __ffi_function_callable: typeof FFIFunctionCallableSymbol;
|
||||
};
|
||||
|
||||
interface PluginBuilder {
|
||||
/**
|
||||
* Register a callback which will be invoked when bundling starts.
|
||||
* @example
|
||||
* ```ts
|
||||
* Bun.plugin({
|
||||
* setup(builder) {
|
||||
* builder.onStart(() => {
|
||||
* console.log("bundle just started!!")
|
||||
* });
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
onStart(callback: OnStartCallback): void;
|
||||
onBeforeParse(
|
||||
constraints: PluginConstraints,
|
||||
callback: { napiModule: unknown; symbol: string; external?: unknown | undefined },
|
||||
): void;
|
||||
/**
|
||||
* Register a callback to load imports with a specific import specifier
|
||||
* @param constraints The constraints to apply the plugin to
|
||||
@@ -3961,20 +3988,6 @@ declare module "bun" {
|
||||
* ```
|
||||
*/
|
||||
onResolve(constraints: PluginConstraints, callback: OnResolveCallback): void;
|
||||
/**
|
||||
* Register a callback which will be invoked when bundling starts.
|
||||
* @example
|
||||
* ```ts
|
||||
* Bun.plugin({
|
||||
* setup(builder) {
|
||||
* builder.onStart(() => {
|
||||
* console.log("bundle just started!!")
|
||||
* });
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
onStart(callback: OnStartCallback): void;
|
||||
/**
|
||||
* The config object passed to `Bun.build` as is. Can be mutated.
|
||||
*/
|
||||
|
||||
24
packages/bun-types/ffi.d.ts
vendored
24
packages/bun-types/ffi.d.ts
vendored
@@ -566,17 +566,21 @@ declare module "bun:ffi" {
|
||||
|
||||
type ToFFIType<T extends FFITypeOrString> = T extends FFIType ? T : T extends string ? FFITypeStringToType[T] : never;
|
||||
|
||||
const FFIFunctionCallableSymbol: unique symbol;
|
||||
type ConvertFns<Fns extends Symbols> = {
|
||||
[K in keyof Fns]: (
|
||||
...args: Fns[K]["args"] extends infer A extends readonly FFITypeOrString[]
|
||||
? { [L in keyof A]: FFITypeToArgsType[ToFFIType<A[L]>] }
|
||||
: // eslint-disable-next-line @definitelytyped/no-single-element-tuple-type
|
||||
[unknown] extends [Fns[K]["args"]]
|
||||
? []
|
||||
: never
|
||||
) => [unknown] extends [Fns[K]["returns"]] // eslint-disable-next-line @definitelytyped/no-single-element-tuple-type
|
||||
? undefined
|
||||
: FFITypeToReturnsType[ToFFIType<NonNullable<Fns[K]["returns"]>>];
|
||||
[K in keyof Fns]: {
|
||||
(
|
||||
...args: Fns[K]["args"] extends infer A extends readonly FFITypeOrString[]
|
||||
? { [L in keyof A]: FFITypeToArgsType[ToFFIType<A[L]>] }
|
||||
: // eslint-disable-next-line @definitelytyped/no-single-element-tuple-type
|
||||
[unknown] extends [Fns[K]["args"]]
|
||||
? []
|
||||
: never
|
||||
): [unknown] extends [Fns[K]["returns"]] // eslint-disable-next-line @definitelytyped/no-single-element-tuple-type
|
||||
? undefined
|
||||
: FFITypeToReturnsType[ToFFIType<NonNullable<Fns[K]["returns"]>>];
|
||||
__ffi_function_callable: typeof FFIFunctionCallableSymbol;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
51
packages/bun-types/test.d.ts
vendored
51
packages/bun-types/test.d.ts
vendored
@@ -1295,6 +1295,57 @@ declare module "bun:test" {
|
||||
* @param hint Hint used to identify the snapshot in the snapshot file.
|
||||
*/
|
||||
toMatchSnapshot(propertyMatchers?: object, hint?: string): void;
|
||||
/**
|
||||
* Asserts that a value matches the most recent inline snapshot.
|
||||
*
|
||||
* @example
|
||||
* expect("Hello").toMatchInlineSnapshot();
|
||||
* expect("Hello").toMatchInlineSnapshot(`"Hello"`);
|
||||
*
|
||||
* @param value The latest automatically-updated snapshot value.
|
||||
*/
|
||||
toMatchInlineSnapshot(value?: string): void;
|
||||
/**
|
||||
* Asserts that a value matches the most recent inline snapshot.
|
||||
*
|
||||
* @example
|
||||
* expect({ c: new Date() }).toMatchInlineSnapshot({ c: expect.any(Date) });
|
||||
* expect({ c: new Date() }).toMatchInlineSnapshot({ c: expect.any(Date) }, `
|
||||
* {
|
||||
* "v": Any<Date>,
|
||||
* }
|
||||
* `);
|
||||
*
|
||||
* @param propertyMatchers Object containing properties to match against the value.
|
||||
* @param value The latest automatically-updated snapshot value.
|
||||
*/
|
||||
toMatchInlineSnapshot(propertyMatchers?: object, value?: string): void;
|
||||
/**
|
||||
* Asserts that a function throws an error matching the most recent snapshot.
|
||||
*
|
||||
* @example
|
||||
* function fail() {
|
||||
* throw new Error("Oops!");
|
||||
* }
|
||||
* expect(fail).toThrowErrorMatchingSnapshot();
|
||||
* expect(fail).toThrowErrorMatchingSnapshot("This one should say Oops!");
|
||||
*
|
||||
* @param value The latest automatically-updated snapshot value.
|
||||
*/
|
||||
toThrowErrorMatchingSnapshot(hint?: string): void;
|
||||
/**
|
||||
* Asserts that a function throws an error matching the most recent snapshot.
|
||||
*
|
||||
* @example
|
||||
* function fail() {
|
||||
* throw new Error("Oops!");
|
||||
* }
|
||||
* expect(fail).toThrowErrorMatchingInlineSnapshot();
|
||||
* expect(fail).toThrowErrorMatchingInlineSnapshot(`"Oops!"`);
|
||||
*
|
||||
* @param value The latest automatically-updated snapshot value.
|
||||
*/
|
||||
toThrowErrorMatchingInlineSnapshot(value?: string): void;
|
||||
/**
|
||||
* Asserts that an object matches a subset of properties.
|
||||
*
|
||||
|
||||
@@ -623,18 +623,34 @@ inline __attribute__((always_inline)) LIBUS_SOCKET_DESCRIPTOR bsd_bind_listen_fd
|
||||
setsockopt(listenFd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *) &optval2, sizeof(optval2));
|
||||
#endif
|
||||
} else {
|
||||
#if defined(SO_REUSEPORT)
|
||||
int optval2 = 1;
|
||||
setsockopt(listenFd, SOL_SOCKET, SO_REUSEPORT, (void *) &optval2, sizeof(optval2));
|
||||
#endif
|
||||
#if defined(SO_REUSEPORT)
|
||||
if((options & LIBUS_LISTEN_REUSE_PORT)) {
|
||||
int optval2 = 1;
|
||||
setsockopt(listenFd, SOL_SOCKET, SO_REUSEPORT, (void *) &optval2, sizeof(optval2));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(SO_REUSEADDR)
|
||||
#ifndef _WIN32
|
||||
|
||||
// Unlike on Unix, here we don't set SO_REUSEADDR, because it doesn't just
|
||||
// allow binding to addresses that are in use by sockets in TIME_WAIT, it
|
||||
// effectively allows 'stealing' a port which is in use by another application.
|
||||
// See libuv issue #1360.
|
||||
|
||||
|
||||
int optval3 = 1;
|
||||
setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (void *) &optval3, sizeof(optval3));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef IPV6_V6ONLY
|
||||
// TODO: revise support to match node.js
|
||||
// if (listenAddr->ai_family == AF_INET6) {
|
||||
// int disabled = (options & LIBUS_SOCKET_IPV6_ONLY) != 0;
|
||||
// setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &disabled, sizeof(disabled));
|
||||
// }
|
||||
int disabled = 0;
|
||||
setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &disabled, sizeof(disabled));
|
||||
#endif
|
||||
@@ -949,8 +965,10 @@ int bsd_connect_udp_socket(LIBUS_SOCKET_DESCRIPTOR fd, const char *host, int por
|
||||
char port_string[16];
|
||||
snprintf(port_string, 16, "%d", port);
|
||||
|
||||
if (getaddrinfo(host, port_string, &hints, &result)) {
|
||||
return -1;
|
||||
int gai_error = getaddrinfo(host, port_string, &hints, &result);
|
||||
|
||||
if (gai_error != 0) {
|
||||
return gai_error;
|
||||
}
|
||||
|
||||
if (result == NULL) {
|
||||
|
||||
@@ -1858,9 +1858,6 @@ ssl_wrapped_context_on_close(struct us_internal_ssl_socket_t *s, int code,
|
||||
(struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(
|
||||
context);
|
||||
|
||||
if (wrapped_context->events.on_close) {
|
||||
wrapped_context->events.on_close((struct us_socket_t *)s, code, reason);
|
||||
}
|
||||
|
||||
// writting here can cause the context to not be writable anymore but its the
|
||||
// user responsability to check for that
|
||||
@@ -1868,6 +1865,10 @@ ssl_wrapped_context_on_close(struct us_internal_ssl_socket_t *s, int code,
|
||||
wrapped_context->old_events.on_close((struct us_socket_t *)s, code, reason);
|
||||
}
|
||||
|
||||
if (wrapped_context->events.on_close) {
|
||||
wrapped_context->events.on_close((struct us_socket_t *)s, code, reason);
|
||||
}
|
||||
|
||||
us_socket_context_unref(0, wrapped_context->tcp_context);
|
||||
return s;
|
||||
}
|
||||
@@ -1880,9 +1881,6 @@ ssl_wrapped_context_on_writable(struct us_internal_ssl_socket_t *s) {
|
||||
(struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(
|
||||
context);
|
||||
|
||||
if (wrapped_context->events.on_writable) {
|
||||
wrapped_context->events.on_writable((struct us_socket_t *)s);
|
||||
}
|
||||
|
||||
// writting here can cause the context to not be writable anymore but its the
|
||||
// user responsability to check for that
|
||||
@@ -1890,6 +1888,10 @@ ssl_wrapped_context_on_writable(struct us_internal_ssl_socket_t *s) {
|
||||
wrapped_context->old_events.on_writable((struct us_socket_t *)s);
|
||||
}
|
||||
|
||||
if (wrapped_context->events.on_writable) {
|
||||
wrapped_context->events.on_writable((struct us_socket_t *)s);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1916,14 +1918,14 @@ ssl_wrapped_context_on_timeout(struct us_internal_ssl_socket_t *s) {
|
||||
struct us_wrapped_socket_context_t *wrapped_context =
|
||||
(struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(
|
||||
context);
|
||||
if (wrapped_context->old_events.on_timeout) {
|
||||
wrapped_context->old_events.on_timeout((struct us_socket_t *)s);
|
||||
}
|
||||
|
||||
if (wrapped_context->events.on_timeout) {
|
||||
wrapped_context->events.on_timeout((struct us_socket_t *)s);
|
||||
}
|
||||
|
||||
if (wrapped_context->old_events.on_timeout) {
|
||||
wrapped_context->old_events.on_timeout((struct us_socket_t *)s);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -1935,15 +1937,14 @@ ssl_wrapped_context_on_long_timeout(struct us_internal_ssl_socket_t *s) {
|
||||
struct us_wrapped_socket_context_t *wrapped_context =
|
||||
(struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(
|
||||
context);
|
||||
if (wrapped_context->old_events.on_long_timeout) {
|
||||
wrapped_context->old_events.on_long_timeout((struct us_socket_t *)s);
|
||||
}
|
||||
|
||||
if (wrapped_context->events.on_long_timeout) {
|
||||
wrapped_context->events.on_long_timeout((struct us_socket_t *)s);
|
||||
}
|
||||
|
||||
if (wrapped_context->old_events.on_long_timeout) {
|
||||
wrapped_context->old_events.on_long_timeout((struct us_socket_t *)s);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1954,14 +1955,13 @@ ssl_wrapped_context_on_end(struct us_internal_ssl_socket_t *s) {
|
||||
struct us_wrapped_socket_context_t *wrapped_context =
|
||||
(struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(
|
||||
context);
|
||||
|
||||
if (wrapped_context->events.on_end) {
|
||||
wrapped_context->events.on_end((struct us_socket_t *)s);
|
||||
}
|
||||
|
||||
if (wrapped_context->old_events.on_end) {
|
||||
wrapped_context->old_events.on_end((struct us_socket_t *)s);
|
||||
}
|
||||
if (wrapped_context->events.on_end) {
|
||||
wrapped_context->events.on_end((struct us_socket_t *)s);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1973,13 +1973,13 @@ ssl_wrapped_on_connect_error(struct us_internal_ssl_socket_t *s, int code) {
|
||||
(struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(
|
||||
context);
|
||||
|
||||
if (wrapped_context->old_events.on_connect_error) {
|
||||
wrapped_context->old_events.on_connect_error((struct us_connecting_socket_t *)s, code);
|
||||
}
|
||||
if (wrapped_context->events.on_connect_error) {
|
||||
wrapped_context->events.on_connect_error((struct us_connecting_socket_t *)s, code);
|
||||
}
|
||||
|
||||
if (wrapped_context->old_events.on_connect_error) {
|
||||
wrapped_context->old_events.on_connect_error((struct us_connecting_socket_t *)s, code);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1990,14 +1990,14 @@ ssl_wrapped_on_socket_connect_error(struct us_internal_ssl_socket_t *s, int code
|
||||
struct us_wrapped_socket_context_t *wrapped_context =
|
||||
(struct us_wrapped_socket_context_t *)us_internal_ssl_socket_context_ext(
|
||||
context);
|
||||
|
||||
if (wrapped_context->old_events.on_connecting_socket_error) {
|
||||
wrapped_context->old_events.on_connecting_socket_error((struct us_socket_t *)s, code);
|
||||
}
|
||||
if (wrapped_context->events.on_connecting_socket_error) {
|
||||
wrapped_context->events.on_connecting_socket_error((struct us_socket_t *)s, code);
|
||||
}
|
||||
|
||||
if (wrapped_context->old_events.on_connecting_socket_error) {
|
||||
wrapped_context->old_events.on_connecting_socket_error((struct us_socket_t *)s, code);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -2153,6 +2153,8 @@ us_socket_context_on_socket_connect_error(
|
||||
socket->ssl_read_wants_write = 0;
|
||||
socket->fatal_error = 0;
|
||||
socket->handshake_state = HANDSHAKE_PENDING;
|
||||
// always resume the socket
|
||||
us_socket_resume(1, &socket->s);
|
||||
return socket;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +96,10 @@ enum {
|
||||
LIBUS_LISTEN_EXCLUSIVE_PORT = 1,
|
||||
/* Allow socket to keep writing after readable side closes */
|
||||
LIBUS_SOCKET_ALLOW_HALF_OPEN = 2,
|
||||
/* Setting reusePort allows multiple sockets on the same host to bind to the same port. Incoming connections are distributed by the operating system to listening sockets. This option is available only on some platforms, such as Linux 3.9+, DragonFlyBSD 3.6+, FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+*/
|
||||
LIBUS_LISTEN_REUSE_PORT = 4,
|
||||
/* etting ipv6Only will disable dual-stack support, i.e., binding to host :: won't make 0.0.0.0 be bound.*/
|
||||
LIBUS_SOCKET_IPV6_ONLY = 8,
|
||||
};
|
||||
|
||||
/* Library types publicly available */
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
|
||||
/* The loop has 2 fallthrough polls */
|
||||
void us_internal_loop_data_init(struct us_loop_t *loop, void (*wakeup_cb)(struct us_loop_t *loop),
|
||||
void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop)) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bun-vscode",
|
||||
"version": "0.0.19",
|
||||
"version": "0.0.22",
|
||||
"author": "oven",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -21,7 +21,6 @@
|
||||
"activationEvents": [
|
||||
"onStartupFinished"
|
||||
],
|
||||
"browser": "dist/web-extension.js",
|
||||
"bugs": {
|
||||
"url": "https://github.com/oven-sh/bun/issues"
|
||||
},
|
||||
@@ -46,6 +45,18 @@
|
||||
"scope": "window",
|
||||
"default": null
|
||||
},
|
||||
"bun.diagnosticsSocket.enabled": {
|
||||
"type": "boolean",
|
||||
"description": "If Bun extension should communicate with Bun over a socket to show errors in editor.",
|
||||
"scope": "window",
|
||||
"default": true
|
||||
},
|
||||
"bun.bunlockb.enabled": {
|
||||
"type": "boolean",
|
||||
"description": "If visual lockfile viewer (`bun.lockb`) should be enabled ",
|
||||
"scope": "window",
|
||||
"default": true
|
||||
},
|
||||
"bun.debugTerminal.enabled": {
|
||||
"type": "boolean",
|
||||
"description": "If Bun should be added to the JavaScript Debug Terminal.",
|
||||
@@ -321,7 +332,7 @@
|
||||
]
|
||||
},
|
||||
"description": "The Visual Studio Code extension for Bun.",
|
||||
"displayName": "Bun for Visual Studio Code",
|
||||
"displayName": "Bun",
|
||||
"engines": {
|
||||
"vscode": "^1.60.0"
|
||||
},
|
||||
@@ -358,4 +369,4 @@
|
||||
"../bun-debug-adapter-protocol",
|
||||
"../bun-inspector-protocol"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -53,3 +53,7 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
// Only register for text editors
|
||||
context.subscriptions.push(vscode.commands.registerTextEditorCommand("extension.bun.runUnsavedCode", runUnsavedCode));
|
||||
}
|
||||
|
||||
export function getConfig<T>(path: string, scope?: vscode.ConfigurationScope) {
|
||||
return vscode.workspace.getConfiguration("bun", scope).get<T>(path);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
UnixSignal,
|
||||
WebSocketDebugAdapter,
|
||||
} from "../../../bun-debug-adapter-protocol";
|
||||
import { getConfig } from "../extension";
|
||||
|
||||
export const DEBUG_CONFIGURATION: vscode.DebugConfiguration = {
|
||||
type: "bun",
|
||||
@@ -120,6 +121,7 @@ async function injectDebugTerminal(terminal: vscode.Terminal): Promise<void> {
|
||||
...env,
|
||||
"BUN_INSPECT": `${adapter.url}?${query}`,
|
||||
"BUN_INSPECT_NOTIFY": signal.url,
|
||||
BUN_INSPECT_CONNECT_TO: "",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -351,6 +353,7 @@ class TerminalDebugSession extends FileDebugSession {
|
||||
env: {
|
||||
"BUN_INSPECT": `${this.adapter.url}?wait=1`,
|
||||
"BUN_INSPECT_NOTIFY": this.signal.url,
|
||||
BUN_INSPECT_CONNECT_TO: "",
|
||||
},
|
||||
isTransient: true,
|
||||
iconPath: new vscode.ThemeIcon("debug-console"),
|
||||
@@ -370,10 +373,6 @@ function getRuntime(scope?: vscode.ConfigurationScope): string {
|
||||
return "bun";
|
||||
}
|
||||
|
||||
function getConfig<T>(path: string, scope?: vscode.ConfigurationScope) {
|
||||
return vscode.workspace.getConfiguration("bun", scope).get<T>(path);
|
||||
}
|
||||
|
||||
export async function runUnsavedCode() {
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
if (!editor || !editor.document.isUntitled) return;
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import * as fs from "node:fs/promises";
|
||||
import { Socket } from "node:net";
|
||||
import * as os from "node:os";
|
||||
import { inspect } from "node:util";
|
||||
import * as vscode from "vscode";
|
||||
import {
|
||||
getAvailablePort,
|
||||
getRandomId,
|
||||
NodeSocketDebugAdapter,
|
||||
TCPSocketSignal,
|
||||
UnixSignal,
|
||||
WebSocketDebugAdapter,
|
||||
} from "../../../../bun-debug-adapter-protocol";
|
||||
import type { JSC } from "../../../../bun-inspector-protocol";
|
||||
import { createGlobalStateGenerationFn, typedGlobalState } from "../../global-state";
|
||||
import { typedGlobalState } from "../../global-state";
|
||||
import { getConfig } from "../../extension";
|
||||
|
||||
const output = vscode.window.createOutputChannel("Bun - Diagnostics");
|
||||
|
||||
@@ -70,15 +71,14 @@ class BunDiagnosticsManager {
|
||||
private readonly editorState: EditorStateManager;
|
||||
private readonly signal: UnixSignal | TCPSocketSignal;
|
||||
private readonly context: vscode.ExtensionContext;
|
||||
public readonly inspectUrl: string;
|
||||
|
||||
public get notifyUrl() {
|
||||
public get signalUrl() {
|
||||
return this.signal.url;
|
||||
}
|
||||
|
||||
private static async getOrRecreateSignal(context: vscode.ExtensionContext) {
|
||||
const globalState = typedGlobalState(context.globalState);
|
||||
const existing = globalState.get("BUN_INSPECT_NOTIFY");
|
||||
const existing = globalState.get("BUN_INSPECT_CONNECT_TO");
|
||||
|
||||
const isWin = os.platform() === "win32";
|
||||
|
||||
@@ -102,7 +102,7 @@ class BunDiagnosticsManager {
|
||||
if (isWin) {
|
||||
const port = await getAvailablePort();
|
||||
|
||||
await globalState.update("BUN_INSPECT_NOTIFY", {
|
||||
await globalState.update("BUN_INSPECT_CONNECT_TO", {
|
||||
type: "tcp",
|
||||
port,
|
||||
});
|
||||
@@ -113,7 +113,7 @@ class BunDiagnosticsManager {
|
||||
} else {
|
||||
const signal = new UnixSignal();
|
||||
|
||||
await globalState.update("BUN_INSPECT_NOTIFY", {
|
||||
await globalState.update("BUN_INSPECT_CONNECT_TO", {
|
||||
type: "unix",
|
||||
url: signal.url,
|
||||
});
|
||||
@@ -124,30 +124,29 @@ class BunDiagnosticsManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static getOrCreateOldVersionInspectURL = createGlobalStateGenerationFn(
|
||||
"DIAGNOSTICS_BUN_INSPECT",
|
||||
async () => {
|
||||
const url =
|
||||
process.platform === "win32"
|
||||
? `ws://127.0.0.1:${await getAvailablePort()}/${getRandomId()}`
|
||||
: `ws+unix://${os.tmpdir()}/${getRandomId()}.sock`;
|
||||
// private static getOrCreateOldVersionInspectURL = createGlobalStateGenerationFn(
|
||||
// "DIAGNOSTICS_BUN_INSPECT",
|
||||
// async () => {
|
||||
// const url =
|
||||
// process.platform === "win32"
|
||||
// ? `ws://127.0.0.1:${await getAvailablePort()}/${getRandomId()}`
|
||||
// : `ws+unix://${os.tmpdir()}/${getRandomId()}.sock`;
|
||||
|
||||
return url;
|
||||
},
|
||||
);
|
||||
// return url;
|
||||
// },
|
||||
// );
|
||||
|
||||
public static async initialize(context: vscode.ExtensionContext) {
|
||||
const signal = await BunDiagnosticsManager.getOrRecreateSignal(context);
|
||||
const oldVersionInspectURL = await BunDiagnosticsManager.getOrCreateOldVersionInspectURL(context.globalState);
|
||||
|
||||
return new BunDiagnosticsManager(context, signal, oldVersionInspectURL);
|
||||
return new BunDiagnosticsManager(context, signal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when Bun pings BUN_INSPECT_NOTIFY (indicating a program has started).
|
||||
*/
|
||||
private async handleSocketConnection() {
|
||||
const debugAdapter = new WebSocketDebugAdapter(this.inspectUrl);
|
||||
private async handleSocketConnection(socket: Socket) {
|
||||
const debugAdapter = new NodeSocketDebugAdapter(socket);
|
||||
|
||||
this.editorState.clearAll("A new socket connected");
|
||||
|
||||
@@ -246,15 +245,10 @@ class BunDiagnosticsManager {
|
||||
});
|
||||
}
|
||||
|
||||
private constructor(
|
||||
context: vscode.ExtensionContext,
|
||||
signal: UnixSignal | TCPSocketSignal,
|
||||
oldVersionInspectURL: string,
|
||||
) {
|
||||
private constructor(context: vscode.ExtensionContext, signal: UnixSignal | TCPSocketSignal) {
|
||||
this.editorState = new EditorStateManager();
|
||||
this.signal = signal;
|
||||
this.context = context;
|
||||
this.inspectUrl = oldVersionInspectURL;
|
||||
|
||||
this.context.subscriptions.push(
|
||||
// on did type
|
||||
@@ -263,9 +257,7 @@ class BunDiagnosticsManager {
|
||||
}),
|
||||
);
|
||||
|
||||
this.signal.on("Signal.received", () => {
|
||||
this.handleSocketConnection();
|
||||
});
|
||||
this.signal.on("Signal.Socket.connect", this.handleSocketConnection.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,12 +266,14 @@ const description = new vscode.MarkdownString(
|
||||
);
|
||||
|
||||
export async function registerDiagnosticsSocket(context: vscode.ExtensionContext) {
|
||||
context.environmentVariableCollection.clear();
|
||||
context.environmentVariableCollection.description = description;
|
||||
|
||||
if (!getConfig("diagnosticsSocket.enabled")) return;
|
||||
|
||||
const manager = await BunDiagnosticsManager.initialize(context);
|
||||
|
||||
context.environmentVariableCollection.replace("BUN_INSPECT_NOTIFY", manager.notifyUrl);
|
||||
context.environmentVariableCollection.replace("BUN_INSPECT", `${manager.inspectUrl}?report=1?wait=1`); // Intentionally invalid query params
|
||||
context.environmentVariableCollection.replace("BUN_INSPECT_CONNECT_TO", manager.signalUrl);
|
||||
|
||||
context.subscriptions.push(manager);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import * as vscode from "vscode";
|
||||
import { styleLockfile } from "./lockfile.style";
|
||||
import { getConfig } from "../../extension";
|
||||
|
||||
export type BunLockfile = vscode.CustomDocument & {
|
||||
readonly preview: string;
|
||||
@@ -36,6 +37,11 @@ export class BunLockfileEditorProvider implements vscode.CustomReadonlyEditorPro
|
||||
}
|
||||
|
||||
function renderLockfile({ webview }: vscode.WebviewPanel, preview: string, extensionUri: vscode.Uri): void {
|
||||
if (!getConfig("bunlockb.enabled")) {
|
||||
webview.html = "<code>bun.bunlockb</code> config option is disabled."
|
||||
return
|
||||
}
|
||||
|
||||
const styleVSCodeUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, "assets", "vscode.css"));
|
||||
const lockfileContent = styleLockfile(preview);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import * as vscode from "vscode";
|
||||
import { debugCommand } from "../debug";
|
||||
import { BunTask } from "./tasks";
|
||||
import { getConfig } from "../../extension";
|
||||
|
||||
/**
|
||||
* Parses tasks defined in the package.json.
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ExtensionContext } from "vscode";
|
||||
export const GLOBAL_STATE_VERSION = 1;
|
||||
|
||||
export type GlobalStateTypes = {
|
||||
BUN_INSPECT_NOTIFY:
|
||||
BUN_INSPECT_CONNECT_TO:
|
||||
| {
|
||||
type: "tcp";
|
||||
port: number;
|
||||
|
||||
@@ -20,6 +20,8 @@ import {
|
||||
getEnv,
|
||||
writeFile,
|
||||
spawnSafe,
|
||||
spawn,
|
||||
mkdir,
|
||||
} from "./utils.mjs";
|
||||
import { parseArgs } from "node:util";
|
||||
|
||||
@@ -49,16 +51,19 @@ async function doBuildkiteAgent(action) {
|
||||
const args = [realpathSync(process.argv[1]), "start"];
|
||||
|
||||
if (isWindows) {
|
||||
const serviceCommand = [
|
||||
"New-Service",
|
||||
"-Name",
|
||||
"buildkite-agent",
|
||||
"-StartupType",
|
||||
"Automatic",
|
||||
"-BinaryPathName",
|
||||
`${escape(command)} ${escape(args.map(escape).join(" "))}`,
|
||||
mkdir(logsPath);
|
||||
|
||||
const nssm = which("nssm", { required: true });
|
||||
const nssmCommands = [
|
||||
[nssm, "install", "buildkite-agent", command, ...args],
|
||||
[nssm, "set", "buildkite-agent", "Start", "SERVICE_AUTO_START"],
|
||||
[nssm, "set", "buildkite-agent", "AppDirectory", homePath],
|
||||
[nssm, "set", "buildkite-agent", "AppStdout", agentLogPath],
|
||||
[nssm, "set", "buildkite-agent", "AppStderr", agentLogPath],
|
||||
];
|
||||
await spawnSafe(["powershell", "-Command", serviceCommand.join(" ")], { stdio: "inherit" });
|
||||
for (const command of nssmCommands) {
|
||||
await spawnSafe(command, { stdio: "inherit" });
|
||||
}
|
||||
}
|
||||
|
||||
if (isOpenRc()) {
|
||||
@@ -124,13 +129,21 @@ async function doBuildkiteAgent(action) {
|
||||
token = await getCloudMetadataTag("buildkite:token");
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
throw new Error(
|
||||
"Buildkite token not found: either set BUILDKITE_AGENT_TOKEN or add a buildkite:token label to the instance",
|
||||
);
|
||||
}
|
||||
|
||||
let shell;
|
||||
if (isWindows) {
|
||||
const pwsh = which(["pwsh", "powershell"], { required: true });
|
||||
shell = `${pwsh} -Command`;
|
||||
// Command Prompt has a faster startup time than PowerShell.
|
||||
// Also, it propogates the exit code of the command, which PowerShell does not.
|
||||
const cmd = which("cmd", { required: true });
|
||||
shell = `"${cmd}" /S /C`;
|
||||
} else {
|
||||
const sh = which(["bash", "sh"], { required: true });
|
||||
shell = `${sh} -c`;
|
||||
const sh = which("sh", { required: true });
|
||||
shell = `${sh} -e -c`;
|
||||
}
|
||||
|
||||
const flags = ["enable-job-log-tmpfile", "no-feature-reporting"];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Version: 4
|
||||
# A powershell script that installs the dependencies needed to build and test Bun.
|
||||
# This should work on Windows 10 or newer.
|
||||
# Version: 7
|
||||
# A script that installs the dependencies needed to build and test Bun.
|
||||
# This should work on Windows 10 or newer with PowerShell.
|
||||
|
||||
# If this script does not work on your machine, please open an issue:
|
||||
# https://github.com/oven-sh/bun/issues
|
||||
@@ -16,6 +16,9 @@ param (
|
||||
[switch]$Optimize = $CI
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force
|
||||
|
||||
function Execute-Command {
|
||||
$command = $args -join ' '
|
||||
Write-Output "$ $command"
|
||||
@@ -43,6 +46,47 @@ function Which {
|
||||
}
|
||||
}
|
||||
|
||||
function Execute-Script {
|
||||
param (
|
||||
[Parameter(Mandatory = $true, Position = 0)]
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
$pwsh = Which pwsh powershell -Required
|
||||
Execute-Command $pwsh $Path
|
||||
}
|
||||
|
||||
function Download-File {
|
||||
param (
|
||||
[Parameter(Mandatory = $true, Position = 0)]
|
||||
[string]$Url,
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$Name,
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
if (-not $Name) {
|
||||
$Name = [System.IO.Path]::ChangeExtension([System.IO.Path]::GetRandomFileName(), [System.IO.Path]::GetExtension($Url))
|
||||
}
|
||||
|
||||
if (-not $Path) {
|
||||
$Path = "$env:TEMP\$Name"
|
||||
}
|
||||
|
||||
$client = New-Object System.Net.WebClient
|
||||
for ($i = 0; $i -lt 10 -and -not (Test-Path $Path); $i++) {
|
||||
try {
|
||||
$client.DownloadFile($Url, $Path)
|
||||
} catch {
|
||||
Write-Warning "Failed to download $Url, retry $i..."
|
||||
Start-Sleep -s $i
|
||||
}
|
||||
}
|
||||
|
||||
return $Path
|
||||
}
|
||||
|
||||
function Install-Chocolatey {
|
||||
if (Which choco) {
|
||||
return
|
||||
@@ -50,7 +94,8 @@ function Install-Chocolatey {
|
||||
|
||||
Write-Output "Installing Chocolatey..."
|
||||
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
|
||||
iex -Command ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
|
||||
$installScript = Download-File "https://community.chocolatey.org/install.ps1"
|
||||
Execute-Script $installScript
|
||||
Refresh-Path
|
||||
}
|
||||
|
||||
@@ -96,10 +141,23 @@ function Add-To-Path {
|
||||
}
|
||||
|
||||
Write-Output "Adding $absolutePath to PATH..."
|
||||
[Environment]::SetEnvironmentVariable("Path", $newPath, "Machine")
|
||||
[Environment]::SetEnvironmentVariable("Path", "$newPath", "Machine")
|
||||
Refresh-Path
|
||||
}
|
||||
|
||||
function Set-Env {
|
||||
param (
|
||||
[Parameter(Mandatory = $true, Position = 0)]
|
||||
[string]$Name,
|
||||
[Parameter(Mandatory = $true, Position = 1)]
|
||||
[string]$Value
|
||||
)
|
||||
|
||||
Write-Output "Setting environment variable $Name=$Value..."
|
||||
[System.Environment]::SetEnvironmentVariable("$Name", "$Value", "Machine")
|
||||
[System.Environment]::SetEnvironmentVariable("$Name", "$Value", "Process")
|
||||
}
|
||||
|
||||
function Install-Package {
|
||||
param (
|
||||
[Parameter(Mandatory = $true, Position = 0)]
|
||||
@@ -137,7 +195,7 @@ function Install-Package {
|
||||
|
||||
function Install-Packages {
|
||||
foreach ($package in $args) {
|
||||
Install-Package -Name $package
|
||||
Install-Package $package
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,12 +203,13 @@ function Install-Common-Software {
|
||||
Install-Chocolatey
|
||||
Install-Pwsh
|
||||
Install-Git
|
||||
Install-Packages curl 7zip
|
||||
Install-Packages curl 7zip nssm
|
||||
Install-NodeJs
|
||||
Install-Bun
|
||||
Install-Cygwin
|
||||
if ($CI) {
|
||||
Install-Tailscale
|
||||
# FIXME: Installing tailscale causes the AWS metadata server to become unreachable
|
||||
# Install-Tailscale
|
||||
Install-Buildkite
|
||||
}
|
||||
}
|
||||
@@ -204,12 +263,13 @@ function Install-Buildkite {
|
||||
|
||||
Write-Output "Installing Buildkite agent..."
|
||||
$env:buildkiteAgentToken = "xxx"
|
||||
iex ((New-Object System.Net.WebClient).DownloadString("https://raw.githubusercontent.com/buildkite/agent/main/install.ps1"))
|
||||
$installScript = Download-File "https://raw.githubusercontent.com/buildkite/agent/main/install.ps1"
|
||||
Execute-Script $installScript
|
||||
Refresh-Path
|
||||
}
|
||||
|
||||
function Install-Build-Essentials {
|
||||
# Install-Visual-Studio
|
||||
Install-Visual-Studio
|
||||
Install-Packages `
|
||||
cmake `
|
||||
make `
|
||||
@@ -219,41 +279,42 @@ function Install-Build-Essentials {
|
||||
golang `
|
||||
nasm `
|
||||
ruby `
|
||||
strawberryperl `
|
||||
mingw
|
||||
Install-Rust
|
||||
Install-Llvm
|
||||
}
|
||||
|
||||
function Install-Visual-Studio {
|
||||
$components = @(
|
||||
"Microsoft.VisualStudio.Workload.NativeDesktop",
|
||||
"Microsoft.VisualStudio.Component.Windows10SDK.18362",
|
||||
"Microsoft.VisualStudio.Component.Windows11SDK.22000",
|
||||
"Microsoft.VisualStudio.Component.Windows11Sdk.WindowsPerformanceToolkit",
|
||||
"Microsoft.VisualStudio.Component.VC.ASAN", # C++ AddressSanitizer
|
||||
"Microsoft.VisualStudio.Component.VC.ATL", # C++ ATL for latest v143 build tools (x86 & x64)
|
||||
"Microsoft.VisualStudio.Component.VC.DiagnosticTools", # C++ Diagnostic Tools
|
||||
"Microsoft.VisualStudio.Component.VC.CLI.Support", # C++/CLI support for v143 build tools (Latest)
|
||||
"Microsoft.VisualStudio.Component.VC.CoreIde", # C++ core features
|
||||
"Microsoft.VisualStudio.Component.VC.Redist.14.Latest" # C++ 2022 Redistributable Update
|
||||
param (
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$Edition = "community"
|
||||
)
|
||||
|
||||
$arch = (Get-WmiObject Win32_Processor).Architecture
|
||||
if ($arch -eq 9) {
|
||||
$components += @(
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64", # MSVC v143 build tools (x86 & x64)
|
||||
"Microsoft.VisualStudio.Component.VC.Modules.x86.x64" # MSVC v143 C++ Modules for latest v143 build tools (x86 & x64)
|
||||
)
|
||||
} elseif ($arch -eq 5) {
|
||||
$components += @(
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.ARM64", # MSVC v143 build tools (ARM64)
|
||||
"Microsoft.VisualStudio.Component.UWP.VC.ARM64" # C++ Universal Windows Platform support for v143 build tools (ARM64/ARM64EC)
|
||||
)
|
||||
}
|
||||
Write-Output "Downloading Visual Studio installer..."
|
||||
$vsInstaller = Download-File "https://aka.ms/vs/17/release/vs_$Edition.exe"
|
||||
|
||||
$packageParameters = $components | ForEach-Object { "--add $_" }
|
||||
Install-Package visualstudio2022community `
|
||||
-ExtraArgs "--package-parameters '--add Microsoft.VisualStudio.Workload.NativeDesktop --includeRecommended --includeOptional'"
|
||||
Write-Output "Installing Visual Studio..."
|
||||
$vsInstallArgs = @(
|
||||
"--passive",
|
||||
"--norestart",
|
||||
"--wait",
|
||||
"--force",
|
||||
"--locale en-US",
|
||||
"--add Microsoft.VisualStudio.Workload.NativeDesktop",
|
||||
"--includeRecommended"
|
||||
)
|
||||
$startInfo = New-Object System.Diagnostics.ProcessStartInfo
|
||||
$startInfo.FileName = $vsInstaller
|
||||
$startInfo.Arguments = $vsInstallArgs -join ' '
|
||||
$startInfo.CreateNoWindow = $true
|
||||
$process = New-Object System.Diagnostics.Process
|
||||
$process.StartInfo = $startInfo
|
||||
$process.Start()
|
||||
$process.WaitForExit()
|
||||
if ($process.ExitCode -ne 0) {
|
||||
throw "Failed to install Visual Studio: code $($process.ExitCode)"
|
||||
}
|
||||
}
|
||||
|
||||
function Install-Rust {
|
||||
@@ -261,18 +322,31 @@ function Install-Rust {
|
||||
return
|
||||
}
|
||||
|
||||
Write-Output "Installing Rustup..."
|
||||
$rustupInit = Download-File "https://win.rustup.rs/" -Name "rustup-init.exe"
|
||||
|
||||
Write-Output "Installing Rust..."
|
||||
$rustupInit = "$env:TEMP\rustup-init.exe"
|
||||
(New-Object System.Net.WebClient).DownloadFile("https://win.rustup.rs/", $rustupInit)
|
||||
Execute-Command $rustupInit -y
|
||||
Add-To-Path "$env:USERPROFILE\.cargo\bin"
|
||||
|
||||
Write-Output "Moving Rust to $env:ProgramFiles..."
|
||||
$rustPath = Join-Path $env:ProgramFiles "Rust"
|
||||
if (-not (Test-Path $rustPath)) {
|
||||
New-Item -Path $rustPath -ItemType Directory
|
||||
}
|
||||
Move-Item "$env:UserProfile\.cargo" "$rustPath\cargo" -Force
|
||||
Move-Item "$env:UserProfile\.rustup" "$rustPath\rustup" -Force
|
||||
|
||||
Write-Output "Setting environment variables for Rust..."
|
||||
Set-Env "CARGO_HOME" "$rustPath\cargo"
|
||||
Set-Env "RUSTUP_HOME" "$rustPath\rustup"
|
||||
Add-To-Path "$rustPath\cargo\bin"
|
||||
}
|
||||
|
||||
function Install-Llvm {
|
||||
Install-Package llvm `
|
||||
-Command clang-cl `
|
||||
-Version "18.1.8"
|
||||
Add-To-Path "C:\Program Files\LLVM\bin"
|
||||
Add-To-Path "$env:ProgramFiles\LLVM\bin"
|
||||
}
|
||||
|
||||
function Optimize-System {
|
||||
@@ -280,6 +354,9 @@ function Optimize-System {
|
||||
Disable-Windows-Threat-Protection
|
||||
Disable-Windows-Services
|
||||
Disable-Power-Management
|
||||
}
|
||||
|
||||
function Optimize-System-Needs-Reboot {
|
||||
Uninstall-Windows-Defender
|
||||
}
|
||||
|
||||
@@ -319,7 +396,7 @@ function Disable-Windows-Services {
|
||||
}
|
||||
|
||||
function Disable-Power-Management {
|
||||
Write-Output "Disabling power management features..."
|
||||
Write-Output "Disabling Power Management..."
|
||||
powercfg /setactive 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c # High performance
|
||||
powercfg /change monitor-timeout-ac 0
|
||||
powercfg /change monitor-timeout-dc 0
|
||||
@@ -329,7 +406,6 @@ function Disable-Power-Management {
|
||||
powercfg /change hibernate-timeout-dc 0
|
||||
}
|
||||
|
||||
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force
|
||||
if ($Optimize) {
|
||||
Optimize-System
|
||||
}
|
||||
@@ -337,3 +413,6 @@ if ($Optimize) {
|
||||
Install-Common-Software
|
||||
Install-Build-Essentials
|
||||
|
||||
if ($Optimize) {
|
||||
Optimize-System-Needs-Reboot
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
# Version: 5
|
||||
# Version: 7
|
||||
|
||||
# A script that installs the dependencies needed to build and test Bun.
|
||||
# This should work on macOS and Linux with a POSIX shell.
|
||||
@@ -92,7 +92,7 @@ download_file() {
|
||||
execute chmod 755 "$tmp"
|
||||
|
||||
path="$tmp/$filename"
|
||||
fetch "$url" > "$path"
|
||||
fetch "$url" >"$path"
|
||||
execute chmod 644 "$path"
|
||||
|
||||
print "$path"
|
||||
@@ -112,14 +112,23 @@ append_to_file() {
|
||||
file="$1"
|
||||
content="$2"
|
||||
|
||||
if ! [ -f "$file" ]; then
|
||||
file_needs_sudo="0"
|
||||
if [ -f "$file" ]; then
|
||||
if ! [ -r "$file" ] || ! [ -w "$file" ]; then
|
||||
file_needs_sudo="1"
|
||||
fi
|
||||
else
|
||||
execute_as_user mkdir -p "$(dirname "$file")"
|
||||
execute_as_user touch "$file"
|
||||
fi
|
||||
|
||||
echo "$content" | while read -r line; do
|
||||
if ! grep -q "$line" "$file"; then
|
||||
echo "$line" >>"$file"
|
||||
if [ "$file_needs_sudo" = "1" ]; then
|
||||
execute_sudo sh -c "echo '$line' >> '$file'"
|
||||
else
|
||||
echo "$line" >>"$file"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -135,7 +144,7 @@ append_to_file_sudo() {
|
||||
|
||||
echo "$content" | while read -r line; do
|
||||
if ! grep -q "$line" "$file"; then
|
||||
echo "$line" | execute_sudo tee "$file" > /dev/null
|
||||
echo "$line" | execute_sudo tee "$file" >/dev/null
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -161,18 +170,21 @@ append_to_path() {
|
||||
export PATH="$path:$PATH"
|
||||
}
|
||||
|
||||
link_to_bin() {
|
||||
path="$1"
|
||||
if ! [ -d "$path" ]; then
|
||||
error "Could not find directory: \"$path\""
|
||||
move_to_bin() {
|
||||
exe_path="$1"
|
||||
if ! [ -f "$exe_path" ]; then
|
||||
error "Could not find executable: \"$exe_path\""
|
||||
fi
|
||||
|
||||
for file in "$path"/*; do
|
||||
if [ -f "$file" ]; then
|
||||
grant_to_user "$file"
|
||||
execute_sudo ln -sf "$file" "/usr/bin/$(basename "$file")"
|
||||
usr_paths="/usr/bin /usr/local/bin"
|
||||
for usr_path in $usr_paths; do
|
||||
if [ -d "$usr_path" ] && [ -w "$usr_path" ]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
grant_to_user "$exe_path"
|
||||
execute_sudo mv -f "$exe_path" "$usr_path/$(basename "$exe_path")"
|
||||
}
|
||||
|
||||
check_features() {
|
||||
@@ -384,6 +396,74 @@ check_user() {
|
||||
fi
|
||||
}
|
||||
|
||||
check_ulimit() {
|
||||
if ! [ "$ci" = "1" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
print "Checking ulimits..."
|
||||
systemd_conf="/etc/systemd/system.conf"
|
||||
if [ -f "$systemd_conf" ]; then
|
||||
limits_conf="/etc/security/limits.d/99-unlimited.conf"
|
||||
if ! [ -f "$limits_conf" ]; then
|
||||
execute_sudo mkdir -p "$(dirname "$limits_conf")"
|
||||
execute_sudo touch "$limits_conf"
|
||||
fi
|
||||
fi
|
||||
|
||||
limits="core data fsize memlock nofile rss stack cpu nproc as locks sigpending msgqueue"
|
||||
for limit in $limits; do
|
||||
limit_upper="$(echo "$limit" | tr '[:lower:]' '[:upper:]')"
|
||||
|
||||
limit_value="unlimited"
|
||||
case "$limit" in
|
||||
nofile | nproc)
|
||||
limit_value="1048576"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -f "$limits_conf" ]; then
|
||||
limit_users="root *"
|
||||
for limit_user in $limit_users; do
|
||||
append_to_file "$limits_conf" "$limit_user soft $limit $limit_value"
|
||||
append_to_file "$limits_conf" "$limit_user hard $limit $limit_value"
|
||||
done
|
||||
fi
|
||||
|
||||
if [ -f "$systemd_conf" ]; then
|
||||
append_to_file "$systemd_conf" "DefaultLimit$limit_upper=$limit_value"
|
||||
fi
|
||||
done
|
||||
|
||||
rc_conf="/etc/rc.conf"
|
||||
if [ -f "$rc_conf" ]; then
|
||||
rc_ulimit=""
|
||||
limit_flags="c d e f i l m n q r s t u v x"
|
||||
for limit_flag in $limit_flags; do
|
||||
limit_value="unlimited"
|
||||
case "$limit_flag" in
|
||||
n | u)
|
||||
limit_value="1048576"
|
||||
;;
|
||||
esac
|
||||
rc_ulimit="$rc_ulimit -$limit_flag $limit_value"
|
||||
done
|
||||
append_to_file "$rc_conf" "rc_ulimit=\"$rc_ulimit\""
|
||||
fi
|
||||
|
||||
pam_confs="/etc/pam.d/common-session /etc/pam.d/common-session-noninteractive"
|
||||
for pam_conf in $pam_confs; do
|
||||
if [ -f "$pam_conf" ]; then
|
||||
append_to_file "$pam_conf" "session optional pam_limits.so"
|
||||
fi
|
||||
done
|
||||
|
||||
systemctl="$(which systemctl)"
|
||||
if [ -f "$systemctl" ]; then
|
||||
execute_sudo "$systemctl" daemon-reload
|
||||
fi
|
||||
}
|
||||
|
||||
package_manager() {
|
||||
case "$pm" in
|
||||
apt)
|
||||
@@ -602,6 +682,14 @@ install_nodejs_headers() {
|
||||
}
|
||||
|
||||
install_bun() {
|
||||
case "$pm" in
|
||||
apk)
|
||||
install_packages \
|
||||
libgcc \
|
||||
libstdc++
|
||||
;;
|
||||
esac
|
||||
|
||||
bash="$(require bash)"
|
||||
script=$(download_file "https://bun.sh/install")
|
||||
|
||||
@@ -615,7 +703,10 @@ install_bun() {
|
||||
;;
|
||||
esac
|
||||
|
||||
link_to_bin "$home/.bun/bin"
|
||||
move_to_bin "$home/.bun/bin/bun"
|
||||
bun_path="$(which bun)"
|
||||
bunx_path="$(dirname "$bun_path")/bunx"
|
||||
execute_sudo ln -sf "$bun_path" "$bunx_path"
|
||||
}
|
||||
|
||||
install_cmake() {
|
||||
@@ -628,14 +719,14 @@ install_cmake() {
|
||||
cmake_version="3.30.5"
|
||||
case "$arch" in
|
||||
x64)
|
||||
url="https://github.com/Kitware/CMake/releases/download/v$cmake_version/cmake-$cmake_version-linux-x86_64.sh"
|
||||
cmake_url="https://github.com/Kitware/CMake/releases/download/v$cmake_version/cmake-$cmake_version-linux-x86_64.sh"
|
||||
;;
|
||||
aarch64)
|
||||
url="https://github.com/Kitware/CMake/releases/download/v$cmake_version/cmake-$cmake_version-linux-aarch64.sh"
|
||||
cmake_url="https://github.com/Kitware/CMake/releases/download/v$cmake_version/cmake-$cmake_version-linux-aarch64.sh"
|
||||
;;
|
||||
esac
|
||||
script=$(download_file "$url")
|
||||
execute_sudo "$sh" "$script" \
|
||||
cmake_script=$(download_file "$cmake_url")
|
||||
execute_sudo "$sh" "$cmake_script" \
|
||||
--skip-license \
|
||||
--prefix=/usr
|
||||
;;
|
||||
@@ -732,13 +823,13 @@ install_llvm() {
|
||||
case "$pm" in
|
||||
apt)
|
||||
bash="$(require bash)"
|
||||
script="$(download_file "https://apt.llvm.org/llvm.sh")"
|
||||
llvm_script="$(download_file "https://apt.llvm.org/llvm.sh")"
|
||||
case "$distro-$release" in
|
||||
ubuntu-24*)
|
||||
execute_sudo "$bash" "$script" "$(llvm_version)" all -njammy
|
||||
execute_sudo "$bash" "$llvm_script" "$(llvm_version)" all -njammy
|
||||
;;
|
||||
*)
|
||||
execute_sudo "$bash" "$script" "$(llvm_version)" all
|
||||
execute_sudo "$bash" "$llvm_script" "$(llvm_version)" all
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
@@ -779,11 +870,6 @@ install_rust() {
|
||||
execute_as_user "$sh" "$script" -y
|
||||
;;
|
||||
esac
|
||||
|
||||
# FIXME: This causes cargo to fail to build:
|
||||
# > error: rustup could not choose a version of cargo to run,
|
||||
# > because one wasn't specified explicitly, and no default is configured.
|
||||
# link_to_bin "$home/.cargo/bin"
|
||||
}
|
||||
|
||||
install_docker() {
|
||||
@@ -796,7 +882,7 @@ install_docker() {
|
||||
*)
|
||||
case "$distro-$release" in
|
||||
amzn-2 | amzn-1)
|
||||
execute amazon-linux-extras install docker
|
||||
execute_sudo amazon-linux-extras install docker
|
||||
;;
|
||||
amzn-* | alpine-*)
|
||||
install_packages docker
|
||||
@@ -832,8 +918,8 @@ install_tailscale() {
|
||||
case "$os" in
|
||||
linux)
|
||||
sh="$(require sh)"
|
||||
script=$(download_file "https://tailscale.com/install.sh")
|
||||
execute "$sh" "$script"
|
||||
tailscale_script=$(download_file "https://tailscale.com/install.sh")
|
||||
execute "$sh" "$tailscale_script"
|
||||
;;
|
||||
darwin)
|
||||
install_packages go
|
||||
@@ -862,24 +948,39 @@ create_buildkite_user() {
|
||||
esac
|
||||
|
||||
if [ -z "$(getent passwd "$user")" ]; then
|
||||
execute_sudo useradd "$user" \
|
||||
--system \
|
||||
--no-create-home \
|
||||
--home-dir "$home"
|
||||
case "$distro" in
|
||||
alpine)
|
||||
execute_sudo addgroup \
|
||||
--system "$group"
|
||||
execute_sudo adduser "$user" \
|
||||
--system \
|
||||
--ingroup "$group" \
|
||||
--shell "$(require sh)" \
|
||||
--home "$home" \
|
||||
--disabled-password
|
||||
;;
|
||||
*)
|
||||
execute_sudo useradd "$user" \
|
||||
--system \
|
||||
--shell "$(require sh)" \
|
||||
--no-create-home \
|
||||
--home-dir "$home"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ -n "$(getent group docker)" ]; then
|
||||
execute_sudo usermod -aG docker "$user"
|
||||
fi
|
||||
|
||||
paths="$home /var/cache/buildkite-agent /var/log/buildkite-agent /var/run/buildkite-agent /var/run/buildkite-agent/buildkite-agent.sock"
|
||||
for path in $paths; do
|
||||
buildkite_paths="$home /var/cache/buildkite-agent /var/log/buildkite-agent /var/run/buildkite-agent /var/run/buildkite-agent/buildkite-agent.sock"
|
||||
for path in $buildkite_paths; do
|
||||
execute_sudo mkdir -p "$path"
|
||||
execute_sudo chown -R "$user:$group" "$path"
|
||||
done
|
||||
|
||||
files="/var/run/buildkite-agent/buildkite-agent.pid"
|
||||
for file in $files; do
|
||||
buildkite_files="/var/run/buildkite-agent/buildkite-agent.pid"
|
||||
for file in $buildkite_files; do
|
||||
execute_sudo touch "$file"
|
||||
execute_sudo chown "$user:$group" "$file"
|
||||
done
|
||||
@@ -890,19 +991,42 @@ install_buildkite() {
|
||||
return
|
||||
fi
|
||||
|
||||
bash="$(require bash)"
|
||||
script="$(download_file "https://raw.githubusercontent.com/buildkite/agent/main/install.sh")"
|
||||
tmp_dir="$(execute dirname "$script")"
|
||||
HOME="$tmp_dir" execute "$bash" "$script"
|
||||
buildkite_version="3.87.0"
|
||||
case "$os-$arch" in
|
||||
linux-aarch64)
|
||||
buildkite_filename="buildkite-agent-linux-arm64-$buildkite_version.tar.gz"
|
||||
;;
|
||||
linux-x64)
|
||||
buildkite_filename="buildkite-agent-linux-amd64-$buildkite_version.tar.gz"
|
||||
;;
|
||||
darwin-aarch64)
|
||||
buildkite_filename="buildkite-agent-darwin-arm64-$buildkite_version.tar.gz"
|
||||
;;
|
||||
darwin-x64)
|
||||
buildkite_filename="buildkite-agent-darwin-amd64-$buildkite_version.tar.gz"
|
||||
;;
|
||||
esac
|
||||
buildkite_url="https://github.com/buildkite/agent/releases/download/v$buildkite_version/$buildkite_filename"
|
||||
buildkite_filepath="$(download_file "$buildkite_url" "$buildkite_filename")"
|
||||
buildkite_tmpdir="$(dirname "$buildkite_filepath")"
|
||||
|
||||
out_dir="$tmp_dir/.buildkite-agent"
|
||||
execute_sudo mv -f "$out_dir/bin/buildkite-agent" "/usr/bin/buildkite-agent"
|
||||
execute tar -xzf "$buildkite_filepath" -C "$buildkite_tmpdir"
|
||||
move_to_bin "$buildkite_tmpdir/buildkite-agent"
|
||||
execute rm -rf "$buildkite_tmpdir"
|
||||
}
|
||||
|
||||
install_chrome_dependencies() {
|
||||
install_chromium() {
|
||||
# https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#chrome-doesnt-launch-on-linux
|
||||
# https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#running-puppeteer-in-the-cloud
|
||||
case "$pm" in
|
||||
apk)
|
||||
install_packages \
|
||||
chromium \
|
||||
nss \
|
||||
freetype \
|
||||
harfbuzz \
|
||||
ttf-freefont
|
||||
;;
|
||||
apt)
|
||||
install_packages \
|
||||
fonts-liberation \
|
||||
@@ -979,22 +1103,17 @@ install_chrome_dependencies() {
|
||||
esac
|
||||
}
|
||||
|
||||
raise_file_descriptor_limit() {
|
||||
append_to_file_sudo /etc/security/limits.conf '* soft nofile 262144'
|
||||
append_to_file_sudo /etc/security/limits.conf '* hard nofile 262144'
|
||||
}
|
||||
|
||||
main() {
|
||||
check_features "$@"
|
||||
check_operating_system
|
||||
check_inside_docker
|
||||
check_user
|
||||
check_ulimit
|
||||
check_package_manager
|
||||
create_buildkite_user
|
||||
install_common_software
|
||||
install_build_essentials
|
||||
install_chrome_dependencies
|
||||
raise_file_descriptor_limit # XXX: temporary
|
||||
install_chromium
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { spawn as nodeSpawn } from "node:child_process";
|
||||
import { existsSync, readFileSync, mkdirSync, cpSync, chmodSync } from "node:fs";
|
||||
import { basename, join, resolve } from "node:path";
|
||||
import { isCI, printEnvironment, startGroup } from "./utils.mjs";
|
||||
|
||||
// https://cmake.org/cmake/help/latest/manual/cmake.1.html#generate-a-project-buildsystem
|
||||
const generateFlags = [
|
||||
@@ -37,6 +38,10 @@ async function build(args) {
|
||||
return spawn("pwsh", ["-NoProfile", "-NoLogo", "-File", shellPath, process.argv0, scriptPath, ...args]);
|
||||
}
|
||||
|
||||
if (isCI) {
|
||||
printEnvironment();
|
||||
}
|
||||
|
||||
const env = {
|
||||
...process.env,
|
||||
FORCE_COLOR: "1",
|
||||
@@ -102,7 +107,8 @@ async function build(args) {
|
||||
const generateArgs = Object.entries(generateOptions).flatMap(([flag, value]) =>
|
||||
flag.startsWith("-D") ? [`${flag}=${value}`] : [flag, value],
|
||||
);
|
||||
await spawn("cmake", generateArgs, { env }, "configuration");
|
||||
|
||||
await startGroup("CMake Configure", () => spawn("cmake", generateArgs, { env }));
|
||||
|
||||
const envPath = resolve(buildPath, ".env");
|
||||
if (existsSync(envPath)) {
|
||||
@@ -116,7 +122,8 @@ async function build(args) {
|
||||
const buildArgs = Object.entries(buildOptions)
|
||||
.sort(([a], [b]) => (a === "--build" ? -1 : a.localeCompare(b)))
|
||||
.flatMap(([flag, value]) => [flag, value]);
|
||||
await spawn("cmake", buildArgs, { env }, "compilation");
|
||||
|
||||
await startGroup("CMake Build", () => spawn("cmake", buildArgs, { env }));
|
||||
|
||||
printDuration("total", Date.now() - startTime);
|
||||
}
|
||||
|
||||
20
scripts/check-node.sh
Executable file
20
scripts/check-node.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
i=0
|
||||
j=0
|
||||
|
||||
for x in $(git ls-files test/js/node/test/parallel --exclude-standard --others | grep test-$1)
|
||||
do
|
||||
i=$((i+1))
|
||||
echo ./$x
|
||||
if timeout 2 $PWD/build/debug/bun-debug ./$x
|
||||
then
|
||||
j=$((j+1))
|
||||
git add ./$x
|
||||
fi
|
||||
echo
|
||||
echo
|
||||
done
|
||||
|
||||
echo $i tests tested
|
||||
echo $j tests passed
|
||||
1985
scripts/machine.mjs
1985
scripts/machine.mjs
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,6 @@ import {
|
||||
accessSync,
|
||||
appendFileSync,
|
||||
readdirSync,
|
||||
rmSync,
|
||||
} from "node:fs";
|
||||
import { spawn, spawnSync } from "node:child_process";
|
||||
import { join, basename, dirname, relative, sep } from "node:path";
|
||||
@@ -27,10 +26,14 @@ import {
|
||||
getBuildUrl,
|
||||
getEnv,
|
||||
getFileUrl,
|
||||
getLoggedInUserCount,
|
||||
getShell,
|
||||
getWindowsExitReason,
|
||||
isArm64,
|
||||
isBuildkite,
|
||||
isCI,
|
||||
isGithubAction,
|
||||
isMacOS,
|
||||
isWindows,
|
||||
printEnvironment,
|
||||
startGroup,
|
||||
@@ -57,6 +60,10 @@ const { values: options, positionals: filters } = parseArgs({
|
||||
type: "string",
|
||||
default: undefined,
|
||||
},
|
||||
["build-id"]: {
|
||||
type: "string",
|
||||
default: undefined,
|
||||
},
|
||||
["bail"]: {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
@@ -97,32 +104,7 @@ const { values: options, positionals: filters } = parseArgs({
|
||||
async function runTests() {
|
||||
let execPath;
|
||||
if (options["step"]) {
|
||||
downloadLoop: for (let i = 0; i < 10; i++) {
|
||||
execPath = await getExecPathFromBuildKite(options["step"]);
|
||||
for (let j = 0; j < 10; j++) {
|
||||
const { error } = spawnSync(execPath, ["--version"], {
|
||||
encoding: "utf-8",
|
||||
timeout: spawnTimeout,
|
||||
env: {
|
||||
PATH: process.env.PATH,
|
||||
BUN_DEBUG_QUIET_LOGS: 1,
|
||||
},
|
||||
});
|
||||
if (!error) {
|
||||
break downloadLoop;
|
||||
}
|
||||
const { code } = error;
|
||||
if (code === "EBUSY") {
|
||||
console.log("Bun appears to be busy, retrying...");
|
||||
continue;
|
||||
}
|
||||
if (code === "UNKNOWN") {
|
||||
console.log("Bun appears to be corrupted, downloading again...");
|
||||
rmSync(execPath, { force: true });
|
||||
continue downloadLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
execPath = await getExecPathFromBuildKite(options["step"], options["build-id"]);
|
||||
} else {
|
||||
execPath = getExecPath(options["exec-path"]);
|
||||
}
|
||||
@@ -206,6 +188,31 @@ async function runTests() {
|
||||
if (results.every(({ ok }) => ok)) {
|
||||
for (const testPath of tests) {
|
||||
const title = relative(cwd, join(testsPath, testPath)).replace(/\\/g, "/");
|
||||
if (title.startsWith("test/js/node/test/parallel/")) {
|
||||
await runTest(title, async () => {
|
||||
const { ok, error, stdout } = await spawnBun(execPath, {
|
||||
cwd: cwd,
|
||||
args: [title],
|
||||
timeout: spawnTimeout,
|
||||
env: {
|
||||
FORCE_COLOR: "0",
|
||||
},
|
||||
stdout: chunk => pipeTestStdout(process.stdout, chunk),
|
||||
stderr: chunk => pipeTestStdout(process.stderr, chunk),
|
||||
});
|
||||
return {
|
||||
testPath: title,
|
||||
ok,
|
||||
status: ok ? "pass" : "fail",
|
||||
error,
|
||||
errors: [],
|
||||
tests: [],
|
||||
stdout,
|
||||
stdoutPreview: "",
|
||||
};
|
||||
});
|
||||
continue;
|
||||
}
|
||||
await runTest(title, async () => spawnBunTest(execPath, join("test", testPath)));
|
||||
}
|
||||
}
|
||||
@@ -455,12 +462,14 @@ async function spawnBun(execPath, { args, cwd, timeout, env, stdout, stderr }) {
|
||||
const path = addPath(dirname(execPath), process.env.PATH);
|
||||
const tmpdirPath = mkdtempSync(join(tmpdir(), "buntmp-"));
|
||||
const { username, homedir } = userInfo();
|
||||
const shellPath = getShell();
|
||||
const bunEnv = {
|
||||
...process.env,
|
||||
PATH: path,
|
||||
TMPDIR: tmpdirPath,
|
||||
USER: username,
|
||||
HOME: homedir,
|
||||
SHELL: shellPath,
|
||||
FORCE_COLOR: "1",
|
||||
BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING: "1",
|
||||
BUN_DEBUG_QUIET_LOGS: "1",
|
||||
@@ -577,7 +586,7 @@ async function spawnBunTest(execPath, testPath, options = { cwd }) {
|
||||
* @returns {number}
|
||||
*/
|
||||
function getTestTimeout(testPath) {
|
||||
if (/integration|3rd_party|docker/i.test(testPath)) {
|
||||
if (/integration|3rd_party|docker|bun-install-registry|v8/i.test(testPath)) {
|
||||
return integrationTimeout;
|
||||
}
|
||||
return testTimeout;
|
||||
@@ -770,7 +779,7 @@ function isJavaScriptTest(path) {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isTest(path) {
|
||||
if (path.replaceAll(sep, "/").includes("/test-cluster-") && path.endsWith(".js")) return true;
|
||||
if (path.startsWith("js/node/test/parallel/") && isMacOS && isArm64) return true;
|
||||
if (path.replaceAll(sep, "/").startsWith("js/node/cluster/test-") && path.endsWith(".ts")) return true;
|
||||
return isTestStrict(path);
|
||||
}
|
||||
@@ -1053,9 +1062,10 @@ function getExecPath(bunExe) {
|
||||
|
||||
/**
|
||||
* @param {string} target
|
||||
* @param {string} [buildId]
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function getExecPathFromBuildKite(target) {
|
||||
async function getExecPathFromBuildKite(target, buildId) {
|
||||
if (existsSync(target) || target.includes("/")) {
|
||||
return getExecPath(target);
|
||||
}
|
||||
@@ -1063,23 +1073,27 @@ async function getExecPathFromBuildKite(target) {
|
||||
const releasePath = join(cwd, "release");
|
||||
mkdirSync(releasePath, { recursive: true });
|
||||
|
||||
const args = ["artifact", "download", "**", releasePath, "--step", target];
|
||||
const buildId = process.env["BUILDKITE_ARTIFACT_BUILD_ID"];
|
||||
if (buildId) {
|
||||
args.push("--build", buildId);
|
||||
}
|
||||
|
||||
await spawnSafe({
|
||||
command: "buildkite-agent",
|
||||
args,
|
||||
});
|
||||
|
||||
let zipPath;
|
||||
for (const entry of readdirSync(releasePath, { recursive: true, encoding: "utf-8" })) {
|
||||
if (/^bun.*\.zip$/i.test(entry) && !entry.includes("-profile.zip")) {
|
||||
zipPath = join(releasePath, entry);
|
||||
break;
|
||||
downloadLoop: for (let i = 0; i < 10; i++) {
|
||||
const args = ["artifact", "download", "**", releasePath, "--step", target];
|
||||
if (buildId) {
|
||||
args.push("--build", buildId);
|
||||
}
|
||||
|
||||
await spawnSafe({
|
||||
command: "buildkite-agent",
|
||||
args,
|
||||
});
|
||||
|
||||
for (const entry of readdirSync(releasePath, { recursive: true, encoding: "utf-8" })) {
|
||||
if (/^bun.*\.zip$/i.test(entry) && !entry.includes("-profile.zip")) {
|
||||
zipPath = join(releasePath, entry);
|
||||
break downloadLoop;
|
||||
}
|
||||
}
|
||||
|
||||
console.warn(`Waiting for ${target}.zip to be available...`);
|
||||
await new Promise(resolve => setTimeout(resolve, i * 1000));
|
||||
}
|
||||
|
||||
if (!zipPath) {
|
||||
@@ -1088,13 +1102,15 @@ async function getExecPathFromBuildKite(target) {
|
||||
|
||||
await unzip(zipPath, releasePath);
|
||||
|
||||
for (const entry of readdirSync(releasePath, { recursive: true, encoding: "utf-8" })) {
|
||||
const releaseFiles = readdirSync(releasePath, { recursive: true, encoding: "utf-8" });
|
||||
for (const entry of releaseFiles) {
|
||||
const execPath = join(releasePath, entry);
|
||||
if (/bun(?:\.exe)?$/i.test(entry) && isExecutable(execPath)) {
|
||||
if (/bun(?:\.exe)?$/i.test(entry) && statSync(execPath).isFile()) {
|
||||
return execPath;
|
||||
}
|
||||
}
|
||||
|
||||
console.warn(`Found ${releaseFiles.length} files in ${releasePath}:`);
|
||||
throw new Error(`Could not find executable from BuildKite: ${releasePath}`);
|
||||
}
|
||||
|
||||
@@ -1439,8 +1455,39 @@ export async function main() {
|
||||
}
|
||||
|
||||
printEnvironment();
|
||||
|
||||
// FIXME: Some DNS tests hang unless we set the DNS server to 8.8.8.8
|
||||
// It also appears to hang on 1.1.1.1, which could explain this issue:
|
||||
// https://github.com/oven-sh/bun/issues/11136
|
||||
if (isWindows && isCI) {
|
||||
await spawn("pwsh", [
|
||||
"-Command",
|
||||
"Set-DnsClientServerAddress -InterfaceAlias 'Ethernet 4' -ServerAddresses ('8.8.8.8','8.8.4.4')",
|
||||
]);
|
||||
}
|
||||
|
||||
const results = await runTests();
|
||||
const ok = results.every(({ ok }) => ok);
|
||||
|
||||
let waitForUser = false;
|
||||
while (isCI) {
|
||||
const userCount = getLoggedInUserCount();
|
||||
if (!userCount) {
|
||||
if (waitForUser) {
|
||||
console.log("No users logged in, exiting runner...");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!waitForUser) {
|
||||
startGroup("Summary");
|
||||
console.warn(`Found ${userCount} users logged in, keeping the runner alive until logout...`);
|
||||
waitForUser = true;
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 60_000));
|
||||
}
|
||||
|
||||
process.exit(getExitCode(ok ? "pass" : "fail"));
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -130,7 +130,20 @@ pub fn raiseIgnoringPanicHandler(sig: bun.SignalCode) noreturn {
|
||||
Output.flush();
|
||||
Output.Source.Stdio.restore();
|
||||
|
||||
// clear segfault handler
|
||||
bun.crash_handler.resetSegfaultHandler();
|
||||
|
||||
// clear signal handler
|
||||
if (bun.Environment.os != .windows) {
|
||||
var sa: std.c.Sigaction = .{
|
||||
.handler = .{ .handler = std.posix.SIG.DFL },
|
||||
.mask = std.posix.empty_sigset,
|
||||
.flags = std.posix.SA.RESETHAND,
|
||||
};
|
||||
_ = std.c.sigaction(@intFromEnum(sig), &sa, null);
|
||||
}
|
||||
|
||||
// kill self
|
||||
_ = std.c.raise(@intFromEnum(sig));
|
||||
std.c.abort();
|
||||
}
|
||||
|
||||
@@ -498,7 +498,7 @@ pub fn BSSMap(comptime ValueType: type, comptime count: anytype, comptime store_
|
||||
}
|
||||
|
||||
pub fn getOrPut(self: *Self, denormalized_key: []const u8) !Result {
|
||||
const key = if (comptime remove_trailing_slashes) std.mem.trimRight(u8, denormalized_key, "/") else denormalized_key;
|
||||
const key = if (comptime remove_trailing_slashes) std.mem.trimRight(u8, denormalized_key, std.fs.path.sep_str) else denormalized_key;
|
||||
const _key = bun.hash(key);
|
||||
|
||||
self.mutex.lock();
|
||||
@@ -526,7 +526,7 @@ pub fn BSSMap(comptime ValueType: type, comptime count: anytype, comptime store_
|
||||
}
|
||||
|
||||
pub fn get(self: *Self, denormalized_key: []const u8) ?*ValueType {
|
||||
const key = if (comptime remove_trailing_slashes) std.mem.trimRight(u8, denormalized_key, "/") else denormalized_key;
|
||||
const key = if (comptime remove_trailing_slashes) std.mem.trimRight(u8, denormalized_key, std.fs.path.sep_str) else denormalized_key;
|
||||
const _key = bun.hash(key);
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
@@ -588,7 +588,7 @@ pub fn BSSMap(comptime ValueType: type, comptime count: anytype, comptime store_
|
||||
defer self.mutex.unlock();
|
||||
|
||||
const key = if (comptime remove_trailing_slashes)
|
||||
std.mem.trimRight(u8, denormalized_key, "/")
|
||||
std.mem.trimRight(u8, denormalized_key, std.fs.path.sep_str)
|
||||
else
|
||||
denormalized_key;
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#include "BakeGlobalObject.h"
|
||||
#include "BakeSourceProvider.h"
|
||||
#include "JSNextTickQueue.h"
|
||||
#include "JavaScriptCore/GlobalObjectMethodTable.h"
|
||||
#include "JavaScriptCore/JSInternalPromise.h"
|
||||
#include "headers-handwritten.h"
|
||||
#include "JavaScriptCore/JSModuleLoader.h"
|
||||
#include "JavaScriptCore/Completion.h"
|
||||
#include "JavaScriptCore/JSSourceCode.h"
|
||||
|
||||
extern "C" BunString BakeProdResolve(JSC::JSGlobalObject*, BunString a, BunString b);
|
||||
|
||||
@@ -72,6 +74,58 @@ JSC::Identifier bakeModuleLoaderResolve(JSC::JSGlobalObject* jsGlobal,
|
||||
return Zig::GlobalObject::moduleLoaderResolve(jsGlobal, loader, key, referrer, origin);
|
||||
}
|
||||
|
||||
static JSC::JSInternalPromise* rejectedInternalPromise(JSC::JSGlobalObject* globalObject, JSC::JSValue value)
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
JSC::JSInternalPromise* promise = JSC::JSInternalPromise::create(vm, globalObject->internalPromiseStructure());
|
||||
promise->internalField(JSC::JSPromise::Field::ReactionsOrResult).set(vm, promise, value);
|
||||
promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, JSC::jsNumber(promise->internalField(JSC::JSPromise::Field::Flags).get().asUInt32AsAnyInt() | JSC::JSPromise::isFirstResolvingFunctionCalledFlag | static_cast<unsigned>(JSC::JSPromise::Status::Rejected)));
|
||||
return promise;
|
||||
}
|
||||
|
||||
static JSC::JSInternalPromise* resolvedInternalPromise(JSC::JSGlobalObject* globalObject, JSC::JSValue value)
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
JSC::JSInternalPromise* promise = JSC::JSInternalPromise::create(vm, globalObject->internalPromiseStructure());
|
||||
promise->internalField(JSC::JSPromise::Field::ReactionsOrResult).set(vm, promise, value);
|
||||
promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, JSC::jsNumber(promise->internalField(JSC::JSPromise::Field::Flags).get().asUInt32AsAnyInt() | JSC::JSPromise::isFirstResolvingFunctionCalledFlag | static_cast<unsigned>(JSC::JSPromise::Status::Fulfilled)));
|
||||
return promise;
|
||||
}
|
||||
|
||||
extern "C" BunString BakeProdLoad(ProductionPerThread* perThreadData, BunString a);
|
||||
|
||||
JSC::JSInternalPromise* bakeModuleLoaderFetch(JSC::JSGlobalObject* globalObject,
|
||||
JSC::JSModuleLoader* loader, JSC::JSValue key,
|
||||
JSC::JSValue parameters, JSC::JSValue script)
|
||||
{
|
||||
Bake::GlobalObject* global = jsCast<Bake::GlobalObject*>(globalObject);
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
auto moduleKey = key.toWTFString(globalObject);
|
||||
if (UNLIKELY(scope.exception()))
|
||||
return rejectedInternalPromise(globalObject, scope.exception()->value());
|
||||
|
||||
if (moduleKey.startsWith("bake:/"_s)) {
|
||||
if (LIKELY(global->m_perThreadData)) {
|
||||
BunString source = BakeProdLoad(global->m_perThreadData, Bun::toString(moduleKey));
|
||||
if (source.tag != BunStringTag::Dead) {
|
||||
JSC::SourceOrigin origin = JSC::SourceOrigin(WTF::URL(moduleKey));
|
||||
JSC::SourceCode sourceCode = JSC::SourceCode(Bake::SourceProvider::create(
|
||||
source.toWTFString(),
|
||||
origin,
|
||||
WTFMove(moduleKey),
|
||||
WTF::TextPosition(),
|
||||
JSC::SourceProviderSourceType::Module));
|
||||
return resolvedInternalPromise(globalObject, JSC::JSSourceCode::create(vm, WTFMove(sourceCode)));
|
||||
}
|
||||
return rejectedInternalPromise(globalObject, createTypeError(globalObject, makeString("Bundle does not have \""_s, moduleKey, "\". This is a bug in Bun's bundler."_s)));
|
||||
}
|
||||
return rejectedInternalPromise(globalObject, createTypeError(globalObject, "BakeGlobalObject does not have per-thread data configured"_s));
|
||||
}
|
||||
|
||||
return Zig::GlobalObject::moduleLoaderFetch(globalObject, loader, key, parameters, script);
|
||||
}
|
||||
|
||||
#define INHERIT_HOOK_METHOD(name) \
|
||||
Zig::GlobalObject::s_globalObjectMethodTable.name
|
||||
|
||||
@@ -83,7 +137,7 @@ const JSC::GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = {
|
||||
INHERIT_HOOK_METHOD(shouldInterruptScriptBeforeTimeout),
|
||||
bakeModuleLoaderImportModule,
|
||||
bakeModuleLoaderResolve,
|
||||
INHERIT_HOOK_METHOD(moduleLoaderFetch),
|
||||
bakeModuleLoaderFetch,
|
||||
INHERIT_HOOK_METHOD(moduleLoaderCreateImportMetaProperties),
|
||||
INHERIT_HOOK_METHOD(moduleLoaderEvaluate),
|
||||
INHERIT_HOOK_METHOD(promiseRejectionTracker),
|
||||
@@ -155,4 +209,9 @@ extern "C" GlobalObject* BakeCreateProdGlobal(void* console)
|
||||
return global;
|
||||
}
|
||||
|
||||
extern "C" void BakeGlobalObject__attachPerThreadData(GlobalObject* global, ProductionPerThread* perThreadData)
|
||||
{
|
||||
global->m_perThreadData = perThreadData;
|
||||
}
|
||||
|
||||
}; // namespace Bake
|
||||
|
||||
@@ -4,10 +4,14 @@
|
||||
|
||||
namespace Bake {
|
||||
|
||||
struct ProductionPerThread;
|
||||
|
||||
class GlobalObject : public Zig::GlobalObject {
|
||||
public:
|
||||
using Base = Zig::GlobalObject;
|
||||
|
||||
ProductionPerThread* m_perThreadData;
|
||||
|
||||
template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
|
||||
|
||||
@@ -21,7 +21,7 @@ extern "C" JSC::EncodedJSValue BakeLoadInitialServerCode(GlobalObject* global, B
|
||||
|
||||
String string = "bake://server-runtime.js"_s;
|
||||
JSC::SourceOrigin origin = JSC::SourceOrigin(WTF::URL(string));
|
||||
JSC::SourceCode sourceCode = JSC::SourceCode(DevSourceProvider::create(
|
||||
JSC::SourceCode sourceCode = JSC::SourceCode(SourceProvider::create(
|
||||
source.toWTFString(),
|
||||
origin,
|
||||
WTFMove(string),
|
||||
@@ -54,7 +54,7 @@ extern "C" JSC::EncodedJSValue BakeLoadServerHmrPatch(GlobalObject* global, BunS
|
||||
|
||||
String string = "bake://server.patch.js"_s;
|
||||
JSC::SourceOrigin origin = JSC::SourceOrigin(WTF::URL(string));
|
||||
JSC::SourceCode sourceCode = JSC::SourceCode(DevSourceProvider::create(
|
||||
JSC::SourceCode sourceCode = JSC::SourceCode(SourceProvider::create(
|
||||
source.toWTFString(),
|
||||
origin,
|
||||
WTFMove(string),
|
||||
@@ -117,7 +117,7 @@ extern "C" JSC::EncodedJSValue BakeRegisterProductionChunk(JSC::JSGlobalObject*
|
||||
String string = virtualPathName.toWTFString();
|
||||
JSC::JSString* key = JSC::jsString(vm, string);
|
||||
JSC::SourceOrigin origin = JSC::SourceOrigin(WTF::URL(string));
|
||||
JSC::SourceCode sourceCode = JSC::SourceCode(DevSourceProvider::create(
|
||||
JSC::SourceCode sourceCode = JSC::SourceCode(SourceProvider::create(
|
||||
source.toWTFString(),
|
||||
origin,
|
||||
WTFMove(string),
|
||||
|
||||
@@ -6,20 +6,20 @@
|
||||
|
||||
namespace Bake {
|
||||
|
||||
class DevSourceProvider final : public JSC::StringSourceProvider {
|
||||
class SourceProvider final : public JSC::StringSourceProvider {
|
||||
public:
|
||||
static Ref<DevSourceProvider> create(
|
||||
static Ref<SourceProvider> create(
|
||||
const String& source,
|
||||
const JSC::SourceOrigin& sourceOrigin,
|
||||
String&& sourceURL,
|
||||
const TextPosition& startPosition,
|
||||
JSC::SourceProviderSourceType sourceType
|
||||
) {
|
||||
return adoptRef(*new DevSourceProvider(source, sourceOrigin, WTFMove(sourceURL), startPosition, sourceType));
|
||||
return adoptRef(*new SourceProvider(source, sourceOrigin, WTFMove(sourceURL), startPosition, sourceType));
|
||||
}
|
||||
|
||||
private:
|
||||
DevSourceProvider(
|
||||
SourceProvider(
|
||||
const String& source,
|
||||
const JSC::SourceOrigin& sourceOrigin,
|
||||
String&& sourceURL,
|
||||
|
||||
@@ -16,10 +16,11 @@ pub const Options = struct {
|
||||
root: []const u8,
|
||||
vm: *VirtualMachine,
|
||||
framework: bake.Framework,
|
||||
bundler_options: bake.SplitBundlerOptions,
|
||||
|
||||
// Debugging features
|
||||
dump_sources: ?[]const u8 = if (Environment.isDebug) ".bake-debug" else null,
|
||||
dump_state_on_crash: bool = false,
|
||||
dump_state_on_crash: ?bool = null,
|
||||
verbose_watcher: bool = false,
|
||||
};
|
||||
|
||||
@@ -93,6 +94,7 @@ generation: usize = 0,
|
||||
bundles_since_last_error: usize = 0,
|
||||
|
||||
framework: bake.Framework,
|
||||
bundler_options: bake.SplitBundlerOptions,
|
||||
// Each logical graph gets its own bundler configuration
|
||||
server_bundler: Bundler,
|
||||
client_bundler: Bundler,
|
||||
@@ -238,8 +240,11 @@ pub fn init(options: Options) bun.JSOOM!*DevServer {
|
||||
.graph_safety_lock = bun.DebugThreadLock.unlocked,
|
||||
.dump_dir = dump_dir,
|
||||
.framework = options.framework,
|
||||
.bundler_options = options.bundler_options,
|
||||
.emit_visualizer_events = 0,
|
||||
.has_pre_crash_handler = options.dump_state_on_crash,
|
||||
.has_pre_crash_handler = bun.FeatureFlags.bake_debugging_features and
|
||||
options.dump_state_on_crash orelse
|
||||
bun.getRuntimeFeatureFlag("BUN_DUMP_STATE_ON_CRASH"),
|
||||
.css_files = .{},
|
||||
.route_js_payloads = .{},
|
||||
// .assets = .{},
|
||||
@@ -307,8 +312,9 @@ pub fn init(options: Options) bun.JSOOM!*DevServer {
|
||||
}
|
||||
|
||||
dev.framework = dev.framework.resolve(&dev.server_bundler.resolver, &dev.client_bundler.resolver, options.arena) catch {
|
||||
try bake.Framework.addReactInstallCommandNote(&dev.log);
|
||||
return global.throwValue2(dev.log.toJSAggregateError(global, "Framework is missing required files!"));
|
||||
if (dev.framework.is_built_in_react)
|
||||
try bake.Framework.addReactInstallCommandNote(&dev.log);
|
||||
return global.throwValue(dev.log.toJSAggregateError(global, "Framework is missing required files!"));
|
||||
};
|
||||
|
||||
errdefer dev.route_lookup.clearAndFree(allocator);
|
||||
@@ -438,7 +444,7 @@ pub fn init(options: Options) bun.JSOOM!*DevServer {
|
||||
// after that line.
|
||||
try dev.scanInitialRoutes();
|
||||
|
||||
if (bun.FeatureFlags.bake_debugging_features and options.dump_state_on_crash)
|
||||
if (bun.FeatureFlags.bake_debugging_features and dev.has_pre_crash_handler)
|
||||
try bun.crash_handler.appendPreCrashHandler(DevServer, dev, dumpStateDueToCrash);
|
||||
|
||||
return dev;
|
||||
@@ -617,7 +623,7 @@ fn ensureRouteIsBundled(
|
||||
.data = switch (kind) {
|
||||
.js_payload => .{ .js_payload = resp },
|
||||
.server_handler => .{
|
||||
.server_handler = (dev.server.?.DebugHTTPServer.prepareJsRequestContext(req, resp) orelse return)
|
||||
.server_handler = (dev.server.?.DebugHTTPServer.prepareJsRequestContext(req, resp, null) orelse return)
|
||||
.save(dev.vm.global, req, resp),
|
||||
},
|
||||
},
|
||||
@@ -670,7 +676,7 @@ fn ensureRouteIsBundled(
|
||||
.data = switch (kind) {
|
||||
.js_payload => .{ .js_payload = resp },
|
||||
.server_handler => .{
|
||||
.server_handler = (dev.server.?.DebugHTTPServer.prepareJsRequestContext(req, resp) orelse return)
|
||||
.server_handler = (dev.server.?.DebugHTTPServer.prepareJsRequestContext(req, resp, null) orelse return)
|
||||
.save(dev.vm.global, req, resp),
|
||||
},
|
||||
},
|
||||
@@ -898,6 +904,7 @@ fn startAsyncBundle(
|
||||
.framework = dev.framework,
|
||||
.client_bundler = &dev.client_bundler,
|
||||
.ssr_bundler = &dev.ssr_bundler,
|
||||
.plugins = dev.bundler_options.plugin,
|
||||
} else @panic("TODO: support non-server components"),
|
||||
allocator,
|
||||
.{ .js = dev.vm.eventLoop() },
|
||||
@@ -1202,10 +1209,10 @@ pub fn finalizeBundle(
|
||||
);
|
||||
|
||||
// Create an entry for this file.
|
||||
const abs_path = ctx.sources[index.get()].path.text;
|
||||
const key = ctx.sources[index.get()].path.keyForIncrementalGraph();
|
||||
// Later code needs to retrieve the CSS content
|
||||
// The hack is to use `entry_point_id`, which is otherwise unused, to store an index.
|
||||
chunk.entry_point.entry_point_id = try dev.insertOrUpdateCssAsset(abs_path, code.buffer);
|
||||
chunk.entry_point.entry_point_id = try dev.insertOrUpdateCssAsset(key, code.buffer);
|
||||
|
||||
try dev.client_graph.receiveChunk(&ctx, index, "", .css, false);
|
||||
|
||||
@@ -1216,7 +1223,7 @@ pub fn finalizeBundle(
|
||||
try dev.server_graph.insertCssFileOnServer(
|
||||
&ctx,
|
||||
index,
|
||||
abs_path,
|
||||
key,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1432,8 +1439,8 @@ pub fn finalizeBundle(
|
||||
try w.writeInt(u32, @intCast(css_chunks.len), .little);
|
||||
const sources = bv2.graph.input_files.items(.source);
|
||||
for (css_chunks) |chunk| {
|
||||
const abs_path = sources[chunk.entry_point.source_index].path.text;
|
||||
try w.writeAll(&std.fmt.bytesToHex(std.mem.asBytes(&bun.hash(abs_path)), .lower));
|
||||
const key = sources[chunk.entry_point.source_index].path.keyForIncrementalGraph();
|
||||
try w.writeAll(&std.fmt.bytesToHex(std.mem.asBytes(&bun.hash(key)), .lower));
|
||||
const css_data = css_values[chunk.entry_point.entry_point_id];
|
||||
try w.writeInt(u32, @intCast(css_data.len), .little);
|
||||
try w.writeAll(css_data);
|
||||
@@ -1588,12 +1595,13 @@ fn insertOrUpdateCssAsset(dev: *DevServer, abs_path: []const u8, code: []const u
|
||||
return @intCast(gop.index);
|
||||
}
|
||||
|
||||
/// Note: The log is not consumed here
|
||||
pub fn handleParseTaskFailure(
|
||||
dev: *DevServer,
|
||||
err: anyerror,
|
||||
graph: bake.Graph,
|
||||
abs_path: []const u8,
|
||||
log: *Log,
|
||||
key: []const u8,
|
||||
log: *const Log,
|
||||
) bun.OOM!void {
|
||||
dev.graph_safety_lock.lock();
|
||||
defer dev.graph_safety_lock.unlock();
|
||||
@@ -1605,23 +1613,23 @@ pub fn handleParseTaskFailure(
|
||||
// TODO: this should walk up the graph one level, and queue all of these
|
||||
// files for re-bundling if they aren't already in the BundleV2 graph.
|
||||
switch (graph) {
|
||||
.server, .ssr => try dev.server_graph.onFileDeleted(abs_path, log),
|
||||
.client => try dev.client_graph.onFileDeleted(abs_path, log),
|
||||
.server, .ssr => try dev.server_graph.onFileDeleted(key, log),
|
||||
.client => try dev.client_graph.onFileDeleted(key, log),
|
||||
}
|
||||
} else {
|
||||
Output.prettyErrorln("<red><b>Error{s} while bundling \"{s}\":<r>", .{
|
||||
if (log.errors +| log.warnings != 1) "s" else "",
|
||||
dev.relativePath(abs_path),
|
||||
dev.relativePath(key),
|
||||
});
|
||||
log.print(Output.errorWriterBuffered()) catch {};
|
||||
Output.flush();
|
||||
|
||||
// Do not index css errors
|
||||
if (!bun.strings.hasSuffixComptime(abs_path, ".css")) {
|
||||
if (!bun.strings.hasSuffixComptime(key, ".css")) {
|
||||
switch (graph) {
|
||||
.server => try dev.server_graph.insertFailure(abs_path, log, false),
|
||||
.ssr => try dev.server_graph.insertFailure(abs_path, log, true),
|
||||
.client => try dev.client_graph.insertFailure(abs_path, log, false),
|
||||
.server => try dev.server_graph.insertFailure(key, log, false),
|
||||
.ssr => try dev.server_graph.insertFailure(key, log, true),
|
||||
.client => try dev.client_graph.insertFailure(key, log, false),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1851,7 +1859,10 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
return struct {
|
||||
// Unless otherwise mentioned, all data structures use DevServer's allocator.
|
||||
|
||||
/// Key contents are owned by `default_allocator`
|
||||
/// Keys are absolute paths for the "file" namespace, or the
|
||||
/// pretty-formatted path value that appear in imports. Absolute paths
|
||||
/// are stored so the watcher can quickly query and invalidate them.
|
||||
/// Key slices are owned by `default_allocator`
|
||||
bundled_files: bun.StringArrayHashMapUnmanaged(File),
|
||||
/// Track bools for files which are "stale", meaning they should be
|
||||
/// re-bundled before being used. Resizing this is usually deferred
|
||||
@@ -2034,7 +2045,8 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
const dev = g.owner();
|
||||
dev.graph_safety_lock.assertLocked();
|
||||
|
||||
const abs_path = ctx.sources[index.get()].path.text;
|
||||
const path = ctx.sources[index.get()].path;
|
||||
const key = path.keyForIncrementalGraph();
|
||||
|
||||
if (Environment.allow_assert) {
|
||||
switch (kind) {
|
||||
@@ -2042,7 +2054,7 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
.js => if (bun.strings.isAllWhitespace(code)) {
|
||||
// Should at least contain the function wrapper
|
||||
bun.Output.panic("Empty chunk is impossible: {s} {s}", .{
|
||||
abs_path,
|
||||
key,
|
||||
switch (side) {
|
||||
.client => "client",
|
||||
.server => if (is_ssr_graph) "ssr" else "server",
|
||||
@@ -2060,7 +2072,7 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
const cwd = dev.root;
|
||||
var a: bun.PathBuffer = undefined;
|
||||
var b: [bun.MAX_PATH_BYTES * 2]u8 = undefined;
|
||||
const rel_path = bun.path.relativeBufZ(&a, cwd, abs_path);
|
||||
const rel_path = bun.path.relativeBufZ(&a, cwd, key);
|
||||
const size = std.mem.replacementSize(u8, rel_path, "../", "_.._/");
|
||||
_ = std.mem.replace(u8, rel_path, "../", "_.._/", &b);
|
||||
const rel_path_escaped = b[0..size];
|
||||
@@ -2073,11 +2085,11 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
};
|
||||
};
|
||||
|
||||
const gop = try g.bundled_files.getOrPut(dev.allocator, abs_path);
|
||||
const gop = try g.bundled_files.getOrPut(dev.allocator, key);
|
||||
const file_index = FileIndex.init(@intCast(gop.index));
|
||||
|
||||
if (!gop.found_existing) {
|
||||
gop.key_ptr.* = try bun.default_allocator.dupe(u8, abs_path);
|
||||
gop.key_ptr.* = try bun.default_allocator.dupe(u8, key);
|
||||
try g.first_dep.append(dev.allocator, .none);
|
||||
try g.first_import.append(dev.allocator, .none);
|
||||
}
|
||||
@@ -2117,7 +2129,7 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
gop.value_ptr.* = File.init(try std.fmt.allocPrint(
|
||||
dev.allocator,
|
||||
css_prefix ++ "/{}.css",
|
||||
.{std.fmt.fmtSliceHexLower(std.mem.asBytes(&bun.hash(abs_path)))},
|
||||
.{std.fmt.fmtSliceHexLower(std.mem.asBytes(&bun.hash(key)))},
|
||||
), flags);
|
||||
} else {
|
||||
// The key is just the file-path
|
||||
@@ -2301,16 +2313,25 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
const log = bun.Output.scoped(.processChunkDependencies, false);
|
||||
for (ctx.import_records[index.get()].slice()) |import_record| {
|
||||
if (!import_record.source_index.isRuntime()) try_index_record: {
|
||||
const key = import_record.path.keyForIncrementalGraph();
|
||||
const imported_file_index = if (import_record.source_index.isInvalid())
|
||||
if (std.fs.path.isAbsolute(import_record.path.text))
|
||||
FileIndex.init(@intCast(
|
||||
g.bundled_files.getIndex(import_record.path.text) orelse break :try_index_record,
|
||||
))
|
||||
else
|
||||
break :try_index_record
|
||||
FileIndex.init(@intCast(
|
||||
g.bundled_files.getIndex(key) orelse break :try_index_record,
|
||||
))
|
||||
else
|
||||
ctx.getCachedIndex(side, import_record.source_index).*;
|
||||
|
||||
if (Environment.isDebug) {
|
||||
if (imported_file_index.get() > g.bundled_files.count()) {
|
||||
Output.debugWarn("Invalid mapped source index {x}. {} was not inserted into IncrementalGraph", .{
|
||||
imported_file_index.get(),
|
||||
bun.fmt.quote(key),
|
||||
});
|
||||
Output.flush();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (quick_lookup.getPtr(imported_file_index)) |lookup| {
|
||||
// If the edge has already been seen, it will be skipped
|
||||
// to ensure duplicate edges never exist.
|
||||
@@ -4331,7 +4352,7 @@ fn dumpStateDueToCrash(dev: *DevServer) !void {
|
||||
const filepath = std.fmt.bufPrintZ(&filepath_buf, "incremental-graph-crash-dump.{d}.html", .{std.time.timestamp()}) catch "incremental-graph-crash-dump.html";
|
||||
const file = std.fs.cwd().createFileZ(filepath, .{}) catch |err| {
|
||||
bun.handleErrorReturnTrace(err, @errorReturnTrace());
|
||||
Output.warn("Could not open directory for dumping sources: {}", .{err});
|
||||
Output.warn("Could not open file for dumping incremental graph: {}", .{err});
|
||||
return;
|
||||
};
|
||||
defer file.close();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user