mirror of
https://github.com/oven-sh/bun
synced 2026-02-06 00:48:55 +00:00
Compare commits
218 Commits
jarred/hmr
...
don/fix/no
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0148b7a6cf | ||
|
|
094c4314ad | ||
|
|
a3f48c1d47 | ||
|
|
de048cb474 | ||
|
|
0c5ee31707 | ||
|
|
c820b0c5e1 | ||
|
|
d09e381cbc | ||
|
|
53d631f1bd | ||
|
|
74768449bc | ||
|
|
294adc2269 | ||
|
|
ff97424667 | ||
|
|
cbf8d7cad6 | ||
|
|
0c41951b58 | ||
|
|
50b36696f8 | ||
|
|
de4182f305 | ||
|
|
4214cc0aaa | ||
|
|
d1c77f5061 | ||
|
|
45e01cdaf2 | ||
|
|
2fc19daeec | ||
|
|
60c0b9ab96 | ||
|
|
7f948f9c3e | ||
|
|
66fb9f1097 | ||
|
|
062a5b9bf8 | ||
|
|
5bedf15462 | ||
|
|
d7aee40387 | ||
|
|
26f08fabd7 | ||
|
|
05b48ce57c | ||
|
|
1ed87f4e83 | ||
|
|
b089558674 | ||
|
|
45df1dbba0 | ||
|
|
beb32770f0 | ||
|
|
3eec297282 | ||
|
|
b0b6c979ee | ||
|
|
7d69ac03ec | ||
|
|
d7e08abce8 | ||
|
|
d907942966 | ||
|
|
fe0e737f7b | ||
|
|
8da959df85 | ||
|
|
d7a047a533 | ||
|
|
c260223127 | ||
|
|
e834a80b7b | ||
|
|
7011dd6524 | ||
|
|
cde668b54c | ||
|
|
01db86e915 | ||
|
|
85376147a4 | ||
|
|
d2ecce272c | ||
|
|
7ee0b428d6 | ||
|
|
9482e4c86a | ||
|
|
42276a9500 | ||
|
|
ae8f78c84d | ||
|
|
9636852224 | ||
|
|
5f72715a42 | ||
|
|
c60b5dd4d6 | ||
|
|
42c474a21f | ||
|
|
04078fbf61 | ||
|
|
28ebbb3f20 | ||
|
|
96fa32bcc1 | ||
|
|
b3246b6971 | ||
|
|
0345414ded | ||
|
|
01d214b276 | ||
|
|
fdd181d68d | ||
|
|
5c7df736bf | ||
|
|
29870cb572 | ||
|
|
32223e90e3 | ||
|
|
31198cdbd9 | ||
|
|
971f2b1ed7 | ||
|
|
832cf91e88 | ||
|
|
2e010073aa | ||
|
|
4c93b72906 | ||
|
|
7091fd5791 | ||
|
|
e5edd388a0 | ||
|
|
b887270e25 | ||
|
|
fc0d0ad8d3 | ||
|
|
ddfc8555f7 | ||
|
|
6d0739f7d9 | ||
|
|
fdd750e4b5 | ||
|
|
9a5afe371a | ||
|
|
5123561889 | ||
|
|
ba7f59355f | ||
|
|
a79f92df9e | ||
|
|
8bc88763ec | ||
|
|
4a0e982bb2 | ||
|
|
013fdddc6e | ||
|
|
a9ca465ad0 | ||
|
|
cd4d75ee7b | ||
|
|
aa2e109f5f | ||
|
|
45e3c9da70 | ||
|
|
cee026b87e | ||
|
|
1a68ce05dc | ||
|
|
bf0253df1d | ||
|
|
2e3e6a15e0 | ||
|
|
589fa6274d | ||
|
|
4cf0d39e58 | ||
|
|
a1952c71f7 | ||
|
|
48df26462d | ||
|
|
66cf62c3c4 | ||
|
|
9d6729fef3 | ||
|
|
20144ced54 | ||
|
|
2e6cbd9a4d | ||
|
|
85f49a7a1a | ||
|
|
6ba858dfbb | ||
|
|
7b423d5ff8 | ||
|
|
ae19729a72 | ||
|
|
2d45ae7441 | ||
|
|
e6cb0de539 | ||
|
|
924e50b6e9 | ||
|
|
1d32e78cf4 | ||
|
|
b5bca2d976 | ||
|
|
1acd4039b6 | ||
|
|
438ec5d1eb | ||
|
|
1a9cadf5f4 | ||
|
|
304b471281 | ||
|
|
6028683f87 | ||
|
|
0b58e791b3 | ||
|
|
1570d4f0a7 | ||
|
|
b6b6efc839 | ||
|
|
d502df353c | ||
|
|
7161326baa | ||
|
|
903d6d058c | ||
|
|
96b305389f | ||
|
|
2a74f0d8f4 | ||
|
|
6e99733320 | ||
|
|
94ab3007a4 | ||
|
|
4645c309ca | ||
|
|
852830eb54 | ||
|
|
4840217156 | ||
|
|
78fb3ce64d | ||
|
|
368ddfdd14 | ||
|
|
f9ebabe898 | ||
|
|
60eb2c4ecb | ||
|
|
8a177f6c85 | ||
|
|
5053d0eaaf | ||
|
|
6ec2b98336 | ||
|
|
bae0921ef7 | ||
|
|
7eab65df99 | ||
|
|
a41d773aaa | ||
|
|
4ef7a43939 | ||
|
|
7dc2e8e98e | ||
|
|
63636f19f1 | ||
|
|
23314188ca | ||
|
|
d429e35cdf | ||
|
|
99d85be529 | ||
|
|
2d0cadc949 | ||
|
|
821f42dd8e | ||
|
|
0d4bd61ae0 | ||
|
|
483302d09d | ||
|
|
5aa2913bce | ||
|
|
1803f73b15 | ||
|
|
9141337c7d | ||
|
|
70dbf582a6 | ||
|
|
1a6a34700f | ||
|
|
6e140b4b13 | ||
|
|
ac07af11de | ||
|
|
078318f33c | ||
|
|
a620db7025 | ||
|
|
99cbdfb004 | ||
|
|
887173c3c3 | ||
|
|
184506ae86 | ||
|
|
25c95f3bdc | ||
|
|
1bf13aa671 | ||
|
|
671d876cf3 | ||
|
|
50856459e6 | ||
|
|
8db2844e80 | ||
|
|
2d74c0162a | ||
|
|
7882418c5f | ||
|
|
01fb872095 | ||
|
|
12a2f412fc | ||
|
|
ee6bdc1588 | ||
|
|
11979f69eb | ||
|
|
65b8b220d2 | ||
|
|
c9e4153826 | ||
|
|
febf6593a6 | ||
|
|
e7790894d9 | ||
|
|
cef38030df | ||
|
|
2fb121e2ed | ||
|
|
7ad46cb118 | ||
|
|
4f58ff7933 | ||
|
|
838c3bbb8b | ||
|
|
fe49ac1a3d | ||
|
|
fbe4d57bae | ||
|
|
f4937678e4 | ||
|
|
b124ba056c | ||
|
|
0237baee92 | ||
|
|
cc481465b5 | ||
|
|
7a033e49c5 | ||
|
|
59551ebc79 | ||
|
|
ad766f2402 | ||
|
|
efabdcbe1f | ||
|
|
174a0f70df | ||
|
|
deeebf0538 | ||
|
|
63bc08e3ca | ||
|
|
d09daca867 | ||
|
|
b3edef5989 | ||
|
|
baca1f4634 | ||
|
|
8bb6dd3cee | ||
|
|
215da32660 | ||
|
|
5c6e20aeb4 | ||
|
|
1060558456 | ||
|
|
a2d028462b | ||
|
|
ec4b9d198b | ||
|
|
fd9a5ea668 | ||
|
|
e8249d885c | ||
|
|
ac8fb0e1f5 | ||
|
|
39fdabc364 | ||
|
|
144a9c2f6d | ||
|
|
caff4e6008 | ||
|
|
1322adbb16 | ||
|
|
11e5a6a2c7 | ||
|
|
a130816004 | ||
|
|
f4e0684603 | ||
|
|
2db9ab4c72 | ||
|
|
2f48282cbd | ||
|
|
1574df835e | ||
|
|
04973a1520 | ||
|
|
4e3c9bc1d1 | ||
|
|
d4c1114f9d | ||
|
|
b829590356 | ||
|
|
04f985523b |
@@ -107,9 +107,9 @@ const buildPlatforms = [
|
||||
{ os: "linux", arch: "aarch64", distro: "amazonlinux", release: "2023", features: ["docker"] },
|
||||
{ os: "linux", arch: "x64", distro: "amazonlinux", release: "2023", features: ["docker"] },
|
||||
{ os: "linux", arch: "x64", baseline: true, distro: "amazonlinux", release: "2023", features: ["docker"] },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.20" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.20" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.20" },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.21" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.21" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.21" },
|
||||
{ os: "windows", arch: "x64", release: "2019" },
|
||||
{ os: "windows", arch: "x64", baseline: true, release: "2019" },
|
||||
];
|
||||
@@ -134,9 +134,9 @@ const testPlatforms = [
|
||||
{ os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "24.04", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "22.04", tier: "previous" },
|
||||
{ os: "linux", arch: "x64", baseline: true, distro: "ubuntu", release: "20.04", tier: "oldest" },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.20", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.20", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.20", tier: "latest" },
|
||||
{ os: "linux", arch: "aarch64", abi: "musl", distro: "alpine", release: "3.21", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", distro: "alpine", release: "3.21", tier: "latest" },
|
||||
{ os: "linux", arch: "x64", abi: "musl", baseline: true, distro: "alpine", release: "3.21", tier: "latest" },
|
||||
{ os: "windows", arch: "x64", release: "2019", tier: "oldest" },
|
||||
{ os: "windows", arch: "x64", release: "2019", baseline: true, tier: "oldest" },
|
||||
];
|
||||
@@ -323,6 +323,7 @@ function getCppAgent(platform, options) {
|
||||
*/
|
||||
function getZigAgent(platform, options) {
|
||||
const { arch } = platform;
|
||||
|
||||
return {
|
||||
queue: "build-zig",
|
||||
};
|
||||
@@ -383,15 +384,21 @@ function getBuildEnv(target, options) {
|
||||
const { canary } = options;
|
||||
const revision = typeof canary === "number" ? canary : 1;
|
||||
|
||||
const isMusl = abi === "musl";
|
||||
|
||||
let CMAKE_BUILD_TYPE = release ? "Release" : profile === "debug" ? "Debug" : "RelWithDebInfo";
|
||||
if (isMusl && release) {
|
||||
CMAKE_BUILD_TYPE = "MinSizeRel";
|
||||
}
|
||||
|
||||
return {
|
||||
CMAKE_BUILD_TYPE: release ? "Release" : profile === "debug" ? "Debug" : "RelWithDebInfo",
|
||||
CMAKE_BUILD_TYPE,
|
||||
ENABLE_BASELINE: baseline ? "ON" : "OFF",
|
||||
ENABLE_CANARY: revision > 0 ? "ON" : "OFF",
|
||||
CANARY_REVISION: revision,
|
||||
ENABLE_ASSERTIONS: release ? "OFF" : "ON",
|
||||
ENABLE_LOGS: release ? "OFF" : "ON",
|
||||
ABI: abi === "musl" ? "musl" : undefined,
|
||||
|
||||
ABI: isMusl ? "musl" : undefined,
|
||||
CMAKE_TLS_VERIFY: "0",
|
||||
};
|
||||
}
|
||||
@@ -464,7 +471,7 @@ function getBuildZigStep(platform, options) {
|
||||
cancel_on_build_failing: isMergeQueue(),
|
||||
env: getBuildEnv(platform, options),
|
||||
command: `bun run build:ci --target bun-zig --toolchain ${toolchain}`,
|
||||
timeout_in_minutes: 25,
|
||||
timeout_in_minutes: 35,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -201,6 +201,8 @@ function create_release() {
|
||||
local artifacts=(
|
||||
bun-darwin-aarch64.zip
|
||||
bun-darwin-aarch64-profile.zip
|
||||
bun-darwin-x64.zip
|
||||
bun-darwin-x64-profile.zip
|
||||
bun-linux-aarch64.zip
|
||||
bun-linux-aarch64-profile.zip
|
||||
bun-linux-x64.zip
|
||||
|
||||
27
.cursor/rules/building-bun.mdc
Normal file
27
.cursor/rules/building-bun.mdc
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
description: How to build Bun
|
||||
globs:
|
||||
---
|
||||
# How to build Bun
|
||||
|
||||
## CMake
|
||||
|
||||
Bun is built using CMake, which you can find in `CMakeLists.txt` and in the `cmake/` directory.
|
||||
|
||||
* `CMakeLists.txt`
|
||||
* `cmake/`
|
||||
* `Globals.cmake` - macros and functions used by all the other files
|
||||
* `Options.cmake` - build options for configuring the build (e.g. debug/release mode)
|
||||
* `CompilerFlags.cmake` - compiler and linker flags used by all the targets
|
||||
* `tools/` - setup scripts for various build tools (e.g. llvm, zig, webkit, rust, etc.)
|
||||
* `targets/` - targets for bun and its dependencies (e.g. brotli, boringssl, libuv, etc.)
|
||||
|
||||
## How to
|
||||
|
||||
There are `package.json` scripts that make it easy to build Bun without calling CMake directly, for example:
|
||||
|
||||
```sh
|
||||
bun run build # builds a debug build: `build/debug/bun-debug`
|
||||
bun run build:release # builds a release build: `build/release/bun`
|
||||
bun run build:assert # builds a release build with debug assertions: `build/assert/bun`
|
||||
```
|
||||
139
.cursor/rules/dev-server-tests.mdc
Normal file
139
.cursor/rules/dev-server-tests.mdc
Normal file
@@ -0,0 +1,139 @@
|
||||
---
|
||||
description: Writing HMR/Dev Server tests
|
||||
globs: test/bake/*
|
||||
---
|
||||
|
||||
# Writing HMR/Dev Server tests
|
||||
|
||||
Dev server tests validate that hot-reloading is robust, correct, and reliable. Remember to write thorough, yet concise tests.
|
||||
|
||||
## File Structure
|
||||
|
||||
- `test/bake/bake-harness.ts` - shared utilities and test harness
|
||||
- primary test functions `devTest` / `prodTest` / `devAndProductionTest`
|
||||
- class `Dev` (controls subprocess for dev server)
|
||||
- class `Client` (controls a happy-dom subprocess for having the page open)
|
||||
- more helpers
|
||||
- `test/bake/client-fixture.mjs` - subprocess for what `Client` controls. it loads a page and uses IPC to query parts of the page, run javascript, and much more.
|
||||
- `test/bake/dev/*.test.ts` - these call `devTest` to test dev server and hot reloading
|
||||
- `test/bake/dev-and-prod.ts` - these use `devAndProductionTest` to run the same test on dev and production mode. these tests cannot really test hot reloading for obvious reasons.
|
||||
|
||||
## Categories
|
||||
|
||||
bundle.test.ts - Bundle tests are tests concerning bundling bugs that only occur in DevServer.
|
||||
css.test.ts - CSS tests concern bundling bugs with CSS files
|
||||
plugins.test.ts - Plugin tests concern plugins in development mode.
|
||||
ecosystem.test.ts - These tests involve ensuring certain libraries are correct. It is preferred to test more concrete bugs than testing entire packages.
|
||||
esm.test.ts - ESM tests are about various esm features in development mode.
|
||||
html.test.ts - HTML tests are tests relating to HTML files themselves.
|
||||
react-spa.test.ts - Tests relating to React, our react-refresh transform, and basic server component transforms.
|
||||
sourcemap.test.ts - Tests verifying source-maps are correct.
|
||||
|
||||
## `devTest` Basics
|
||||
|
||||
A test takes in two primary inputs: `files` and `async test(dev) {`
|
||||
|
||||
```ts
|
||||
import { devTest, emptyHtmlFile } from "../bake-harness";
|
||||
|
||||
devTest("html file is watched", {
|
||||
files: {
|
||||
"index.html": emptyHtmlFile({
|
||||
scripts: ["/script.ts"],
|
||||
body: "<h1>Hello</h1>",
|
||||
}),
|
||||
"script.ts": `
|
||||
console.log("hello");
|
||||
`,
|
||||
},
|
||||
async test(dev) {
|
||||
await dev.fetch("/").expect.toInclude("<h1>Hello</h1>");
|
||||
await dev.fetch("/").expect.toInclude("<h1>Hello</h1>");
|
||||
await dev.patch("index.html", {
|
||||
find: "Hello",
|
||||
replace: "World",
|
||||
});
|
||||
await dev.fetch("/").expect.toInclude("<h1>World</h1>");
|
||||
|
||||
// Works
|
||||
await using c = await dev.client("/");
|
||||
await c.expectMessage("hello");
|
||||
|
||||
// Editing HTML reloads
|
||||
await c.expectReload(async () => {
|
||||
await dev.patch("index.html", {
|
||||
find: "World",
|
||||
replace: "Hello",
|
||||
});
|
||||
await dev.fetch("/").expect.toInclude("<h1>Hello</h1>");
|
||||
});
|
||||
await c.expectMessage("hello");
|
||||
|
||||
await c.expectReload(async () => {
|
||||
await dev.patch("index.html", {
|
||||
find: "Hello",
|
||||
replace: "Bar",
|
||||
});
|
||||
await dev.fetch("/").expect.toInclude("<h1>Bar</h1>");
|
||||
});
|
||||
await c.expectMessage("hello");
|
||||
|
||||
await c.expectReload(async () => {
|
||||
await dev.patch("script.ts", {
|
||||
find: "hello",
|
||||
replace: "world",
|
||||
});
|
||||
});
|
||||
await c.expectMessage("world");
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
`files` holds the initial state, and the callback runs with the server running. `dev.fetch()` runs HTTP requests, while `dev.client()` opens a browser instance to the code.
|
||||
|
||||
Functions `dev.write` and `dev.patch` and `dev.delete` mutate the filesystem. Do not use `node:fs` APIs, as the dev server ones are hooked to wait for hot-reload, and all connected clients to recieve changes.
|
||||
|
||||
When a change performs a hard-reload, that must be explicitly annotated with `expectReload`. This tells `client-fixture.mjs` that the test is meant to reload the page once; All other hard reloads automatically fail the test.
|
||||
|
||||
Client's have `console.log` instrumented, so that any unasserted logs fail the test. This makes it more obvious when an extra reload or re-evaluation. Messages are awaited via `c.expectMessage("log")` or with multiple arguments if there are multiple logs.
|
||||
|
||||
## Testing for bundling errors
|
||||
|
||||
By default, a client opening a page to an error will fail the test. This makes testing errors explicit.
|
||||
|
||||
```ts
|
||||
devTest("import then create", {
|
||||
files: {
|
||||
"index.html": `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<script type="module" src="/script.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
`,
|
||||
"script.ts": `
|
||||
import data from "./data";
|
||||
console.log(data);
|
||||
`,
|
||||
},
|
||||
async test(dev) {
|
||||
const c = await dev.client("/", {
|
||||
errors: ['script.ts:1:18: error: Could not resolve: "./data"'],
|
||||
});
|
||||
await c.expectReload(async () => {
|
||||
await dev.write("data.ts", "export default 'data';");
|
||||
});
|
||||
await c.expectMessage("data");
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Many functions take an options value to allow specifying it will produce errors. For example, this delete is going to cause a resolution failure.
|
||||
|
||||
```ts
|
||||
await dev.delete("other.ts", {
|
||||
errors: ['index.ts:1:16: error: Could not resolve: "./other"'],
|
||||
});
|
||||
```
|
||||
91
.cursor/rules/writing-tests.mdc
Normal file
91
.cursor/rules/writing-tests.mdc
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
description: Writing tests for Bun
|
||||
globs:
|
||||
---
|
||||
# Writing tests for Bun
|
||||
|
||||
## Where tests are found
|
||||
|
||||
You'll find all of Bun's tests in the `test/` directory.
|
||||
|
||||
* `test/`
|
||||
* `cli/` - CLI command tests, like `bun install` or `bun init`
|
||||
* `js/` - JavaScript & TypeScript tests
|
||||
* `bun/` - `Bun` APIs tests, seperated by category, for example: `glob/` for `Bun.Glob` tests
|
||||
* `node/` - Node.js module tests, seperated by module, for example: `assert/` for `node:assert` tests
|
||||
* `test/` - Vendored Node.js tests, taken from the Node.js repository (does not conform to Bun's test style)
|
||||
* `web/` - Web API tests, seperated by category, for example: `fetch/` for `Request` and `Response` tests
|
||||
* `third_party/` - npm package tests, to validate that basic usage works in Bun
|
||||
* `napi/` - N-API tests
|
||||
* `v8/` - V8 C++ API tests
|
||||
* `bundler/` - Bundler, transpiler, CSS, and `bun build` tests
|
||||
* `regression/issue/[number]` - Regression tests, always make one when fixing a particular issue
|
||||
|
||||
## How tests are written
|
||||
|
||||
Bun's tests are written as JavaScript and TypeScript files with the Jest-style APIs, like `test`, `describe`, and `expect`. They are tested using Bun's own test runner, `bun test`.
|
||||
|
||||
```js
|
||||
import { describe, test, expect } from "bun:test";
|
||||
import assert, { AssertionError } from "assert";
|
||||
|
||||
describe("assert(expr)", () => {
|
||||
test.each([true, 1, "foo"])(`assert(%p) does not throw`, expr => {
|
||||
expect(() => assert(expr)).not.toThrow();
|
||||
});
|
||||
|
||||
test.each([false, 0, "", null, undefined])(`assert(%p) throws`, expr => {
|
||||
expect(() => assert(expr)).toThrow(AssertionError);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Testing conventions
|
||||
|
||||
* See `test/harness.ts` for common test utilities and helpers
|
||||
* Be rigorous and test for edge-cases and unexpected inputs
|
||||
* Use data-driven tests, e.g. `test.each`, to reduce boilerplate when possible
|
||||
* When you need to test Bun as a CLI, use the following pattern:
|
||||
|
||||
```js
|
||||
import { test, expect } from "bun:test";
|
||||
import { spawn } from "bun";
|
||||
import { bunExe, bunEnv } from "harness";
|
||||
|
||||
test("bun --version", async () => {
|
||||
const { exited, stdout: stdoutStream, stderr: stderrStream } = spawn({
|
||||
cmd: [bunExe(), "--version"],
|
||||
env: bunEnv,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
const [ exitCode, stdout, stderr ] = await Promise.all([
|
||||
exited,
|
||||
new Response(stdoutStream).text(),
|
||||
new Response(stderrStream).text(),
|
||||
]);
|
||||
expect({ exitCode, stdout, stderr }).toMatchObject({
|
||||
exitCode: 0,
|
||||
stdout: expect.stringContaining(Bun.version),
|
||||
stderr: "",
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Before writing a test
|
||||
|
||||
* If you are fixing a bug, write the test first and make sure it fails (as expected) with the canary version of Bun
|
||||
* If you are fixing a Node.js compatibility bug, create a throw-away snippet of code and test that it works as you expect in Node.js, then that it fails (as expected) with the canary version of Bun
|
||||
* When the expected behaviour is ambigious, defer to matching what happens in Node.js
|
||||
* Always attempt to find related tests in an existing test file before creating a new test file
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
55
.github/workflows/packages-ci.yml
vendored
Normal file
55
.github/workflows/packages-ci.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Packages CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "packages/**"
|
||||
- .prettierrc
|
||||
- .prettierignore
|
||||
- tsconfig.json
|
||||
- oxlint.json
|
||||
- "!**/*.md"
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "packages/**"
|
||||
- .prettierrc
|
||||
- .prettierignore
|
||||
- tsconfig.json
|
||||
- oxlint.json
|
||||
- "!**/*.md"
|
||||
|
||||
env:
|
||||
BUN_VERSION: "canary"
|
||||
|
||||
jobs:
|
||||
bun-plugin-svelte:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Bun
|
||||
uses: ./.github/actions/setup-bun
|
||||
with:
|
||||
bun-version: ${{ env.BUN_VERSION }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
bun install
|
||||
pushd ./packages/bun-plugin-svelte && bun install
|
||||
|
||||
- name: Lint
|
||||
run: |
|
||||
bunx oxlint@0.15 --format github --deny-warnings
|
||||
bunx prettier --config ../../.prettierrc --check .
|
||||
working-directory: ./packages/bun-plugin-svelte
|
||||
|
||||
- name: Check types
|
||||
run: bun check:types
|
||||
working-directory: ./packages/bun-plugin-svelte
|
||||
|
||||
- name: Test
|
||||
run: bun test
|
||||
working-directory: ./packages/bun-plugin-svelte
|
||||
2
.github/workflows/update-cares.yml
vendored
2
.github/workflows/update-cares.yml
vendored
@@ -89,4 +89,6 @@ jobs:
|
||||
|
||||
Updates c-ares to version ${{ steps.check-version.outputs.tag }}
|
||||
|
||||
Compare: https://github.com/c-ares/c-ares/compare/${{ steps.check-version.outputs.current }}...${{ steps.check-version.outputs.latest }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-cares.yml)
|
||||
|
||||
2
.github/workflows/update-libarchive.yml
vendored
2
.github/workflows/update-libarchive.yml
vendored
@@ -89,4 +89,6 @@ jobs:
|
||||
|
||||
Updates libarchive to version ${{ steps.check-version.outputs.tag }}
|
||||
|
||||
Compare: https://github.com/libarchive/libarchive/compare/${{ steps.check-version.outputs.current }}...${{ steps.check-version.outputs.latest }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-libarchive.yml)
|
||||
|
||||
2
.github/workflows/update-libdeflate.yml
vendored
2
.github/workflows/update-libdeflate.yml
vendored
@@ -89,4 +89,6 @@ jobs:
|
||||
|
||||
Updates libdeflate to version ${{ steps.check-version.outputs.tag }}
|
||||
|
||||
Compare: https://github.com/ebiggers/libdeflate/compare/${{ steps.check-version.outputs.current }}...${{ steps.check-version.outputs.latest }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-libdeflate.yml)
|
||||
|
||||
2
.github/workflows/update-lolhtml.yml
vendored
2
.github/workflows/update-lolhtml.yml
vendored
@@ -89,4 +89,6 @@ jobs:
|
||||
|
||||
Updates lolhtml to version ${{ steps.check-version.outputs.tag }}
|
||||
|
||||
Compare: https://github.com/cloudflare/lol-html/compare/${{ steps.check-version.outputs.current }}...${{ steps.check-version.outputs.latest }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-lolhtml.yml)
|
||||
|
||||
2
.github/workflows/update-lshpack.yml
vendored
2
.github/workflows/update-lshpack.yml
vendored
@@ -89,4 +89,6 @@ jobs:
|
||||
|
||||
Updates lshpack to version ${{ steps.check-version.outputs.tag }}
|
||||
|
||||
Compare: https://github.com/litespeedtech/ls-hpack/compare/${{ steps.check-version.outputs.current }}...${{ steps.check-version.outputs.latest }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-lshpack.yml)
|
||||
|
||||
82
.github/workflows/update-root-certs.yml
vendored
Normal file
82
.github/workflows/update-root-certs.yml
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
name: Daily Root Certs Update Check
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *" # Runs at 00:00 UTC every day
|
||||
workflow_dispatch: # Allows manual trigger
|
||||
|
||||
jobs:
|
||||
check-and-update:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Generate root certs and capture output
|
||||
id: generate-certs
|
||||
run: |
|
||||
cd packages/bun-usockets/
|
||||
OUTPUT=$(bun generate-root-certs.mjs -v)
|
||||
echo "cert_output<<EOF" >> $GITHUB_ENV
|
||||
echo "$OUTPUT" >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
|
||||
- name: Check for changes and stage files
|
||||
id: check-changes
|
||||
run: |
|
||||
if [[ -n "$(git status --porcelain)" ]]; then
|
||||
echo "Found changes, staging modified files..."
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
# Get list of modified files and add them
|
||||
git status --porcelain | while read -r status file; do
|
||||
# Remove leading status and whitespace
|
||||
file=$(echo "$file" | sed 's/^.* //')
|
||||
echo "Adding changed file: $file"
|
||||
git add "$file"
|
||||
done
|
||||
|
||||
echo "changes=true" >> $GITHUB_OUTPUT
|
||||
|
||||
# Store the list of changed files
|
||||
CHANGED_FILES=$(git status --porcelain)
|
||||
echo "changed_files<<EOF" >> $GITHUB_ENV
|
||||
echo "$CHANGED_FILES" >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
else
|
||||
echo "No changes detected"
|
||||
echo "changes=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Create Pull Request
|
||||
if: steps.check-changes.outputs.changes == 'true'
|
||||
uses: peter-evans/create-pull-request@v5
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
commit-message: "update(root_certs): Update root certificates $(date +'%Y-%m-%d')"
|
||||
title: "update(root_certs) $(date +'%Y-%m-%d')"
|
||||
body: |
|
||||
Automated root certificates update
|
||||
|
||||
${{ env.cert_output }}
|
||||
|
||||
## Changed Files:
|
||||
```
|
||||
${{ env.changed_files }}
|
||||
```
|
||||
branch: certs/update-root-certs-${{ github.run_number }}
|
||||
base: main
|
||||
delete-branch: true
|
||||
labels:
|
||||
- "automation"
|
||||
- "root-certs"
|
||||
2
.github/workflows/update-sqlite3.yml
vendored
2
.github/workflows/update-sqlite3.yml
vendored
@@ -106,4 +106,6 @@ jobs:
|
||||
|
||||
Updates SQLite to version ${{ steps.check-version.outputs.latest }}
|
||||
|
||||
Compare: https://sqlite.org/src/vdiff?from=${{ steps.check-version.outputs.current }}&to=${{ steps.check-version.outputs.latest }}
|
||||
|
||||
Auto-updated by [this workflow](https://github.com/oven-sh/bun/actions/workflows/update-sqlite3.yml)
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -181,3 +181,5 @@ tmp
|
||||
.buildkite/ci.yml
|
||||
*.sock
|
||||
scratch*.{js,ts,tsx,cjs,mjs}
|
||||
|
||||
*.bun-build
|
||||
18
.lldbinit
18
.lldbinit
@@ -1,4 +1,16 @@
|
||||
# command script import vendor/zig/tools/lldb_pretty_printers.py
|
||||
command script import vendor/WebKit/Tools/lldb/lldb_webkit.py
|
||||
# Tell LLDB what to do when the debugged process receives SIGPWR: pass it through to the process
|
||||
# (-p), but do not stop the process (-s) or notify the user (-n).
|
||||
#
|
||||
# JSC's garbage collector sends this signal (as configured by Bun WebKit in
|
||||
# Thread::initializePlatformThreading() in ThreadingPOSIX.cpp) to the JS thread to suspend or resume
|
||||
# it. So stopping the process would just create noise when debugging any long-running script.
|
||||
process handle -p true -s false -n false SIGPWR
|
||||
|
||||
# type summary add --summary-string "${var} | inner=${var[0-30]}, source=${var[33-64]}, tag=${var[31-32]}" "unsigned long"
|
||||
command script import misctools/lldb/lldb_pretty_printers.py
|
||||
type category enable zig.lang
|
||||
type category enable zig.std
|
||||
|
||||
command script import misctools/lldb/lldb_webkit.py
|
||||
|
||||
command script delete btjs
|
||||
command alias btjs p {printf("gathering btjs trace...\n");printf("%s\n", (char*)dumpBtjsTrace())}
|
||||
47
.vscode/launch.json
generated
vendored
47
.vscode/launch.json
generated
vendored
@@ -22,7 +22,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -38,7 +37,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -60,7 +58,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -76,7 +73,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -92,7 +88,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -108,7 +103,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -125,7 +119,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
"serverReadyAction": {
|
||||
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
|
||||
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
|
||||
@@ -147,7 +140,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
"serverReadyAction": {
|
||||
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
|
||||
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
|
||||
@@ -169,7 +161,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -188,7 +179,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -203,7 +193,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -221,7 +210,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -236,7 +224,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -253,7 +240,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
"serverReadyAction": {
|
||||
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
|
||||
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
|
||||
@@ -275,7 +261,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
"serverReadyAction": {
|
||||
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
|
||||
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
|
||||
@@ -297,7 +282,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -313,7 +297,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -329,7 +312,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -345,7 +327,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -361,7 +342,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -378,7 +358,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
"serverReadyAction": {
|
||||
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
|
||||
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
|
||||
@@ -400,7 +379,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
"serverReadyAction": {
|
||||
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
|
||||
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
|
||||
@@ -421,7 +399,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
// bun test [*]
|
||||
{
|
||||
@@ -437,7 +414,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -452,7 +428,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -468,7 +443,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
"serverReadyAction": {
|
||||
"pattern": "https://debug.bun.sh/#localhost:([0-9]+)/",
|
||||
"uriFormat": "https://debug.bun.sh/#ws://localhost:%s/",
|
||||
@@ -488,7 +462,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
@@ -503,7 +476,6 @@
|
||||
},
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
// Windows: bun test [file]
|
||||
{
|
||||
@@ -1129,7 +1101,24 @@
|
||||
],
|
||||
"console": "internalConsole",
|
||||
// Don't pause when the GC runs while the debugger is open.
|
||||
"postRunCommands": ["command source '${workspaceFolder}/misctools/lldb/lldb_commands'"],
|
||||
},
|
||||
{
|
||||
"type": "bun",
|
||||
"name": "[JS] bun test [file]",
|
||||
"runtime": "${workspaceFolder}/build/debug/bun-debug",
|
||||
"runtimeArgs": ["test", "${file}"],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"env": {
|
||||
"BUN_DEBUG_QUIET_LOGS": "1",
|
||||
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "midas-rr",
|
||||
"request": "attach",
|
||||
"name": "rr",
|
||||
"trace": "Off",
|
||||
"setupCommands": ["handle SIGPWR nostop noprint pass"],
|
||||
},
|
||||
],
|
||||
"inputs": [
|
||||
|
||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -141,9 +141,11 @@
|
||||
"packages/bun-uws/fuzzing": true,
|
||||
},
|
||||
"files.associations": {
|
||||
"*.css": "tailwindcss",
|
||||
"*.idl": "cpp",
|
||||
"*.mdc": "markdown",
|
||||
"array": "cpp",
|
||||
"ios": "cpp",
|
||||
},
|
||||
"C_Cpp.files.exclude": {
|
||||
"**/.vscode": true,
|
||||
|
||||
@@ -205,18 +205,30 @@ WebKit is not cloned by default (to save time and disk space). To clone and buil
|
||||
# Clone WebKit into ./vendor/WebKit
|
||||
$ git clone https://github.com/oven-sh/WebKit vendor/WebKit
|
||||
|
||||
# Check out the commit hash specified in `set(WEBKIT_VERSION <commit_hash>)` in cmake/tools/SetupWebKit.cmake
|
||||
$ git -C vendor/WebKit checkout <commit_hash>
|
||||
|
||||
# Make a debug build of JSC. This will output build artifacts in ./vendor/WebKit/WebKitBuild/Debug
|
||||
# Optionally, you can use `make jsc` for a release build
|
||||
$ make jsc-debug
|
||||
$ make jsc-debug && rm vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/DerivedSources/inspector/InspectorProtocolObjects.h
|
||||
|
||||
# Build bun with the local JSC build
|
||||
$ bun run build:local
|
||||
```
|
||||
|
||||
Using `bun run build:local` will build Bun in the `./build/debug-local` directory (instead of `./build/debug`), you'll have to change a couple of places to use this new directory:
|
||||
|
||||
- The first line in [`src/js/builtins.d.ts`](/src/js/builtins.d.ts)
|
||||
- The `CompilationDatabase` line in [`.clangd` config](/.clangd) should be `CompilationDatabase: build/debug-local`
|
||||
- In [`build.zig`](/build.zig), the `codegen_path` option should be `build/debug-local/codegen` (instead of `build/debug/codegen`)
|
||||
- In [`.vscode/launch.json`](/.vscode/launch.json), many configurations use `./build/debug/`, change them as you see fit
|
||||
|
||||
Note that the WebKit folder, including build artifacts, is 8GB+ in size.
|
||||
|
||||
If you are using a JSC debug build and using VScode, make sure to run the `C/C++: Select a Configuration` command to configure intellisense to find the debug headers.
|
||||
|
||||
Note that if you change make changes to our [WebKit fork](https://github.com/oven-sh/WebKit), you will also have to change [`SetupWebKit.cmake`](/cmake/tools/SetupWebKit.cmake) to point to the commit hash.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### 'span' file not found on Ubuntu
|
||||
|
||||
53
bench/crypto/diffie-hellman.mjs
Normal file
53
bench/crypto/diffie-hellman.mjs
Normal file
@@ -0,0 +1,53 @@
|
||||
import crypto from "node:crypto";
|
||||
import { bench, run } from "../runner.mjs";
|
||||
|
||||
// Pre-generate DH params to avoid including setup in benchmarks
|
||||
const dhSize = 1024; // Reduced from 2048 for faster testing
|
||||
const dh = crypto.createDiffieHellman(dhSize);
|
||||
const dhPrime = dh.getPrime();
|
||||
const dhGenerator = dh.getGenerator();
|
||||
|
||||
// Classical Diffie-Hellman
|
||||
bench("DH - generateKeys", () => {
|
||||
const alice = crypto.createDiffieHellman(dhPrime, dhGenerator);
|
||||
return alice.generateKeys();
|
||||
});
|
||||
|
||||
bench("DH - computeSecret", () => {
|
||||
// Setup
|
||||
const alice = crypto.createDiffieHellman(dhPrime, dhGenerator);
|
||||
const aliceKey = alice.generateKeys();
|
||||
const bob = crypto.createDiffieHellman(dhPrime, dhGenerator);
|
||||
const bobKey = bob.generateKeys();
|
||||
|
||||
// Benchmark just the secret computation
|
||||
return alice.computeSecret(bobKey);
|
||||
});
|
||||
|
||||
// ECDH with prime256v1 (P-256)
|
||||
bench("ECDH-P256 - generateKeys", () => {
|
||||
const ecdh = crypto.createECDH("prime256v1");
|
||||
return ecdh.generateKeys();
|
||||
});
|
||||
|
||||
bench("ECDH-P256 - computeSecret", () => {
|
||||
// Setup
|
||||
const alice = crypto.createECDH("prime256v1");
|
||||
const aliceKey = alice.generateKeys();
|
||||
const bob = crypto.createECDH("prime256v1");
|
||||
const bobKey = bob.generateKeys();
|
||||
|
||||
// Benchmark just the secret computation
|
||||
return alice.computeSecret(bobKey);
|
||||
});
|
||||
|
||||
// ECDH with secp384r1 (P-384)
|
||||
bench("ECDH-P384 - computeSecret", () => {
|
||||
const alice = crypto.createECDH("secp384r1");
|
||||
const aliceKey = alice.generateKeys();
|
||||
const bob = crypto.createECDH("secp384r1");
|
||||
const bobKey = bob.generateKeys();
|
||||
return alice.computeSecret(bobKey);
|
||||
});
|
||||
|
||||
await run();
|
||||
44
bench/crypto/ecdh-convert-key.mjs
Normal file
44
bench/crypto/ecdh-convert-key.mjs
Normal file
@@ -0,0 +1,44 @@
|
||||
import crypto from "node:crypto";
|
||||
import { bench, run } from "../runner.mjs";
|
||||
|
||||
function generateTestKeyPairs() {
|
||||
const curves = crypto.getCurves();
|
||||
const keys = {};
|
||||
|
||||
for (const curve of curves) {
|
||||
const ecdh = crypto.createECDH(curve);
|
||||
ecdh.generateKeys();
|
||||
|
||||
keys[curve] = {
|
||||
compressed: ecdh.getPublicKey("hex", "compressed"),
|
||||
uncompressed: ecdh.getPublicKey("hex", "uncompressed"),
|
||||
instance: ecdh,
|
||||
};
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
const testKeys = generateTestKeyPairs();
|
||||
|
||||
bench("ECDH key format - P256 compressed to uncompressed", () => {
|
||||
const publicKey = testKeys["prime256v1"].compressed;
|
||||
return crypto.ECDH.convertKey(publicKey, "prime256v1", "hex", "hex", "uncompressed");
|
||||
});
|
||||
|
||||
bench("ECDH key format - P256 uncompressed to compressed", () => {
|
||||
const publicKey = testKeys["prime256v1"].uncompressed;
|
||||
return crypto.ECDH.convertKey(publicKey, "prime256v1", "hex", "hex", "compressed");
|
||||
});
|
||||
|
||||
bench("ECDH key format - P384 compressed to uncompressed", () => {
|
||||
const publicKey = testKeys["secp384r1"].compressed;
|
||||
return crypto.ECDH.convertKey(publicKey, "secp384r1", "hex", "hex", "uncompressed");
|
||||
});
|
||||
|
||||
bench("ECDH key format - P384 uncompressed to compressed", () => {
|
||||
const publicKey = testKeys["secp384r1"].uncompressed;
|
||||
return crypto.ECDH.convertKey(publicKey, "secp384r1", "hex", "hex", "compressed");
|
||||
});
|
||||
|
||||
await run();
|
||||
43
bench/crypto/primes.mjs
Normal file
43
bench/crypto/primes.mjs
Normal file
@@ -0,0 +1,43 @@
|
||||
import { checkPrime, checkPrimeSync, generatePrime, generatePrimeSync } from "node:crypto";
|
||||
import { bench, run } from "../runner.mjs";
|
||||
|
||||
const prime512 = generatePrimeSync(512);
|
||||
const prime2048 = generatePrimeSync(2048);
|
||||
|
||||
bench("checkPrimeSync 512", () => {
|
||||
return checkPrimeSync(prime512);
|
||||
});
|
||||
|
||||
bench("checkPrimeSync 2048", () => {
|
||||
return checkPrimeSync(prime2048);
|
||||
});
|
||||
|
||||
bench("checkPrime 512", async () => {
|
||||
const promises = Array.from({ length: 10 }, () => new Promise(resolve => checkPrime(prime512, resolve)));
|
||||
await Promise.all(promises);
|
||||
});
|
||||
|
||||
bench("checkPrime 2048", async () => {
|
||||
const promises = Array.from({ length: 10 }, () => new Promise(resolve => checkPrime(prime2048, resolve)));
|
||||
await Promise.all(promises);
|
||||
});
|
||||
|
||||
bench("generatePrimeSync 512", () => {
|
||||
return generatePrimeSync(512);
|
||||
});
|
||||
|
||||
bench("generatePrimeSync 2048", () => {
|
||||
return generatePrimeSync(2048);
|
||||
});
|
||||
|
||||
bench("generatePrime 512", async () => {
|
||||
const promises = Array.from({ length: 10 }, () => new Promise(resolve => generatePrime(512, resolve)));
|
||||
await Promise.all(promises);
|
||||
});
|
||||
|
||||
bench("generatePrime 2048", async () => {
|
||||
const promises = Array.from({ length: 10 }, () => new Promise(resolve => generatePrime(2048, resolve)));
|
||||
await Promise.all(promises);
|
||||
});
|
||||
|
||||
await run();
|
||||
50
bench/crypto/random.mjs
Normal file
50
bench/crypto/random.mjs
Normal file
@@ -0,0 +1,50 @@
|
||||
import crypto from "crypto";
|
||||
import { bench, run } from "../runner.mjs";
|
||||
|
||||
bench("randomInt - sync", () => {
|
||||
crypto.randomInt(1000);
|
||||
});
|
||||
|
||||
bench("randomInt - async", async () => {
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
crypto.randomInt(1000, () => {
|
||||
resolve();
|
||||
});
|
||||
await promise;
|
||||
});
|
||||
|
||||
bench("randonBytes - 32", () => {
|
||||
crypto.randomBytes(32);
|
||||
});
|
||||
|
||||
bench("randomBytes - 256", () => {
|
||||
crypto.randomBytes(256);
|
||||
});
|
||||
|
||||
const buf = Buffer.alloc(256);
|
||||
|
||||
bench("randomFill - 32", async () => {
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
crypto.randomFill(buf, 0, 32, () => {
|
||||
resolve();
|
||||
});
|
||||
await promise;
|
||||
});
|
||||
|
||||
bench("randomFill - 256", async () => {
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
crypto.randomFill(buf, 0, 256, () => {
|
||||
resolve();
|
||||
});
|
||||
await promise;
|
||||
});
|
||||
|
||||
bench("randomFillSync - 32", () => {
|
||||
crypto.randomFillSync(buf, 0, 32);
|
||||
});
|
||||
|
||||
bench("randomFillSync - 256", () => {
|
||||
crypto.randomFillSync(buf, 0, 256);
|
||||
});
|
||||
|
||||
await run();
|
||||
@@ -12,6 +12,7 @@
|
||||
"eventemitter3": "^5.0.0",
|
||||
"execa": "^8.0.1",
|
||||
"fast-glob": "3.3.1",
|
||||
"fastify": "^5.0.0",
|
||||
"fdir": "^6.1.0",
|
||||
"mitata": "^1.0.25",
|
||||
"react": "^18.3.1",
|
||||
|
||||
13
bench/snippets/express-hello.mjs
Normal file
13
bench/snippets/express-hello.mjs
Normal file
@@ -0,0 +1,13 @@
|
||||
import express from "express";
|
||||
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
|
||||
var i = 0;
|
||||
app.get("/", (req, res) => {
|
||||
res.send("Hello World!" + i++);
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Express app listening at http://localhost:${port}`);
|
||||
});
|
||||
20
bench/snippets/fastify.mjs
Normal file
20
bench/snippets/fastify.mjs
Normal file
@@ -0,0 +1,20 @@
|
||||
import Fastify from "fastify";
|
||||
|
||||
const fastify = Fastify({
|
||||
logger: false,
|
||||
});
|
||||
|
||||
fastify.get("/", async (request, reply) => {
|
||||
return { hello: "world" };
|
||||
});
|
||||
|
||||
const start = async () => {
|
||||
try {
|
||||
await fastify.listen({ port: 3000 });
|
||||
} catch (err) {
|
||||
fastify.log.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
start();
|
||||
36
build.zig
36
build.zig
@@ -19,16 +19,17 @@ const OperatingSystem = @import("src/env.zig").OperatingSystem;
|
||||
const pathRel = fs.path.relative;
|
||||
|
||||
/// Do not rename this constant. It is scanned by some scripts to determine which zig version to install.
|
||||
const recommended_zig_version = "0.14.0-dev.2987+183bb8b08";
|
||||
const recommended_zig_version = "0.14.0";
|
||||
|
||||
comptime {
|
||||
if (!std.mem.eql(u8, builtin.zig_version_string, recommended_zig_version)) {
|
||||
@compileError(
|
||||
"" ++
|
||||
"Bun requires Zig version " ++ recommended_zig_version ++ ". This is" ++
|
||||
"automatically configured via Bun's CMake setup. You likely meant to run" ++
|
||||
"`bun setup`. If you are trying to upgrade the Zig compiler," ++
|
||||
"run `./scripts/download-zig.sh master` or comment this message out.",
|
||||
"Bun requires Zig version " ++ recommended_zig_version ++ ", but you have " ++
|
||||
builtin.zig_version_string ++ ". This is automatically configured via Bun's " ++
|
||||
"CMake setup. You likely meant to run `bun run build`. If you are trying to " ++
|
||||
"upgrade the Zig compiler, edit ZIG_COMMIT in cmake/tools/SetupZig.cmake or " ++
|
||||
"comment this error out.",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -318,7 +319,21 @@ pub fn build(b: *Build) !void {
|
||||
.{ .os = .linux, .arch = .aarch64 },
|
||||
.{ .os = .linux, .arch = .x86_64, .musl = true },
|
||||
.{ .os = .linux, .arch = .aarch64, .musl = true },
|
||||
});
|
||||
}, &.{ .Debug, .ReleaseFast });
|
||||
}
|
||||
|
||||
// zig build check-all-debug
|
||||
{
|
||||
const step = b.step("check-all-debug", "Check for semantic analysis errors on all supported platforms in debug mode");
|
||||
addMultiCheck(b, step, build_options, &.{
|
||||
.{ .os = .windows, .arch = .x86_64 },
|
||||
.{ .os = .mac, .arch = .x86_64 },
|
||||
.{ .os = .mac, .arch = .aarch64 },
|
||||
.{ .os = .linux, .arch = .x86_64 },
|
||||
.{ .os = .linux, .arch = .aarch64 },
|
||||
.{ .os = .linux, .arch = .x86_64, .musl = true },
|
||||
.{ .os = .linux, .arch = .aarch64, .musl = true },
|
||||
}, &.{.Debug});
|
||||
}
|
||||
|
||||
// zig build check-windows
|
||||
@@ -326,21 +341,21 @@ pub fn build(b: *Build) !void {
|
||||
const step = b.step("check-windows", "Check for semantic analysis errors on Windows");
|
||||
addMultiCheck(b, step, build_options, &.{
|
||||
.{ .os = .windows, .arch = .x86_64 },
|
||||
});
|
||||
}, &.{ .Debug, .ReleaseFast });
|
||||
}
|
||||
{
|
||||
const step = b.step("check-macos", "Check for semantic analysis errors on Windows");
|
||||
addMultiCheck(b, step, build_options, &.{
|
||||
.{ .os = .mac, .arch = .x86_64 },
|
||||
.{ .os = .mac, .arch = .aarch64 },
|
||||
});
|
||||
}, &.{ .Debug, .ReleaseFast });
|
||||
}
|
||||
{
|
||||
const step = b.step("check-linux", "Check for semantic analysis errors on Windows");
|
||||
addMultiCheck(b, step, build_options, &.{
|
||||
.{ .os = .linux, .arch = .x86_64 },
|
||||
.{ .os = .linux, .arch = .aarch64 },
|
||||
});
|
||||
}, &.{ .Debug, .ReleaseFast });
|
||||
}
|
||||
|
||||
// zig build translate-c-headers
|
||||
@@ -368,9 +383,10 @@ pub fn addMultiCheck(
|
||||
parent_step: *Step,
|
||||
root_build_options: BunBuildOptions,
|
||||
to_check: []const struct { os: OperatingSystem, arch: Arch, musl: bool = false },
|
||||
optimize: []const std.builtin.OptimizeMode,
|
||||
) void {
|
||||
for (to_check) |check| {
|
||||
for ([_]std.builtin.Mode{ .Debug, .ReleaseFast }) |mode| {
|
||||
for (optimize) |mode| {
|
||||
const check_target = b.resolveTargetQuery(.{
|
||||
.os_tag = OperatingSystem.stdOSTag(check.os),
|
||||
.cpu_arch = check.arch,
|
||||
|
||||
66
bun.lock
66
bun.lock
@@ -45,21 +45,21 @@
|
||||
"packages": {
|
||||
"@biomejs/biome": ["@biomejs/biome@1.8.3", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.8.3", "@biomejs/cli-darwin-x64": "1.8.3", "@biomejs/cli-linux-arm64": "1.8.3", "@biomejs/cli-linux-arm64-musl": "1.8.3", "@biomejs/cli-linux-x64": "1.8.3", "@biomejs/cli-linux-x64-musl": "1.8.3", "@biomejs/cli-win32-arm64": "1.8.3", "@biomejs/cli-win32-x64": "1.8.3" }, "bin": { "biome": "bin/biome" } }, "sha512-/uUV3MV+vyAczO+vKrPdOW0Iaet7UnJMU4bNMinggGJTAnBPjCoLEYcyYtYHNnUNYlv4xZMH6hVIQCAozq8d5w=="],
|
||||
|
||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.8.3", "", { "os":"darwin", "cpu":"arm64" }, "sha512-9DYOjclFpKrH/m1Oz75SSExR8VKvNSSsLnVIqdnKexj6NwmiMlKk94Wa1kZEdv6MCOHGHgyyoV57Cw8WzL5n3A=="],
|
||||
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.8.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9DYOjclFpKrH/m1Oz75SSExR8VKvNSSsLnVIqdnKexj6NwmiMlKk94Wa1kZEdv6MCOHGHgyyoV57Cw8WzL5n3A=="],
|
||||
|
||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.8.3", "", { "os":"darwin", "cpu":"x64" }, "sha512-UeW44L/AtbmOF7KXLCoM+9PSgPo0IDcyEUfIoOXYeANaNXXf9mLUwV1GeF2OWjyic5zj6CnAJ9uzk2LT3v/wAw=="],
|
||||
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.8.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-UeW44L/AtbmOF7KXLCoM+9PSgPo0IDcyEUfIoOXYeANaNXXf9mLUwV1GeF2OWjyic5zj6CnAJ9uzk2LT3v/wAw=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.8.3", "", { "os":"linux", "cpu":"arm64" }, "sha512-fed2ji8s+I/m8upWpTJGanqiJ0rnlHOK3DdxsyVLZQ8ClY6qLuPc9uehCREBifRJLl/iJyQpHIRufLDeotsPtw=="],
|
||||
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.8.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-fed2ji8s+I/m8upWpTJGanqiJ0rnlHOK3DdxsyVLZQ8ClY6qLuPc9uehCREBifRJLl/iJyQpHIRufLDeotsPtw=="],
|
||||
|
||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.8.3", "", { "os":"linux", "cpu":"arm64" }, "sha512-9yjUfOFN7wrYsXt/T/gEWfvVxKlnh3yBpnScw98IF+oOeCYb5/b/+K7YNqKROV2i1DlMjg9g/EcN9wvj+NkMuQ=="],
|
||||
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.8.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-9yjUfOFN7wrYsXt/T/gEWfvVxKlnh3yBpnScw98IF+oOeCYb5/b/+K7YNqKROV2i1DlMjg9g/EcN9wvj+NkMuQ=="],
|
||||
|
||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.8.3", "", { "os":"linux", "cpu":"x64" }, "sha512-I8G2QmuE1teISyT8ie1HXsjFRz9L1m5n83U1O6m30Kw+kPMPSKjag6QGUn+sXT8V+XWIZxFFBoTDEDZW2KPDDw=="],
|
||||
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.8.3", "", { "os": "linux", "cpu": "x64" }, "sha512-I8G2QmuE1teISyT8ie1HXsjFRz9L1m5n83U1O6m30Kw+kPMPSKjag6QGUn+sXT8V+XWIZxFFBoTDEDZW2KPDDw=="],
|
||||
|
||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.8.3", "", { "os":"linux", "cpu":"x64" }, "sha512-UHrGJX7PrKMKzPGoEsooKC9jXJMa28TUSMjcIlbDnIO4EAavCoVmNQaIuUSH0Ls2mpGMwUIf+aZJv657zfWWjA=="],
|
||||
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.8.3", "", { "os": "linux", "cpu": "x64" }, "sha512-UHrGJX7PrKMKzPGoEsooKC9jXJMa28TUSMjcIlbDnIO4EAavCoVmNQaIuUSH0Ls2mpGMwUIf+aZJv657zfWWjA=="],
|
||||
|
||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.8.3", "", { "os":"win32", "cpu":"arm64" }, "sha512-J+Hu9WvrBevfy06eU1Na0lpc7uR9tibm9maHynLIoAjLZpQU3IW+OKHUtyL8p6/3pT2Ju5t5emReeIS2SAxhkQ=="],
|
||||
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.8.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-J+Hu9WvrBevfy06eU1Na0lpc7uR9tibm9maHynLIoAjLZpQU3IW+OKHUtyL8p6/3pT2Ju5t5emReeIS2SAxhkQ=="],
|
||||
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.8.3", "", { "os":"win32", "cpu":"x64" }, "sha512-/PJ59vA1pnQeKahemaQf4Nyj7IKUvGQSc3Ze1uIGi+Wvr1xF7rGobSrAAG01T/gUDG21vkDsZYM03NAmPiVkqg=="],
|
||||
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.8.3", "", { "os": "win32", "cpu": "x64" }, "sha512-/PJ59vA1pnQeKahemaQf4Nyj7IKUvGQSc3Ze1uIGi+Wvr1xF7rGobSrAAG01T/gUDG21vkDsZYM03NAmPiVkqg=="],
|
||||
|
||||
"@definitelytyped/dts-critic": ["@definitelytyped/dts-critic@0.0.191", "", { "dependencies": { "@definitelytyped/header-parser": "0.0.190", "command-exists": "^1.2.9", "semver": "^7.5.4", "tmp": "^0.2.1", "typescript": "^5.2.2", "yargs": "^17.7.2" } }, "sha512-j5HK3pQYiQwSXRLJzyhXJ6KxdzLl4gXXhz3ysCtLnRQkj+zsEfloDkEZ3x2bZMWS0OsKLXmR91JeQ2/c9DFEjg=="],
|
||||
|
||||
@@ -75,51 +75,51 @@
|
||||
|
||||
"@es-joy/jsdoccomment": ["@es-joy/jsdoccomment@0.39.4", "", { "dependencies": { "comment-parser": "1.3.1", "esquery": "^1.5.0", "jsdoc-type-pratt-parser": "~4.0.0" } }, "sha512-Jvw915fjqQct445+yron7Dufix9A+m9j1fCJYlCo1FWlRvTxa3pjJelxdSTdaLWcTwRU6vbL+NYjO4YuNIS5Qg=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os":"aix", "cpu":"ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os":"android", "cpu":"arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
|
||||
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os":"android", "cpu":"arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="],
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="],
|
||||
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os":"android", "cpu":"x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="],
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="],
|
||||
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os":"darwin", "cpu":"arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="],
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="],
|
||||
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os":"darwin", "cpu":"x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="],
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="],
|
||||
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os":"freebsd", "cpu":"arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="],
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="],
|
||||
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os":"freebsd", "cpu":"x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="],
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="],
|
||||
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os":"linux", "cpu":"arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="],
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="],
|
||||
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os":"linux", "cpu":"arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="],
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="],
|
||||
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os":"linux", "cpu":"ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="],
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="],
|
||||
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os":"linux", "cpu":"none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="],
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="],
|
||||
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os":"linux", "cpu":"none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="],
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="],
|
||||
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os":"linux", "cpu":"ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="],
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="],
|
||||
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os":"linux", "cpu":"none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="],
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="],
|
||||
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os":"linux", "cpu":"s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="],
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="],
|
||||
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os":"linux", "cpu":"x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="],
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="],
|
||||
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os":"none", "cpu":"x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="],
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="],
|
||||
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os":"openbsd", "cpu":"x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="],
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="],
|
||||
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os":"sunos", "cpu":"x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="],
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="],
|
||||
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os":"win32", "cpu":"arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="],
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="],
|
||||
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os":"win32", "cpu":"ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="],
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="],
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os":"win32", "cpu":"x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
|
||||
|
||||
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.4.0", "", { "dependencies": { "eslint-visitor-keys": "^3.3.0" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA=="],
|
||||
|
||||
@@ -151,13 +151,13 @@
|
||||
|
||||
"@qiwi/npm-registry-client": ["@qiwi/npm-registry-client@8.9.1", "", { "dependencies": { "concat-stream": "^2.0.0", "graceful-fs": "^4.2.4", "normalize-package-data": "~1.0.1 || ^2.0.0 || ^3.0.0", "npm-package-arg": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^8.0.0", "once": "^1.4.0", "request": "^2.88.2", "retry": "^0.12.0", "safe-buffer": "^5.2.1", "semver": "2 >=2.2.1 || 3.x || 4 || 5 || 7", "slide": "^1.1.6", "ssri": "^8.0.0" }, "optionalDependencies": { "npmlog": "2 || ^3.1.0 || ^4.0.0" } }, "sha512-rZF+mG+NfijR0SHphhTLHRr4aM4gtfdwoAMY6we2VGQam8vkN1cxGG1Lg/Llrj8Dd0Mu6VjdFQRyMMRZxtZR2A=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.1.17", "", { "dependencies": { "bun-types": "1.1.44" } }, "sha512-zZt0Kao/8hAwNOXh4bmt8nKbMEd4QD8n7PeTGF+NZTVY5ouXhU/TX7jUj4He1p7mgY+WdplnU1B6MB1j17vdzg=="],
|
||||
"@types/bun": ["@types/bun@1.2.2", "", { "dependencies": { "bun-types": "1.2.2" } }, "sha512-tr74gdku+AEDN5ergNiBnplr7hpDp3V1h7fqI2GcR/rsUaM39jpSeKH0TFibRvU0KwniRx5POgaYnaXbk0hU+w=="],
|
||||
|
||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||
|
||||
"@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="],
|
||||
|
||||
"@types/node": ["@types/node@22.10.7", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg=="],
|
||||
"@types/node": ["@types/node@22.13.5", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg=="],
|
||||
|
||||
"@types/prop-types": ["@types/prop-types@15.7.12", "", {}, "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="],
|
||||
|
||||
|
||||
@@ -2,3 +2,7 @@
|
||||
# https://github.com/oven-sh/bun/issues/16289
|
||||
[test]
|
||||
preload = ["./test/js/node/harness.ts", "./test/preload.ts"]
|
||||
|
||||
[install]
|
||||
# Node.js never auto-installs modules.
|
||||
auto = "disable"
|
||||
|
||||
84
ci/README.md
84
ci/README.md
@@ -1,84 +0,0 @@
|
||||
# CI
|
||||
|
||||
This directory contains scripts for building CI images for Bun.
|
||||
|
||||
## Building
|
||||
|
||||
### `macOS`
|
||||
|
||||
On macOS, images are built using [`tart`](https://tart.run/), a tool that abstracts over the [`Virtualization.Framework`](https://developer.apple.com/documentation/virtualization) APIs, to run macOS VMs.
|
||||
|
||||
To install the dependencies required, run:
|
||||
|
||||
```sh
|
||||
$ cd ci
|
||||
$ bun run bootstrap
|
||||
```
|
||||
|
||||
To build a vanilla macOS VM, run:
|
||||
|
||||
```sh
|
||||
$ bun run build:darwin-aarch64-vanilla
|
||||
```
|
||||
|
||||
This builds a vanilla macOS VM with the current macOS release on your machine. It runs scripts to disable things like spotlight and siri, but it does not install any software.
|
||||
|
||||
> Note: The image size is 50GB, so make sure you have enough disk space.
|
||||
|
||||
If you want to build a specific macOS release, you can run:
|
||||
|
||||
```sh
|
||||
$ bun run build:darwin-aarch64-vanilla-15
|
||||
```
|
||||
|
||||
> Note: You cannot build a newer release of macOS on an older macOS machine.
|
||||
|
||||
To build a macOS VM with software installed to build and test Bun, run:
|
||||
|
||||
```sh
|
||||
$ bun run build:darwin-aarch64
|
||||
```
|
||||
|
||||
## Running
|
||||
|
||||
### `macOS`
|
||||
|
||||
## How To
|
||||
|
||||
### Support a new macOS release
|
||||
|
||||
1. Visit [`ipsw.me`](https://ipsw.me/VirtualMac2,1) and find the IPSW of the macOS release you want to build.
|
||||
|
||||
2. Add an entry to [`ci/darwin/variables.pkr.hcl`](/ci/darwin/variables.pkr.hcl) with the following format:
|
||||
|
||||
```hcl
|
||||
sonoma = {
|
||||
distro = "sonoma"
|
||||
release = "15"
|
||||
ipsw = "https://updates.cdn-apple.com/..."
|
||||
}
|
||||
```
|
||||
|
||||
3. Add matching scripts to [`ci/package.json`](/ci/package.json) to build the image, then test it:
|
||||
|
||||
```sh
|
||||
$ bun run build:darwin-aarch64-vanilla-15
|
||||
```
|
||||
|
||||
> Note: If you need to troubleshoot the build, you can remove the `headless = true` property from [`ci/darwin/image-vanilla.pkr.hcl`](/ci/darwin/image-vanilla.pkr.hcl) and the VM's screen will be displayed.
|
||||
|
||||
4. Test and build the non-vanilla image:
|
||||
|
||||
```sh
|
||||
$ bun run build:darwin-aarch64-15
|
||||
```
|
||||
|
||||
This will use the vanilla image and run the [`scripts/bootstrap.sh`](/scripts/bootstrap.sh) script to install the required software to build and test Bun.
|
||||
|
||||
5. Publish the images:
|
||||
|
||||
```sh
|
||||
$ bun run login
|
||||
$ bun run publish:darwin-aarch64-vanilla-15
|
||||
$ bun run publish:darwin-aarch64-15
|
||||
```
|
||||
@@ -1,22 +0,0 @@
|
||||
FROM alpine:edge AS build
|
||||
ARG GIT_SHA
|
||||
ENV GIT_SHA=${GIT_SHA}
|
||||
WORKDIR /app/bun
|
||||
ENV HOME=/root
|
||||
|
||||
COPY . .
|
||||
RUN touch $HOME/.bashrc
|
||||
RUN ./scripts/bootstrap.sh
|
||||
RUN . $HOME/.bashrc && bun run build:release
|
||||
|
||||
RUN apk add file
|
||||
RUN file ./build/release/bun
|
||||
RUN ldd ./build/release/bun
|
||||
RUN ./build/release/bun
|
||||
|
||||
RUN cp -R /app/bun/build/* /output
|
||||
|
||||
FROM scratch AS artifact
|
||||
COPY --from=build /output /
|
||||
|
||||
# docker build -f ./ci/alpine/build.Dockerfile --progress=plain --build-arg GIT_SHA="$(git rev-parse HEAD)" --target=artifact --output type=local,dest=./build-alpine .
|
||||
@@ -1,20 +0,0 @@
|
||||
FROM alpine:edge
|
||||
ENV HOME=/root
|
||||
WORKDIR /root
|
||||
COPY ./build-alpine/release/bun .
|
||||
COPY ./test ./test
|
||||
COPY ./scripts ./scripts
|
||||
COPY ./package.json ./package.json
|
||||
COPY ./packages ./packages
|
||||
|
||||
RUN apk update
|
||||
RUN apk add nodejs lsb-release-minimal git python3 npm make g++
|
||||
RUN apk add file
|
||||
|
||||
RUN file /root/bun
|
||||
RUN ldd /root/bun
|
||||
RUN /root/bun
|
||||
|
||||
RUN ./scripts/runner.node.mjs --exec-path /root/bun
|
||||
|
||||
# docker build -f ./ci/alpine/test.Dockerfile --progress=plain .
|
||||
@@ -1,46 +0,0 @@
|
||||
# Generates a vanilla macOS VM with optimized settings for virtualized environments.
|
||||
# See login.sh and optimize.sh for details.
|
||||
|
||||
data "external-raw" "boot-script" {
|
||||
program = ["sh", "-c", templatefile("scripts/boot-image.sh", var)]
|
||||
}
|
||||
|
||||
source "tart-cli" "bun-darwin-aarch64-vanilla" {
|
||||
vm_name = "bun-darwin-aarch64-vanilla-${local.release.distro}-${local.release.release}"
|
||||
from_ipsw = local.release.ipsw
|
||||
cpu_count = local.cpu_count
|
||||
memory_gb = local.memory_gb
|
||||
disk_size_gb = local.disk_size_gb
|
||||
ssh_username = local.username
|
||||
ssh_password = local.password
|
||||
ssh_timeout = "120s"
|
||||
create_grace_time = "30s"
|
||||
boot_command = split("\n", data.external-raw.boot-script.result)
|
||||
headless = true # Disable if you need to debug why the boot_command is not working
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["source.tart-cli.bun-darwin-aarch64-vanilla"]
|
||||
|
||||
provisioner "file" {
|
||||
content = file("scripts/setup-login.sh")
|
||||
destination = "/tmp/setup-login.sh"
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
inline = ["echo \"${local.password}\" | sudo -S sh -c 'sh /tmp/setup-login.sh \"${local.username}\" \"${local.password}\"'"]
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
content = file("scripts/optimize-machine.sh")
|
||||
destination = "/tmp/optimize-machine.sh"
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
inline = ["sudo sh /tmp/optimize-machine.sh"]
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
inline = ["sudo rm -rf /tmp/*"]
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
# Generates a macOS VM with software installed to build and test Bun.
|
||||
|
||||
source "tart-cli" "bun-darwin-aarch64" {
|
||||
vm_name = "bun-darwin-aarch64-${local.release.distro}-${local.release.release}"
|
||||
vm_base_name = "bun-darwin-aarch64-vanilla-${local.release.distro}-${local.release.release}"
|
||||
cpu_count = local.cpu_count
|
||||
memory_gb = local.memory_gb
|
||||
disk_size_gb = local.disk_size_gb
|
||||
ssh_username = local.username
|
||||
ssh_password = local.password
|
||||
ssh_timeout = "120s"
|
||||
headless = true
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["source.tart-cli.bun-darwin-aarch64"]
|
||||
|
||||
provisioner "file" {
|
||||
content = file("../../scripts/bootstrap.sh")
|
||||
destination = "/tmp/bootstrap.sh"
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
inline = ["CI=true sh /tmp/bootstrap.sh"]
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
source = "darwin/plists/"
|
||||
destination = "/tmp/"
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
inline = [
|
||||
"sudo ls /tmp/",
|
||||
"sudo mv /tmp/*.plist /Library/LaunchDaemons/",
|
||||
"sudo chown root:wheel /Library/LaunchDaemons/*.plist",
|
||||
"sudo chmod 644 /Library/LaunchDaemons/*.plist",
|
||||
]
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
inline = ["sudo rm -rf /tmp/*"]
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.buildkite.buildkite-agent</string>
|
||||
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/local/bin/buildkite-agent</string>
|
||||
<string>start</string>
|
||||
</array>
|
||||
|
||||
<key>KeepAlive</key>
|
||||
<dict>
|
||||
<key>SuccessfulExit</key>
|
||||
<false />
|
||||
</dict>
|
||||
|
||||
<key>RunAtLoad</key>
|
||||
<true />
|
||||
|
||||
<key>StandardOutPath</key>
|
||||
<string>/var/buildkite-agent/logs/buildkite-agent.log</string>
|
||||
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/var/buildkite-agent/logs/buildkite-agent.log</string>
|
||||
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>BUILDKITE_AGENT_CONFIG</key>
|
||||
<string>/etc/buildkite-agent/buildkite-agent.cfg</string>
|
||||
</dict>
|
||||
|
||||
<key>LimitLoadToSessionType</key>
|
||||
<array>
|
||||
<string>Aqua</string>
|
||||
<string>LoginWindow</string>
|
||||
<string>Background</string>
|
||||
<string>StandardIO</string>
|
||||
<string>System</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.tailscale.tailscaled</string>
|
||||
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/local/bin/tailscale</string>
|
||||
<string>up</string>
|
||||
<string>--ssh</string>
|
||||
<string>--authkey</string>
|
||||
<string>${TAILSCALE_AUTHKEY}</string>
|
||||
</array>
|
||||
|
||||
<key>RunAtLoad</key>
|
||||
<true />
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.tailscale.tailscaled</string>
|
||||
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/local/bin/tailscaled</string>
|
||||
</array>
|
||||
|
||||
<key>RunAtLoad</key>
|
||||
<true />
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,124 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script generates the boot commands for the macOS installer GUI.
|
||||
# It is run on your local machine, not inside the VM.
|
||||
|
||||
# Sources:
|
||||
# - https://github.com/cirruslabs/macos-image-templates/blob/master/templates/vanilla-sequoia.pkr.hcl
|
||||
|
||||
if ! [ "${release}" ] || ! [ "${username}" ] || ! [ "${password}" ]; then
|
||||
echo "Script must be run with variables: release, username, and password" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Hello, hola, bonjour, etc.
|
||||
echo "<wait120s><spacebar>"
|
||||
|
||||
# Select Your Country and Region
|
||||
echo "<wait30s>italiano<esc>english<enter>"
|
||||
echo "<wait30s>united states<leftShiftOn><tab><leftShiftOff><spacebar>"
|
||||
|
||||
# Written and Spoken Languages
|
||||
echo "<wait30s><leftShiftOn><tab><leftShiftOff><spacebar>"
|
||||
|
||||
# Accessibility
|
||||
echo "<wait30s><leftShiftOn><tab><leftShiftOff><spacebar>"
|
||||
|
||||
# Data & Privacy
|
||||
echo "<wait30s><leftShiftOn><tab><leftShiftOff><spacebar>"
|
||||
|
||||
# Migration Assistant
|
||||
echo "<wait30s><tab><tab><tab><spacebar>"
|
||||
|
||||
# Sign In with Your Apple ID
|
||||
echo "<wait30s><leftShiftOn><tab><leftShiftOff><leftShiftOn><tab><leftShiftOff><spacebar>"
|
||||
|
||||
# Are you sure you want to skip signing in with an Apple ID?
|
||||
echo "<wait30s><tab><spacebar>"
|
||||
|
||||
# Terms and Conditions
|
||||
echo "<wait30s><leftShiftOn><tab><leftShiftOff><spacebar>"
|
||||
|
||||
# I have read and agree to the macOS Software License Agreement
|
||||
echo "<wait30s><tab><spacebar>"
|
||||
|
||||
# Create a Computer Account
|
||||
echo "<wait30s>${username}<tab><tab>${password}<tab>${password}<tab><tab><tab><spacebar>"
|
||||
|
||||
# Enable Location Services
|
||||
echo "<wait60s><leftShiftOn><tab><leftShiftOff><spacebar>"
|
||||
|
||||
# Are you sure you don't want to use Location Services?
|
||||
echo "<wait30s><tab><spacebar>"
|
||||
|
||||
# Select Your Time Zone
|
||||
echo "<wait30s><tab>UTC<enter><leftShiftOn><tab><leftShiftOff><spacebar>"
|
||||
|
||||
# Analytics
|
||||
echo "<wait30s><leftShiftOn><tab><leftShiftOff><spacebar>"
|
||||
|
||||
# Screen Time
|
||||
echo "<wait30s><tab><spacebar>"
|
||||
|
||||
# Siri
|
||||
echo "<wait30s><tab><spacebar><leftShiftOn><tab><leftShiftOff><spacebar>"
|
||||
|
||||
# Choose Your Look
|
||||
echo "<wait30s><leftShiftOn><tab><leftShiftOff><spacebar>"
|
||||
|
||||
if [ "${release}" = "13" ] || [ "${release}" = "14" ]; then
|
||||
# Enable Voice Over
|
||||
echo "<wait30s><leftAltOn><f5><leftAltOff><wait5s>v"
|
||||
else
|
||||
# Welcome to Mac
|
||||
echo "<wait30s><spacebar>"
|
||||
|
||||
# Enable Keyboard navigation
|
||||
echo "<wait30s><leftAltOn><spacebar><leftAltOff>Terminal<enter>"
|
||||
echo "<wait30s>defaults write NSGlobalDomain AppleKeyboardUIMode -int 3<enter>"
|
||||
echo "<wait30s><leftAltOn>q<leftAltOff>"
|
||||
fi
|
||||
|
||||
# Now that the installation is done, open "System Settings"
|
||||
echo "<wait30s><leftAltOn><spacebar><leftAltOff>System Settings<enter>"
|
||||
|
||||
# Navigate to "Sharing"
|
||||
echo "<wait30s><leftAltOn>f<leftAltOff>sharing<enter>"
|
||||
|
||||
if [ "${release}" = "13" ]; then
|
||||
# Navigate to "Screen Sharing" and enable it
|
||||
echo "<wait30s><tab><down><spacebar>"
|
||||
|
||||
# Navigate to "Remote Login" and enable it
|
||||
echo "<wait30s><tab><tab><tab><tab><tab><tab><spacebar>"
|
||||
|
||||
# Open "Remote Login" details
|
||||
echo "<wait30s><tab><spacebar>"
|
||||
|
||||
# Enable "Full Disk Access"
|
||||
echo "<wait30s><tab><spacebar>"
|
||||
|
||||
# Click "Done"
|
||||
echo "<wait30s><leftShiftOn><tab><leftShiftOff><leftShiftOn><tab><leftShiftOff><spacebar>"
|
||||
|
||||
# Disable Voice Over
|
||||
echo "<leftAltOn><f5><leftAltOff>"
|
||||
elif [ "${release}" = "14" ]; then
|
||||
# Navigate to "Screen Sharing" and enable it
|
||||
echo "<wait30s><tab><tab><tab><tab><tab><spacebar>"
|
||||
|
||||
# Navigate to "Remote Login" and enable it
|
||||
echo "<wait30s><tab><tab><tab><tab><tab><tab><tab><tab><tab><tab><tab><tab><spacebar>"
|
||||
|
||||
# Disable Voice Over
|
||||
echo "<wait30s><leftAltOn><f5><leftAltOff>"
|
||||
elif [ "${release}" = "15" ]; then
|
||||
# Navigate to "Screen Sharing" and enable it
|
||||
echo "<wait30s><tab><tab><tab><tab><tab><spacebar>"
|
||||
|
||||
# Navigate to "Remote Login" and enable it
|
||||
echo "<wait30s><tab><tab><tab><tab><tab><tab><tab><tab><tab><tab><tab><tab><spacebar>"
|
||||
fi
|
||||
|
||||
# Quit System Settings
|
||||
echo "<wait30s><leftAltOn>q<leftAltOff>"
|
||||
@@ -1,122 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script optimizes macOS for virtualized environments.
|
||||
# It disables things like spotlight, screen saver, and sleep.
|
||||
|
||||
# Sources:
|
||||
# - https://github.com/sickcodes/osx-optimizer
|
||||
# - https://github.com/koding88/MacBook-Optimization-Script
|
||||
# - https://www.macstadium.com/blog/simple-optimizations-for-macos-and-ios-build-agents
|
||||
|
||||
if [ "$(id -u)" != "0" ]; then
|
||||
echo "This script must be run using sudo." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
execute() {
|
||||
echo "$ $@" >&2
|
||||
if ! "$@"; then
|
||||
echo "Command failed: $@" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
disable_software_update() {
|
||||
execute softwareupdate --schedule off
|
||||
execute defaults write com.apple.SoftwareUpdate AutomaticDownload -bool false
|
||||
execute defaults write com.apple.SoftwareUpdate AutomaticCheckEnabled -bool false
|
||||
execute defaults write com.apple.SoftwareUpdate ConfigDataInstall -int 0
|
||||
execute defaults write com.apple.SoftwareUpdate CriticalUpdateInstall -int 0
|
||||
execute defaults write com.apple.SoftwareUpdate ScheduleFrequency -int 0
|
||||
execute defaults write com.apple.SoftwareUpdate AutomaticDownload -int 0
|
||||
execute defaults write com.apple.commerce AutoUpdate -bool false
|
||||
execute defaults write com.apple.commerce AutoUpdateRestartRequired -bool false
|
||||
}
|
||||
|
||||
disable_spotlight() {
|
||||
execute mdutil -i off -a
|
||||
execute mdutil -E /
|
||||
}
|
||||
|
||||
disable_siri() {
|
||||
execute launchctl unload -w /System/Library/LaunchAgents/com.apple.Siri.agent.plist
|
||||
execute defaults write com.apple.Siri StatusMenuVisible -bool false
|
||||
execute defaults write com.apple.Siri UserHasDeclinedEnable -bool true
|
||||
execute defaults write com.apple.assistant.support "Assistant Enabled" 0
|
||||
}
|
||||
|
||||
disable_sleep() {
|
||||
execute systemsetup -setsleep Never
|
||||
execute systemsetup -setcomputersleep Never
|
||||
execute systemsetup -setdisplaysleep Never
|
||||
execute systemsetup -setharddisksleep Never
|
||||
}
|
||||
|
||||
disable_screen_saver() {
|
||||
execute defaults write com.apple.screensaver loginWindowIdleTime 0
|
||||
execute defaults write com.apple.screensaver idleTime 0
|
||||
}
|
||||
|
||||
disable_screen_lock() {
|
||||
execute defaults write com.apple.loginwindow DisableScreenLock -bool true
|
||||
}
|
||||
|
||||
disable_wallpaper() {
|
||||
execute defaults write com.apple.loginwindow DesktopPicture ""
|
||||
}
|
||||
|
||||
disable_application_state() {
|
||||
execute defaults write com.apple.loginwindow TALLogoutSavesState -bool false
|
||||
}
|
||||
|
||||
disable_accessibility() {
|
||||
execute defaults write com.apple.Accessibility DifferentiateWithoutColor -int 1
|
||||
execute defaults write com.apple.Accessibility ReduceMotionEnabled -int 1
|
||||
execute defaults write com.apple.universalaccess reduceMotion -int 1
|
||||
execute defaults write com.apple.universalaccess reduceTransparency -int 1
|
||||
}
|
||||
|
||||
disable_dashboard() {
|
||||
execute defaults write com.apple.dashboard mcx-disabled -boolean YES
|
||||
execute killall Dock
|
||||
}
|
||||
|
||||
disable_animations() {
|
||||
execute defaults write NSGlobalDomain NSAutomaticWindowAnimationsEnabled -bool false
|
||||
execute defaults write -g QLPanelAnimationDuration -float 0
|
||||
execute defaults write com.apple.finder DisableAllAnimations -bool true
|
||||
}
|
||||
|
||||
disable_time_machine() {
|
||||
execute tmutil disable
|
||||
}
|
||||
|
||||
enable_performance_mode() {
|
||||
# https://support.apple.com/en-us/101992
|
||||
if ! [ $(nvram boot-args 2>/dev/null | grep -q serverperfmode) ]; then
|
||||
execute nvram boot-args="serverperfmode=1 $(nvram boot-args 2>/dev/null | cut -f 2-)"
|
||||
fi
|
||||
}
|
||||
|
||||
add_terminal_to_desktop() {
|
||||
execute ln -sf /System/Applications/Utilities/Terminal.app ~/Desktop/Terminal
|
||||
}
|
||||
|
||||
main() {
|
||||
disable_software_update
|
||||
disable_spotlight
|
||||
disable_siri
|
||||
disable_sleep
|
||||
disable_screen_saver
|
||||
disable_screen_lock
|
||||
disable_wallpaper
|
||||
disable_application_state
|
||||
disable_accessibility
|
||||
disable_dashboard
|
||||
disable_animations
|
||||
disable_time_machine
|
||||
enable_performance_mode
|
||||
add_terminal_to_desktop
|
||||
}
|
||||
|
||||
main
|
||||
@@ -1,78 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script generates a /etc/kcpassword file to enable auto-login on macOS.
|
||||
# Yes, this stores your password in plain text. Do NOT do this on your local machine.
|
||||
|
||||
# Sources:
|
||||
# - https://github.com/xfreebird/kcpassword/blob/master/kcpassword
|
||||
|
||||
if [ "$(id -u)" != "0" ]; then
|
||||
echo "This script must be run using sudo." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
execute() {
|
||||
echo "$ $@" >&2
|
||||
if ! "$@"; then
|
||||
echo "Command failed: $@" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
kcpassword() {
|
||||
passwd="$1"
|
||||
key="7d 89 52 23 d2 bc dd ea a3 b9 1f"
|
||||
passwd_hex=$(printf "%s" "$passwd" | xxd -p | tr -d '\n')
|
||||
|
||||
key_len=33
|
||||
passwd_len=${#passwd_hex}
|
||||
remainder=$((passwd_len % key_len))
|
||||
if [ $remainder -ne 0 ]; then
|
||||
padding=$((key_len - remainder))
|
||||
passwd_hex="${passwd_hex}$(printf '%0*x' $((padding / 2)) 0)"
|
||||
fi
|
||||
|
||||
result=""
|
||||
i=0
|
||||
while [ $i -lt ${#passwd_hex} ]; do
|
||||
for byte in $key; do
|
||||
[ $i -ge ${#passwd_hex} ] && break
|
||||
p="${passwd_hex:$i:2}"
|
||||
r=$(printf '%02x' $((0x$p ^ 0x$byte)))
|
||||
result="${result}${r}"
|
||||
i=$((i + 2))
|
||||
done
|
||||
done
|
||||
|
||||
echo "$result"
|
||||
}
|
||||
|
||||
login() {
|
||||
username="$1"
|
||||
password="$2"
|
||||
|
||||
enable_passwordless_sudo() {
|
||||
execute mkdir -p /etc/sudoers.d/
|
||||
echo "${username} ALL=(ALL) NOPASSWD: ALL" | EDITOR=tee execute visudo "/etc/sudoers.d/${username}-nopasswd"
|
||||
}
|
||||
|
||||
enable_auto_login() {
|
||||
echo "00000000: 1ced 3f4a bcbc ba2c caca 4e82" | execute xxd -r - /etc/kcpassword
|
||||
execute defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser "${username}"
|
||||
}
|
||||
|
||||
disable_screen_lock() {
|
||||
execute sysadminctl -screenLock off -password "${password}"
|
||||
}
|
||||
|
||||
enable_passwordless_sudo
|
||||
enable_auto_login
|
||||
disable_screen_lock
|
||||
}
|
||||
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Usage: $0 <username> <password>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
login "$@"
|
||||
@@ -1,78 +0,0 @@
|
||||
packer {
|
||||
required_plugins {
|
||||
tart = {
|
||||
version = ">= 1.12.0"
|
||||
source = "github.com/cirruslabs/tart"
|
||||
}
|
||||
external = {
|
||||
version = ">= 0.0.2"
|
||||
source = "github.com/joomcode/external"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "release" {
|
||||
type = number
|
||||
default = 13
|
||||
}
|
||||
|
||||
variable "username" {
|
||||
type = string
|
||||
default = "admin"
|
||||
}
|
||||
|
||||
variable "password" {
|
||||
type = string
|
||||
default = "admin"
|
||||
}
|
||||
|
||||
variable "cpu_count" {
|
||||
type = number
|
||||
default = 2
|
||||
}
|
||||
|
||||
variable "memory_gb" {
|
||||
type = number
|
||||
default = 4
|
||||
}
|
||||
|
||||
variable "disk_size_gb" {
|
||||
type = number
|
||||
default = 50
|
||||
}
|
||||
|
||||
locals {
|
||||
sequoia = {
|
||||
tier = 1
|
||||
distro = "sequoia"
|
||||
release = "15"
|
||||
ipsw = "https://updates.cdn-apple.com/2024FallFCS/fullrestores/062-78489/BDA44327-C79E-4608-A7E0-455A7E91911F/UniversalMac_15.0_24A335_Restore.ipsw"
|
||||
}
|
||||
|
||||
sonoma = {
|
||||
tier = 2
|
||||
distro = "sonoma"
|
||||
release = "14"
|
||||
ipsw = "https://updates.cdn-apple.com/2023FallFCS/fullrestores/042-54934/0E101AD6-3117-4B63-9BF1-143B6DB9270A/UniversalMac_14.0_23A344_Restore.ipsw"
|
||||
}
|
||||
|
||||
ventura = {
|
||||
tier = 2
|
||||
distro = "ventura"
|
||||
release = "13"
|
||||
ipsw = "https://updates.cdn-apple.com/2022FallFCS/fullrestores/012-92188/2C38BCD1-2BFF-4A10-B358-94E8E28BE805/UniversalMac_13.0_22A380_Restore.ipsw"
|
||||
}
|
||||
|
||||
releases = {
|
||||
15 = local.sequoia
|
||||
14 = local.sonoma
|
||||
13 = local.ventura
|
||||
}
|
||||
|
||||
release = local.releases[var.release]
|
||||
username = var.username
|
||||
password = var.password
|
||||
cpu_count = var.cpu_count
|
||||
memory_gb = var.memory_gb
|
||||
disk_size_gb = var.disk_size_gb
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
ARG IMAGE=debian:11
|
||||
FROM $IMAGE
|
||||
COPY ./scripts/bootstrap.sh /tmp/bootstrap.sh
|
||||
ENV CI=true
|
||||
RUN sh /tmp/bootstrap.sh && rm -rf /tmp/*
|
||||
WORKDIR /workspace/bun
|
||||
COPY bunfig.toml bunfig.toml
|
||||
COPY package.json package.json
|
||||
COPY CMakeLists.txt CMakeLists.txt
|
||||
COPY cmake/ cmake/
|
||||
COPY scripts/ scripts/
|
||||
COPY patches/ patches/
|
||||
COPY *.zig ./
|
||||
COPY src/ src/
|
||||
COPY packages/ packages/
|
||||
COPY test/ test/
|
||||
RUN bun i
|
||||
RUN bun run build:ci
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script sets the hostname of the current machine.
|
||||
|
||||
execute() {
|
||||
echo "$ $@" >&2
|
||||
if ! "$@"; then
|
||||
echo "Command failed: $@" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <hostname>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f "$(which hostnamectl)" ]; then
|
||||
execute hostnamectl set-hostname "$1"
|
||||
else
|
||||
echo "Error: hostnamectl is not installed." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script starts tailscale on the current machine.
|
||||
|
||||
execute() {
|
||||
echo "$ $@" >&2
|
||||
if ! "$@"; then
|
||||
echo "Command failed: $@" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <auth-key>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
execute tailscale up --reset --ssh --accept-risk=lose-ssh --auth-key="$1"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"bootstrap": "brew install gh jq cirruslabs/cli/tart cirruslabs/cli/sshpass hashicorp/tap/packer && packer init darwin",
|
||||
"login": "token=$(gh auth token); username=$(gh api user --jq .login); echo \"Login as $username...\"; echo \"$token\" | tart login ghcr.io --username \"$username\" --password-stdin; echo \"$token\" | docker login ghcr.io --username \"$username\" --password-stdin",
|
||||
"fetch:image-name": "echo ghcr.io/oven-sh/bun-vm",
|
||||
"fetch:darwin-version": "echo 1",
|
||||
"fetch:macos-version": "sw_vers -productVersion | cut -d. -f1",
|
||||
"fetch:script-version": "cat ../scripts/bootstrap.sh | grep 'v=' | sed 's/v=\"//;s/\"//' | head -n 1",
|
||||
"build:darwin-aarch64-vanilla": "packer build '-only=*.bun-darwin-aarch64-vanilla' -var release=$(bun fetch:macos-version) darwin/",
|
||||
"build:darwin-aarch64-vanilla-15": "packer build '-only=*.bun-darwin-aarch64-vanilla' -var release=15 darwin/",
|
||||
"build:darwin-aarch64-vanilla-14": "packer build '-only=*.bun-darwin-aarch64-vanilla' -var release=14 darwin/",
|
||||
"build:darwin-aarch64-vanilla-13": "packer build '-only=*.bun-darwin-aarch64-vanilla' -var release=13 darwin/",
|
||||
"build:darwin-aarch64": "packer build '-only=*.bun-darwin-aarch64' -var release=$(bun fetch:macos-version) darwin/",
|
||||
"build:darwin-aarch64-15": "packer build '-only=*.bun-darwin-aarch64' -var release=15 darwin/",
|
||||
"build:darwin-aarch64-14": "packer build '-only=*.bun-darwin-aarch64' -var release=14 darwin/",
|
||||
"build:darwin-aarch64-13": "packer build '-only=*.bun-darwin-aarch64' -var release=13 darwin/",
|
||||
"publish:darwin-aarch64-vanilla": "image=$(tart list --format json | jq -r \".[] | select(.Name | test(\\\"^bun-darwin-aarch64-vanilla-.*-$(bun fetch:macos-version)$\\\")) | .Name\" | head -n 1 | sed 's/bun-//'); tart push \"bun-$image\" \"ghcr.io/oven-sh/bun-vm:$image-v$(bun fetch:darwin-version)\"",
|
||||
"publish:darwin-aarch64-vanilla-15": "tart push bun-darwin-aarch64-vanilla-sequoia-15 \"$(bun fetch:image-name):darwin-aarch64-vanilla-sequoia-15-v$(bun fetch:darwin-version)\"",
|
||||
"publish:darwin-aarch64-vanilla-14": "tart push bun-darwin-aarch64-vanilla-sonoma-14 \"$(bun fetch:image-name):darwin-aarch64-vanilla-sonoma-14-v$(bun fetch:darwin-version)\"",
|
||||
"publish:darwin-aarch64-vanilla-13": "tart push bun-darwin-aarch64-vanilla-ventura-13 \"$(bun fetch:image-name):darwin-aarch64-vanilla-ventura-13-v$(bun fetch:darwin-version)\"",
|
||||
"publish:darwin-aarch64": "image=$(tart list --format json | jq -r \".[] | select(.Name | test(\\\"^bun-darwin-aarch64-.*-$(bun fetch:macos-version)$\\\")) | .Name\" | head -n 1 | sed 's/bun-//'); tart push \"bun-$image\" \"ghcr.io/oven-sh/bun-vm:$image-v$(bun fetch:script-version)\"",
|
||||
"publish:darwin-aarch64-15": "tart push bun-darwin-aarch64-sequoia-15 \"$(bun fetch:image-name):darwin-aarch64-sequoia-15-v$(bun fetch:script-version)\"",
|
||||
"publish:darwin-aarch64-14": "tart push bun-darwin-aarch64-sonoma-14 \"$(bun fetch:image-name):darwin-aarch64-sonoma-14-v$(bun fetch:script-version)\"",
|
||||
"publish:darwin-aarch64-13": "tart push bun-darwin-aarch64-ventura-13 \"$(bun fetch:image-name):darwin-aarch64-ventura-13-v$(bun fetch:script-version)\""
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
if(DEBUG)
|
||||
set(bun bun-debug)
|
||||
elseif(ENABLE_SMOL)
|
||||
set(bun bun-smol-profile)
|
||||
set(bunStrip bun-smol)
|
||||
# elseif(ENABLE_SMOL)
|
||||
# set(bun bun-smol-profile)
|
||||
# set(bunStrip bun-smol)
|
||||
elseif(ENABLE_VALGRIND)
|
||||
set(bun bun-valgrind)
|
||||
elseif(ENABLE_ASSERTIONS)
|
||||
@@ -622,6 +622,7 @@ file(GLOB BUN_CXX_SOURCES ${CONFIGURE_DEPENDS}
|
||||
${CWD}/src/bun.js/bindings/sqlite/*.cpp
|
||||
${CWD}/src/bun.js/bindings/webcrypto/*.cpp
|
||||
${CWD}/src/bun.js/bindings/webcrypto/*/*.cpp
|
||||
${CWD}/src/bun.js/bindings/node/crypto/*.cpp
|
||||
${CWD}/src/bun.js/bindings/v8/*.cpp
|
||||
${CWD}/src/bun.js/bindings/v8/shim/*.cpp
|
||||
${CWD}/src/bake/*.cpp
|
||||
@@ -759,6 +760,7 @@ target_include_directories(${bun} PRIVATE
|
||||
${CWD}/src/bun.js/bindings
|
||||
${CWD}/src/bun.js/bindings/webcore
|
||||
${CWD}/src/bun.js/bindings/webcrypto
|
||||
${CWD}/src/bun.js/bindings/node/crypto
|
||||
${CWD}/src/bun.js/bindings/sqlite
|
||||
${CWD}/src/bun.js/bindings/v8
|
||||
${CWD}/src/bun.js/modules
|
||||
|
||||
@@ -4,7 +4,7 @@ register_repository(
|
||||
REPOSITORY
|
||||
cloudflare/lol-html
|
||||
COMMIT
|
||||
4f8becea13a0021c8b71abd2dcc5899384973b66
|
||||
67f1d4ffd6b74db7e053fb129dcce620193c180d
|
||||
)
|
||||
|
||||
set(LOLHTML_CWD ${VENDOR_PATH}/lolhtml/c-api)
|
||||
|
||||
@@ -2,7 +2,7 @@ option(WEBKIT_VERSION "The version of WebKit to use")
|
||||
option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of downloading")
|
||||
|
||||
if(NOT WEBKIT_VERSION)
|
||||
set(WEBKIT_VERSION c333de54223425d7148603d63dd3f6152d0bc348)
|
||||
set(WEBKIT_VERSION 2ffd4be9f9513c30333d9e23c175cab4760584e3)
|
||||
endif()
|
||||
|
||||
string(SUBSTRING ${WEBKIT_VERSION} 0 16 WEBKIT_VERSION_PREFIX)
|
||||
|
||||
@@ -20,7 +20,7 @@ else()
|
||||
unsupported(CMAKE_SYSTEM_NAME)
|
||||
endif()
|
||||
|
||||
set(ZIG_COMMIT "02c57c7ee3b8fde7528c74dd06490834d2d6fae9")
|
||||
set(ZIG_COMMIT "cd1995944508e4c946deb75bd70947d302e0db37")
|
||||
optionx(ZIG_TARGET STRING "The zig target to use" DEFAULT ${DEFAULT_ZIG_TARGET})
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
|
||||
@@ -204,7 +204,7 @@ To customize the TLS validation, use the `checkServerIdentity` option in `tls`
|
||||
await fetch("https://example.com", {
|
||||
tls: {
|
||||
checkServerIdentity: (hostname, peerCertificate) => {
|
||||
// Return an error if the certificate is invalid
|
||||
// Return an Error if the certificate is invalid
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -418,7 +418,7 @@ export interface Post {
|
||||
|
||||
### Routing performance
|
||||
|
||||
`Bun.serve()`'s router builds on top uWebSocket's [tree-based approach](https://github.com/oven-sh/bun/blob/0d1a00fa0f7830f8ecd99c027fce8096c9d459b6/packages/bun-uws/src/HttpRouter.h#L57-L64) to add [SIMD-accelerated route parameter decoding](https://github.com/oven-sh/bun/blob/jarred/optional-fetch/src/bun.js/bindings/decodeURIComponentSIMD.cpp#L21-L271) and [JavaScriptCore structure caching](https://github.com/oven-sh/bun/blob/jarred/optional-fetch/src/bun.js/bindings/ServerRouteList.cpp#L100-L101) to push the performance limits of what modern hardware allows.
|
||||
`Bun.serve()`'s router builds on top uWebSocket's [tree-based approach](https://github.com/oven-sh/bun/blob/0d1a00fa0f7830f8ecd99c027fce8096c9d459b6/packages/bun-uws/src/HttpRouter.h#L57-L64) to add [SIMD-accelerated route parameter decoding](https://github.com/oven-sh/bun/blob/main/src/bun.js/bindings/decodeURIComponentSIMD.cpp#L21-L271) and [JavaScriptCore structure caching](https://github.com/oven-sh/bun/blob/main/src/bun.js/bindings/ServerRouteList.cpp#L100-L101) to push the performance limits of what modern hardware allows.
|
||||
|
||||
### `fetch` request handler
|
||||
|
||||
|
||||
@@ -12,5 +12,3 @@ Alternatively, use `process.dlopen`:
|
||||
let mod = { exports: {} };
|
||||
process.dlopen(mod, "./my-node-module.node");
|
||||
```
|
||||
|
||||
Bun polyfills the [`detect-libc`](https://npmjs.com/package/detect-libc) package, which is used by many Node-API modules to detect which `.node` binding to `require`.
|
||||
|
||||
@@ -77,6 +77,16 @@ console.log(text); // "const input = "hello world".repeat(400); ..."
|
||||
|
||||
---
|
||||
|
||||
- `ReadableStream`
|
||||
- Use a readable stream as input.
|
||||
|
||||
---
|
||||
|
||||
- `Blob`
|
||||
- Use a blob as input.
|
||||
|
||||
---
|
||||
|
||||
- `number`
|
||||
- Read from the file with a given file descriptor.
|
||||
|
||||
@@ -129,13 +139,13 @@ Configure the output stream by passing one of the following values to `stdout/st
|
||||
|
||||
---
|
||||
|
||||
- `Bun.file()`
|
||||
- Write to the specified file.
|
||||
- `"ignore"`
|
||||
- Discard the output.
|
||||
|
||||
---
|
||||
|
||||
- `null`
|
||||
- Write to `/dev/null`.
|
||||
- `Bun.file()`
|
||||
- Write to the specified file.
|
||||
|
||||
---
|
||||
|
||||
@@ -174,7 +184,8 @@ const proc = Bun.spawn(["bun", "--version"]);
|
||||
proc.kill();
|
||||
proc.killed; // true
|
||||
|
||||
proc.kill(); // specify an exit code
|
||||
proc.kill(15); // specify a signal code
|
||||
proc.kill("SIGTERM"); // specify a signal name
|
||||
```
|
||||
|
||||
The parent `bun` process will not terminate until all child processes have exited. Use `proc.unref()` to detach the child process from the parent.
|
||||
@@ -184,6 +195,64 @@ const proc = Bun.spawn(["bun", "--version"]);
|
||||
proc.unref();
|
||||
```
|
||||
|
||||
## Resource usage
|
||||
|
||||
You can get information about the process's resource usage after it has exited:
|
||||
|
||||
```ts
|
||||
const proc = Bun.spawn(["bun", "--version"]);
|
||||
await proc.exited;
|
||||
|
||||
const usage = proc.resourceUsage();
|
||||
console.log(`Max memory used: ${usage.maxRSS} bytes`);
|
||||
console.log(`CPU time (user): ${usage.cpuTime.user} µs`);
|
||||
console.log(`CPU time (system): ${usage.cpuTime.system} µs`);
|
||||
```
|
||||
|
||||
## Using AbortSignal
|
||||
|
||||
You can abort a subprocess using an `AbortSignal`:
|
||||
|
||||
```ts
|
||||
const controller = new AbortController();
|
||||
const { signal } = controller;
|
||||
|
||||
const proc = Bun.spawn({
|
||||
cmd: ["sleep", "100"],
|
||||
signal,
|
||||
});
|
||||
|
||||
// Later, to abort the process:
|
||||
controller.abort();
|
||||
```
|
||||
|
||||
## Using timeout and killSignal
|
||||
|
||||
You can set a timeout for a subprocess to automatically terminate after a specific duration:
|
||||
|
||||
```ts
|
||||
// Kill the process after 5 seconds
|
||||
const proc = Bun.spawn({
|
||||
cmd: ["sleep", "10"],
|
||||
timeout: 5000, // 5 seconds in milliseconds
|
||||
});
|
||||
|
||||
await proc.exited; // Will resolve after 5 seconds
|
||||
```
|
||||
|
||||
By default, timed-out processes are killed with the `SIGTERM` signal. You can specify a different signal with the `killSignal` option:
|
||||
|
||||
```ts
|
||||
// Kill the process with SIGKILL after 5 seconds
|
||||
const proc = Bun.spawn({
|
||||
cmd: ["sleep", "10"],
|
||||
timeout: 5000,
|
||||
killSignal: "SIGKILL", // Can be string name or signal number
|
||||
});
|
||||
```
|
||||
|
||||
The `killSignal` option also controls which signal is sent when an AbortSignal is aborted.
|
||||
|
||||
## Inter-process communication (IPC)
|
||||
|
||||
Bun supports direct inter-process communication channel between two `bun` processes. To receive messages from a spawned Bun subprocess, specify an `ipc` handler.
|
||||
@@ -233,11 +302,17 @@ process.send("Hello from child as string");
|
||||
process.send({ message: "Hello from child as object" });
|
||||
```
|
||||
|
||||
The `ipcMode` option controls the underlying communication format between the two processes:
|
||||
The `serialization` option controls the underlying communication format between the two processes:
|
||||
|
||||
- `advanced`: (default) Messages are serialized using the JSC `serialize` API, which supports cloning [everything `structuredClone` supports](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm). This does not support transferring ownership of objects.
|
||||
- `json`: Messages are serialized using `JSON.stringify` and `JSON.parse`, which does not support as many object types as `advanced` does.
|
||||
|
||||
To disconnect the IPC channel from the parent process, call:
|
||||
|
||||
```ts
|
||||
childProc.disconnect();
|
||||
```
|
||||
|
||||
### IPC between Bun & Node.js
|
||||
|
||||
To use IPC between a `bun` process and a Node.js process, set `serialization: "json"` in `Bun.spawn`. This is because Node.js and Bun use different JavaScript engines with different object serialization formats.
|
||||
@@ -310,7 +385,7 @@ spawnSync echo hi 1.47 ms/iter (1.14 ms … 2.64 ms) 1.57 ms 2.37 ms
|
||||
|
||||
## Reference
|
||||
|
||||
A simple reference of the Spawn API and types are shown below. The real types have complex generics to strongly type the `Subprocess` streams with the options passed to `Bun.spawn` and `Bun.spawnSync`. For full details, find these types as defined [bun.d.ts](https://github.com/oven-sh/bun/blob/main/packages/bun-types/bun.d.ts).
|
||||
A reference of the Spawn API and types are shown below. The real types have complex generics to strongly type the `Subprocess` streams with the options passed to `Bun.spawn` and `Bun.spawnSync`. For full details, find these types as defined [bun.d.ts](https://github.com/oven-sh/bun/blob/main/packages/bun-types/bun.d.ts).
|
||||
|
||||
```ts
|
||||
interface Bun {
|
||||
@@ -329,16 +404,25 @@ interface Bun {
|
||||
namespace SpawnOptions {
|
||||
interface OptionsObject {
|
||||
cwd?: string;
|
||||
env?: Record<string, string>;
|
||||
stdin?: SpawnOptions.Readable;
|
||||
stdout?: SpawnOptions.Writable;
|
||||
stderr?: SpawnOptions.Writable;
|
||||
onExit?: (
|
||||
proc: Subprocess,
|
||||
env?: Record<string, string | undefined>;
|
||||
stdio?: [Writable, Readable, Readable];
|
||||
stdin?: Writable;
|
||||
stdout?: Readable;
|
||||
stderr?: Readable;
|
||||
onExit?(
|
||||
subprocess: Subprocess,
|
||||
exitCode: number | null,
|
||||
signalCode: string | null,
|
||||
error: Error | null,
|
||||
) => void;
|
||||
signalCode: number | null,
|
||||
error?: ErrorLike,
|
||||
): void | Promise<void>;
|
||||
ipc?(message: any, subprocess: Subprocess): void;
|
||||
serialization?: "json" | "advanced";
|
||||
windowsHide?: boolean;
|
||||
windowsVerbatimArguments?: boolean;
|
||||
argv0?: string;
|
||||
signal?: AbortSignal;
|
||||
timeout?: number;
|
||||
killSignal?: string | number;
|
||||
}
|
||||
|
||||
type Readable =
|
||||
@@ -366,39 +450,62 @@ namespace SpawnOptions {
|
||||
| Request;
|
||||
}
|
||||
|
||||
interface Subprocess<Stdin, Stdout, Stderr> {
|
||||
interface Subprocess extends AsyncDisposable {
|
||||
readonly stdin: FileSink | number | undefined;
|
||||
readonly stdout: ReadableStream<Uint8Array> | number | undefined;
|
||||
readonly stderr: ReadableStream<Uint8Array> | number | undefined;
|
||||
readonly readable: ReadableStream<Uint8Array> | number | undefined;
|
||||
readonly pid: number;
|
||||
// the exact stream types here are derived from the generic parameters
|
||||
readonly stdin: number | ReadableStream | FileSink | undefined;
|
||||
readonly stdout: number | ReadableStream | undefined;
|
||||
readonly stderr: number | ReadableStream | undefined;
|
||||
|
||||
readonly exited: Promise<number>;
|
||||
|
||||
readonly exitCode: number | undefined;
|
||||
readonly signalCode: Signal | null;
|
||||
readonly exitCode: number | null;
|
||||
readonly signalCode: NodeJS.Signals | null;
|
||||
readonly killed: boolean;
|
||||
|
||||
kill(exitCode?: number | NodeJS.Signals): void;
|
||||
ref(): void;
|
||||
unref(): void;
|
||||
kill(code?: number): void;
|
||||
|
||||
send(message: any): void;
|
||||
disconnect(): void;
|
||||
resourceUsage(): ResourceUsage | undefined;
|
||||
}
|
||||
|
||||
interface SyncSubprocess<Stdout, Stderr> {
|
||||
readonly pid: number;
|
||||
readonly success: boolean;
|
||||
// the exact buffer types here are derived from the generic parameters
|
||||
readonly stdout: Buffer | undefined;
|
||||
readonly stderr: Buffer | undefined;
|
||||
interface SyncSubprocess {
|
||||
stdout: Buffer | undefined;
|
||||
stderr: Buffer | undefined;
|
||||
exitCode: number;
|
||||
success: boolean;
|
||||
resourceUsage: ResourceUsage;
|
||||
signalCode?: string;
|
||||
exitedDueToTimeout?: true;
|
||||
pid: number;
|
||||
}
|
||||
|
||||
type ReadableSubprocess = Subprocess<any, "pipe", "pipe">;
|
||||
type WritableSubprocess = Subprocess<"pipe", any, any>;
|
||||
type PipedSubprocess = Subprocess<"pipe", "pipe", "pipe">;
|
||||
type NullSubprocess = Subprocess<null, null, null>;
|
||||
interface ResourceUsage {
|
||||
contextSwitches: {
|
||||
voluntary: number;
|
||||
involuntary: number;
|
||||
};
|
||||
|
||||
type ReadableSyncSubprocess = SyncSubprocess<"pipe", "pipe">;
|
||||
type NullSyncSubprocess = SyncSubprocess<null, null>;
|
||||
cpuTime: {
|
||||
user: number;
|
||||
system: number;
|
||||
total: number;
|
||||
};
|
||||
maxRSS: number;
|
||||
|
||||
messages: {
|
||||
sent: number;
|
||||
received: number;
|
||||
};
|
||||
ops: {
|
||||
in: number;
|
||||
out: number;
|
||||
};
|
||||
shmSize: number;
|
||||
signalCount: number;
|
||||
swapCount: number;
|
||||
}
|
||||
|
||||
type Signal =
|
||||
| "SIGABRT"
|
||||
|
||||
@@ -87,7 +87,7 @@ await sql`INSERT INTO users ${sql(users)}`;
|
||||
|
||||
### Picking columns to insert
|
||||
|
||||
You can use `sql(object, Array<string>)` to pick which columns to insert. Each of the columns must be defined on the object.
|
||||
You can use `sql(object, ...string)` to pick which columns to insert. Each of the columns must be defined on the object.
|
||||
|
||||
```ts
|
||||
const user = {
|
||||
@@ -96,7 +96,7 @@ const user = {
|
||||
age: 25,
|
||||
};
|
||||
|
||||
await sql`INSERT INTO users ${sql(user, ["name", "email"])}`;
|
||||
await sql`INSERT INTO users ${sql(user, "name", "email")}`;
|
||||
// Only inserts name and email columns, ignoring other fields
|
||||
```
|
||||
|
||||
@@ -165,6 +165,31 @@ await sql`
|
||||
`;
|
||||
```
|
||||
|
||||
### Dynamic columns in updates
|
||||
|
||||
You can use `sql(object, ...string)` to pick which columns to update. Each of the columns must be defined on the object. If the columns are not informed all keys will be used to update the row.
|
||||
|
||||
```ts
|
||||
await sql`UPDATE users SET ${sql(user, "name", "email")} WHERE id = ${user.id}`;
|
||||
// uses all keys from the object to update the row
|
||||
await sql`UPDATE users SET ${sql(user)} WHERE id = ${user.id}`;
|
||||
```
|
||||
|
||||
### Dynamic values and `where in`
|
||||
|
||||
Value lists can also be created dynamically, making where in queries simple too. Optionally you can pass a array of objects and inform what key to use to create the list.
|
||||
|
||||
```ts
|
||||
await sql`SELECT * FROM users WHERE id IN ${sql([1, 2, 3])}`;
|
||||
|
||||
const users = [
|
||||
{ id: 1, name: "Alice" },
|
||||
{ id: 2, name: "Bob" },
|
||||
{ id: 3, name: "Charlie" },
|
||||
];
|
||||
await sql`SELECT * FROM users WHERE id IN ${sql(users, "id")}`;
|
||||
```
|
||||
|
||||
## `sql``.simple()`
|
||||
|
||||
The PostgreSQL wire protocol supports two types of queries: "simple" and "extended". Simple queries can contain multiple statements but don't support parameters, while extended queries (the default) support parameters but only allow one statement.
|
||||
|
||||
@@ -11,7 +11,7 @@ Bun.listen({
|
||||
socket: {
|
||||
data(socket, data) {}, // message received from client
|
||||
open(socket) {}, // socket opened
|
||||
close(socket) {}, // socket closed
|
||||
close(socket, error) {}, // socket closed
|
||||
drain(socket) {}, // socket ready for more data
|
||||
error(socket, error) {}, // error handler
|
||||
},
|
||||
@@ -30,7 +30,7 @@ Bun.listen({
|
||||
open(socket) {},
|
||||
data(socket, data) {},
|
||||
drain(socket) {},
|
||||
close(socket) {},
|
||||
close(socket, error) {},
|
||||
error(socket, error) {},
|
||||
},
|
||||
});
|
||||
@@ -122,7 +122,7 @@ const socket = await Bun.connect({
|
||||
socket: {
|
||||
data(socket, data) {},
|
||||
open(socket) {},
|
||||
close(socket) {},
|
||||
close(socket, error) {},
|
||||
drain(socket) {},
|
||||
error(socket, error) {},
|
||||
|
||||
|
||||
1028
docs/bundler/css.md
Normal file
1028
docs/bundler/css.md
Normal file
File diff suppressed because it is too large
Load Diff
145
docs/bundler/css_modules.md
Normal file
145
docs/bundler/css_modules.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# CSS Modules
|
||||
|
||||
Bun's bundler also supports bundling [CSS modules](https://css-tricks.com/css-modules-part-1-need/) in addition to [regular CSS](/docs/bundler/css) with support for the following features:
|
||||
|
||||
- Automatically detecting CSS module files (`.module.css`) with zero configuration
|
||||
- Composition (`composes` property)
|
||||
- Importing CSS modules into JSX/TSX
|
||||
- Warnings/errors for invalid usages of CSS modules
|
||||
|
||||
A CSS module is a CSS file (with the `.module.css` extension) where are all class names and animations are scoped to the file. This helps you avoid class name collisions as CSS declarations are globally scoped by default.
|
||||
|
||||
Under the hood, Bun's bundler transforms locally scoped class names into unique identifiers.
|
||||
|
||||
## Getting started
|
||||
|
||||
Create a CSS file with the `.module.css` extension:
|
||||
|
||||
```css
|
||||
/* styles.module.css */
|
||||
.button {
|
||||
color: red;
|
||||
}
|
||||
|
||||
/* other-styles.module.css */
|
||||
.button {
|
||||
color: blue;
|
||||
}
|
||||
```
|
||||
|
||||
You can then import this file, for example into a TSX file:
|
||||
|
||||
```tsx
|
||||
import styles from "./styles.module.css";
|
||||
import otherStyles from "./other-styles.module.css";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<>
|
||||
<button className={styles.button}>Red button!</button>
|
||||
<button className={otherStyles.button}>Blue button!</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
The `styles` object from importing the CSS module file will be an object with all class names as keys and
|
||||
their unique identifiers as values:
|
||||
|
||||
```tsx
|
||||
import styles from "./styles.module.css";
|
||||
import otherStyles from "./other-styles.module.css";
|
||||
|
||||
console.log(styles);
|
||||
console.log(otherStyles);
|
||||
```
|
||||
|
||||
This will output:
|
||||
|
||||
```ts
|
||||
{
|
||||
button: "button_123";
|
||||
}
|
||||
|
||||
{
|
||||
button: "button_456";
|
||||
}
|
||||
```
|
||||
|
||||
As you can see, the class names are unique to each file, avoiding any collisions!
|
||||
|
||||
### Composition
|
||||
|
||||
CSS modules allow you to _compose_ class selectors together. This lets you reuse style rules across multiple classes.
|
||||
|
||||
For example:
|
||||
|
||||
```css
|
||||
/* styles.module.css */
|
||||
.button {
|
||||
composes: background;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.background {
|
||||
background-color: blue;
|
||||
}
|
||||
```
|
||||
|
||||
Would be the same as writing:
|
||||
|
||||
```css
|
||||
.button {
|
||||
background-color: blue;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.background {
|
||||
background-color: blue;
|
||||
}
|
||||
```
|
||||
|
||||
{% callout %}
|
||||
There are a couple rules to keep in mind when using `composes`:
|
||||
|
||||
- A `composes` property must come before any regular CSS properties or declarations
|
||||
- You can only use `composes` on a **simple selector with a single class name**:
|
||||
|
||||
```css
|
||||
#button {
|
||||
/* Invalid! `#button` is not a class selector */
|
||||
composes: background;
|
||||
}
|
||||
|
||||
.button,
|
||||
.button-secondary {
|
||||
/* Invalid! `.button, .button-secondary` is not a simple selector */
|
||||
composes: background;
|
||||
}
|
||||
```
|
||||
|
||||
{% /callout %}
|
||||
|
||||
### Composing from a separate CSS module file
|
||||
|
||||
You can also compose from a separate CSS module file:
|
||||
|
||||
```css
|
||||
/* background.module.css */
|
||||
.background {
|
||||
background-color: blue;
|
||||
}
|
||||
|
||||
/* styles.module.css */
|
||||
.button {
|
||||
composes: background from "./background.module.css";
|
||||
color: red;
|
||||
}
|
||||
```
|
||||
|
||||
{% callout %}
|
||||
When composing classes from separate files, be sure that they do not contain the same properties.
|
||||
|
||||
The CSS module spec says that composing classes from separate files with conflicting properties is
|
||||
undefined behavior, meaning that the output may differ and be unreliable.
|
||||
{% /callout %}
|
||||
@@ -75,14 +75,16 @@ bun build --compile --target=bun-darwin-x64 ./path/to/my/app.ts --outfile myapp
|
||||
|
||||
The order of the `--target` flag does not matter, as long as they're delimited by a `-`.
|
||||
|
||||
| --target | Operating System | Architecture | Modern | Baseline |
|
||||
| --------------------- | ---------------- | ------------ | ------ | -------- |
|
||||
| bun-linux-x64 | Linux | x64 | ✅ | ✅ |
|
||||
| bun-linux-arm64 | Linux | arm64 | ✅ | N/A |
|
||||
| bun-windows-x64 | Windows | x64 | ✅ | ✅ |
|
||||
| ~~bun-windows-arm64~~ | Windows | arm64 | ❌ | ❌ |
|
||||
| bun-darwin-x64 | macOS | x64 | ✅ | ✅ |
|
||||
| bun-darwin-arm64 | macOS | arm64 | ✅ | N/A |
|
||||
| --target | Operating System | Architecture | Modern | Baseline | Libc |
|
||||
| --------------------- | ---------------- | ------------ | ------ | -------- | ----- |
|
||||
| bun-linux-x64 | Linux | x64 | ✅ | ✅ | glibc |
|
||||
| bun-linux-arm64 | Linux | arm64 | ✅ | N/A | glibc |
|
||||
| bun-windows-x64 | Windows | x64 | ✅ | ✅ | - |
|
||||
| ~~bun-windows-arm64~~ | Windows | arm64 | ❌ | ❌ | - |
|
||||
| bun-darwin-x64 | macOS | x64 | ✅ | ✅ | - |
|
||||
| bun-darwin-arm64 | macOS | arm64 | ✅ | N/A | - |
|
||||
| bun-linux-x64-musl | Linux | x64 | ✅ | ✅ | musl |
|
||||
| bun-linux-arm64-musl | Linux | arm64 | ✅ | N/A | musl |
|
||||
|
||||
On x64 platforms, Bun uses SIMD optimizations which require a modern CPU supporting AVX2 instructions. The `-baseline` build of Bun is for older CPUs that don't support these optimizations. Normally, when you install Bun we automatically detect which version to use but this can be harder to do when cross-compiling since you might not know the target CPU. You usually don't need to worry about it on Darwin x64, but it is relevant for Windows x64 and Linux x64. If you or your users see `"Illegal instruction"` errors, you might need to use the baseline version.
|
||||
|
||||
@@ -294,6 +296,55 @@ These flags currently cannot be used when cross-compiling because they depend on
|
||||
|
||||
{% /callout %}
|
||||
|
||||
## Code signing on macOS
|
||||
|
||||
To codesign a standalone executable on macOS (which fixes Gatekeeper warnings), use the `codesign` command.
|
||||
|
||||
```sh
|
||||
$ codesign --deep --force -vvvv --sign "XXXXXXXXXX" ./myapp
|
||||
```
|
||||
|
||||
We recommend including an `entitlements.plist` file with JIT permissions.
|
||||
|
||||
```xml#entitlements.plist
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-executable-page-protection</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
To codesign with JIT support, pass the `--entitlements` flag to `codesign`.
|
||||
|
||||
```sh
|
||||
$ codesign --deep --force -vvvv --sign "XXXXXXXXXX" --entitlements entitlements.plist ./myapp
|
||||
```
|
||||
|
||||
After codesigning, verify the executable:
|
||||
|
||||
```sh
|
||||
$ codesign -vvv --verify ./myapp
|
||||
./myapp: valid on disk
|
||||
./myapp: satisfies its Designated Requirement
|
||||
```
|
||||
|
||||
{% callout %}
|
||||
|
||||
Codesign support requires Bun v1.2.4 or newer.
|
||||
|
||||
{% /callout %}
|
||||
|
||||
## Unsupported CLI arguments
|
||||
|
||||
Currently, the `--compile` flag can only accept a single entrypoint at a time and does not support the following flags:
|
||||
|
||||
@@ -309,5 +309,4 @@ This works similarly to how [`Bun.build` processes HTML files](/docs/bundler/htm
|
||||
|
||||
## This is a work in progress
|
||||
|
||||
- ~Client-side hot reloading isn't wired up yet. It will be in the future.~ New in Bun v1.2.3
|
||||
- This doesn't support `bun build` yet. It also will in the future.
|
||||
|
||||
@@ -1,266 +1,234 @@
|
||||
{% callout type="warning" %}
|
||||
**Work in Progress** — Bun's HMR API is currently in early development stages. Of the APIs described in this document, only `import.meta.hot.dispose()` and `import.meta.hot.data` are fully implemented. The other methods either log warnings, throw errors, or do nothing. The implementation does perform recursive module re-evaluation (rather than full page reloads), but granular module acceptance boundaries through `import.meta.hot.accept()` are not yet functional. The current implementation is useful for state persistence between reloads, but more sophisticated HMR patterns are still in development.
|
||||
{% /callout %}
|
||||
Hot Module Replacement (HMR) allows you to update modules in a running
|
||||
application without needing a full page reload. This preserves the application
|
||||
state and improves the development experience.
|
||||
|
||||
Hot Module Replacement (HMR) allows you to update modules in a running application without needing a full page reload. This preserves the application state and improves the development experience.
|
||||
HMR is enabled by default when using Bun's full-stack development server.
|
||||
|
||||
Bun's bundler includes a client-side HMR implementation modeled after [Vite's `import.meta.hot` API](https://vitejs.dev/guide/api-hmr.html), making it familiar to developers who have used Vite or similar bundlers. The `import.meta.hot` API described in this document is currently only supported for frontend hot reloading, but we plan to extend it to support server-side hot reloading in future releases.
|
||||
## `import.meta.hot` API Reference
|
||||
|
||||
## Overview
|
||||
Bun implements a client-side HMR API modeled after [Vite's `import.meta.hot` API](https://vitejs.dev/guide/api-hmr.html). It can be checked for with `if (import.meta.hot)`, tree-shaking it in production
|
||||
|
||||
When you use Bun's bundler in development mode, it automatically sets up a WebSocket connection between the browser and the bundler server. When you change a file, Bun:
|
||||
|
||||
1. Rebuilds only the changed modules
|
||||
2. Sends the updated modules to the browser via WebSocket
|
||||
3. Recursively re-evaluates the module tree from the changed modules
|
||||
4. Calls `dispose` handlers to clean up resources before module replacement
|
||||
5. Preserves state via the `hot.data` object between module evaluations
|
||||
|
||||
## Usage
|
||||
|
||||
There are two ways to get started with `import.meta.hot` in Bun:
|
||||
|
||||
### Frontend Hot Reloading
|
||||
|
||||
Hot Reloading is enabled by default when you run `bun ./index.html`.
|
||||
|
||||
From there, you can use `import.meta.hot.data` to persist state between module evaluations. The `import.meta.hot` object is referentially equal between module evaluations, so you can use it to persist state across module updates.
|
||||
|
||||
To disable HMR when using HTML entrypoints, set `NODE_ENV=production` as an environment variable.
|
||||
|
||||
### Full-Stack Hot Reloading
|
||||
|
||||
Frontend and server-side hot reloading are independent features that can be used together.
|
||||
|
||||
To enable frontend hot reloading, pass `development: true` to `Bun.serve()`.
|
||||
|
||||
```ts#server.ts
|
||||
import { serve } from "bun";
|
||||
import homepage from "./index.html";
|
||||
|
||||
serve({
|
||||
// Enable frontend hot reloading
|
||||
development: true,
|
||||
|
||||
routes: {
|
||||
// Setup frontend routes
|
||||
"/": homepage,
|
||||
|
||||
// Setup API routes
|
||||
"/api/v1/users": {
|
||||
async GET() {
|
||||
return Response.json([{ id: 1, name: "John Doe" }]);
|
||||
},
|
||||
|
||||
async POST(req) {
|
||||
const user = await req.json();
|
||||
return Response.json({ success: true, user });
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
To enable server-side hot reloading, pass `--hot` to the Bun CLI:
|
||||
|
||||
```sh
|
||||
$ bun --hot ./server.ts
|
||||
```
|
||||
|
||||
`--hot` and `development: true` can both be used at the same time.
|
||||
|
||||
## API Reference
|
||||
|
||||
### Compatibility Table
|
||||
|
||||
| | Method | Notes |
|
||||
| --- | ------------------ | --------------------------------------------------------------------------------- |
|
||||
| 🚧 | `hot.accept()` | API exists but only logs "TODO" warning; accepts no module boundaries |
|
||||
| ✅ | `hot.dispose()` | Fully functional; registers callbacks that are executed before module replacement |
|
||||
| ✅ | `hot.data` | Fully functional; persists data between module evaluations |
|
||||
| ❌ | `hot.decline()` | Exists as empty function for compatibility with Vite APIs |
|
||||
| 🚧 | `hot.invalidate()` | Exists but throws "TODO: implement ImportMetaHot.invalidate" error |
|
||||
| 🚧 | `hot.on()` | Exists but throws "TODO: implement ImportMetaHot.on" error |
|
||||
| 🚧 | `hot.off()` | Exists but throws "TODO: implement ImportMetaHot.off" error |
|
||||
| 🚧 | `hot.send()` | Exists but throws "TODO: implement ImportMetaHot.send" error |
|
||||
| 🚧 | `hot.prune()` | Exists but throws "TODO: implement ImportMetaHot.prune" error |
|
||||
|
||||
**Currently Functional APIs:**
|
||||
|
||||
- `hot.dispose()`: Successfully registers cleanup callbacks that run before module replacement
|
||||
- `hot.data`: Successfully maintains state between module evaluations
|
||||
|
||||
### `hot.accept()`
|
||||
|
||||
{% callout %}
|
||||
❌ **Not implemented** — Currently, `hot.accept()` logs a warning message and does not execute the provided callbacks. The module tree will still be recursively re-evaluated, but without respecting the specified acceptance boundaries.
|
||||
{% /callout %}
|
||||
|
||||
Accepts hot updates for this module or its dependencies.
|
||||
|
||||
```ts#hmr-accept.ts
|
||||
// Accept self (simplest form)
|
||||
import.meta.hot.accept();
|
||||
// This logs: "TODO: implement ImportMetaHot.accept" and does nothing
|
||||
|
||||
// Accept self with a callback to handle updates
|
||||
import.meta.hot.accept(newModule => {
|
||||
// This callback will not be called in the current implementation
|
||||
console.log("Updated module received", newModule);
|
||||
});
|
||||
|
||||
// Accept updates to specific dependencies
|
||||
import.meta.hot.accept(["./dep1.js", "./dep2.js"], ([dep1, dep2]) => {
|
||||
// This callback will not be called in the current implementation
|
||||
console.log("Dependencies updated");
|
||||
});
|
||||
```
|
||||
|
||||
In future releases, when a module is accepted, Bun will properly respect the acceptance boundaries, preventing propagation of updates beyond accepted modules and executing the provided callbacks. This will enable more granular control over how module updates are handled.
|
||||
|
||||
### `hot.dispose()`
|
||||
|
||||
{% callout type="success" %}
|
||||
✅ **Fully implemented** — `hot.dispose()` works as expected, allowing you to register callbacks that will be executed before the module is replaced.
|
||||
{% /callout %}
|
||||
|
||||
Registers a callback that will be called when the module is about to be replaced.
|
||||
|
||||
```ts#dispose.ts
|
||||
import.meta.hot.dispose(data => {
|
||||
// Clean up resources or save state
|
||||
data.state = {
|
||||
/* state to preserve */
|
||||
};
|
||||
|
||||
// Teardown side effects
|
||||
myEventListener.disconnect();
|
||||
|
||||
// Close connections
|
||||
myWebSocket.close();
|
||||
|
||||
// Clear timers
|
||||
clearTimeout(myTimer);
|
||||
});
|
||||
```
|
||||
|
||||
The `data` object is passed to the callback and can be used to store information that will be available via `hot.data` in the next instance of the module. This is particularly useful for preserving state across module updates.
|
||||
|
||||
### `hot.data`
|
||||
|
||||
{% callout type="success" %}
|
||||
✅ **Fully implemented** — `hot.data` works as expected, preserving data between module evaluations through the values set in `dispose` callbacks.
|
||||
{% /callout %}
|
||||
|
||||
An object that persists between updates. Contains data from the previous module instance that was passed in the `dispose` handler.
|
||||
|
||||
```ts#counter.ts
|
||||
// Simple counter with hot reload state persistence
|
||||
let count = 0;
|
||||
|
||||
// Restore previous count when hot reloaded
|
||||
```ts
|
||||
if (import.meta.hot) {
|
||||
// Recover previous count from hot.data
|
||||
if (import.meta.hot.data.count !== undefined) {
|
||||
count = import.meta.hot.data.count;
|
||||
// HMR APIs are available.
|
||||
}
|
||||
```
|
||||
|
||||
However, **this check is often not needed** as Bun will dead-code-eliminate
|
||||
calls to all of the HMR APIs in production builds.
|
||||
|
||||
```ts
|
||||
// This entire function call will be removed in production!
|
||||
import.meta.hot.dispose(() => {
|
||||
console.log("dispose");
|
||||
});
|
||||
```
|
||||
|
||||
For this to work, Bun forces these APIs to be called without indirection. That means the following do not work:
|
||||
|
||||
```ts#invalid-hmr-usage.ts
|
||||
// INVALID: Assigning `hot` to a variable
|
||||
const hot = import.meta.hot;
|
||||
hot.accept();
|
||||
|
||||
// INVALID: Assigning `import.meta` to a variable
|
||||
const meta = import.meta;
|
||||
meta.hot.accept();
|
||||
console.log(meta.hot.data);
|
||||
|
||||
// INVALID: Passing to a function
|
||||
doSomething(import.meta.hot.dispose);
|
||||
|
||||
// OK: The full phrase "import.meta.hot.<API>" must be called directly:
|
||||
import.meta.hot.accept();
|
||||
|
||||
// OK: `data` can be passed to functions:
|
||||
doSomething(import.meta.hot.data);
|
||||
```
|
||||
|
||||
{% callout %}
|
||||
|
||||
**Note** — The HMR API is still a work in progress. Some features are missing. HMR can be disabled in `Bun.serve` by setting the `development` option to `{ hmr: false }`.
|
||||
|
||||
{% endcallout %}
|
||||
|
||||
| | Method | Notes |
|
||||
| --- | ------------------ | --------------------------------------------------------------------- |
|
||||
| ✅ | `hot.accept()` | Indicate that a hot update can be replaced gracefully. |
|
||||
| ✅ | `hot.data` | Persist data between module evaluations. |
|
||||
| ✅ | `hot.dispose()` | Add a callback function to run when a module is about to be replaced. |
|
||||
| ❌ | `hot.invalidate()` | |
|
||||
| ✅ | `hot.on()` | Attach an event listener |
|
||||
| ✅ | `hot.off()` | Remove an event listener from `on`. |
|
||||
| ❌ | `hot.send()` | |
|
||||
| 🚧 | `hot.prune()` | **NOTE**: Callback is currently never called. |
|
||||
| ✅ | `hot.decline()` | No-op to match Vite's `import.meta.hot` |
|
||||
|
||||
### `import.meta.hot.accept()`
|
||||
|
||||
The `accept()` method indicates that a module can be hot-replaced. When called
|
||||
without arguments, it indicates that this module can be replaced simply by
|
||||
re-evaluating the file. After a hot update, importers of this module will be
|
||||
automatically patched.
|
||||
|
||||
```ts#index.ts
|
||||
import { getCount } from "./foo.ts";
|
||||
|
||||
console.log("count is ", getCount());
|
||||
|
||||
import.meta.hot.accept();
|
||||
|
||||
export function getNegativeCount() {
|
||||
return -getCount();
|
||||
}
|
||||
```
|
||||
|
||||
This creates a hot-reloading boundary for all of the files that `index.ts`
|
||||
imports. That means whenever `foo.ts` or any of its dependencies are saved, the
|
||||
update will bubble up to `index.ts` will re-evaluate. Files that import
|
||||
`index.ts` will then be patched to import the new version of
|
||||
`getNegativeCount()`. If only `index.ts` is updated, only the one file will be
|
||||
re-evaluated, and the counter in `foo.ts` is reused.
|
||||
|
||||
This may be used in combination with `import.meta.hot.data` to transfer state
|
||||
from the previous module to the new one.
|
||||
|
||||
When no modules call `import.meta.hot.accept()` (and there isn't React Fast
|
||||
Refresh or a plugin calling it for you), the page will reload when the file
|
||||
updates, and a console warning shows which files were invalidated. This warning
|
||||
is safe to ignore if it makes more sense to rely on full page reloads.
|
||||
|
||||
#### With callback
|
||||
|
||||
When provided one callback, `import.meta.hot.accept` will function how it does
|
||||
in Vite. Instead of patching the importers of this module, it will call the
|
||||
callback with the new module.
|
||||
|
||||
```ts
|
||||
export const count = 0;
|
||||
|
||||
import.meta.hot.accept(newModule => {
|
||||
if (newModule) {
|
||||
// newModule is undefined when SyntaxError happened
|
||||
console.log("updated: count is now ", newModule.count);
|
||||
}
|
||||
|
||||
// Save state before module is replaced
|
||||
import.meta.hot.dispose(data => {
|
||||
data.count = count;
|
||||
});
|
||||
|
||||
import.meta.hot.accept();
|
||||
}
|
||||
|
||||
export function increment() {
|
||||
return ++count;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Without this pattern, the counter would reset to 0 each time the module is hot-reloaded. With `hot.data`, the counter's value persists across updates.
|
||||
Prefer using `import.meta.hot.accept()` without an argument as it usually makes your code easier to understand.
|
||||
|
||||
This is one of the fully implemented features of Bun's HMR system, allowing you to maintain state even as modules are replaced.
|
||||
#### Accepting other modules
|
||||
|
||||
### `hot.decline()`
|
||||
```ts
|
||||
import { count } from "./foo";
|
||||
|
||||
{% callout %}
|
||||
⚠️ **Not implemented** — This method is a no-op in Bun's implementation (for compatibility with Vite).
|
||||
{% /callout %}
|
||||
import.meta.hot.accept("./foo", () => {
|
||||
if (!newModule) return;
|
||||
|
||||
Indicates that this module cannot be hot updated. This method is included for API compatibility but doesn't have any effect in Bun's implementation.
|
||||
|
||||
```ts#decline.ts
|
||||
// This won't have any effect in Bun's implementation
|
||||
import.meta.hot.decline();
|
||||
console.log("updated: count is now ", count);
|
||||
});
|
||||
```
|
||||
|
||||
### `hot.invalidate()`
|
||||
Indicates that a dependency's module can be accepted. When the dependency is updated, the callback will be called with the new module.
|
||||
|
||||
{% callout %}
|
||||
❌ **Not implemented** — Currently, `hot.invalidate()` throws an error: "TODO: implement ImportMetaHot.invalidate"
|
||||
{% /callout %}
|
||||
#### With multiple dependencies
|
||||
|
||||
Forces a full page reload.
|
||||
|
||||
```ts#invalidate.ts
|
||||
if (cannotUpdate) {
|
||||
// This will throw an error in the current implementation:
|
||||
// "Error: TODO: implement ImportMetaHot.invalidate"
|
||||
import.meta.hot.invalidate();
|
||||
}
|
||||
```ts
|
||||
import.meta.hot.accept(["./foo", "./bar"], newModules => {
|
||||
// newModules is an array where each item corresponds to the updated module
|
||||
// or undefined if that module had a syntax error
|
||||
});
|
||||
```
|
||||
|
||||
### `hot.on()` & `hot.off()`
|
||||
Indicates that multiple dependencies' modules can be accepted. This variant accepts an array of dependencies, where the callback will receive the updated modules, and `undefined` for any that had errors.
|
||||
|
||||
{% callout %}
|
||||
❌ **Not implemented** — These methods throw an error: "TODO: implement ImportMetaHot.on/off"
|
||||
{% /callout %}
|
||||
### `import.meta.hot.data`
|
||||
|
||||
Register and unregister event listeners. These methods exist for API compatibility but throw errors in the current implementation.
|
||||
`import.meta.hot.data` maintains state between module instances during hot
|
||||
replacement, enabling data transfer from previous to new versions. When
|
||||
`import.meta.hot.data` is written into, Bun will also mark this module as
|
||||
capable of self-accepting (equivalent of calling `import.meta.hot.accept()`).
|
||||
|
||||
```ts#event-listeners.ts
|
||||
try {
|
||||
// This will throw an error: "Error: TODO: implement ImportMetaHot.on"
|
||||
import.meta.hot.on("custom-event", data => {
|
||||
console.log("Custom event received", data);
|
||||
});
|
||||
```ts
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { App } from "./app";
|
||||
|
||||
// This will throw an error: "Error: TODO: implement ImportMetaHot.off"
|
||||
import.meta.hot.off("custom-event", listener);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
const root = import.meta.hot.data.root ??= createRoot(elem);
|
||||
root.render(<App />); // re-use an existing root
|
||||
```
|
||||
|
||||
### `hot.send()`
|
||||
In production, `data` is inlined to be `{}`, meaning it cannot be used as a state holder.
|
||||
|
||||
{% callout %}
|
||||
❌ **Not implemented** — This method throws an error: "TODO: implement ImportMetaHot.send"
|
||||
{% /callout %}
|
||||
The above pattern is recommended for stateful modules because Bun knows it can minify `{}.prop ??= value` into `value` in production.
|
||||
|
||||
Send custom events to the dev server. This method exists for API compatibility but throws an error in the current implementation.
|
||||
### `import.meta.hot.dispose()`
|
||||
|
||||
```ts#send-event.ts
|
||||
try {
|
||||
// This will throw an error: "Error: TODO: implement ImportMetaHot.send"
|
||||
import.meta.hot.send("client-event", { data: "example" });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
Attaches an on-dispose callback. This is called:
|
||||
|
||||
- Just before the module is replaced with another copy (before the next is loaded)
|
||||
- After the module is detached (removing all imports to this module, see `import.meta.hot.prune()`)
|
||||
|
||||
```ts
|
||||
const sideEffect = setupSideEffect();
|
||||
|
||||
import.meta.hot.dispose(() => {
|
||||
sideEffect.cleanup();
|
||||
});
|
||||
```
|
||||
|
||||
## Hot Reloading CSS
|
||||
This callback is not called on route navigation or when the browser tab closes.
|
||||
|
||||
CSS files are automatically hot-reloaded in the browser without a page refresh, providing immediate visual feedback during development. This includes:
|
||||
Returning a promise will delay module replacement until the module is disposed.
|
||||
All dispose callbacks are called in parallel.
|
||||
|
||||
- `@import`ed CSS files
|
||||
- CSS files imported from JavaScript using `import` statements
|
||||
- CSS files bundled with your application
|
||||
- `<link rel="stylesheet">` tags in HTML entry points
|
||||
### `import.meta.hot.prune()`
|
||||
|
||||
## React Fast Refresh
|
||||
Attaches an on-prune callback. This is called when all imports to this module
|
||||
are removed, but the module was previously loaded.
|
||||
|
||||
Bun's bundler includes built-in support for React Fast Refresh, a feature that updates React components in-place without losing their state. This creates a seamless development experience where UI changes are immediately visible while preserving user interactions.
|
||||
This can be used to clean up resources that were created when the module was
|
||||
loaded. Unlike `import.meta.hot.dispose()`, this pairs much better with `accept`
|
||||
and `data` to manage stateful resources. A full example managing a `WebSocket`:
|
||||
|
||||
There's no dependency to install to enable React Fast Refresh support in Bun. The transpiler transform automatically enabled when using React components (so long as a jsxImportSource points to React and there're file(s) using React)
|
||||
```ts
|
||||
import { something } from "./something";
|
||||
|
||||
// Initialize or re-use a WebSocket connection
|
||||
export const ws = (import.meta.hot.data.ws ??= new WebSocket(location.origin));
|
||||
|
||||
// If the module's import is removed, clean up the WebSocket connection.
|
||||
import.meta.hot.prune(() => {
|
||||
ws.close();
|
||||
});
|
||||
```
|
||||
|
||||
If `dispose` was used instead, the WebSocket would close and re-open on every
|
||||
hot update. Both versions of the code will prevent page reloads when imported
|
||||
files are updated.
|
||||
|
||||
### `import.meta.hot.on()` and `off()`
|
||||
|
||||
`on()` and `off()` are used to listen for events from the HMR runtime. Event names are prefixed with a prefix so that plugins do not conflict with each other.
|
||||
|
||||
```ts
|
||||
import.meta.hot.on("bun:beforeUpdate", () => {
|
||||
console.log("before a hot update");
|
||||
});
|
||||
```
|
||||
|
||||
When a file is replaced, all of its event listeners are automatically removed.
|
||||
|
||||
A list of all built-in events:
|
||||
|
||||
| Event | Emitted when |
|
||||
| ---------------------- | ----------------------------------------------------------------------------------------------- |
|
||||
| `bun:beforeUpdate` | before a hot update is applied. |
|
||||
| `bun:afterUpdate` | after a hot update is applied. |
|
||||
| `bun:beforeFullReload` | before a full page reload happens. |
|
||||
| `bun:beforePrune` | before prune callbacks are called. |
|
||||
| `bun:invalidate` | when a module is invalidated with `import.meta.hot.invalidate()` |
|
||||
| `bun:error` | when a build or runtime error occurs |
|
||||
| `bun:ws:disconnect` | when the HMR WebSocket connection is lost. This can indicate the development server is offline. |
|
||||
| `bun:ws:connect` | when the HMR WebSocket connects or re-connects. |
|
||||
|
||||
For compatibility with Vite, the above events are also available via `vite:*` prefix instead of `bun:*`.
|
||||
|
||||
@@ -4,6 +4,12 @@ The Bun bundler implements a set of default loaders out of the box. As a rule of
|
||||
|
||||
Bun uses the file extension to determine which built-in _loader_ should be used to parse the file. Every loader has a name, such as `js`, `tsx`, or `json`. These names are used when building [plugins](https://bun.sh/docs/bundler/plugins) that extend Bun with custom loaders.
|
||||
|
||||
You can explicitly specify which loader to use using the 'loader' import attribute.
|
||||
|
||||
```ts
|
||||
import my_toml from "./my_file" with { loader: "toml" };
|
||||
```
|
||||
|
||||
## Built-in loaders
|
||||
|
||||
### `js`
|
||||
|
||||
@@ -12,9 +12,12 @@ Options for the `pack` command:
|
||||
|
||||
- `--dry-run`: Perform all tasks except writing the tarball to disk.
|
||||
- `--destination`: Specify the directory where the tarball will be saved.
|
||||
- `--filename`: Specify an exact file name for the tarball to be saved at.
|
||||
- `--ignore-scripts`: Skip running pre/postpack and prepare scripts.
|
||||
- `--gzip-level`: Set a custom compression level for gzip, ranging from 0 to 9 (default is 9).
|
||||
|
||||
> Note `--filename` and `--destination` cannot be used at the same time
|
||||
|
||||
## bin
|
||||
|
||||
To print the path to the `bin` directory for the local project:
|
||||
|
||||
@@ -82,6 +82,11 @@ The `--dry-run` flag can be used to simulate the publish process without actuall
|
||||
$ bun publish --dry-run
|
||||
```
|
||||
|
||||
### `--gzip-level`
|
||||
|
||||
Specify the level of gzip compression to use when packing the package. Only applies to `bun publish` without a tarball path argument. Values range from `0` to `9` (default is `9`).
|
||||
{% bunCLIUsage command="publish" /%}
|
||||
|
||||
### `--auth-type`
|
||||
|
||||
If you have 2FA enabled for your npm account, `bun publish` will prompt you for a one-time password. This can be done through a browser or the CLI. The `--auth-type` flag can be used to tell the npm registry which method you prefer. The possible values are `web` and `legacy`, with `web` being the default.
|
||||
@@ -102,7 +107,6 @@ Provide a one-time password directly to the CLI. If the password is valid, this
|
||||
$ bun publish --otp 123456
|
||||
```
|
||||
|
||||
### `--gzip-level`
|
||||
|
||||
Specify the level of gzip compression to use when packing the package. Only applies to `bun publish` without a tarball path argument. Values range from `0` to `9` (default is `9`).
|
||||
{% bunCLIUsage command="publish" /%}
|
||||
{% callout %}
|
||||
**Note** - `bun publish` respects the `NPM_CONFIG_TOKEN` environment variable which can be used when publishing in github actions or automated workflows.
|
||||
{% /callout %}
|
||||
|
||||
56
docs/guides/runtime/codesign-macos-executable.md
Normal file
56
docs/guides/runtime/codesign-macos-executable.md
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
name: Codesign a single-file JavaScript executable on macOS
|
||||
description: Fix the "can't be opened because it is from an unidentified developer" Gatekeeper warning when running your JavaScript executable.
|
||||
---
|
||||
|
||||
Compile your executable using the `--compile` flag.
|
||||
|
||||
```sh
|
||||
$ bun build --compile ./path/to/entry.ts --outfile myapp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
List your available signing identities. One of these will be your signing identity that you pass to the `codesign` command. This command requires macOS.
|
||||
|
||||
```sh
|
||||
$ security find-identity -v -p codesigning
|
||||
1. XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX "Developer ID Application: Your Name (ZZZZZZZZZZ)"
|
||||
1 valid identities found
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Optional, but recommended: create an `entitlements.plist` file with the necessary permissions for the JavaScript engine to work correctly.
|
||||
|
||||
```xml#entitlements.plist
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-executable-page-protection</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Sign your executable using the `codesign` command and verify it works.
|
||||
|
||||
```bash
|
||||
$ codesign --entitlements entitlements.plist -vvvv --deep --sign "XXXXXXXXXX" ./myapp --force
|
||||
$ codesign -vvv --verify ./myapp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
For more information on macOS codesigning, refer to [Apple's Code Signing documentation](https://developer.apple.com/documentation/security/code_signing_services). For details about creating single-file executables with Bun, see [Standalone Executables](/docs/bundler/executables). This guide requires Bun v1.2.4 or newer.
|
||||
@@ -8,7 +8,7 @@ VSCode extension support is currently buggy. We recommend the [Web Debugger](htt
|
||||
|
||||
{% /note %}
|
||||
|
||||
Bun speaks the [WebKit Inspector Protocol](https://github.com/oven-sh/bun/blob/main/packages/bun-vscode/types/jsc.d.ts) so you can debug your code with an interactive debugger.
|
||||
Bun speaks the [WebKit Inspector Protocol](https://github.com/oven-sh/bun/blob/main/packages/bun-inspector-protocol/src/protocol/jsc/index.d.ts) so you can debug your code with an interactive debugger.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
Running `bun install` will create a lockfile called `bun.lock`.
|
||||
|
||||
https://bun.sh/blog/bun-lock-text-lockfile
|
||||
|
||||
#### Should it be committed to git?
|
||||
|
||||
Yes
|
||||
|
||||
@@ -63,6 +63,20 @@ $ bun install --filter "pkg-*" --filter "!pkg-c"
|
||||
$ bun install --filter "./packages/pkg-*" --filter "!pkg-c" # or --filter "!./packages/pkg-c"
|
||||
```
|
||||
|
||||
When publishing, `workspace:` versions are replaced by the package's `package.json` version,
|
||||
|
||||
```
|
||||
"workspace:*" -> "1.0.1"
|
||||
"workspace:^" -> "^1.0.1"
|
||||
"workspace:~" -> "~1.0.1"
|
||||
```
|
||||
|
||||
Setting a specific version takes precedence over the package's `package.json` version,
|
||||
|
||||
```
|
||||
"workspace:1.0.2" -> "1.0.2" // Even if current version is 1.0.1
|
||||
```
|
||||
|
||||
Workspaces have a couple major benefits.
|
||||
|
||||
- **Code can be split into logical parts.** If one package relies on another, you can simply add it as a dependency in `package.json`. If package `b` depends on `a`, `bun install` will install your local `packages/a` directory into `node_modules` instead of downloading it from the npm registry.
|
||||
|
||||
@@ -215,9 +215,12 @@ export default {
|
||||
page("bundler", "`Bun.build`", {
|
||||
description: "Bundle code for consumption in the browser with Bun's native bundler.",
|
||||
}),
|
||||
page("bundler/html", "Bundle frontend & static sites", {
|
||||
page("bundler/html", "HTML & static sites", {
|
||||
description: `Zero-config HTML bundler for single-page apps and multi-page apps. Automatic bundling, TailwindCSS plugins, TypeScript, JSX, React support, and incredibly fast builds`,
|
||||
}),
|
||||
page("bundler/css", "CSS", {
|
||||
description: `Production ready CSS bundler with support for modern CSS features, CSS modules, and more.`,
|
||||
}),
|
||||
page("bundler/fullstack", "Fullstack Dev Server", {
|
||||
description: "Serve your frontend and backend from the same app with Bun's dev server.",
|
||||
}),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: Debugging
|
||||
---
|
||||
|
||||
Bun speaks the [WebKit Inspector Protocol](https://github.com/oven-sh/bun/blob/main/packages/bun-types/jsc.d.ts), so you can debug your code with an interactive debugger. For demonstration purposes, consider the following simple web server.
|
||||
Bun speaks the [WebKit Inspector Protocol](https://github.com/oven-sh/bun/blob/main/packages/bun-inspector-protocol/src/protocol/jsc/index.d.ts), so you can debug your code with an interactive debugger. For demonstration purposes, consider the following simple web server.
|
||||
|
||||
## Debugging JavaScript and TypeScript
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ Some methods are not optimized yet.
|
||||
|
||||
### [`node:util`](https://nodejs.org/api/util.html)
|
||||
|
||||
🟡 Missing `MIMEParams` `MIMEType` `debug` `getSystemErrorMap` `transferableAbortController` `transferableAbortSignal` `stripVTControlCharacters`
|
||||
🟡 Missing `getCallSite` `getCallSites` `getSystemErrorMap` `getSystemErrorMessage` `transferableAbortSignal` `transferableAbortController` `MIMEType` `MIMEParams`
|
||||
|
||||
### [`node:v8`](https://nodejs.org/api/v8.html)
|
||||
|
||||
@@ -346,7 +346,7 @@ The table below lists all globals implemented by Node.js and Bun's current compa
|
||||
|
||||
### [`process`](https://nodejs.org/api/process.html)
|
||||
|
||||
🟡 Mostly implemented. `process.binding` (internal Node.js bindings some packages rely on) is partially implemented. `process.title` is a currently a no-op on macOS & Linux. `getActiveResourcesInfo` `setActiveResourcesInfo`, `getActiveResources` and `setSourceMapsEnabled` are stubs. Newer APIs like `process.loadEnvFile` and `process.getBuiltinModule` are not implemented yet.
|
||||
🟡 Mostly implemented. `process.binding` (internal Node.js bindings some packages rely on) is partially implemented. `process.title` is currently a no-op on macOS & Linux. `getActiveResourcesInfo` `setActiveResourcesInfo`, `getActiveResources` and `setSourceMapsEnabled` are stubs. Newer APIs like `process.loadEnvFile` and `process.getBuiltinModule` are not implemented yet.
|
||||
|
||||
### [`queueMicrotask()`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask)
|
||||
|
||||
|
||||
@@ -329,7 +329,7 @@ await Bun.build({
|
||||
|
||||
{% callout %}
|
||||
|
||||
**NOTE**: Plugin lifcycle callbacks (`onStart()`, `onResolve()`, etc.) do not have the ability to modify the `build.config` object in the `setup()` function. If you want to mutate `build.config`, you must do so directly in the `setup()` function:
|
||||
**NOTE**: Plugin lifecycle callbacks (`onStart()`, `onResolve()`, etc.) do not have the ability to modify the `build.config` object in the `setup()` function. If you want to mutate `build.config`, you must do so directly in the `setup()` function:
|
||||
|
||||
```ts
|
||||
await Bun.build({
|
||||
@@ -400,7 +400,7 @@ type Loader = "js" | "jsx" | "ts" | "tsx" | "css" | "json" | "toml" | "object";
|
||||
|
||||
### Namespaces
|
||||
|
||||
`onLoad` and `onResolve` accept an optional `namespace` string. What is a namespaace?
|
||||
`onLoad` and `onResolve` accept an optional `namespace` string. What is a namespace?
|
||||
|
||||
Every module has a namespace. Namespaces are used to prefix the import in transpiled code; for instance, a loader with a `filter: /\.yaml$/` and `namespace: "yaml:"` will transform an import from `./myfile.yaml` into `yaml:./myfile.yaml`.
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# Tell LLDB what to do when the debugged process receives SIGPWR: pass it through to the process
|
||||
# (-p), but do not stop the process (-s) or notify the user (-n).
|
||||
#
|
||||
# JSC's garbage collector sends this signal (as configured by Bun WebKit in
|
||||
# Thread::initializePlatformThreading() in ThreadingPOSIX.cpp) to the JS thread to suspend or resume
|
||||
# it. So stopping the process would just create noise when debugging any long-running script.
|
||||
process handle -p true -s false -n false SIGPWR
|
||||
|
||||
command script import misctools/lldb/lldb_pretty_printers.py
|
||||
type category enable zig.lang
|
||||
type category enable zig.std
|
||||
|
||||
command script import misctools/lldb/lldb_webkit.py
|
||||
@@ -329,7 +329,7 @@ def btjs(debugger, command, result, internal_dict):
|
||||
addressFormat = '#0{width}x'.format(width=target.GetAddressByteSize() * 2 + 2)
|
||||
process = target.GetProcess()
|
||||
thread = process.GetSelectedThread()
|
||||
jscModule = target.module["JavaScriptCore"]
|
||||
jscModule = target.module["JavaScriptCore"] or target.module["bun"] or target.module["bun-debug"]
|
||||
|
||||
if jscModule.FindSymbol("JSC::CallFrame::describeFrame").GetSize() or jscModule.FindSymbol("_ZN3JSC9CallFrame13describeFrameEv").GetSize():
|
||||
annotateJSFrames = True
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "bun",
|
||||
"version": "1.2.4",
|
||||
"version": "1.2.6",
|
||||
"workspaces": [
|
||||
"./packages/bun-types"
|
||||
],
|
||||
@@ -53,7 +53,7 @@
|
||||
"lint:fix": "oxlint --config oxlint.json --fix",
|
||||
"test": "node scripts/runner.node.mjs --exec-path ./build/debug/bun-debug",
|
||||
"test:release": "node scripts/runner.node.mjs --exec-path ./build/release/bun",
|
||||
"banned": "bun packages/bun-internal-test/src/linter.ts",
|
||||
"banned": "bun test test/internal/ban-words.test.ts",
|
||||
"zig": "vendor/zig/zig.exe",
|
||||
"zig:fmt": "bun run zig-format",
|
||||
"zig:check": "bun run zig build check --summary new",
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
" != undefined": "This is by definition Undefined Behavior.",
|
||||
" == undefined": "This is by definition Undefined Behavior.",
|
||||
"@import(\"root\").bun.": "Only import 'bun' once",
|
||||
"std.debug.assert": "Use bun.assert instead",
|
||||
"std.debug.dumpStackTrace": "Use bun.handleErrorReturnTrace or bun.crash_handler.dumpStackTrace instead",
|
||||
"std.debug.print": "Don't let this be committed",
|
||||
"std.mem.indexOfAny(u8": "Use bun.strings.indexOfAny",
|
||||
"undefined != ": "This is by definition Undefined Behavior.",
|
||||
"undefined == ": "This is by definition Undefined Behavior.",
|
||||
"bun.toFD(std.fs.cwd().fd)": "Use bun.FD.cwd()",
|
||||
"std.StringArrayHashMapUnmanaged(": "bun.StringArrayHashMapUnmanaged has a faster `eql`",
|
||||
"std.StringArrayHashMap(": "bun.StringArrayHashMap has a faster `eql`",
|
||||
"std.StringHashMapUnmanaged(": "bun.StringHashMapUnmanaged has a faster `eql`",
|
||||
"std.StringHashMap(": "bun.StringHashMaphas a faster `eql`",
|
||||
"std.enums.tagName(": "Use bun.tagName instead",
|
||||
"": ""
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import { $ } from "bun";
|
||||
import BANNED from "./banned.json";
|
||||
import * as action from "@actions/core";
|
||||
|
||||
const IGNORED_FOLDERS = [
|
||||
// list of folders to ignore
|
||||
"windows-shim",
|
||||
];
|
||||
|
||||
const ci = !!process.env["GITHUB_ACTIONS"];
|
||||
process.chdir(require("path").join(import.meta.dir, "../../../"));
|
||||
let bad = [];
|
||||
let report = "";
|
||||
const write = (text: string) => {
|
||||
process.stdout.write(text);
|
||||
report += text;
|
||||
};
|
||||
for (const [banned, suggestion] of Object.entries(BANNED)) {
|
||||
if (banned.length === 0) continue;
|
||||
// Run git grep to find occurrences of std.debug.assert in .zig files
|
||||
// .nothrow() is here since git will exit with non-zero if no matches are found.
|
||||
let stdout = await $`git grep -n -F "${banned}" "src/**.zig" | grep -v -F '//' | grep -v -F bench`.nothrow().text();
|
||||
|
||||
stdout = stdout.trim();
|
||||
if (stdout.length === 0) continue;
|
||||
|
||||
let lines = stdout.split("\n");
|
||||
// Parse each line to extract filename and line number
|
||||
const matches = lines
|
||||
.filter(line => !IGNORED_FOLDERS.some(folder => line.includes(folder)))
|
||||
.map(line => {
|
||||
const [path, lineNumber, ...text] = line.split(":");
|
||||
return { path, lineNumber, banned, suggestion, text: text.join(":") };
|
||||
});
|
||||
// Check if we got any output
|
||||
// Split the output into lines
|
||||
if (matches.length === 0) continue;
|
||||
|
||||
write(`Banned **'${banned}'** found in the following locations:` + "\n");
|
||||
matches.forEach(match => {
|
||||
write(`${match.path}:${match.lineNumber}: ${match.text.trim()}` + "\n");
|
||||
});
|
||||
bad = bad.concat(matches);
|
||||
}
|
||||
|
||||
if (report.length === 0) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
function link({ path, lineNumber, suggestion, banned }) {
|
||||
action.error(`Lint failure: ${banned} is banned, ${suggestion}`, {
|
||||
file: path,
|
||||
startLine: Number(lineNumber),
|
||||
endLine: Number(lineNumber),
|
||||
});
|
||||
return `[\`${path}:${lineNumber}\`](https://github.com/oven-sh/bun/blob/${process.env.GITHUB_SHA}/${path}#L${lineNumber})`;
|
||||
}
|
||||
|
||||
if (ci) {
|
||||
if (report.length > 0) {
|
||||
action.setFailed(`${bad.length} lint failures`);
|
||||
}
|
||||
action.setOutput("count", bad.length);
|
||||
action.setOutput("text_output", bad.map(m => `- ${link(m)}: ${m.banned} is banned, ${m.suggestion}`).join("\n"));
|
||||
action.setOutput("json_output", JSON.stringify(bad));
|
||||
action.summary.addRaw(report);
|
||||
await action.summary.write();
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
34
packages/bun-plugin-svelte/.gitignore
vendored
Normal file
34
packages/bun-plugin-svelte/.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# dependencies (bun install)
|
||||
node_modules
|
||||
|
||||
# output
|
||||
out
|
||||
dist
|
||||
*.tgz
|
||||
|
||||
# code coverage
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# logs
|
||||
logs
|
||||
_.log
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# caches
|
||||
.eslintcache
|
||||
.cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# IntelliJ based IDEs
|
||||
.idea
|
||||
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
69
packages/bun-plugin-svelte/README.md
Normal file
69
packages/bun-plugin-svelte/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
<p align="center">
|
||||
<a href="https://bun.sh"><img src="https://github.com/user-attachments/assets/50282090-adfd-4ddb-9e27-c30753c6b161" alt="Logo" height=170></a>
|
||||
</p>
|
||||
<h1 align="center"><code>bun-plugin-svelte</code></h1>
|
||||
|
||||
The official [Svelte](https://svelte.dev/) plugin for [Bun](https://bun.sh/).
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
$ bun add -D bun-plugin-svelte
|
||||
```
|
||||
|
||||
## Dev Server Usage
|
||||
|
||||
`bun-plugin-svelte` integrates with Bun's [Fullstack Dev Server](https://bun.sh/docs/bundler/fullstack), giving you
|
||||
HMR when developing your Svelte app.
|
||||
|
||||
Start by registering it in your [bunfig.toml](https://bun.sh/docs/runtime/bunfig):
|
||||
|
||||
```toml
|
||||
[serve.static]
|
||||
plugins = ["bun-plugin-svelte"]
|
||||
```
|
||||
|
||||
Then start your dev server:
|
||||
|
||||
```
|
||||
$ bun index.html
|
||||
```
|
||||
|
||||
See the [example](https://github.com/oven-sh/bun/tree/main/packages/bun-plugin-svelte/example) for a complete example.
|
||||
|
||||
## Bundler Usage
|
||||
|
||||
`bun-plugin-svelte` lets you bundle Svelte components with [`Bun.build`](https://bun.sh/docs/bundler).
|
||||
|
||||
```ts
|
||||
// build.ts
|
||||
// to use: bun run build.ts
|
||||
import { SveltePlugin } from "bun-plugin-svelte"; // NOTE: not published to npm yet
|
||||
|
||||
Bun.build({
|
||||
entrypoints: ["src/index.ts"],
|
||||
outdir: "dist",
|
||||
target: "browser",
|
||||
sourcemap: true, // sourcemaps not yet supported
|
||||
plugins: [
|
||||
SveltePlugin({
|
||||
development: true, // turn off for prod builds. Defaults to false
|
||||
}),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## Server-Side Usage
|
||||
|
||||
`bun-plugin-svelte` does not yet support server-side imports (e.g. for SSR).
|
||||
This will be added in the near future.
|
||||
|
||||
## Not Yet Supported
|
||||
|
||||
Support for these features will be added in the near future
|
||||
|
||||
- Server-side imports/rendering
|
||||
- Source maps
|
||||
- CSS extensions (e.g. tailwind) in `<style>` blocks
|
||||
- TypeScript-specific features (e.g. enums and namespaces). If you're using
|
||||
TypeScript 5.8, consider enabling [`--erasableSyntaxOnly`](https://devblogs.microsoft.com/typescript/announcing-typescript-5-8-beta/#the---erasablesyntaxonly-option)
|
||||
65
packages/bun-plugin-svelte/bun.lock
Normal file
65
packages/bun-plugin-svelte/bun.lock
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "bun-plugin-svelte",
|
||||
"devDependencies": {
|
||||
"bun-types": "canary",
|
||||
"svelte": "^5.20.4",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^5",
|
||||
"typescript": "^5",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
|
||||
|
||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
|
||||
|
||||
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||
|
||||
"@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="],
|
||||
|
||||
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="],
|
||||
|
||||
"@types/node": ["@types/node@22.13.5", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg=="],
|
||||
|
||||
"@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
|
||||
|
||||
"acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
|
||||
|
||||
"acorn-typescript": ["acorn-typescript@1.4.13", "", { "peerDependencies": { "acorn": ">=8.9.0" } }, "sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q=="],
|
||||
|
||||
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
|
||||
|
||||
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.4-canary.20250226T140704", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-P8b2CGLtbvi/kQ4dPHBhU5qkguIjHMYCjNqjWDTKSnodWDTbcv9reBdktZJ7m5SF4m15JLthfFq2PtwKpA9a+w=="],
|
||||
|
||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||
|
||||
"esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
|
||||
|
||||
"esrap": ["esrap@1.4.5", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-CjNMjkBWWZeHn+VX+gS8YvFwJ5+NDhg8aWZBSFJPR8qQduDNjbJodA2WcwCm7uQa5Rjqj+nZvVmceg1RbHFB9g=="],
|
||||
|
||||
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
|
||||
|
||||
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
|
||||
|
||||
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
|
||||
|
||||
"svelte": ["svelte@5.20.4", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "@jridgewell/sourcemap-codec": "^1.5.0", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "acorn-typescript": "^1.4.13", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^1.4.3", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-2Mo/AfObaw9zuD0u1JJ7sOVzRCGcpETEyDkLbtkcctWpCMCIyT0iz83xD8JT29SR7O4SgswuPRIDYReYF/607A=="],
|
||||
|
||||
"typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
|
||||
|
||||
"undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"zimmerframe": ["zimmerframe@1.1.2", "", {}, "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="],
|
||||
}
|
||||
}
|
||||
311
packages/bun-plugin-svelte/example/App.svelte
Normal file
311
packages/bun-plugin-svelte/example/App.svelte
Normal file
@@ -0,0 +1,311 @@
|
||||
<script lang="ts">
|
||||
import FeatureCard from "./FeatureCard.svelte";
|
||||
|
||||
const links = [
|
||||
{ text: "Bun Documentation", url: "https://bun.sh/docs" },
|
||||
{ text: "Svelte Documentation", url: "https://svelte.dev/docs" },
|
||||
{ text: "GitHub", url: "https://github.com/oven-sh/bun/tree/main/packages/bun-plugin-svelte" },
|
||||
];
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<div class="hero">
|
||||
<div class="logo-container">
|
||||
<a href="https://bun.sh" class="bun-logo">
|
||||
<img
|
||||
src="https://github.com/user-attachments/assets/50282090-adfd-4ddb-9e27-c30753c6b161"
|
||||
alt="Bun Logo"
|
||||
height="42"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<h1><span class="highlight">bun-plugin-svelte</span></h1>
|
||||
<p class="tagline">The official Svelte plugin for <a href="https://bun.sh" target="_blank">Bun</a></p>
|
||||
|
||||
<div class="cta-buttons">
|
||||
<a href="https://bun.sh/docs/bundler/html" class="button primary">🚀 Get Started</a>
|
||||
<a href="https://github.com/oven-sh/bun/tree/main/packages/bun-plugin-svelte/example" class="button secondary"
|
||||
>👀 View Examples</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="usage">
|
||||
<h2>🏃➡️ Quick Start</h2>
|
||||
|
||||
<div class="flex-grid">
|
||||
<div>
|
||||
<h3>1. Install from <a href="https://npmjs.com/package/bun-plugin-svelte" target="_blank">NPM</a></h3>
|
||||
<pre><code class="language-bash">bun add -D bun-plugin-svelte</code></pre>
|
||||
</div>
|
||||
<div>
|
||||
<h3>2. Add it to your <a href="https://bun.sh/docs/runtime/bunfig" target="_blank">bunfig.toml</a></h3>
|
||||
<pre><code class="language-toml">
|
||||
[serve.static]
|
||||
plugins = ["bun-plugin-svelte"];
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="features">
|
||||
<h2>✨ Features</h2>
|
||||
<div class="feature-grid">
|
||||
<FeatureCard title="🔥 HMR Support" link="https://bun.sh/docs/bundler/html">
|
||||
Integrates with Bun's Fullstack Dev Server for hot module replacement
|
||||
</FeatureCard>
|
||||
<FeatureCard title="📦 Bundling" link="https://bun.sh/docs/bundler">
|
||||
Bundle Svelte components with <a href="https://bun.sh/docs/bundler">Bun.build</a>
|
||||
</FeatureCard>
|
||||
</div>
|
||||
|
||||
<section class="resources">
|
||||
<h2>📖 Resources</h2>
|
||||
<ul class="resource-links">
|
||||
{#each links as link}
|
||||
<li><a href={link.url} target="_blank" rel="noopener noreferrer">{link.text}</a></li>
|
||||
{/each}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<p>Made with ❤️ by the Bun team</p>
|
||||
</footer>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
:global(body) {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans",
|
||||
"Helvetica Neue", sans-serif;
|
||||
background-color: #f9f9f9;
|
||||
color: #333;
|
||||
}
|
||||
:global(a) {
|
||||
color: #ff3e00;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
:global(a::after) {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: #ff3e00;
|
||||
transform: scaleX(0);
|
||||
transform-origin: bottom right;
|
||||
transition: transform 0.3s ease-out;
|
||||
}
|
||||
|
||||
:global(a:hover::after) {
|
||||
transform: scaleX(1);
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
|
||||
:global(a:visited) {
|
||||
color: #ff3e00;
|
||||
}
|
||||
|
||||
:global(pre > code.hljs) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.hero {
|
||||
text-align: center;
|
||||
padding: 3rem 1rem;
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
margin-bottom: 1.5rem;
|
||||
margin: auto 25%;
|
||||
}
|
||||
|
||||
.bun-logo {
|
||||
display: block;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.bun-logo:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.bun-logo img {
|
||||
max-width: 33vw;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: #ff3e00;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Don't apply the underline effect to buttons and resource links */
|
||||
.button::after,
|
||||
.resource-links li a::after,
|
||||
.bun-logo::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
font-size: 1.2rem;
|
||||
color: #666;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.cta-buttons {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
padding: 0.8rem 1.5rem;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.button.primary {
|
||||
background-color: #ff3e00;
|
||||
color: white;
|
||||
box-shadow: 0 2px 10px rgba(255, 62, 0, 0.2);
|
||||
}
|
||||
|
||||
.button.primary:hover {
|
||||
background-color: #e63600;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(255, 62, 0, 0.3);
|
||||
}
|
||||
|
||||
.button.secondary {
|
||||
background-color: #f0f0f0;
|
||||
color: #333;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.button.secondary:hover {
|
||||
background-color: #e6e6e6;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
section {
|
||||
margin-bottom: 3rem;
|
||||
padding: 2rem;
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 1.5rem;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.flex-grid {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
.flex-grid > div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.feature-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: #f5f5f5;
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
code {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.resource-links {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.resource-links li a {
|
||||
display: inline-block;
|
||||
padding: 0.5rem 1rem;
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.resource-links li a:hover {
|
||||
background-color: #ff3e00;
|
||||
color: white;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 2rem 0;
|
||||
color: #666;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.feature-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.cta-buttons {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
margin-bottom: 0.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.resource-links {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
28
packages/bun-plugin-svelte/example/FeatureCard.svelte
Normal file
28
packages/bun-plugin-svelte/example/FeatureCard.svelte
Normal file
@@ -0,0 +1,28 @@
|
||||
<script lang="ts">
|
||||
let { title, link, children } = $props();
|
||||
</script>
|
||||
|
||||
<div class="feature-card">
|
||||
<h3>
|
||||
<a href={link}>
|
||||
{title}
|
||||
</a>
|
||||
</h3>
|
||||
<p>
|
||||
{@render children()}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.feature-card {
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.feature-card h3 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
2
packages/bun-plugin-svelte/example/bunfig.toml
Normal file
2
packages/bun-plugin-svelte/example/bunfig.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[serve.static]
|
||||
plugins = ["bun-plugin-svelte"]
|
||||
25
packages/bun-plugin-svelte/example/index.html
Normal file
25
packages/bun-plugin-svelte/example/index.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<html>
|
||||
<head>
|
||||
<script type="module" src="./index.ts"></script>
|
||||
|
||||
<link rel="prefetch" href="https://bun.sh/docs/bundler/plugins" />
|
||||
<link rel="preconnect" href="https://bun.sh" />
|
||||
<link rel="preconnect" href="https://github.com" />
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/default.min.css"
|
||||
/>
|
||||
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/languages/toml.min.js"></script>
|
||||
|
||||
<script>
|
||||
hljs.highlightAll();
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
29
packages/bun-plugin-svelte/example/index.ts
Normal file
29
packages/bun-plugin-svelte/example/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { mount, unmount } from "svelte";
|
||||
import App from "./App.svelte";
|
||||
|
||||
declare global {
|
||||
var didMount: boolean | undefined;
|
||||
var hljs: any;
|
||||
}
|
||||
|
||||
let app: Record<string, any> | undefined;
|
||||
|
||||
// mount the application entrypoint to the DOM on first load. On subsequent hot
|
||||
// updates, the app will be unmounted and re-mounted via the accept handler.
|
||||
|
||||
const root = document.getElementById("root")!;
|
||||
if (!globalThis.didMount) {
|
||||
app = mount(App, { target: root });
|
||||
}
|
||||
globalThis.didMount = true;
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(async () => {
|
||||
// avoid unmounting twice when another update gets accepted while outros are playing
|
||||
if (!app) return;
|
||||
const prevApp = app;
|
||||
app = undefined;
|
||||
await unmount(prevApp, { outro: true });
|
||||
app = mount(App, { target: root });
|
||||
});
|
||||
}
|
||||
42
packages/bun-plugin-svelte/package.json
Normal file
42
packages/bun-plugin-svelte/package.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "bun-plugin-svelte",
|
||||
"version": "0.0.5",
|
||||
"description": "Official Svelte plugin for Bun",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/oven-sh/bun",
|
||||
"directory": "packages/bun-plugin-svelte"
|
||||
},
|
||||
"homepage": "https://bun.sh",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"module": "src/index.ts",
|
||||
"index": "src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"example": "bun --config=./example/bunfig.toml example/index.html",
|
||||
"lint": "oxlint .",
|
||||
"fmt": "prettier --write .",
|
||||
"check:types": "tsc --noEmit",
|
||||
"build:types": "tsc --emitDeclarationOnly --declaration --declarationDir ./dist"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bun-types": "canary",
|
||||
"svelte": "^5.20.4",
|
||||
"@threlte/core": "8.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"svelte": "^5"
|
||||
},
|
||||
"files": [
|
||||
"README.md",
|
||||
"bunfig.toml",
|
||||
"tsconfig.json",
|
||||
"modules.d.ts",
|
||||
"dist",
|
||||
"src",
|
||||
"!src/**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
17
packages/bun-plugin-svelte/src/index.spec.ts
Normal file
17
packages/bun-plugin-svelte/src/index.spec.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { describe, it, expect } from "bun:test";
|
||||
import { SveltePlugin } from "./index";
|
||||
|
||||
describe("SveltePlugin", () => {
|
||||
it.each([true, false, 0, 1, "hi"])("throws if passed a non-object (%p)", (badOptions: any) => {
|
||||
expect(() => SveltePlugin(badOptions)).toThrow(TypeError);
|
||||
});
|
||||
it("may be nullish or not provided", () => {
|
||||
expect(() => SveltePlugin()).not.toThrow();
|
||||
expect(() => SveltePlugin(null as any)).not.toThrow();
|
||||
expect(() => SveltePlugin(undefined)).not.toThrow();
|
||||
});
|
||||
|
||||
it.each([null, 1, "hi", {}, "Client"])("throws if forceSide is not 'client' or 'server' (%p)", (forceSide: any) => {
|
||||
expect(() => SveltePlugin({ forceSide })).toThrow(TypeError);
|
||||
});
|
||||
});
|
||||
144
packages/bun-plugin-svelte/src/index.ts
Normal file
144
packages/bun-plugin-svelte/src/index.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import type { BunPlugin, BuildConfig, OnLoadResult } from "bun";
|
||||
import { basename } from "node:path";
|
||||
import { compile, compileModule } from "svelte/compiler";
|
||||
import {
|
||||
getBaseCompileOptions,
|
||||
validateOptions,
|
||||
type SvelteOptions,
|
||||
hash,
|
||||
getBaseModuleCompileOptions,
|
||||
} from "./options";
|
||||
|
||||
const kEmptyObject = Object.create(null);
|
||||
const virtualNamespace = "bun-svelte";
|
||||
|
||||
function SveltePlugin(options: SvelteOptions = kEmptyObject as SvelteOptions): BunPlugin {
|
||||
if (options != null) validateOptions(options);
|
||||
|
||||
/**
|
||||
* import specifier -> CSS source code
|
||||
*/
|
||||
const virtualCssModules = new Map<string, VirtualCSSModule>();
|
||||
type VirtualCSSModule = {
|
||||
/** Path to the svelte file whose css this is for */
|
||||
sourcePath: string;
|
||||
/** Source code */
|
||||
source: string;
|
||||
};
|
||||
|
||||
return {
|
||||
name: "bun-plugin-svelte",
|
||||
setup(builder) {
|
||||
// resolve "svelte" export conditions
|
||||
//
|
||||
// FIXME: the dev server does not currently respect bundler configs; it
|
||||
// just passes a fake one to plugins and then never uses it. we need to to
|
||||
// update it to ~not~ do this.
|
||||
if (builder?.config) {
|
||||
var conditions = builder.config.conditions ?? [];
|
||||
if (typeof conditions === "string") {
|
||||
conditions = [conditions];
|
||||
}
|
||||
conditions.push("svelte");
|
||||
builder.config.conditions = conditions;
|
||||
}
|
||||
|
||||
const { config = kEmptyObject as Partial<BuildConfig> } = builder;
|
||||
const baseCompileOptions = getBaseCompileOptions(options ?? (kEmptyObject as Partial<SvelteOptions>), config);
|
||||
const baseModuleCompileOptions = getBaseModuleCompileOptions(
|
||||
options ?? (kEmptyObject as Partial<SvelteOptions>),
|
||||
config,
|
||||
);
|
||||
|
||||
const ts = new Bun.Transpiler({
|
||||
loader: "ts",
|
||||
target: config.target,
|
||||
});
|
||||
|
||||
builder
|
||||
.onLoad({ filter: /\.svelte$/ }, async function onLoadSvelte(args) {
|
||||
const { path } = args;
|
||||
|
||||
const sourceText = await Bun.file(path).text();
|
||||
|
||||
const side =
|
||||
args && "side" in args // "side" only passed when run from dev server
|
||||
? (args as { side: "client" | "server" }).side
|
||||
: "server";
|
||||
const generate = baseCompileOptions.generate ?? side;
|
||||
|
||||
const hmr = Boolean((args as { hmr?: boolean })["hmr"] ?? process.env.NODE_ENV !== "production");
|
||||
const result = compile(sourceText, {
|
||||
...baseCompileOptions,
|
||||
generate,
|
||||
filename: args.path,
|
||||
hmr,
|
||||
});
|
||||
|
||||
var { js, css } = result;
|
||||
if (css?.code && generate != "server") {
|
||||
const uid = `${basename(path)}-${hash(path)}-style`.replaceAll(`"`, `'`);
|
||||
const virtualName = virtualNamespace + ":" + uid + ".css";
|
||||
virtualCssModules.set(virtualName, { sourcePath: path, source: css.code });
|
||||
js.code += `\nimport "${virtualName}";`;
|
||||
}
|
||||
|
||||
return {
|
||||
contents: result.js.code,
|
||||
loader: "ts",
|
||||
} satisfies OnLoadResult;
|
||||
// TODO: allow plugins to return multiple results.
|
||||
// TODO: support layered sourcemaps
|
||||
})
|
||||
.onLoad({ filter: /\.svelte.[tj]s$/ }, async function onLoadSvelteModule(args) {
|
||||
const { path } = args;
|
||||
|
||||
const side =
|
||||
args && "side" in args // "side" only passed when run from dev server
|
||||
? (args as { side: "client" | "server" }).side
|
||||
: "server";
|
||||
const generate = baseModuleCompileOptions.generate ?? side;
|
||||
|
||||
var sourceText = await Bun.file(path).text();
|
||||
if (path.endsWith(".ts")) {
|
||||
sourceText = await ts.transform(sourceText);
|
||||
}
|
||||
const result = compileModule(sourceText, {
|
||||
...baseModuleCompileOptions,
|
||||
generate,
|
||||
filename: args.path,
|
||||
});
|
||||
|
||||
// NOTE: we assume js/ts modules won't have CSS blocks in them, so no
|
||||
// virtual modules get created.
|
||||
return {
|
||||
contents: result.js.code,
|
||||
loader: "js",
|
||||
};
|
||||
})
|
||||
.onResolve({ filter: /^bun-svelte:/ }, args => {
|
||||
return {
|
||||
path: args.path,
|
||||
namespace: "bun-svelte",
|
||||
};
|
||||
})
|
||||
.onLoad({ filter: /\.css$/, namespace: virtualNamespace }, args => {
|
||||
const { path } = args;
|
||||
|
||||
const mod = virtualCssModules.get(path);
|
||||
if (!mod) throw new Error("Virtual CSS module not found: " + path);
|
||||
const { sourcePath, source } = mod;
|
||||
virtualCssModules.delete(path);
|
||||
|
||||
return {
|
||||
contents: source,
|
||||
loader: "css",
|
||||
watchFiles: [sourcePath],
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default SveltePlugin({ development: true }) as BunPlugin;
|
||||
export { SveltePlugin, type SvelteOptions };
|
||||
4
packages/bun-plugin-svelte/src/modules.d.ts
vendored
Normal file
4
packages/bun-plugin-svelte/src/modules.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
declare module "*.svelte" {
|
||||
const content: any;
|
||||
export default content;
|
||||
}
|
||||
45
packages/bun-plugin-svelte/src/options.spec.ts
Normal file
45
packages/bun-plugin-svelte/src/options.spec.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { describe, beforeAll, it, expect } from "bun:test";
|
||||
import type { BuildConfig } from "bun";
|
||||
import type { CompileOptions } from "svelte/compiler";
|
||||
|
||||
import { getBaseCompileOptions, type SvelteOptions } from "./options";
|
||||
|
||||
describe("getBaseCompileOptions", () => {
|
||||
describe("when no options are provided", () => {
|
||||
const pluginOptions: SvelteOptions = {};
|
||||
let fullDefault: Readonly<CompileOptions>;
|
||||
|
||||
beforeAll(() => {
|
||||
fullDefault = Object.freeze(getBaseCompileOptions(pluginOptions, {}));
|
||||
});
|
||||
|
||||
it("when minification is disabled, whitespace and comments are preserved", () => {
|
||||
expect(getBaseCompileOptions(pluginOptions, { minify: false })).toEqual(
|
||||
expect.objectContaining({
|
||||
preserveWhitespace: true,
|
||||
preserveComments: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("defaults to production mode", () => {
|
||||
expect(fullDefault.dev).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
it.each([{}, { side: "server" }, { side: "client" }, { side: undefined }] as Partial<BuildConfig>[])(
|
||||
"when present, forceSide takes precedence over config (%o)",
|
||||
buildConfig => {
|
||||
expect(getBaseCompileOptions({ forceSide: "client" }, buildConfig)).toEqual(
|
||||
expect.objectContaining({
|
||||
generate: "client",
|
||||
}),
|
||||
);
|
||||
expect(getBaseCompileOptions({ forceSide: "server" }, buildConfig)).toEqual(
|
||||
expect.objectContaining({
|
||||
generate: "server",
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
111
packages/bun-plugin-svelte/src/options.ts
Normal file
111
packages/bun-plugin-svelte/src/options.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { strict as assert } from "node:assert";
|
||||
import { type BuildConfig } from "bun";
|
||||
import type { CompileOptions, ModuleCompileOptions } from "svelte/compiler";
|
||||
|
||||
export interface SvelteOptions {
|
||||
/**
|
||||
* Force client-side or server-side generation.
|
||||
*
|
||||
* By default, this plugin will detect the side of the build based on how
|
||||
* it's used. For example, `"client"` code will be generated when used with {@link Bun.build}.
|
||||
*/
|
||||
forceSide?: "client" | "server";
|
||||
|
||||
/**
|
||||
* When `true`, this plugin will generate development-only checks and other
|
||||
* niceties.
|
||||
*
|
||||
* When `false`, this plugin will generate production-ready code
|
||||
*
|
||||
* Defaults to `true` when run via Bun's dev server, `false` otherwise.
|
||||
*/
|
||||
development?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function validateOptions(options: unknown): asserts options is SvelteOptions {
|
||||
assert(options && typeof options === "object", new TypeError("bun-svelte-plugin: options must be an object"));
|
||||
if ("forceSide" in options) {
|
||||
switch (options.forceSide) {
|
||||
case "client":
|
||||
case "server":
|
||||
break;
|
||||
default:
|
||||
throw new TypeError(
|
||||
`bun-svelte-plugin: forceSide must be either 'client' or 'server', got ${options.forceSide}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function getBaseCompileOptions(pluginOptions: SvelteOptions, config: Partial<BuildConfig>): CompileOptions {
|
||||
let { development = false } = pluginOptions;
|
||||
const { minify = false } = config;
|
||||
|
||||
const shouldMinify = Boolean(minify);
|
||||
const {
|
||||
whitespace: minifyWhitespace,
|
||||
syntax: _minifySyntax,
|
||||
identifiers: _minifyIdentifiers,
|
||||
} = typeof minify === "object"
|
||||
? minify
|
||||
: {
|
||||
whitespace: shouldMinify,
|
||||
syntax: shouldMinify,
|
||||
identifiers: shouldMinify,
|
||||
};
|
||||
|
||||
const generate = generateSide(pluginOptions, config);
|
||||
|
||||
return {
|
||||
css: "external",
|
||||
generate,
|
||||
preserveWhitespace: !minifyWhitespace,
|
||||
preserveComments: !shouldMinify,
|
||||
dev: development,
|
||||
cssHash({ css }) {
|
||||
// same prime number seed used by svelte/compiler.
|
||||
// TODO: ensure this provides enough entropy
|
||||
return `svelte-${hash(css)}`;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function getBaseModuleCompileOptions(
|
||||
pluginOptions: SvelteOptions,
|
||||
config: Partial<BuildConfig>,
|
||||
): ModuleCompileOptions {
|
||||
const { development = false } = pluginOptions;
|
||||
const generate = generateSide(pluginOptions, config);
|
||||
return {
|
||||
dev: development,
|
||||
generate,
|
||||
};
|
||||
}
|
||||
|
||||
function generateSide(pluginOptions: SvelteOptions, config: Partial<BuildConfig>) {
|
||||
let { forceSide } = pluginOptions;
|
||||
const { target } = config;
|
||||
|
||||
if (forceSide == null && typeof target === "string") {
|
||||
switch (target) {
|
||||
case "browser":
|
||||
forceSide = "client";
|
||||
break;
|
||||
case "node":
|
||||
case "bun":
|
||||
forceSide = "server";
|
||||
break;
|
||||
default:
|
||||
// warn? throw?
|
||||
}
|
||||
}
|
||||
return forceSide;
|
||||
}
|
||||
|
||||
export const hash = (content: string): string => Bun.hash(content, 5381).toString(36);
|
||||
@@ -0,0 +1,91 @@
|
||||
// Bun Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Bun.plugin using { forceSide: 'server' } allows for imported components to be SSR'd: foo.svelte - head 1`] = `""`;
|
||||
|
||||
exports[`Bun.plugin using { forceSide: 'server' } allows for imported components to be SSR'd: foo.svelte - body 1`] = `
|
||||
"<!--[--><!---->
|
||||
|
||||
<main class="app svelte-30r5b3lexyb64">
|
||||
<h1 class="svelte-30r5b3lexyb64">Hello World!</h1>
|
||||
</main>
|
||||
|
||||
<!--]-->"
|
||||
`;
|
||||
|
||||
exports[`Bun.plugin Generates server-side code: foo.svelte - head 1`] = `""`;
|
||||
|
||||
exports[`Bun.plugin Generates server-side code: foo.svelte - body 1`] = `
|
||||
"<!--[--><!---->
|
||||
|
||||
<main class="app svelte-30r5b3lexyb64">
|
||||
<h1 class="svelte-30r5b3lexyb64">Hello World!</h1>
|
||||
</main>
|
||||
|
||||
<!--]-->"
|
||||
`;
|
||||
|
||||
exports[`Bun.build Generates server-side code when targeting "node" or "bun": foo.svelte - server-side (node) 1`] = `
|
||||
{
|
||||
"body":
|
||||
"<!--[--><!---->
|
||||
|
||||
<main class="app svelte-30r5b3lexyb64">
|
||||
<h1 class="svelte-30r5b3lexyb64">Hello World!</h1>
|
||||
</main>
|
||||
|
||||
<!--]-->"
|
||||
,
|
||||
"head": "",
|
||||
"html":
|
||||
"<!--[--><!---->
|
||||
|
||||
<main class="app svelte-30r5b3lexyb64">
|
||||
<h1 class="svelte-30r5b3lexyb64">Hello World!</h1>
|
||||
</main>
|
||||
|
||||
<!--]-->"
|
||||
,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Bun.build Generates server-side code when targeting "node" or "bun": foo.svelte - server-side (bun) 1`] = `
|
||||
{
|
||||
"body":
|
||||
"<!--[--><!---->
|
||||
|
||||
<main class="app svelte-30r5b3lexyb64">
|
||||
<h1 class="svelte-30r5b3lexyb64">Hello World!</h1>
|
||||
</main>
|
||||
|
||||
<!--]-->"
|
||||
,
|
||||
"head": "",
|
||||
"html":
|
||||
"<!--[--><!---->
|
||||
|
||||
<main class="app svelte-30r5b3lexyb64">
|
||||
<h1 class="svelte-30r5b3lexyb64">Hello World!</h1>
|
||||
</main>
|
||||
|
||||
<!--]-->"
|
||||
,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Bun.build Generates client-side code when targeting 'browser': foo.svelte - client-side 1`] = `
|
||||
"// test/fixtures/foo.svelte
|
||||
var foo_default = "./foo-y5ajevk1.svelte";
|
||||
export {
|
||||
foo_default as default
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Bun.build Generates client-side code when targeting 'browser': foo.svelte - client-side index 1`] = `
|
||||
"// test/fixtures/foo.svelte
|
||||
var foo_default = "./foo-y5ajevk1.svelte";
|
||||
export {
|
||||
foo_default as default
|
||||
};
|
||||
"
|
||||
`;
|
||||
19
packages/bun-plugin-svelte/test/fixtures/foo.svelte
vendored
Normal file
19
packages/bun-plugin-svelte/test/fixtures/foo.svelte
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<script>
|
||||
let name = "World";
|
||||
</script>
|
||||
|
||||
<main class="app">
|
||||
<h1>Hello {name}!</h1>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
color: #ff3e00;
|
||||
text-align: center;
|
||||
font-size: 2em;
|
||||
font-weight: 100;
|
||||
}
|
||||
.app {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
17
packages/bun-plugin-svelte/test/fixtures/svelte-export-condition.svelte
vendored
Normal file
17
packages/bun-plugin-svelte/test/fixtures/svelte-export-condition.svelte
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<script>
|
||||
import { Canvas } from "@threlte/core";
|
||||
let name = "Bun";
|
||||
</script>
|
||||
|
||||
<main class="app">
|
||||
<h1>Cookin up apps with {name}</h1>
|
||||
<Canvas />
|
||||
</main>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
color: #ff3e00;
|
||||
text-align: center;
|
||||
font-size: 2em;
|
||||
}
|
||||
</style>
|
||||
15
packages/bun-plugin-svelte/test/fixtures/todo-cjs.svelte.ts
vendored
Normal file
15
packages/bun-plugin-svelte/test/fixtures/todo-cjs.svelte.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
class Todo {
|
||||
title: string | undefined = $state();
|
||||
done: boolean = $state(false);
|
||||
createdAt: Date = $state(new Date());
|
||||
|
||||
constructor(title: string) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public toggle(): void {
|
||||
this.done = !this.done;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Todo;
|
||||
13
packages/bun-plugin-svelte/test/fixtures/todo.svelte.ts
vendored
Normal file
13
packages/bun-plugin-svelte/test/fixtures/todo.svelte.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
export class Todo {
|
||||
title: string | undefined = $state();
|
||||
done: boolean = $state(false);
|
||||
createdAt: Date = $state(new Date());
|
||||
|
||||
constructor(title: string) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public toggle(): void {
|
||||
this.done = !this.done;
|
||||
}
|
||||
}
|
||||
25
packages/bun-plugin-svelte/test/fixtures/with-cjs.svelte
vendored
Normal file
25
packages/bun-plugin-svelte/test/fixtures/with-cjs.svelte
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
const Todo = require("./todo-cjs.svelte.ts");
|
||||
|
||||
let name = "World";
|
||||
let todo: Todo = $state(new Todo("Hello World!"));
|
||||
</script>
|
||||
|
||||
<main class="app">
|
||||
<h1>Hello {todo.title}!</h1>
|
||||
<!-- clicking calls toggle -->
|
||||
<input type="checkbox" bind:checked={todo.done} />
|
||||
<button onclick={todo.toggle}>Toggle</button>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
color: #ff3e00;
|
||||
text-align: center;
|
||||
font-size: 2em;
|
||||
font-weight: 100;
|
||||
}
|
||||
.app {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
25
packages/bun-plugin-svelte/test/fixtures/with-modules.svelte
vendored
Normal file
25
packages/bun-plugin-svelte/test/fixtures/with-modules.svelte
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
import { Todo } from "./todo.svelte";
|
||||
|
||||
let name = "World";
|
||||
let todo: Todo = $state(new Todo("Hello World!"));
|
||||
</script>
|
||||
|
||||
<main class="app">
|
||||
<h1>Hello {todo.title}!</h1>
|
||||
<!-- clicking calls toggle -->
|
||||
<input type="checkbox" bind:checked={todo.done} />
|
||||
<button onclick={todo.toggle}>Toggle</button>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
color: #ff3e00;
|
||||
text-align: center;
|
||||
font-size: 2em;
|
||||
font-weight: 100;
|
||||
}
|
||||
.app {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
138
packages/bun-plugin-svelte/test/index.test.ts
Normal file
138
packages/bun-plugin-svelte/test/index.test.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import { describe, beforeAll, it, expect, afterEach, afterAll } from "bun:test";
|
||||
import path from "node:path";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import { render } from "svelte/server";
|
||||
import { SveltePlugin } from "../src";
|
||||
import type { BuildOutput } from "bun";
|
||||
|
||||
const fixturePath = (...segs: string[]) => path.join(import.meta.dirname, "fixtures", ...segs);
|
||||
|
||||
// temp dir that gets deleted after all tests
|
||||
let outdir: string;
|
||||
|
||||
beforeAll(() => {
|
||||
const prefix = `svelte-test-${Math.random().toString(36).substring(2, 15)}`;
|
||||
outdir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
try {
|
||||
fs.rmSync(outdir, { recursive: true, force: true });
|
||||
} catch {
|
||||
// suppress
|
||||
}
|
||||
});
|
||||
|
||||
it("hello world component", async () => {
|
||||
const res = await Bun.build({
|
||||
entrypoints: [fixturePath("foo.svelte")],
|
||||
outdir,
|
||||
plugins: [SveltePlugin()],
|
||||
});
|
||||
expect(res.success).toBeTrue();
|
||||
});
|
||||
|
||||
describe("when importing `.svelte.ts` files with ESM", () => {
|
||||
let res: BuildOutput;
|
||||
|
||||
beforeAll(async () => {
|
||||
res = await Bun.build({
|
||||
entrypoints: [fixturePath("with-modules.svelte")],
|
||||
outdir,
|
||||
plugins: [SveltePlugin()],
|
||||
});
|
||||
});
|
||||
|
||||
it("builds successfully", () => {
|
||||
expect(res.success).toBeTrue();
|
||||
});
|
||||
|
||||
it(`handles "svelte" export condition`, async () => {
|
||||
const res = await Bun.build({
|
||||
entrypoints: [fixturePath("svelte-export-condition.svelte")],
|
||||
outdir,
|
||||
plugins: [SveltePlugin()],
|
||||
});
|
||||
expect(res.success).toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when importing `.svelte.ts` files with CJS", () => {
|
||||
let res: BuildOutput;
|
||||
|
||||
beforeAll(async () => {
|
||||
res = await Bun.build({
|
||||
entrypoints: [fixturePath("with-cjs.svelte")],
|
||||
outdir,
|
||||
plugins: [SveltePlugin()],
|
||||
});
|
||||
});
|
||||
|
||||
it("builds successfully", () => {
|
||||
expect(res.success).toBeTrue();
|
||||
});
|
||||
|
||||
it("does not double-wrap the module with function(module, exports, __filename, __dirname)", async () => {
|
||||
const ts = res.outputs.find(output => output.loader === "ts");
|
||||
expect(ts).toBeDefined();
|
||||
const code = await ts!.text();
|
||||
expect(code).toContain("require_todo_cjs_svelte");
|
||||
expect(code).toContain("var require_todo_cjs_svelte = __commonJS((exports, module) => {\n");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Bun.build", () => {
|
||||
it.each(["node", "bun"] as const)('Generates server-side code when targeting "node" or "bun"', async target => {
|
||||
const res = await Bun.build({
|
||||
entrypoints: [fixturePath("foo.svelte")],
|
||||
outdir,
|
||||
target,
|
||||
plugins: [SveltePlugin({ forceSide: "server" })],
|
||||
});
|
||||
expect(res.success).toBeTrue();
|
||||
const componentPath = res.outputs[0].path;
|
||||
const component = await import(componentPath);
|
||||
expect(component.default).toBeTypeOf("function");
|
||||
expect(render(component.default)).toMatchSnapshot(`foo.svelte - server-side (${target})`);
|
||||
});
|
||||
|
||||
it("Generates client-side code when targeting 'browser'", async () => {
|
||||
const res = await Bun.build({
|
||||
entrypoints: [fixturePath("foo.svelte")],
|
||||
outdir,
|
||||
target: "browser",
|
||||
});
|
||||
|
||||
expect(res.success).toBeTrue();
|
||||
const componentPath = path.resolve(res.outputs[0].path);
|
||||
const entrypoint = await res.outputs[0].text();
|
||||
expect(entrypoint).toMatchSnapshot(`foo.svelte - client-side index`);
|
||||
expect(await Bun.file(componentPath).text()).toMatchSnapshot(`foo.svelte - client-side`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Bun.plugin", () => {
|
||||
afterEach(() => {
|
||||
Bun.plugin.clearAll();
|
||||
});
|
||||
|
||||
// test.only("using { forceSide: 'server' } allows for imported components to be SSR'd", async () => {
|
||||
it("Generates server-side code", async () => {
|
||||
Bun.plugin(SveltePlugin());
|
||||
|
||||
const foo = await import(fixturePath("foo.svelte"));
|
||||
expect(foo).toBeTypeOf("object");
|
||||
expect(foo).toHaveProperty("default");
|
||||
|
||||
const actual = render(foo.default);
|
||||
expect(actual).toEqual(
|
||||
expect.objectContaining({
|
||||
head: expect.any(String),
|
||||
body: expect.any(String),
|
||||
}),
|
||||
);
|
||||
expect(actual.head).toMatchSnapshot("foo.svelte - head");
|
||||
expect(actual.body).toMatchSnapshot("foo.svelte - body");
|
||||
});
|
||||
});
|
||||
34
packages/bun-plugin-svelte/tsconfig.json
Normal file
34
packages/bun-plugin-svelte/tsconfig.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Enable latest features
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"emitDeclarationOnly": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"stripInternal": true,
|
||||
|
||||
// thank you Titian
|
||||
"isolatedDeclarations": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noImplicitAny": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
}
|
||||
}
|
||||
6
packages/bun-types/ambient.d.ts
vendored
6
packages/bun-types/ambient.d.ts
vendored
@@ -23,3 +23,9 @@ declare module "*.html" {
|
||||
var contents: any;
|
||||
export = contents;
|
||||
}
|
||||
|
||||
declare module "*.svg" {
|
||||
// Bun 1.2.3 added support for frontend dev server
|
||||
var contents: `${string}.svg`;
|
||||
export = contents;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user