mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
Compare commits
101 Commits
bun-v1.3.2
...
claude/fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1458b45e7c | ||
|
|
ddcec61f59 | ||
|
|
29051f9340 | ||
|
|
7076fbbe68 | ||
|
|
67be07fca4 | ||
|
|
25b91e5c86 | ||
|
|
3c60fcda33 | ||
|
|
8da699681e | ||
|
|
7a06dfcb89 | ||
|
|
9ed53283a4 | ||
|
|
4450d738fa | ||
|
|
7ec1aa8c95 | ||
|
|
abb1b0c4d7 | ||
|
|
274e01c737 | ||
|
|
a0c5edb15b | ||
|
|
5702b39ef1 | ||
|
|
b72ba31441 | ||
|
|
b92d2edcff | ||
|
|
a4af0aa4a8 | ||
|
|
28b950e2b0 | ||
|
|
595ad7de93 | ||
|
|
b38ba38a18 | ||
|
|
788f03454d | ||
|
|
0e23375d20 | ||
|
|
d584c86d5b | ||
|
|
0480d55a67 | ||
|
|
9189fc4fa1 | ||
|
|
b554626662 | ||
|
|
0054506538 | ||
|
|
1b36d35253 | ||
|
|
9a9473d4b9 | ||
|
|
2d954995cd | ||
|
|
11b20aa508 | ||
|
|
cac8e62635 | ||
|
|
f03957474e | ||
|
|
ab80bbe4c2 | ||
|
|
af498a0483 | ||
|
|
7c485177ee | ||
|
|
9513c1d1d9 | ||
|
|
509a97a435 | ||
|
|
983bb52df7 | ||
|
|
8b5b36ec7a | ||
|
|
87eca6bbc7 | ||
|
|
2cb8d4eae8 | ||
|
|
e53ceb62ec | ||
|
|
277fc558e2 | ||
|
|
5908bfbfc6 | ||
|
|
19f21c00bd | ||
|
|
8650e7ace4 | ||
|
|
b2c219a56c | ||
|
|
f216673f98 | ||
|
|
a70f2b7ff9 | ||
|
|
65a215bb4e | ||
|
|
c3c91442ac | ||
|
|
93ab167a8d | ||
|
|
d8ee26509c | ||
|
|
21d582a3cd | ||
|
|
d7bf4fb443 | ||
|
|
263d1ab178 | ||
|
|
08843030f5 | ||
|
|
9ccc8fb795 | ||
|
|
4c03d3b8b6 | ||
|
|
6d2ce3892b | ||
|
|
e03d3bee10 | ||
|
|
fff47f0267 | ||
|
|
4e1d9a2cbc | ||
|
|
1f0c885e91 | ||
|
|
ab32a2fc4a | ||
|
|
8912957aa5 | ||
|
|
df4e42bf1c | ||
|
|
f67bec90c5 | ||
|
|
fa099336da | ||
|
|
7f8dff64c4 | ||
|
|
98a01e5d2a | ||
|
|
d1fa27acce | ||
|
|
cf6662d48f | ||
|
|
2563a9b3ad | ||
|
|
e0aae8adc1 | ||
|
|
6b8a75f6ab | ||
|
|
97c113d010 | ||
|
|
7f4e65464e | ||
|
|
4b05629131 | ||
|
|
d868c6019c | ||
|
|
0c42b46af3 | ||
|
|
1cee6cf36b | ||
|
|
9671a98dca | ||
|
|
c6aa5a97dc | ||
|
|
925e8bcfe1 | ||
|
|
b87ac4a781 | ||
|
|
b876938f6d | ||
|
|
6b70b71895 | ||
|
|
80a5b59fe5 | ||
|
|
d87a928b94 | ||
|
|
05d0475c6c | ||
|
|
143ad2ea58 | ||
|
|
6f9843ea9a | ||
|
|
0a307ed880 | ||
|
|
b4f85c8866 | ||
|
|
614e8292e3 | ||
|
|
3829b6d0aa | ||
|
|
f30e3951a7 |
19
.aikido
Normal file
19
.aikido
Normal file
@@ -0,0 +1,19 @@
|
||||
exclude:
|
||||
paths:
|
||||
- test
|
||||
- scripts
|
||||
- bench
|
||||
- packages/bun-lambda
|
||||
- packages/bun-release
|
||||
- packages/bun-wasm
|
||||
- packages/bun-vscode
|
||||
- packages/bun-plugin-yaml
|
||||
- packages/bun-plugin-svelte
|
||||
- packages/bun-native-plugin-rs
|
||||
- packages/bun-native-bundler-plugin-api
|
||||
- packages/bun-inspector-protocol
|
||||
- packages/bun-inspector-frontend
|
||||
- packages/bun-error
|
||||
- packages/bun-debug-adapter-protocol
|
||||
- packages/bun-build-mdx-rs
|
||||
- packages/@types/bun
|
||||
@@ -133,6 +133,20 @@ RUN ARCH=$(if [ "$TARGETARCH" = "arm64" ]; then echo "arm64"; else echo "amd64";
|
||||
|
||||
RUN mkdir -p /var/cache/buildkite-agent /var/log/buildkite-agent /var/run/buildkite-agent /etc/buildkite-agent /var/lib/buildkite-agent/cache/bun
|
||||
|
||||
# The following is necessary to configure buildkite to use a stable
|
||||
# checkout directory. sccache hashes absolute paths into its cache keys,
|
||||
# so if buildkite uses a different checkout path each time (which it does
|
||||
# by default), sccache will be useless.
|
||||
RUN mkdir -p -m 755 /var/lib/buildkite-agent/hooks && \
|
||||
cat <<'EOF' > /var/lib/buildkite-agent/hooks/environment
|
||||
#!/bin/sh
|
||||
set -efu
|
||||
|
||||
export BUILDKITE_BUILD_CHECKOUT_PATH=/var/lib/buildkite-agent/build
|
||||
EOF
|
||||
|
||||
RUN chmod 744 /var/lib/buildkite-agent/hooks/environment
|
||||
|
||||
COPY ../*/agent.mjs /var/bun/scripts/
|
||||
|
||||
ENV BUN_INSTALL_CACHE=/var/lib/buildkite-agent/cache/bun
|
||||
|
||||
@@ -556,7 +556,6 @@ function getBuildBunStep(platform, options) {
|
||||
/**
|
||||
* @typedef {Object} TestOptions
|
||||
* @property {string} [buildId]
|
||||
* @property {boolean} [unifiedTests]
|
||||
* @property {string[]} [testFiles]
|
||||
* @property {boolean} [dryRun]
|
||||
*/
|
||||
@@ -569,7 +568,7 @@ function getBuildBunStep(platform, options) {
|
||||
*/
|
||||
function getTestBunStep(platform, options, testOptions = {}) {
|
||||
const { os, profile } = platform;
|
||||
const { buildId, unifiedTests, testFiles } = testOptions;
|
||||
const { buildId, testFiles } = testOptions;
|
||||
|
||||
const args = [`--step=${getTargetKey(platform)}-build-bun`];
|
||||
if (buildId) {
|
||||
@@ -591,7 +590,7 @@ function getTestBunStep(platform, options, testOptions = {}) {
|
||||
agents: getTestAgent(platform, options),
|
||||
retry: getRetry(),
|
||||
cancel_on_build_failing: isMergeQueue(),
|
||||
parallelism: unifiedTests ? undefined : os === "darwin" ? 2 : 10,
|
||||
parallelism: os === "darwin" ? 2 : 10,
|
||||
timeout_in_minutes: profile === "asan" || os === "windows" ? 45 : 30,
|
||||
env: {
|
||||
ASAN_OPTIONS: "allow_user_segv_handler=1:disable_coredump=0:detect_leaks=0",
|
||||
@@ -773,8 +772,6 @@ function getBenchmarkStep() {
|
||||
* @property {Platform[]} [buildPlatforms]
|
||||
* @property {Platform[]} [testPlatforms]
|
||||
* @property {string[]} [testFiles]
|
||||
* @property {boolean} [unifiedBuilds]
|
||||
* @property {boolean} [unifiedTests]
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -945,22 +942,6 @@ function getOptionsStep() {
|
||||
default: "false",
|
||||
options: booleanOptions,
|
||||
},
|
||||
{
|
||||
key: "unified-builds",
|
||||
select: "Do you want to build each platform in a single step?",
|
||||
hint: "If true, builds will not be split into separate steps (this will likely slow down the build)",
|
||||
required: false,
|
||||
default: "false",
|
||||
options: booleanOptions,
|
||||
},
|
||||
{
|
||||
key: "unified-tests",
|
||||
select: "Do you want to run tests in a single step?",
|
||||
hint: "If true, tests will not be split into separate steps (this will be very slow)",
|
||||
required: false,
|
||||
default: "false",
|
||||
options: booleanOptions,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -1026,8 +1007,6 @@ async function getPipelineOptions() {
|
||||
buildImages: parseBoolean(options["build-images"]),
|
||||
publishImages: parseBoolean(options["publish-images"]),
|
||||
testFiles: parseArray(options["test-files"]),
|
||||
unifiedBuilds: parseBoolean(options["unified-builds"]),
|
||||
unifiedTests: parseBoolean(options["unified-tests"]),
|
||||
buildPlatforms: buildPlatformKeys?.length
|
||||
? buildPlatformKeys.flatMap(key => buildProfiles.map(profile => ({ ...buildPlatformsMap.get(key), profile })))
|
||||
: Array.from(buildPlatformsMap.values()),
|
||||
@@ -1109,7 +1088,7 @@ async function getPipeline(options = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
let { skipBuilds, forceBuilds, unifiedBuilds, dryRun } = options;
|
||||
let { skipBuilds, forceBuilds, dryRun } = options;
|
||||
dryRun = dryRun || !!buildImages;
|
||||
|
||||
/** @type {string | undefined} */
|
||||
@@ -1140,13 +1119,16 @@ async function getPipeline(options = {}) {
|
||||
dependsOn.push(`${imageKey}-build-image`);
|
||||
}
|
||||
|
||||
const steps = [];
|
||||
steps.push(getBuildCppStep(target, options));
|
||||
steps.push(getBuildZigStep(target, options));
|
||||
steps.push(getLinkBunStep(target, options));
|
||||
|
||||
return getStepWithDependsOn(
|
||||
{
|
||||
key: getTargetKey(target),
|
||||
group: getTargetLabel(target),
|
||||
steps: unifiedBuilds
|
||||
? [getBuildBunStep(target, options)]
|
||||
: [getBuildCppStep(target, options), getBuildZigStep(target, options), getLinkBunStep(target, options)],
|
||||
steps,
|
||||
},
|
||||
...dependsOn,
|
||||
);
|
||||
@@ -1155,13 +1137,13 @@ async function getPipeline(options = {}) {
|
||||
}
|
||||
|
||||
if (!isMainBranch()) {
|
||||
const { skipTests, forceTests, unifiedTests, testFiles } = options;
|
||||
const { skipTests, forceTests, testFiles } = options;
|
||||
if (!skipTests || forceTests) {
|
||||
steps.push(
|
||||
...testPlatforms.map(target => ({
|
||||
key: getTargetKey(target),
|
||||
group: getTargetLabel(target),
|
||||
steps: [getTestBunStep(target, options, { unifiedTests, testFiles, buildId })],
|
||||
steps: [getTestBunStep(target, options, { testFiles, buildId })],
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -16,6 +16,7 @@
|
||||
*.map text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
|
||||
*.md text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
|
||||
*.mdc text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
|
||||
*.mdx text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
|
||||
*.mjs text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
|
||||
*.mts text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
|
||||
|
||||
|
||||
2
.github/workflows/format.yml
vendored
2
.github/workflows/format.yml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
pull_request:
|
||||
merge_group:
|
||||
env:
|
||||
BUN_VERSION: "1.2.20"
|
||||
BUN_VERSION: "1.3.2"
|
||||
LLVM_VERSION: "19.1.7"
|
||||
LLVM_VERSION_MAJOR: "19"
|
||||
|
||||
|
||||
@@ -9,3 +9,6 @@ test/snippets
|
||||
test/js/node/test
|
||||
test/napi/node-napi-tests
|
||||
bun.lock
|
||||
|
||||
# the output codeblocks need to stay minified
|
||||
docs/bundler/minifier.mdx
|
||||
|
||||
@@ -25,16 +25,6 @@ if(CMAKE_HOST_APPLE)
|
||||
endif()
|
||||
include(SetupLLVM)
|
||||
|
||||
find_program(SCCACHE_PROGRAM sccache)
|
||||
if(SCCACHE_PROGRAM AND NOT DEFINED ENV{NO_SCCACHE})
|
||||
include(SetupSccache)
|
||||
else()
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if(CCACHE_PROGRAM)
|
||||
include(SetupCcache)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# --- Project ---
|
||||
|
||||
parse_package_json(VERSION_VARIABLE DEFAULT_VERSION)
|
||||
@@ -57,6 +47,16 @@ include(SetupEsbuild)
|
||||
include(SetupZig)
|
||||
include(SetupRust)
|
||||
|
||||
find_program(SCCACHE_PROGRAM sccache)
|
||||
if(SCCACHE_PROGRAM AND NOT DEFINED ENV{NO_SCCACHE})
|
||||
include(SetupSccache)
|
||||
else()
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if(CCACHE_PROGRAM)
|
||||
include(SetupCcache)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Generate dependency versions header
|
||||
include(GenerateDependencyVersions)
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ Bun generally takes about 2.5 minutes to compile a debug build when there are Zi
|
||||
- Batch up your changes
|
||||
- Ensure zls is running with incremental watching for LSP errors (if you use VSCode and install Zig and run `bun run build` once to download Zig, this should just work)
|
||||
- Prefer using the debugger ("CodeLLDB" in VSCode) to step through the code.
|
||||
- Use debug logs. `BUN_DEBUG_<scope>=1` will enable debug logging for the corresponding `Output.scoped(.<scope>, .hidden)` logs. You can also set `BUN_DEBUG_QUIET_LOGS=1` to disable all debug logging that isn't explicitly enabled. To dump debug lgos into a file, `BUN_DEBUG=<path-to-file>.log`. Debug logs are aggressively removed in release builds.
|
||||
- Use debug logs. `BUN_DEBUG_<scope>=1` will enable debug logging for the corresponding `Output.scoped(.<scope>, .hidden)` logs. You can also set `BUN_DEBUG_QUIET_LOGS=1` to disable all debug logging that isn't explicitly enabled. To dump debug logs into a file, `BUN_DEBUG=<path-to-file>.log`. Debug logs are aggressively removed in release builds.
|
||||
- src/js/\*\*.ts changes are pretty much instant to rebuild. C++ changes are a bit slower, but still much faster than the Zig code (Zig is one compilation unit, C++ is many).
|
||||
|
||||
## Code generation scripts
|
||||
|
||||
10
README.md
10
README.md
@@ -54,7 +54,7 @@ Bun supports Linux (x64 & arm64), macOS (x64 & Apple Silicon) and Windows (x64).
|
||||
curl -fsSL https://bun.com/install | bash
|
||||
|
||||
# on windows
|
||||
powershell -c "irm bun.com/install.ps1 | iex"
|
||||
powershell -c "irm bun.sh/install.ps1 | iex"
|
||||
|
||||
# with npm
|
||||
npm install -g bun
|
||||
@@ -104,13 +104,13 @@ bun upgrade --canary
|
||||
- [File types (Loaders)](https://bun.com/docs/runtime/loaders)
|
||||
- [TypeScript](https://bun.com/docs/runtime/typescript)
|
||||
- [JSX](https://bun.com/docs/runtime/jsx)
|
||||
- [Environment variables](https://bun.com/docs/runtime/env)
|
||||
- [Environment variables](https://bun.com/docs/runtime/environment-variables)
|
||||
- [Bun APIs](https://bun.com/docs/runtime/bun-apis)
|
||||
- [Web APIs](https://bun.com/docs/runtime/web-apis)
|
||||
- [Node.js compatibility](https://bun.com/docs/runtime/nodejs-apis)
|
||||
- [Node.js compatibility](https://bun.com/docs/runtime/nodejs-compat)
|
||||
- [Single-file executable](https://bun.com/docs/bundler/executables)
|
||||
- [Plugins](https://bun.com/docs/runtime/plugins)
|
||||
- [Watch mode / Hot Reloading](https://bun.com/docs/runtime/hot)
|
||||
- [Watch mode / Hot Reloading](https://bun.com/docs/runtime/watch-mode)
|
||||
- [Module resolution](https://bun.com/docs/runtime/modules)
|
||||
- [Auto-install](https://bun.com/docs/runtime/autoimport)
|
||||
- [bunfig.toml](https://bun.com/docs/runtime/bunfig)
|
||||
@@ -230,7 +230,7 @@ bun upgrade --canary
|
||||
|
||||
- Ecosystem
|
||||
- [Use React and JSX](https://bun.com/guides/ecosystem/react)
|
||||
- [Use EdgeDB with Bun](https://bun.com/guides/ecosystem/edgedb)
|
||||
- [Use Gel with Bun](https://bun.com/guides/ecosystem/gel)
|
||||
- [Use Prisma with Bun](https://bun.com/guides/ecosystem/prisma)
|
||||
- [Add Sentry to a Bun app](https://bun.com/guides/ecosystem/sentry)
|
||||
- [Create a Discord bot](https://bun.com/guides/ecosystem/discordjs)
|
||||
|
||||
@@ -13,7 +13,4 @@ export function run(opts = {}) {
|
||||
}
|
||||
|
||||
export const bench = Mitata.bench;
|
||||
|
||||
export function group(_name, fn) {
|
||||
return Mitata.group(fn);
|
||||
}
|
||||
export const group = Mitata.group;
|
||||
|
||||
156
bench/snippets/compression-streams.mjs
Normal file
156
bench/snippets/compression-streams.mjs
Normal file
@@ -0,0 +1,156 @@
|
||||
import { bench, group, run } from "../runner.mjs";
|
||||
|
||||
const runAll = !process.argv.includes("--simple");
|
||||
|
||||
const small = new Uint8Array(1024);
|
||||
const medium = new Uint8Array(1024 * 100);
|
||||
const large = new Uint8Array(1024 * 1024);
|
||||
|
||||
for (let i = 0; i < large.length; i++) {
|
||||
const value = Math.floor(Math.sin(i / 100) * 128 + 128);
|
||||
if (i < small.length) small[i] = value;
|
||||
if (i < medium.length) medium[i] = value;
|
||||
large[i] = value;
|
||||
}
|
||||
|
||||
const format = new Intl.NumberFormat("en-US", { notation: "compact", unit: "byte" });
|
||||
|
||||
async function compress(data, format) {
|
||||
const cs = new CompressionStream(format);
|
||||
const writer = cs.writable.getWriter();
|
||||
const reader = cs.readable.getReader();
|
||||
|
||||
writer.write(data);
|
||||
writer.close();
|
||||
|
||||
const chunks = [];
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
chunks.push(value);
|
||||
}
|
||||
|
||||
const result = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));
|
||||
let offset = 0;
|
||||
for (const chunk of chunks) {
|
||||
result.set(chunk, offset);
|
||||
offset += chunk.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function decompress(data, format) {
|
||||
const ds = new DecompressionStream(format);
|
||||
const writer = ds.writable.getWriter();
|
||||
const reader = ds.readable.getReader();
|
||||
|
||||
writer.write(data);
|
||||
writer.close();
|
||||
|
||||
const chunks = [];
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
chunks.push(value);
|
||||
}
|
||||
|
||||
const result = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));
|
||||
let offset = 0;
|
||||
for (const chunk of chunks) {
|
||||
result.set(chunk, offset);
|
||||
offset += chunk.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function roundTrip(data, format) {
|
||||
const compressed = await compress(data, format);
|
||||
return await decompress(compressed, format);
|
||||
}
|
||||
|
||||
const formats = ["deflate", "gzip", "deflate-raw"];
|
||||
if (runAll) formats.push("brotli", "zstd");
|
||||
|
||||
// Small data benchmarks (1KB)
|
||||
group(`CompressionStream ${format.format(small.length)}`, () => {
|
||||
for (const fmt of formats) {
|
||||
try {
|
||||
new CompressionStream(fmt);
|
||||
bench(fmt, async () => await compress(small, fmt));
|
||||
} catch (e) {
|
||||
// Skip unsupported formats
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Medium data benchmarks (100KB)
|
||||
group(`CompressionStream ${format.format(medium.length)}`, () => {
|
||||
for (const fmt of formats) {
|
||||
try {
|
||||
new CompressionStream(fmt);
|
||||
bench(fmt, async () => await compress(medium, fmt));
|
||||
} catch (e) {}
|
||||
}
|
||||
});
|
||||
|
||||
// Large data benchmarks (1MB)
|
||||
group(`CompressionStream ${format.format(large.length)}`, () => {
|
||||
for (const fmt of formats) {
|
||||
try {
|
||||
new CompressionStream(fmt);
|
||||
bench(fmt, async () => await compress(large, fmt));
|
||||
} catch (e) {
|
||||
// Skip unsupported formats
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const compressedData = {};
|
||||
for (const fmt of formats) {
|
||||
try {
|
||||
compressedData[fmt] = {
|
||||
small: await compress(small, fmt),
|
||||
medium: await compress(medium, fmt),
|
||||
large: await compress(large, fmt),
|
||||
};
|
||||
} catch (e) {
|
||||
// Skip unsupported formats
|
||||
}
|
||||
}
|
||||
|
||||
group(`DecompressionStream ${format.format(small.length)}`, () => {
|
||||
for (const fmt of formats) {
|
||||
if (compressedData[fmt]) {
|
||||
bench(fmt, async () => await decompress(compressedData[fmt].small, fmt));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
group(`DecompressionStream ${format.format(medium.length)}`, () => {
|
||||
for (const fmt of formats) {
|
||||
if (compressedData[fmt]) {
|
||||
bench(fmt, async () => await decompress(compressedData[fmt].medium, fmt));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
group(`DecompressionStream ${format.format(large.length)}`, () => {
|
||||
for (const fmt of formats) {
|
||||
if (compressedData[fmt]) {
|
||||
bench(fmt, async () => await decompress(compressedData[fmt].large, fmt));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
group(`roundtrip ${format.format(large.length)}`, () => {
|
||||
for (const fmt of formats) {
|
||||
try {
|
||||
new CompressionStream(fmt);
|
||||
bench(fmt, async () => await roundTrip(large, fmt));
|
||||
} catch (e) {
|
||||
// Skip unsupported formats
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await run();
|
||||
57
build.zig
57
build.zig
@@ -18,22 +18,6 @@ const OperatingSystem = @import("src/env.zig").OperatingSystem;
|
||||
|
||||
const pathRel = fs.path.relative;
|
||||
|
||||
/// When updating this, make sure to adjust SetupZig.cmake
|
||||
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 ++ ", 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.",
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
const zero_sha = "0000000000000000000000000000000000000000";
|
||||
|
||||
const BunBuildOptions = struct {
|
||||
@@ -48,6 +32,7 @@ const BunBuildOptions = struct {
|
||||
/// enable debug logs in release builds
|
||||
enable_logs: bool = false,
|
||||
enable_asan: bool,
|
||||
enable_fuzzilli: bool,
|
||||
enable_valgrind: bool,
|
||||
use_mimalloc: bool,
|
||||
tracy_callstack_depth: u16,
|
||||
@@ -97,9 +82,10 @@ const BunBuildOptions = struct {
|
||||
opts.addOption(bool, "baseline", this.isBaseline());
|
||||
opts.addOption(bool, "enable_logs", this.enable_logs);
|
||||
opts.addOption(bool, "enable_asan", this.enable_asan);
|
||||
opts.addOption(bool, "enable_fuzzilli", this.enable_fuzzilli);
|
||||
opts.addOption(bool, "enable_valgrind", this.enable_valgrind);
|
||||
opts.addOption(bool, "use_mimalloc", this.use_mimalloc);
|
||||
opts.addOption([]const u8, "reported_nodejs_version", b.fmt("{}", .{this.reported_nodejs_version}));
|
||||
opts.addOption([]const u8, "reported_nodejs_version", b.fmt("{f}", .{this.reported_nodejs_version}));
|
||||
opts.addOption(bool, "zig_self_hosted_backend", this.no_llvm);
|
||||
opts.addOption(bool, "override_no_export_cpp_apis", this.override_no_export_cpp_apis);
|
||||
|
||||
@@ -134,8 +120,8 @@ pub fn getOSVersionMin(os: OperatingSystem) ?Target.Query.OsVersion {
|
||||
|
||||
pub fn getOSGlibCVersion(os: OperatingSystem) ?Version {
|
||||
return switch (os) {
|
||||
// Compiling with a newer glibc than this will break certain cloud environments.
|
||||
.linux => .{ .major = 2, .minor = 27, .patch = 0 },
|
||||
// Compiling with a newer glibc than this will break certain cloud environments. See symbols.test.ts.
|
||||
.linux => .{ .major = 2, .minor = 26, .patch = 0 },
|
||||
|
||||
else => null,
|
||||
};
|
||||
@@ -271,6 +257,7 @@ pub fn build(b: *Build) !void {
|
||||
.tracy_callstack_depth = b.option(u16, "tracy_callstack_depth", "") orelse 10,
|
||||
.enable_logs = b.option(bool, "enable_logs", "Enable logs in release") orelse false,
|
||||
.enable_asan = b.option(bool, "enable_asan", "Enable asan") orelse false,
|
||||
.enable_fuzzilli = b.option(bool, "enable_fuzzilli", "Enable fuzzilli instrumentation") orelse false,
|
||||
.enable_valgrind = b.option(bool, "enable_valgrind", "Enable valgrind") orelse false,
|
||||
.use_mimalloc = b.option(bool, "use_mimalloc", "Use mimalloc as default allocator") orelse false,
|
||||
.llvm_codegen_threads = b.option(u32, "llvm_codegen_threads", "Number of threads to use for LLVM codegen") orelse 1,
|
||||
@@ -290,14 +277,16 @@ pub fn build(b: *Build) !void {
|
||||
var o = build_options;
|
||||
var unit_tests = b.addTest(.{
|
||||
.name = "bun-test",
|
||||
.optimize = build_options.optimize,
|
||||
.root_source_file = b.path("src/unit_test.zig"),
|
||||
.test_runner = .{ .path = b.path("src/main_test.zig"), .mode = .simple },
|
||||
.target = build_options.target,
|
||||
.root_module = b.createModule(.{
|
||||
.optimize = build_options.optimize,
|
||||
.root_source_file = b.path("src/unit_test.zig"),
|
||||
.target = build_options.target,
|
||||
.omit_frame_pointer = false,
|
||||
.strip = false,
|
||||
}),
|
||||
.use_llvm = !build_options.no_llvm,
|
||||
.use_lld = if (build_options.os == .mac) false else !build_options.no_llvm,
|
||||
.omit_frame_pointer = false,
|
||||
.strip = false,
|
||||
});
|
||||
configureObj(b, &o, unit_tests);
|
||||
// Setting `linker_allow_shlib_undefined` causes the linker to ignore
|
||||
@@ -331,6 +320,7 @@ pub fn build(b: *Build) !void {
|
||||
var step = b.step("check", "Check for semantic analysis errors");
|
||||
var bun_check_obj = addBunObject(b, &build_options);
|
||||
bun_check_obj.generated_bin = null;
|
||||
// bun_check_obj.use_llvm = false;
|
||||
step.dependOn(&bun_check_obj.step);
|
||||
|
||||
// The default install step will run zig build check. This is so ZLS
|
||||
@@ -503,6 +493,7 @@ fn addMultiCheck(
|
||||
.no_llvm = root_build_options.no_llvm,
|
||||
.enable_asan = root_build_options.enable_asan,
|
||||
.enable_valgrind = root_build_options.enable_valgrind,
|
||||
.enable_fuzzilli = root_build_options.enable_fuzzilli,
|
||||
.use_mimalloc = root_build_options.use_mimalloc,
|
||||
.override_no_export_cpp_apis = root_build_options.override_no_export_cpp_apis,
|
||||
};
|
||||
@@ -616,15 +607,22 @@ fn configureObj(b: *Build, opts: *BunBuildOptions, obj: *Compile) void {
|
||||
obj.llvm_codegen_threads = opts.llvm_codegen_threads orelse 0;
|
||||
}
|
||||
|
||||
obj.no_link_obj = true;
|
||||
obj.no_link_obj = opts.os != .windows;
|
||||
|
||||
|
||||
if (opts.enable_asan and !enableFastBuild(b)) {
|
||||
if (@hasField(Build.Module, "sanitize_address")) {
|
||||
if (opts.enable_fuzzilli) {
|
||||
obj.sanitize_coverage_trace_pc_guard = true;
|
||||
}
|
||||
obj.root_module.sanitize_address = true;
|
||||
} else {
|
||||
const fail_step = b.addFail("asan is not supported on this platform");
|
||||
obj.step.dependOn(&fail_step.step);
|
||||
}
|
||||
} else if (opts.enable_fuzzilli) {
|
||||
const fail_step = b.addFail("fuzzilli requires asan");
|
||||
obj.step.dependOn(&fail_step.step);
|
||||
}
|
||||
obj.bundle_compiler_rt = false;
|
||||
obj.bundle_ubsan_rt = false;
|
||||
@@ -779,6 +777,13 @@ fn addInternalImports(b: *Build, mod: *Module, opts: *BunBuildOptions) void {
|
||||
mod.addImport("cpp", cppImport);
|
||||
cppImport.addImport("bun", mod);
|
||||
}
|
||||
{
|
||||
const ciInfoImport = b.createModule(.{
|
||||
.root_source_file = (std.Build.LazyPath{ .cwd_relative = opts.codegen_path }).path(b, "ci_info.zig"),
|
||||
});
|
||||
mod.addImport("ci_info", ciInfoImport);
|
||||
ciInfoImport.addImport("bun", mod);
|
||||
}
|
||||
inline for (.{
|
||||
.{ .import = "completions-bash", .file = b.path("completions/bun.bash") },
|
||||
.{ .import = "completions-zsh", .file = b.path("completions/bun.zsh") },
|
||||
@@ -804,7 +809,7 @@ fn addInternalImports(b: *Build, mod: *Module, opts: *BunBuildOptions) void {
|
||||
fn propagateImports(source_mod: *Module) !void {
|
||||
var seen = std.AutoHashMap(*Module, void).init(source_mod.owner.graph.arena);
|
||||
defer seen.deinit();
|
||||
var queue = std.ArrayList(*Module).init(source_mod.owner.graph.arena);
|
||||
var queue = std.array_list.Managed(*Module).init(source_mod.owner.graph.arena);
|
||||
defer queue.deinit();
|
||||
try queue.appendSlice(source_mod.import_table.values());
|
||||
while (queue.pop()) |mod| {
|
||||
|
||||
11
bun.lock
11
bun.lock
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 0,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "bun",
|
||||
@@ -31,12 +32,6 @@
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19",
|
||||
},
|
||||
},
|
||||
},
|
||||
"overrides": {
|
||||
@@ -162,8 +157,6 @@
|
||||
|
||||
"@types/node": ["@types/node@24.2.1", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ=="],
|
||||
|
||||
"@types/react": ["@types/react@19.1.10", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg=="],
|
||||
|
||||
"aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="],
|
||||
|
||||
"before-after-hook": ["before-after-hook@2.2.3", "", {}, "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="],
|
||||
@@ -192,8 +185,6 @@
|
||||
|
||||
"constant-case": ["constant-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", "upper-case": "^2.0.2" } }, "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
"deprecation": ["deprecation@2.3.1", "", {}, "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="],
|
||||
|
||||
"detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
|
||||
|
||||
@@ -51,6 +51,23 @@ if(ENABLE_ASAN)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENABLE_FUZZILLI)
|
||||
register_compiler_flags(
|
||||
DESCRIPTION "Enable coverage instrumentation for fuzzing"
|
||||
-fsanitize-coverage=trace-pc-guard
|
||||
)
|
||||
|
||||
register_linker_flags(
|
||||
DESCRIPTION "Link coverage instrumentation"
|
||||
-fsanitize-coverage=trace-pc-guard
|
||||
)
|
||||
|
||||
register_compiler_flags(
|
||||
DESCRIPTION "Enable fuzzilli-specific code"
|
||||
-DFUZZILLI_ENABLED
|
||||
)
|
||||
endif()
|
||||
|
||||
# --- Optimization level ---
|
||||
if(DEBUG)
|
||||
register_compiler_flags(
|
||||
|
||||
@@ -125,7 +125,8 @@ setx(CWD ${CMAKE_SOURCE_DIR})
|
||||
setx(BUILD_PATH ${CMAKE_BINARY_DIR})
|
||||
|
||||
optionx(CACHE_PATH FILEPATH "The path to the cache directory" DEFAULT ${BUILD_PATH}/cache)
|
||||
optionx(CACHE_STRATEGY "read-write|read-only|none" "The strategy to use for caching" DEFAULT "read-write")
|
||||
optionx(CACHE_STRATEGY "auto|distributed|local|none" "The strategy to use for caching" DEFAULT
|
||||
"auto")
|
||||
|
||||
optionx(CI BOOL "If CI is enabled" DEFAULT OFF)
|
||||
optionx(ENABLE_ANALYSIS BOOL "If static analysis targets should be enabled" DEFAULT OFF)
|
||||
@@ -141,9 +142,39 @@ optionx(TMP_PATH FILEPATH "The path to the temporary directory" DEFAULT ${BUILD_
|
||||
|
||||
# --- Helper functions ---
|
||||
|
||||
# list_filter_out_regex()
|
||||
#
|
||||
# Description:
|
||||
# Filters out elements from a list that match a regex pattern.
|
||||
#
|
||||
# Arguments:
|
||||
# list - The list of strings to traverse
|
||||
# pattern - The regex pattern to filter out
|
||||
# touched - A variable to set if any items were removed
|
||||
function(list_filter_out_regex list pattern touched)
|
||||
set(result_list "${${list}}")
|
||||
set(keep_list)
|
||||
set(was_modified OFF)
|
||||
|
||||
foreach(line IN LISTS result_list)
|
||||
if(line MATCHES "${pattern}")
|
||||
set(was_modified ON)
|
||||
else()
|
||||
list(APPEND keep_list ${line})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(${list} "${keep_list}" PARENT_SCOPE)
|
||||
set(${touched} ${was_modified} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# setenv()
|
||||
# Description:
|
||||
# Sets an environment variable during the build step, and writes it to a .env file.
|
||||
#
|
||||
# See Also:
|
||||
# unsetenv()
|
||||
#
|
||||
# Arguments:
|
||||
# variable string - The variable to set
|
||||
# value string - The value to set the variable to
|
||||
@@ -156,13 +187,7 @@ function(setenv variable value)
|
||||
|
||||
if(EXISTS ${ENV_PATH})
|
||||
file(STRINGS ${ENV_PATH} ENV_FILE ENCODING UTF-8)
|
||||
|
||||
foreach(line ${ENV_FILE})
|
||||
if(line MATCHES "^${variable}=")
|
||||
list(REMOVE_ITEM ENV_FILE ${line})
|
||||
set(ENV_MODIFIED ON)
|
||||
endif()
|
||||
endforeach()
|
||||
list_filter_out_regex(ENV_FILE "^${variable}=" ENV_MODIFIED)
|
||||
|
||||
if(ENV_MODIFIED)
|
||||
list(APPEND ENV_FILE "${variable}=${value}")
|
||||
@@ -178,6 +203,28 @@ function(setenv variable value)
|
||||
message(STATUS "Set ENV ${variable}: ${value}")
|
||||
endfunction()
|
||||
|
||||
# See setenv()
|
||||
# Description:
|
||||
# Exact opposite of setenv().
|
||||
# Arguments:
|
||||
# variable string - The variable to unset.
|
||||
# See Also:
|
||||
# setenv()
|
||||
function(unsetenv variable)
|
||||
set(ENV_PATH ${BUILD_PATH}/.env)
|
||||
if(NOT EXISTS ${ENV_PATH})
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(STRINGS ${ENV_PATH} ENV_FILE ENCODING UTF-8)
|
||||
list_filter_out_regex(ENV_FILE "^${variable}=" ENV_MODIFIED)
|
||||
|
||||
if(ENV_MODIFIED)
|
||||
list(JOIN ENV_FILE "\n" ENV_FILE)
|
||||
file(WRITE ${ENV_PATH} ${ENV_FILE})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# satisfies_range()
|
||||
# Description:
|
||||
# Check if a version satisfies a version range or list of ranges
|
||||
|
||||
@@ -127,6 +127,8 @@ if (NOT ENABLE_ASAN)
|
||||
set(ENABLE_ZIG_ASAN OFF)
|
||||
endif()
|
||||
|
||||
optionx(ENABLE_FUZZILLI BOOL "If fuzzilli support should be enabled" DEFAULT OFF)
|
||||
|
||||
if(RELEASE AND LINUX AND CI AND NOT ENABLE_ASSERTIONS AND NOT ENABLE_ASAN)
|
||||
set(DEFAULT_LTO ON)
|
||||
else()
|
||||
|
||||
@@ -34,26 +34,6 @@ register_command(
|
||||
ALWAYS_RUN
|
||||
)
|
||||
|
||||
if(GIT_CHANGED_SOURCES)
|
||||
set(CLANG_FORMAT_CHANGED_SOURCES)
|
||||
foreach(source ${CLANG_FORMAT_SOURCES})
|
||||
list(FIND GIT_CHANGED_SOURCES ${source} index)
|
||||
if(NOT ${index} EQUAL -1)
|
||||
list(APPEND CLANG_FORMAT_CHANGED_SOURCES ${source})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(CLANG_FORMAT_CHANGED_SOURCES)
|
||||
set(CLANG_FORMAT_DIFF_COMMAND ${CLANG_FORMAT_PROGRAM}
|
||||
-i # edits files in-place
|
||||
--verbose
|
||||
${CLANG_FORMAT_CHANGED_SOURCES}
|
||||
)
|
||||
else()
|
||||
set(CLANG_FORMAT_DIFF_COMMAND ${CMAKE_COMMAND} -E echo "No changed files for clang-format")
|
||||
endif()
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
clang-format-diff
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
set(CLANG_TIDY_SOURCES ${BUN_C_SOURCES} ${BUN_CXX_SOURCES})
|
||||
|
||||
set(CLANG_TIDY_COMMAND ${CLANG_TIDY_PROGRAM}
|
||||
-p ${BUILD_PATH}
|
||||
-p ${BUILD_PATH}
|
||||
--config-file=${CWD}/.clang-tidy
|
||||
)
|
||||
|
||||
@@ -40,27 +40,6 @@ register_command(
|
||||
ALWAYS_RUN
|
||||
)
|
||||
|
||||
if(GIT_CHANGED_SOURCES)
|
||||
set(CLANG_TIDY_CHANGED_SOURCES)
|
||||
foreach(source ${CLANG_TIDY_SOURCES})
|
||||
list(FIND GIT_CHANGED_SOURCES ${source} index)
|
||||
if(NOT ${index} EQUAL -1)
|
||||
list(APPEND CLANG_TIDY_CHANGED_SOURCES ${source})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(CLANG_TIDY_CHANGED_SOURCES)
|
||||
set(CLANG_TIDY_DIFF_COMMAND ${CLANG_TIDY_PROGRAM}
|
||||
${CLANG_TIDY_CHANGED_SOURCES}
|
||||
--fix
|
||||
--fix-errors
|
||||
--fix-notes
|
||||
)
|
||||
else()
|
||||
set(CLANG_TIDY_DIFF_COMMAND ${CMAKE_COMMAND} -E echo "No changed files for clang-tidy")
|
||||
endif()
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
clang-tidy-diff
|
||||
|
||||
@@ -92,26 +92,6 @@ register_command(
|
||||
ALWAYS_RUN
|
||||
)
|
||||
|
||||
if(GIT_CHANGED_SOURCES)
|
||||
set(PRETTIER_CHANGED_SOURCES)
|
||||
foreach(source ${PRETTIER_SOURCES})
|
||||
list(FIND GIT_CHANGED_SOURCES ${source} index)
|
||||
if(NOT ${index} EQUAL -1)
|
||||
list(APPEND PRETTIER_CHANGED_SOURCES ${source})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(PRETTIER_CHANGED_SOURCES)
|
||||
set(PRETTIER_DIFF_COMMAND ${PRETTIER_COMMAND}
|
||||
--write
|
||||
--plugin=prettier-plugin-organize-imports
|
||||
${PRETTIER_CHANGED_SOURCES}
|
||||
)
|
||||
else()
|
||||
set(PRETTIER_DIFF_COMMAND ${CMAKE_COMMAND} -E echo "No changed files for prettier")
|
||||
endif()
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
prettier-diff
|
||||
|
||||
@@ -25,25 +25,6 @@ register_command(
|
||||
ALWAYS_RUN
|
||||
)
|
||||
|
||||
if(GIT_CHANGED_SOURCES)
|
||||
set(ZIG_FORMAT_CHANGED_SOURCES)
|
||||
foreach(source ${ZIG_FORMAT_SOURCES})
|
||||
list(FIND GIT_CHANGED_SOURCES ${source} index)
|
||||
if(NOT ${index} EQUAL -1)
|
||||
list(APPEND ZIG_FORMAT_CHANGED_SOURCES ${source})
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(ZIG_FORMAT_CHANGED_SOURCES)
|
||||
set(ZIG_FORMAT_DIFF_COMMAND ${ZIG_EXECUTABLE}
|
||||
fmt
|
||||
${ZIG_FORMAT_CHANGED_SOURCES}
|
||||
)
|
||||
else()
|
||||
set(ZIG_FORMAT_DIFF_COMMAND ${CMAKE_COMMAND} -E echo "No changed files for zig-format")
|
||||
endif()
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
zig-format-diff
|
||||
|
||||
@@ -317,6 +317,10 @@ set(BUN_CPP_OUTPUTS
|
||||
${CODEGEN_PATH}/cpp.zig
|
||||
)
|
||||
|
||||
set(BUN_CI_INFO_OUTPUTS
|
||||
${CODEGEN_PATH}/ci_info.zig
|
||||
)
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
bun-cppbind
|
||||
@@ -334,6 +338,21 @@ register_command(
|
||||
${BUN_CPP_OUTPUTS}
|
||||
)
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
bun-ci-info
|
||||
COMMENT
|
||||
"Generating CI info"
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE}
|
||||
${CWD}/src/codegen/ci_info.ts
|
||||
${CODEGEN_PATH}/ci_info.zig
|
||||
SOURCES
|
||||
${BUN_JAVASCRIPT_CODEGEN_SOURCES}
|
||||
OUTPUTS
|
||||
${BUN_CI_INFO_OUTPUTS}
|
||||
)
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
bun-js-modules
|
||||
@@ -612,6 +631,7 @@ set(BUN_ZIG_GENERATED_SOURCES
|
||||
${BUN_ZIG_GENERATED_CLASSES_OUTPUTS}
|
||||
${BUN_JAVASCRIPT_OUTPUTS}
|
||||
${BUN_CPP_OUTPUTS}
|
||||
${BUN_CI_INFO_OUTPUTS}
|
||||
${BUN_BINDGENV2_ZIG_OUTPUTS}
|
||||
)
|
||||
|
||||
@@ -675,6 +695,7 @@ register_command(
|
||||
-Dcpu=${ZIG_CPU}
|
||||
-Denable_logs=$<IF:$<BOOL:${ENABLE_LOGS}>,true,false>
|
||||
-Denable_asan=$<IF:$<BOOL:${ENABLE_ZIG_ASAN}>,true,false>
|
||||
-Denable_fuzzilli=$<IF:$<BOOL:${ENABLE_FUZZILLI}>,true,false>
|
||||
-Denable_valgrind=$<IF:$<BOOL:${ENABLE_VALGRIND}>,true,false>
|
||||
-Duse_mimalloc=$<IF:$<BOOL:${USE_MIMALLOC_AS_DEFAULT_ALLOCATOR}>,true,false>
|
||||
-Dllvm_codegen_threads=${LLVM_ZIG_CODEGEN_THREADS}
|
||||
|
||||
@@ -4,41 +4,9 @@ find_command(
|
||||
COMMAND
|
||||
git
|
||||
REQUIRED
|
||||
OFF
|
||||
${CI}
|
||||
)
|
||||
|
||||
if(NOT GIT_PROGRAM)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(GIT_DIFF_COMMAND ${GIT_PROGRAM} diff --no-color --name-only --diff-filter=AMCR origin/main HEAD)
|
||||
|
||||
execute_process(
|
||||
COMMAND
|
||||
${GIT_DIFF_COMMAND}
|
||||
WORKING_DIRECTORY
|
||||
${CWD}
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
OUTPUT_VARIABLE
|
||||
GIT_DIFF
|
||||
ERROR_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_VARIABLE
|
||||
GIT_DIFF_ERROR
|
||||
RESULT_VARIABLE
|
||||
GIT_DIFF_RESULT
|
||||
)
|
||||
|
||||
if(NOT GIT_DIFF_RESULT EQUAL 0)
|
||||
message(WARNING "Command failed: ${GIT_DIFF_COMMAND} ${GIT_DIFF_ERROR}")
|
||||
return()
|
||||
endif()
|
||||
|
||||
string(REPLACE "\n" ";" GIT_CHANGED_SOURCES "${GIT_DIFF}")
|
||||
|
||||
if(CI)
|
||||
set(GIT_CHANGED_SOURCES "${GIT_CHANGED_SOURCES}")
|
||||
message(STATUS "Set GIT_CHANGED_SOURCES: ${GIT_CHANGED_SOURCES}")
|
||||
endif()
|
||||
|
||||
list(TRANSFORM GIT_CHANGED_SOURCES PREPEND ${CWD}/)
|
||||
list(LENGTH GIT_CHANGED_SOURCES GIT_CHANGED_SOURCES_COUNT)
|
||||
|
||||
@@ -1,60 +1,108 @@
|
||||
# Setup sccache as the C and C++ compiler launcher to speed up builds by caching
|
||||
if(CACHE_STRATEGY STREQUAL "none")
|
||||
return()
|
||||
endif()
|
||||
|
||||
function(check_aws_credentials OUT_VAR)
|
||||
set(HAS_CREDENTIALS FALSE)
|
||||
set(SCCACHE_SHARED_CACHE_REGION "us-west-1")
|
||||
set(SCCACHE_SHARED_CACHE_BUCKET "bun-build-sccache-store")
|
||||
|
||||
if(DEFINED ENV{AWS_ACCESS_KEY_ID} AND DEFINED ENV{AWS_SECRET_ACCESS_KEY})
|
||||
set(HAS_CREDENTIALS TRUE)
|
||||
message(NOTICE
|
||||
"sccache: Using AWS credentials found in environment variables")
|
||||
# Function to check if the system AWS credentials have access to the sccache S3 bucket.
|
||||
function(check_aws_credentials OUT_VAR)
|
||||
# Install dependencies first
|
||||
execute_process(
|
||||
COMMAND ${BUN_EXECUTABLE} install --frozen-lockfile
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/scripts/build-cache
|
||||
RESULT_VARIABLE INSTALL_EXIT_CODE
|
||||
OUTPUT_VARIABLE INSTALL_OUTPUT
|
||||
ERROR_VARIABLE INSTALL_ERROR
|
||||
)
|
||||
|
||||
if(NOT INSTALL_EXIT_CODE EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to install dependencies in scripts/build-cache\n"
|
||||
"Exit code: ${INSTALL_EXIT_CODE}\n"
|
||||
"Output: ${INSTALL_OUTPUT}\n"
|
||||
"Error: ${INSTALL_ERROR}")
|
||||
endif()
|
||||
|
||||
# Check for ~/.aws directory since sccache may use that.
|
||||
if(NOT HAS_CREDENTIALS)
|
||||
if(WIN32)
|
||||
set(AWS_CONFIG_DIR "$ENV{USERPROFILE}/.aws")
|
||||
else()
|
||||
set(AWS_CONFIG_DIR "$ENV{HOME}/.aws")
|
||||
endif()
|
||||
# Check AWS credentials
|
||||
execute_process(
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE}
|
||||
run
|
||||
have-access.ts
|
||||
--bucket ${SCCACHE_SHARED_CACHE_BUCKET}
|
||||
--region ${SCCACHE_SHARED_CACHE_REGION}
|
||||
WORKING_DIRECTORY
|
||||
${CMAKE_SOURCE_DIR}/scripts/build-cache
|
||||
RESULT_VARIABLE HAVE_ACCESS_EXIT_CODE
|
||||
)
|
||||
|
||||
if(EXISTS "${AWS_CONFIG_DIR}/credentials")
|
||||
set(HAS_CREDENTIALS TRUE)
|
||||
message(NOTICE
|
||||
"sccache: Using AWS credentials found in ${AWS_CONFIG_DIR}/credentials")
|
||||
endif()
|
||||
if(HAVE_ACCESS_EXIT_CODE EQUAL 0)
|
||||
set(HAS_CREDENTIALS TRUE)
|
||||
else()
|
||||
set(HAS_CREDENTIALS FALSE)
|
||||
endif()
|
||||
|
||||
set(${OUT_VAR} ${HAS_CREDENTIALS} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(check_running_in_ci OUT_VAR)
|
||||
set(IS_CI FALSE)
|
||||
|
||||
# Query EC2 instance metadata service to check if running on buildkite-agent
|
||||
# The IP address 169.254.169.254 is a well-known link-local address for querying EC2 instance
|
||||
# metdata:
|
||||
# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html
|
||||
execute_process(
|
||||
COMMAND curl -s -m 0.5 http://169.254.169.254/latest/meta-data/tags/instance/Service
|
||||
OUTPUT_VARIABLE METADATA_OUTPUT
|
||||
ERROR_VARIABLE METADATA_ERROR
|
||||
RESULT_VARIABLE METADATA_RESULT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
# Check if the request succeeded and returned exactly "buildkite-agent"
|
||||
if(METADATA_RESULT EQUAL 0 AND METADATA_OUTPUT STREQUAL "buildkite-agent")
|
||||
set(IS_CI TRUE)
|
||||
endif()
|
||||
|
||||
set(${OUT_VAR} ${IS_CI} PARENT_SCOPE)
|
||||
# Configure sccache to use the local cache only.
|
||||
function(sccache_configure_local_filesystem)
|
||||
unsetenv(SCCACHE_BUCKET)
|
||||
unsetenv(SCCACHE_REGION)
|
||||
setenv(SCCACHE_DIR "${CACHE_PATH}/sccache")
|
||||
endfunction()
|
||||
|
||||
check_running_in_ci(IS_IN_CI)
|
||||
find_command(VARIABLE SCCACHE_PROGRAM COMMAND sccache REQUIRED ${IS_IN_CI})
|
||||
# Configure sccache to use the distributed cache (S3 + local).
|
||||
function(sccache_configure_distributed)
|
||||
setenv(SCCACHE_BUCKET "${SCCACHE_SHARED_CACHE_BUCKET}")
|
||||
setenv(SCCACHE_REGION "${SCCACHE_SHARED_CACHE_REGION}")
|
||||
setenv(SCCACHE_DIR "${CACHE_PATH}/sccache")
|
||||
endfunction()
|
||||
|
||||
function(sccache_configure_environment_ci)
|
||||
if(CACHE_STRATEGY STREQUAL "auto" OR CACHE_STRATEGY STREQUAL "distributed")
|
||||
check_aws_credentials(HAS_AWS_CREDENTIALS)
|
||||
if(HAS_AWS_CREDENTIALS)
|
||||
sccache_configure_distributed()
|
||||
message(NOTICE "sccache: Using distributed cache strategy.")
|
||||
else()
|
||||
message(FATAL_ERROR "CI CACHE_STRATEGY is set to '${CACHE_STRATEGY}', but no valid AWS "
|
||||
"credentials were found. Note that 'auto' requires AWS credentials to access the shared "
|
||||
"cache in CI.")
|
||||
endif()
|
||||
elseif(CACHE_STRATEGY STREQUAL "local")
|
||||
# We disallow this because we want our CI runs to always used the shared cache to accelerate
|
||||
# builds.
|
||||
# none, distributed and auto are all okay.
|
||||
#
|
||||
# If local is configured, it's as good as "none", so this is probably user error.
|
||||
message(FATAL_ERROR "CI CACHE_STRATEGY is set to 'local', which is not allowed.")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(sccache_configure_environment_developer)
|
||||
# Local environments can use any strategy they like. S3 is set up in such a way so as to clean
|
||||
# itself from old entries automatically.
|
||||
if (CACHE_STRATEGY STREQUAL "auto" OR CACHE_STRATEGY STREQUAL "local")
|
||||
# In the local environment, we prioritize using the local cache. This is because sccache takes
|
||||
# into consideration the whole absolute path of the files being compiled, and it's very
|
||||
# unlikely users will have the same absolute paths on their local machines.
|
||||
sccache_configure_local_filesystem()
|
||||
message(NOTICE "sccache: Using local cache strategy.")
|
||||
elseif(CACHE_STRATEGY STREQUAL "distributed")
|
||||
check_aws_credentials(HAS_AWS_CREDENTIALS)
|
||||
if(HAS_AWS_CREDENTIALS)
|
||||
sccache_configure_distributed()
|
||||
message(NOTICE "sccache: Using distributed cache strategy.")
|
||||
else()
|
||||
message(FATAL_ERROR "CACHE_STRATEGY is set to 'distributed', but no valid AWS credentials "
|
||||
"were found.")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
find_command(VARIABLE SCCACHE_PROGRAM COMMAND sccache REQUIRED ${CI})
|
||||
if(NOT SCCACHE_PROGRAM)
|
||||
message(WARNING "sccache not found. Your builds will be slower.")
|
||||
return()
|
||||
@@ -66,25 +114,10 @@ foreach(arg ${SCCACHE_ARGS})
|
||||
list(APPEND CMAKE_ARGS -D${arg}=${${arg}})
|
||||
endforeach()
|
||||
|
||||
# Configure S3 bucket for distributed caching
|
||||
setenv(SCCACHE_BUCKET "bun-build-sccache-store")
|
||||
setenv(SCCACHE_REGION "us-west-1")
|
||||
setenv(SCCACHE_DIR "${CACHE_PATH}/sccache")
|
||||
|
||||
# Handle credentials based on cache strategy
|
||||
if (CACHE_STRATEGY STREQUAL "read-only")
|
||||
setenv(SCCACHE_S3_NO_CREDENTIALS "1")
|
||||
message(STATUS "sccache configured in read-only mode.")
|
||||
else()
|
||||
# Check for AWS credentials and enable anonymous access if needed
|
||||
check_aws_credentials(HAS_AWS_CREDENTIALS)
|
||||
if(NOT IS_IN_CI AND NOT HAS_AWS_CREDENTIALS)
|
||||
setenv(SCCACHE_S3_NO_CREDENTIALS "1")
|
||||
message(NOTICE "sccache: No AWS credentials found, enabling anonymous S3 "
|
||||
"access. Writing to the cache will be disabled.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
setenv(SCCACHE_LOG "info")
|
||||
|
||||
message(STATUS "sccache configured for bun-build-sccache-store (us-west-1).")
|
||||
if (CI)
|
||||
sccache_configure_environment_ci()
|
||||
else()
|
||||
sccache_configure_environment_developer()
|
||||
endif()
|
||||
|
||||
@@ -20,7 +20,7 @@ else()
|
||||
unsupported(CMAKE_SYSTEM_NAME)
|
||||
endif()
|
||||
|
||||
set(ZIG_COMMIT "55fdbfa0c86be86b68d43a4ba761e6909eb0d7b2")
|
||||
set(ZIG_COMMIT "c1423ff3fc7064635773a4a4616c5bf986eb00fe")
|
||||
optionx(ZIG_TARGET STRING "The zig target to use" DEFAULT ${DEFAULT_ZIG_TARGET})
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
@@ -55,13 +55,7 @@ optionx(ZIG_OBJECT_FORMAT "obj|bc" "Output file format for Zig object files" DEF
|
||||
optionx(ZIG_LOCAL_CACHE_DIR FILEPATH "The path to local the zig cache directory" DEFAULT ${CACHE_PATH}/zig/local)
|
||||
optionx(ZIG_GLOBAL_CACHE_DIR FILEPATH "The path to the global zig cache directory" DEFAULT ${CACHE_PATH}/zig/global)
|
||||
|
||||
if(CI)
|
||||
set(ZIG_COMPILER_SAFE_DEFAULT ON)
|
||||
else()
|
||||
set(ZIG_COMPILER_SAFE_DEFAULT OFF)
|
||||
endif()
|
||||
|
||||
optionx(ZIG_COMPILER_SAFE BOOL "Download a ReleaseSafe build of the Zig compiler." DEFAULT ${ZIG_COMPILER_SAFE_DEFAULT})
|
||||
optionx(ZIG_COMPILER_SAFE BOOL "Download a ReleaseSafe build of the Zig compiler." DEFAULT ${CI})
|
||||
|
||||
setenv(ZIG_LOCAL_CACHE_DIR ${ZIG_LOCAL_CACHE_DIR})
|
||||
setenv(ZIG_GLOBAL_CACHE_DIR ${ZIG_GLOBAL_CACHE_DIR})
|
||||
|
||||
@@ -34,7 +34,7 @@ By default, Bun's CSS bundler targets the following browsers:
|
||||
|
||||
The CSS Nesting specification allows you to write more concise and intuitive stylesheets by nesting selectors inside one another. Instead of repeating parent selectors across your CSS file, you can write child styles directly within their parent blocks.
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```scss title="styles.css" icon="file-code"
|
||||
/* With nesting */
|
||||
.card {
|
||||
background: white;
|
||||
@@ -72,7 +72,7 @@ Bun's CSS bundler automatically converts this nested syntax into traditional fla
|
||||
|
||||
You can also nest media queries and other at-rules inside selectors, eliminating the need to repeat selector patterns:
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```scss title="styles.css" icon="file-code"
|
||||
.responsive-element {
|
||||
display: block;
|
||||
|
||||
@@ -100,7 +100,7 @@ This compiles to:
|
||||
|
||||
The `color-mix()` function gives you an easy way to blend two colors together according to a specified ratio in a chosen color space. This powerful feature lets you create color variations without manually calculating the resulting values.
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
```scss title="styles.css" icon="file-code"
|
||||
.button {
|
||||
/* Mix blue and red in the RGB color space with a 30/70 proportion */
|
||||
background-color: color-mix(in srgb, blue 30%, red);
|
||||
|
||||
@@ -231,23 +231,67 @@ const myPlugin: BunPlugin = {
|
||||
### onResolve
|
||||
|
||||
<Tabs>
|
||||
<Tab title="options">- 🟢 `filter` - 🟢 `namespace`</Tab>
|
||||
<Tab title="options">
|
||||
|
||||
- 🟢 `filter`
|
||||
- 🟢 `namespace`
|
||||
|
||||
</Tab>
|
||||
<Tab title="arguments">
|
||||
- 🟢 `path` - 🟢 `importer` - 🔴 `namespace` - 🔴 `resolveDir` - 🔴 `kind` - 🔴 `pluginData`
|
||||
|
||||
- 🟢 `path`
|
||||
- 🟢 `importer`
|
||||
- 🔴 `namespace`
|
||||
- 🔴 `resolveDir`
|
||||
- 🔴 `kind`
|
||||
- 🔴 `pluginData`
|
||||
|
||||
</Tab>
|
||||
<Tab title="results">
|
||||
- 🟢 `namespace` - 🟢 `path` - 🔴 `errors` - 🔴 `external` - 🔴 `pluginData` - 🔴 `pluginName` - 🔴 `sideEffects` -
|
||||
🔴 `suffix` - 🔴 `warnings` - 🔴 `watchDirs` - 🔴 `watchFiles`
|
||||
|
||||
- 🟢 `namespace`
|
||||
- 🟢 `path`
|
||||
- 🔴 `errors`
|
||||
- 🔴 `external`
|
||||
- 🔴 `pluginData`
|
||||
- 🔴 `pluginName`
|
||||
- 🔴 `sideEffects`
|
||||
- 🔴 `suffix`
|
||||
- 🔴 `warnings`
|
||||
- 🔴 `watchDirs`
|
||||
- 🔴 `watchFiles`
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### onLoad
|
||||
|
||||
<Tabs>
|
||||
<Tab title="options">- 🟢 `filter` - 🟢 `namespace`</Tab>
|
||||
<Tab title="arguments">- 🟢 `path` - 🔴 `namespace` - 🔴 `suffix` - 🔴 `pluginData`</Tab>
|
||||
<Tab title="options">
|
||||
|
||||
- 🟢 `filter`
|
||||
- 🟢 `namespace`
|
||||
|
||||
</Tab>
|
||||
<Tab title="arguments">
|
||||
|
||||
- 🟢 `path`
|
||||
- 🔴 `namespace`
|
||||
- 🔴 `suffix`
|
||||
- 🔴 `pluginData`
|
||||
|
||||
</Tab>
|
||||
<Tab title="results">
|
||||
- 🟢 `contents` - 🟢 `loader` - 🔴 `errors` - 🔴 `pluginData` - 🔴 `pluginName` - 🔴 `resolveDir` - 🔴 `warnings` -
|
||||
🔴 `watchDirs` - 🔴 `watchFiles`
|
||||
|
||||
- 🟢 `contents`
|
||||
- 🟢 `loader`
|
||||
- 🔴 `errors`
|
||||
- 🔴 `pluginData`
|
||||
- 🔴 `pluginName`
|
||||
- 🔴 `resolveDir`
|
||||
- 🔴 `warnings`
|
||||
- 🔴 `watchDirs`
|
||||
- 🔴 `watchFiles`
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -90,7 +90,7 @@ The order of the `--target` flag does not matter, as long as they're delimited b
|
||||
| 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-windows-arm64~~ | ~~Windows~~ | ~~arm64~~ | ❌ | ❌ | - |
|
||||
| bun-darwin-x64 | macOS | x64 | ✅ | ✅ | - |
|
||||
| bun-darwin-arm64 | macOS | arm64 | ✅ | N/A | - |
|
||||
| bun-linux-x64-musl | Linux | x64 | ✅ | ✅ | musl |
|
||||
@@ -118,7 +118,7 @@ These constants are embedded directly into your compiled binary at build time, p
|
||||
|
||||
<Note>
|
||||
For comprehensive examples and advanced patterns, see the [Build-time constants
|
||||
guide](https://bun.com/guides/runtime/build-time-constants).
|
||||
guide](/guides/runtime/build-time-constants).
|
||||
</Note>
|
||||
|
||||
---
|
||||
@@ -154,8 +154,8 @@ Using bytecode compilation, `tsc` starts 2x faster:
|
||||
Bytecode compilation moves parsing overhead for large input files from runtime to bundle time. Your app starts faster, in exchange for making the `bun build` command a little slower. It doesn't obscure source code.
|
||||
|
||||
<Warning>
|
||||
**Experimental:** Bytecode compilation is an experimental feature introduced in Bun v1.1.30. Only `cjs` format is
|
||||
supported (which means no top-level-await). Let us know if you run into any issues!
|
||||
**Experimental:** Bytecode compilation is an experimental feature. Only `cjs` format is supported (which means no
|
||||
top-level-await). Let us know if you run into any issues!
|
||||
</Warning>
|
||||
|
||||
### What do these flags do?
|
||||
@@ -183,6 +183,35 @@ console.log(process.execArgv); // ["--smol", "--user-agent=MyBot"]
|
||||
|
||||
---
|
||||
|
||||
## Disabling automatic config loading
|
||||
|
||||
By default, standalone executables look for `.env` and `bunfig.toml` files in the directory where the executable is run. You can disable this behavior at build time for deterministic execution regardless of the user's working directory.
|
||||
|
||||
```bash icon="terminal" terminal
|
||||
# Disable .env loading
|
||||
bun build --compile --no-compile-autoload-dotenv ./app.ts --outfile myapp
|
||||
|
||||
# Disable bunfig.toml loading
|
||||
bun build --compile --no-compile-autoload-bunfig ./app.ts --outfile myapp
|
||||
|
||||
# Disable both
|
||||
bun build --compile --no-compile-autoload-dotenv --no-compile-autoload-bunfig ./app.ts --outfile myapp
|
||||
```
|
||||
|
||||
You can also configure this via the JavaScript API:
|
||||
|
||||
```ts
|
||||
await Bun.build({
|
||||
entrypoints: ["./app.ts"],
|
||||
compile: {
|
||||
autoloadDotenv: false, // Disable .env loading
|
||||
autoloadBunfig: false, // Disable bunfig.toml loading
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Act as the Bun CLI
|
||||
|
||||
<Note>New in Bun v1.2.16</Note>
|
||||
@@ -216,7 +245,7 @@ However, with the `BUN_BE_BUN=1` environment variable, it acts just like the `bu
|
||||
|
||||
```bash icon="terminal" terminal
|
||||
# With the env var, the executable acts like the `bun` CLI
|
||||
bun_BE_BUN=1 ./such-bun install
|
||||
BUN_BE_BUN=1 ./such-bun install
|
||||
```
|
||||
|
||||
```txt
|
||||
@@ -259,12 +288,12 @@ console.log(`Server running at http://localhost:${server.port}`);
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello World</h1>
|
||||
<script src="./app.js"></script>
|
||||
<script src="./app.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```ts app.js icon="file-code"
|
||||
```ts app.ts icon="file-code"
|
||||
console.log("Hello from the client!");
|
||||
```
|
||||
|
||||
@@ -320,7 +349,7 @@ new Worker(new URL("./my-worker.ts", import.meta.url));
|
||||
new Worker(new URL("./my-worker.ts", import.meta.url).href);
|
||||
```
|
||||
|
||||
As of Bun v1.1.25, when you add multiple entrypoints to a standalone executable, they will be bundled separately into the executable.
|
||||
When you add multiple entrypoints to a standalone executable, they will be bundled separately into the executable.
|
||||
|
||||
In the future, we may automatically detect usages of statically-known paths in `new Worker(path)` and then bundle those into the executable, but for now, you'll need to add it to the shell command manually like the above example.
|
||||
|
||||
@@ -395,7 +424,7 @@ This database is read-write, but all changes are lost when the executable exits
|
||||
|
||||
### Embed N-API Addons
|
||||
|
||||
As of Bun v1.0.23, you can embed `.node` files into executables.
|
||||
You can embed `.node` files into executables.
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
const addon = require("./addon.node");
|
||||
@@ -524,12 +553,46 @@ codesign -vvv --verify ./myapp
|
||||
|
||||
---
|
||||
|
||||
## Code splitting
|
||||
|
||||
Standalone executables support code splitting. Use `--compile` with `--splitting` to create an executable that loads code-split chunks at runtime.
|
||||
|
||||
```bash
|
||||
bun build --compile --splitting ./src/entry.ts --outdir ./build
|
||||
```
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```ts src/entry.ts icon="/icons/typescript.svg"
|
||||
console.log("Entrypoint loaded");
|
||||
const lazy = await import("./lazy.ts");
|
||||
lazy.hello();
|
||||
```
|
||||
|
||||
```ts src/lazy.ts icon="/icons/typescript.svg"
|
||||
export function hello() {
|
||||
console.log("Lazy module loaded");
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
./build/entry
|
||||
```
|
||||
|
||||
```txt
|
||||
Entrypoint loaded
|
||||
Lazy module loaded
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Unsupported CLI arguments
|
||||
|
||||
Currently, the `--compile` flag can only accept a single entrypoint at a time and does not support the following flags:
|
||||
|
||||
- `--outdir` — use `outfile` instead.
|
||||
- `--splitting`
|
||||
- `--outdir` — use `outfile` instead (except when using with `--splitting`).
|
||||
- `--public-path`
|
||||
- `--target=node` or `--target=browser`
|
||||
- `--no-bundle` - we always bundle everything into the executable.
|
||||
|
||||
@@ -632,7 +632,7 @@ const server = serve({
|
||||
console.log(`🚀 Server running on ${server.url}`);
|
||||
```
|
||||
|
||||
```html title="public/index.html"
|
||||
```html title="public/index.html" icon="file-code"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
@@ -757,7 +757,7 @@ export function App() {
|
||||
}
|
||||
```
|
||||
|
||||
```css title="src/styles.css"
|
||||
```css title="src/styles.css" icon="file-code"
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -999,7 +999,7 @@ CMD ["bun", "index.js"]
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```bash title=".env.production" icon="file-code"
|
||||
```ini title=".env.production" icon="file-code"
|
||||
NODE_ENV=production
|
||||
PORT=3000
|
||||
DATABASE_URL=postgresql://user:pass@localhost:5432/myapp
|
||||
|
||||
@@ -9,7 +9,7 @@ Hot Module Replacement (HMR) allows you to update modules in a running applicati
|
||||
|
||||
## `import.meta.hot` API Reference
|
||||
|
||||
Bun implements a client-side HMR API modeled after [Vite's `import.meta.hot` API](https://vitejs.dev/guide/api-hmr.html). It can be checked for with `if (import.meta.hot)`, tree-shaking it in production.
|
||||
Bun implements a client-side HMR API modeled after [Vite's `import.meta.hot` API](https://vite.dev/guide/api-hmr). It can be checked for with `if (import.meta.hot)`, tree-shaking it in production.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
if (import.meta.hot) {
|
||||
@@ -144,7 +144,7 @@ Indicates that multiple dependencies' modules can be accepted. This variant acce
|
||||
|
||||
`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()`).
|
||||
|
||||
```jsx title="index.ts" icon="/icons/typescript.svg"
|
||||
```tsx title="index.tsx" icon="/icons/typescript.svg"
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { App } from "./app";
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ bun ./index.html
|
||||
```
|
||||
|
||||
```
|
||||
Bun v1.2.20
|
||||
Bun v1.3.3
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Press h + Enter to show shortcuts
|
||||
@@ -51,7 +51,7 @@ bun index.html
|
||||
```
|
||||
|
||||
```
|
||||
Bun v1.2.20
|
||||
Bun v1.3.3
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Press h + Enter to show shortcuts
|
||||
@@ -81,7 +81,7 @@ bun ./index.html ./about.html
|
||||
```
|
||||
|
||||
```txt
|
||||
Bun v1.2.20
|
||||
Bun v1.3.3
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Routes:
|
||||
@@ -104,7 +104,7 @@ bun ./**/*.html
|
||||
```
|
||||
|
||||
```
|
||||
Bun v1.2.20
|
||||
Bun v1.3.3
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Routes:
|
||||
@@ -122,7 +122,7 @@ bun ./index.html ./about/index.html ./about/foo/index.html
|
||||
```
|
||||
|
||||
```
|
||||
Bun v1.2.20
|
||||
Bun v1.3.3
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Routes:
|
||||
@@ -164,7 +164,7 @@ For example:
|
||||
}
|
||||
```
|
||||
|
||||
```css abc.css
|
||||
```css abc.css icon="file-code"
|
||||
body {
|
||||
background-color: red;
|
||||
}
|
||||
@@ -174,7 +174,7 @@ body {
|
||||
|
||||
This outputs:
|
||||
|
||||
```css
|
||||
```css styles.css icon="file-code"
|
||||
body {
|
||||
background-color: red;
|
||||
}
|
||||
@@ -237,13 +237,27 @@ Then, reference TailwindCSS in your HTML via `<link>` tag, `@import` in CSS, or
|
||||
|
||||
<Tabs>
|
||||
<Tab title="index.html">
|
||||
|
||||
```html title="index.html" icon="file-code"
|
||||
{/* Reference TailwindCSS in your HTML */}
|
||||
<!-- Reference TailwindCSS in your HTML -->
|
||||
<link rel="stylesheet" href="tailwindcss" />
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="styles.css">
|
||||
|
||||
```css title="styles.css" icon="file-code"
|
||||
@import "tailwindcss";
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="app.ts">
|
||||
|
||||
```ts title="app.ts" icon="/icons/typescript.svg"
|
||||
import "tailwindcss";
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="styles.css">```css title="styles.css" icon="file-code" @import "tailwindcss"; ```</Tab>
|
||||
<Tab title="app.ts">```ts title="app.ts" icon="/icons/typescript.svg" import "tailwindcss"; ```</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Info>Only one of those are necessary, not all three.</Info>
|
||||
@@ -259,7 +273,7 @@ bun ./index.html --console
|
||||
```
|
||||
|
||||
```
|
||||
Bun v1.2.20
|
||||
Bun v1.3.3
|
||||
ready in 6.62ms
|
||||
→ http://localhost:3000/
|
||||
Press h + Enter to show shortcuts
|
||||
@@ -371,7 +385,8 @@ All paths are resolved relative to your HTML file, making it easy to organize yo
|
||||
- Need more configuration options for things like asset handling
|
||||
- Need a way to configure CORS, headers, etc.
|
||||
|
||||
If you want to submit a PR, most of the code is [here](https://github.com/oven-sh/bun/blob/main/src/bun.js/api/bun/html-rewriter.ts). You could even copy paste that file into your project and use it as a starting point.
|
||||
{/* todo: find the correct link to link to as this 404's and there isn't any similar files */}
|
||||
{/* If you want to submit a PR, most of the code is [here](https://github.com/oven-sh/bun/blob/main/src/bun.js/api/bun/html-rewriter.ts). You could even copy paste that file into your project and use it as a starting point. */}
|
||||
|
||||
</Warning>
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ For each file specified in `entrypoints`, Bun will generate a new bundle. This b
|
||||
|
||||
The contents of `out/index.js` will look something like this:
|
||||
|
||||
```ts title="out/index.js" icon="/icons/javascript.svg"
|
||||
```js title="out/index.js" icon="/icons/javascript.svg"
|
||||
// out/index.js
|
||||
// ...
|
||||
// ~20k lines of code
|
||||
@@ -160,8 +160,12 @@ Like the Bun runtime, the bundler supports an array of file types out of the box
|
||||
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `.js` `.jsx` `.cjs` `.mjs` `.mts` `.cts` `.ts` `.tsx` | Uses Bun's built-in transpiler to parse the file and transpile TypeScript/JSX syntax to vanilla JavaScript. The bundler executes a set of default transforms including dead code elimination and tree shaking. At the moment Bun does not attempt to down-convert syntax; if you use recently ECMAScript syntax, that will be reflected in the bundled code. |
|
||||
| `.json` | JSON files are parsed and inlined into the bundle as a JavaScript object.<br/><br/>`js<br/>import pkg from "./package.json";<br/>pkg.name; // => "my-package"<br/>` |
|
||||
| `.jsonc` | JSON with comments. Files are parsed and inlined into the bundle as a JavaScript object.<br/><br/>`js<br/>import config from "./config.jsonc";<br/>config.name; // => "my-config"<br/>` |
|
||||
| `.toml` | TOML files are parsed and inlined into the bundle as a JavaScript object.<br/><br/>`js<br/>import config from "./bunfig.toml";<br/>config.logLevel; // => "debug"<br/>` |
|
||||
| `.yaml` `.yml` | YAML files are parsed and inlined into the bundle as a JavaScript object.<br/><br/>`js<br/>import config from "./config.yaml";<br/>config.name; // => "my-app"<br/>` |
|
||||
| `.txt` | The contents of the text file are read and inlined into the bundle as a string.<br/><br/>`js<br/>import contents from "./file.txt";<br/>console.log(contents); // => "Hello, world!"<br/>` |
|
||||
| `.html` | HTML files are processed and any referenced assets (scripts, stylesheets, images) are bundled. |
|
||||
| `.css` | CSS files are bundled together into a single `.css` file in the output directory. |
|
||||
| `.node` `.wasm` | These files are supported by the Bun runtime, but during bundling they are treated as assets. |
|
||||
|
||||
### Assets
|
||||
@@ -523,7 +527,7 @@ Injects environment variables into the bundled output by converting `process.env
|
||||
|
||||
For the input below:
|
||||
|
||||
```ts title="input.js" icon="/icons/javascript.svg"
|
||||
```js title="input.js" icon="/icons/javascript.svg"
|
||||
// input.js
|
||||
console.log(process.env.FOO);
|
||||
console.log(process.env.BAZ);
|
||||
@@ -531,7 +535,7 @@ console.log(process.env.BAZ);
|
||||
|
||||
The generated bundle will contain the following code:
|
||||
|
||||
```ts title="output.js" icon="/icons/javascript.svg"
|
||||
```js title="output.js" icon="/icons/javascript.svg"
|
||||
// output.js
|
||||
console.log("bar");
|
||||
console.log("123");
|
||||
@@ -576,7 +580,7 @@ console.log(process.env.BAZ);
|
||||
|
||||
The generated bundle will contain the following code:
|
||||
|
||||
```ts title="output.js" icon="/icons/javascript.svg"
|
||||
```js title="output.js" icon="/icons/javascript.svg"
|
||||
console.log(process.env.FOO);
|
||||
console.log("https://acme.com");
|
||||
console.log(process.env.BAZ);
|
||||
@@ -718,7 +722,7 @@ Normally, bundling `index.tsx` would generate a bundle containing the entire sou
|
||||
|
||||
The generated bundle will look something like this:
|
||||
|
||||
```ts title="out/index.js" icon="/icons/javascript.svg"
|
||||
```js title="out/index.js" icon="/icons/javascript.svg"
|
||||
import { z } from "zod";
|
||||
|
||||
// ...
|
||||
@@ -1022,7 +1026,7 @@ Setting `publicPath` will prefix all file paths with the specified value.
|
||||
|
||||
The output file would now look something like this.
|
||||
|
||||
```ts title="out/index.js" icon="/icons/javascript.svg"
|
||||
```js title="out/index.js" icon="/icons/javascript.svg"
|
||||
var logo = "https://cdn.example.com/logo-a7305bdef.svg";
|
||||
```
|
||||
|
||||
@@ -1352,10 +1356,12 @@ interface BuildConfig {
|
||||
* JSX configuration object for controlling JSX transform behavior
|
||||
*/
|
||||
jsx?: {
|
||||
runtime?: "automatic" | "classic";
|
||||
importSource?: string;
|
||||
factory?: string;
|
||||
fragment?: string;
|
||||
importSource?: string;
|
||||
runtime?: "automatic" | "classic";
|
||||
sideEffects?: boolean;
|
||||
development?: boolean;
|
||||
};
|
||||
naming?:
|
||||
| string
|
||||
@@ -1372,7 +1378,7 @@ interface BuildConfig {
|
||||
publicPath?: string;
|
||||
define?: Record<string, string>;
|
||||
loader?: { [k in string]: Loader };
|
||||
sourcemap?: "none" | "linked" | "inline" | "external" | "linked" | boolean; // default: "none", true -> "inline"
|
||||
sourcemap?: "none" | "linked" | "inline" | "external" | boolean; // default: "none", true -> "inline"
|
||||
/**
|
||||
* package.json `exports` conditions used when resolving imports
|
||||
*
|
||||
@@ -1439,13 +1445,20 @@ interface BuildConfig {
|
||||
drop?: string[];
|
||||
|
||||
/**
|
||||
* When set to `true`, the returned promise rejects with an AggregateError when a build failure happens.
|
||||
* When set to `false`, the `success` property of the returned object will be `false` when a build failure happens.
|
||||
* - When set to `true`, the returned promise rejects with an AggregateError when a build failure happens.
|
||||
* - When set to `false`, returns a {@link BuildOutput} with `{success: false}`
|
||||
*
|
||||
* This defaults to `false` in Bun 1.1 and will change to `true` in Bun 1.2
|
||||
* as most usage of `Bun.build` forgets to check for errors.
|
||||
* @default true
|
||||
*/
|
||||
throw?: boolean;
|
||||
|
||||
/**
|
||||
* Custom tsconfig.json file path to use for path resolution.
|
||||
* Equivalent to `--tsconfig-override` in the CLI.
|
||||
*/
|
||||
tsconfig?: string;
|
||||
|
||||
outdir?: string;
|
||||
}
|
||||
|
||||
interface BuildOutput {
|
||||
@@ -1462,7 +1475,21 @@ interface BuildArtifact extends Blob {
|
||||
sourcemap: BuildArtifact | null;
|
||||
}
|
||||
|
||||
type Loader = "js" | "jsx" | "ts" | "tsx" | "json" | "toml" | "file" | "napi" | "wasm" | "text";
|
||||
type Loader =
|
||||
| "js"
|
||||
| "jsx"
|
||||
| "ts"
|
||||
| "tsx"
|
||||
| "css"
|
||||
| "json"
|
||||
| "jsonc"
|
||||
| "toml"
|
||||
| "yaml"
|
||||
| "text"
|
||||
| "file"
|
||||
| "napi"
|
||||
| "wasm"
|
||||
| "html";
|
||||
|
||||
interface BuildOutput {
|
||||
outputs: BuildArtifact[];
|
||||
|
||||
@@ -7,14 +7,16 @@ The Bun bundler implements a set of default loaders out of the box.
|
||||
|
||||
> As a rule of thumb: **the bundler and the runtime both support the same set of file types out of the box.**
|
||||
|
||||
`.js` `.cjs` `.mjs` `.mts` `.cts` `.ts` `.tsx` `.jsx` `.toml` `.json` `.txt` `.wasm` `.node` `.html`
|
||||
`.js` `.cjs` `.mjs` `.mts` `.cts` `.ts` `.tsx` `.jsx` `.css` `.json` `.jsonc` `.toml` `.yaml` `.yml` `.txt` `.wasm` `.node` `.html` `.sh`
|
||||
|
||||
Bun uses the file extension to determine which built-in loader should be used to parse the file. Every loader has a name, such as `js`, `tsx`, or `json`. These names are used when building plugins that extend Bun with custom loaders.
|
||||
|
||||
You can explicitly specify which loader to use using the `'loader'` import attribute.
|
||||
You can explicitly specify which loader to use using the `'type'` import attribute.
|
||||
|
||||
```ts title="index.ts" icon="/icons/typescript.svg"
|
||||
import my_toml from "./my_file" with { loader: "toml" };
|
||||
import my_toml from "./my_file" with { type: "toml" };
|
||||
// or with dynamic imports
|
||||
const { default: my_toml } = await import("./my_file", { with: { type: "toml" } });
|
||||
```
|
||||
|
||||
## Built-in loaders
|
||||
@@ -85,7 +87,7 @@ If a `.json` file is passed as an entrypoint to the bundler, it will be converte
|
||||
}
|
||||
```
|
||||
|
||||
```ts Output
|
||||
```js Output
|
||||
export default {
|
||||
name: "John Doe",
|
||||
age: 35,
|
||||
@@ -97,7 +99,32 @@ export default {
|
||||
|
||||
---
|
||||
|
||||
### toml
|
||||
### `jsonc`
|
||||
|
||||
**JSON with Comments loader.** Default for `.jsonc`.
|
||||
|
||||
JSONC (JSON with Comments) files can be directly imported. Bun will parse them, stripping out comments and trailing commas.
|
||||
|
||||
```js
|
||||
import config from "./config.jsonc";
|
||||
console.log(config);
|
||||
```
|
||||
|
||||
During bundling, the parsed JSONC is inlined into the bundle as a JavaScript object, identical to the `json` loader.
|
||||
|
||||
```js
|
||||
var config = {
|
||||
option: "value",
|
||||
};
|
||||
```
|
||||
|
||||
<Note>
|
||||
Bun automatically uses the `jsonc` loader for `tsconfig.json`, `jsconfig.json`, `package.json`, and `bun.lock` files.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
### `toml`
|
||||
|
||||
**TOML loader.** Default for `.toml`.
|
||||
|
||||
@@ -131,7 +158,7 @@ age = 35
|
||||
email = "johndoe@example.com"
|
||||
```
|
||||
|
||||
```ts Output
|
||||
```js Output
|
||||
export default {
|
||||
name: "John Doe",
|
||||
age: 35,
|
||||
@@ -143,7 +170,53 @@ export default {
|
||||
|
||||
---
|
||||
|
||||
### text
|
||||
### `yaml`
|
||||
|
||||
**YAML loader.** Default for `.yaml` and `.yml`.
|
||||
|
||||
YAML files can be directly imported. Bun will parse them with its fast native YAML parser.
|
||||
|
||||
```js
|
||||
import config from "./config.yaml";
|
||||
console.log(config);
|
||||
|
||||
// via import attribute:
|
||||
import data from "./data.txt" with { type: "yaml" };
|
||||
```
|
||||
|
||||
During bundling, the parsed YAML is inlined into the bundle as a JavaScript object.
|
||||
|
||||
```js
|
||||
var config = {
|
||||
name: "my-app",
|
||||
version: "1.0.0",
|
||||
// ...other fields
|
||||
};
|
||||
```
|
||||
|
||||
If a `.yaml` or `.yml` file is passed as an entrypoint, it will be converted to a `.js` module that `export default`s the parsed object.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```yaml Input
|
||||
name: John Doe
|
||||
age: 35
|
||||
email: johndoe@example.com
|
||||
```
|
||||
|
||||
```js Output
|
||||
export default {
|
||||
name: "John Doe",
|
||||
age: 35,
|
||||
email: "johndoe@example.com",
|
||||
};
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
---
|
||||
|
||||
### `text`
|
||||
|
||||
**Text loader.** Default for `.txt`.
|
||||
|
||||
@@ -173,7 +246,7 @@ If a `.txt` file is passed as an entrypoint, it will be converted to a `.js` mod
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
```ts Output
|
||||
```js Output
|
||||
export default "Hello, world!";
|
||||
```
|
||||
|
||||
@@ -181,7 +254,7 @@ export default "Hello, world!";
|
||||
|
||||
---
|
||||
|
||||
### napi
|
||||
### `napi`
|
||||
|
||||
**Native addon loader.** Default for `.node`.
|
||||
|
||||
@@ -196,7 +269,7 @@ console.log(addon);
|
||||
|
||||
---
|
||||
|
||||
### sqlite
|
||||
### `sqlite`
|
||||
|
||||
**SQLite loader.** Requires `with { "type": "sqlite" }` import attribute.
|
||||
|
||||
@@ -226,7 +299,9 @@ Otherwise, the database to embed is copied into the `outdir` with a hashed filen
|
||||
|
||||
---
|
||||
|
||||
### html
|
||||
### `html`
|
||||
|
||||
**HTML loader.** Default for `.html`.
|
||||
|
||||
The `html` loader processes HTML files and bundles any referenced assets. It will:
|
||||
|
||||
@@ -237,7 +312,7 @@ The `html` loader processes HTML files and bundles any referenced assets. It wil
|
||||
|
||||
For example, given this HTML file:
|
||||
|
||||
```html title="src/index.html"
|
||||
```html title="src/index.html" icon="file-code"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
@@ -250,7 +325,7 @@ For example, given this HTML file:
|
||||
|
||||
It will output a new HTML file with the bundled assets:
|
||||
|
||||
```html title="dist/index.html"
|
||||
```html title="dist/index.html" icon="file-code"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
@@ -301,7 +376,27 @@ The `html` loader behaves differently depending on how it's used:
|
||||
|
||||
---
|
||||
|
||||
### sh
|
||||
### `css`
|
||||
|
||||
**CSS loader.** Default for `.css`.
|
||||
|
||||
CSS files can be directly imported. The bundler will parse and bundle CSS files, handling `@import` statements and `url()` references.
|
||||
|
||||
```js
|
||||
import "./styles.css";
|
||||
```
|
||||
|
||||
During bundling, all imported CSS files are bundled together into a single `.css` file in the output directory.
|
||||
|
||||
```css
|
||||
.my-class {
|
||||
background: url("./image.png");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `sh`
|
||||
|
||||
**Bun Shell loader.** Default for `.sh` files.
|
||||
|
||||
@@ -313,7 +408,7 @@ bun run ./script.sh
|
||||
|
||||
---
|
||||
|
||||
### file
|
||||
### `file`
|
||||
|
||||
**File loader.** Default for all unrecognized file types.
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ macro();
|
||||
|
||||
When shipping a library containing a macro to npm or another package registry, use the `"macro"` export condition to provide a special version of your package exclusively for the macro environment.
|
||||
|
||||
```json title="package.json" icon="file-code"
|
||||
```json title="package.json" icon="file-json"
|
||||
{
|
||||
"name": "my-package",
|
||||
"exports": {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -42,7 +42,21 @@ type PluginBuilder = {
|
||||
config: BuildConfig;
|
||||
};
|
||||
|
||||
type Loader = "js" | "jsx" | "ts" | "tsx" | "css" | "json" | "toml";
|
||||
type Loader =
|
||||
| "js"
|
||||
| "jsx"
|
||||
| "ts"
|
||||
| "tsx"
|
||||
| "json"
|
||||
| "jsonc"
|
||||
| "toml"
|
||||
| "yaml"
|
||||
| "file"
|
||||
| "napi"
|
||||
| "wasm"
|
||||
| "text"
|
||||
| "css"
|
||||
| "html";
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -188,7 +188,7 @@
|
||||
{
|
||||
"group": "Publishing & Analysis",
|
||||
"icon": "upload",
|
||||
"pages": ["/pm/cli/publish", "/pm/cli/outdated", "/pm/cli/why", "/pm/cli/audit"]
|
||||
"pages": ["/pm/cli/publish", "/pm/cli/outdated", "/pm/cli/why", "/pm/cli/audit", "/pm/cli/info"]
|
||||
},
|
||||
{
|
||||
"group": "Workspace Management",
|
||||
@@ -354,7 +354,7 @@
|
||||
"/guides/ecosystem/discordjs",
|
||||
"/guides/ecosystem/docker",
|
||||
"/guides/ecosystem/drizzle",
|
||||
"/guides/ecosystem/edgedb",
|
||||
"/guides/ecosystem/gel",
|
||||
"/guides/ecosystem/elysia",
|
||||
"/guides/ecosystem/express",
|
||||
"/guides/ecosystem/hono",
|
||||
@@ -369,6 +369,7 @@
|
||||
"/guides/ecosystem/qwik",
|
||||
"/guides/ecosystem/react",
|
||||
"/guides/ecosystem/remix",
|
||||
"/guides/ecosystem/tanstack-start",
|
||||
"/guides/ecosystem/sentry",
|
||||
"/guides/ecosystem/solidstart",
|
||||
"/guides/ecosystem/ssr-react",
|
||||
@@ -463,6 +464,7 @@
|
||||
"/guides/test/update-snapshots",
|
||||
"/guides/test/coverage",
|
||||
"/guides/test/coverage-threshold",
|
||||
"/guides/test/concurrent-test-glob",
|
||||
"/guides/test/skip-tests",
|
||||
"/guides/test/todo-tests",
|
||||
"/guides/test/timeout",
|
||||
|
||||
@@ -4,13 +4,9 @@ description: Share feedback, bug reports, and feature requests
|
||||
mode: center
|
||||
---
|
||||
|
||||
import Feedback from "/snippets/cli/feedback.mdx";
|
||||
|
||||
Whether you've found a bug, have a performance issue, or just want to suggest an improvement, here's how you can open a helpful issue:
|
||||
|
||||
<Callout icon="discord">
|
||||
For general questions, please join our [Discord](https://discord.com/invite/CXdq2DP29u).
|
||||
</Callout>
|
||||
<Callout icon="discord">For general questions, please join our [Discord](https://bun.com/discord).</Callout>
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
@@ -56,9 +52,7 @@ Whether you've found a bug, have a performance issue, or just want to suggest an
|
||||
<Note>
|
||||
- For MacOS and Linux: copy the output of `uname -mprs`
|
||||
- For Windows: copy the output of this command in the powershell console:
|
||||
```powershell
|
||||
"$([Environment]::OSVersion | ForEach-Object VersionString) $(if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" })"
|
||||
```
|
||||
`"$([Environment]::OSVersion | ForEach-Object VersionString) $(if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" })"`
|
||||
</Note>
|
||||
</Step>
|
||||
|
||||
@@ -79,7 +73,3 @@ echo "please document X" | bun feedback --email you@example.com
|
||||
```
|
||||
|
||||
You can provide feedback as text arguments, file paths, or piped input.
|
||||
|
||||
---
|
||||
|
||||
<Feedback />
|
||||
|
||||
@@ -26,4 +26,4 @@ const regularArr = Array.from(uintArr);
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -23,4 +23,4 @@ blob.type; // => "application/octet-stream"
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -24,4 +24,4 @@ const nodeBuffer = Buffer.from(arrBuffer, 0, 16); // view first 16 bytes
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -14,4 +14,4 @@ const str = decoder.decode(buf);
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -38,4 +38,4 @@ const arr = new Uint8Array(buffer, 0, 16); // view first 16 bytes
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Utils](https://bun.com/docs/api/utils) for more useful utilities.
|
||||
See [Docs > API > Utils](/runtime/utils) for more useful utilities.
|
||||
|
||||
@@ -13,4 +13,4 @@ const buf = await blob.arrayBuffer();
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -13,4 +13,4 @@ const arr = new DataView(await blob.arrayBuffer());
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -13,4 +13,4 @@ const stream = await blob.stream();
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -14,4 +14,4 @@ const str = await blob.text();
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -13,4 +13,4 @@ const arr = new Uint8Array(await blob.arrayBuffer());
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -13,4 +13,4 @@ const arrBuf = nodeBuf.buffer;
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -13,4 +13,4 @@ const blob = new Blob([buf]);
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -40,4 +40,4 @@ const stream = blob.stream(1024);
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -24,4 +24,4 @@ const str = buf.toString("utf8", 0, 5);
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -13,4 +13,4 @@ buf instanceof Uint8Array; // => true
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -14,4 +14,4 @@ const str = decoder.decode(dv);
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -24,4 +24,4 @@ arr.byteLength; // => 32
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -15,4 +15,4 @@ console.log(await blob.text());
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -13,4 +13,4 @@ const buf = Buffer.from(arr);
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -13,4 +13,4 @@ const dv = new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -40,4 +40,4 @@ const stream = blob.stream(1024);
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -15,4 +15,4 @@ const str = decoder.decode(arr);
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > Binary Data](https://bun.com/docs/api/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
See [Docs > API > Binary Data](/runtime/binary-data#conversion) for complete documentation on manipulating binary data with Bun.
|
||||
|
||||
@@ -132,9 +132,7 @@ CMD ["bun", "index.ts"]
|
||||
<Note>
|
||||
Make sure that the start command corresponds to your application's entry point. This can also be `CMD ["bun", "run", "start"]` if you have a start script in your `package.json`.
|
||||
|
||||
This image installs dependencies and runs your app with Bun inside a container. If your app doesn't have dependencies, you can omit the `RUN bun install --production --frozen-lockfile` line.
|
||||
|
||||
This image installs dependencies and runs your app with Bun inside a container. If your app doesn't have dependencies, you can omit the `RUN bun install --production --frozen-lockfile` line.
|
||||
This image installs dependencies and runs your app with Bun inside a container. If your app doesn't have dependencies, you can omit the `RUN bun install --production --frozen-lockfile` line.
|
||||
|
||||
</Note>
|
||||
|
||||
@@ -159,8 +157,7 @@ Make sure you're in the directory containing your `Dockerfile`, then deploy dire
|
||||
|
||||
<Note>
|
||||
Update the `--region` flag to your preferred region. You can also omit this flag to get an interactive prompt to
|
||||
select a region. Update the `--region` flag to your preferred region. You can also omit this flag to get an
|
||||
interactive prompt to select a region.
|
||||
select a region.
|
||||
</Note>
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
|
||||
@@ -4,8 +4,6 @@ sidebarTitle: Deploy on Vercel
|
||||
mode: center
|
||||
---
|
||||
|
||||
import { ProductCard } from "/snippets/product-card.mdx";
|
||||
|
||||
[Vercel](https://vercel.com/) is a cloud platform that lets you build, deploy, and scale your apps.
|
||||
|
||||
<Warning>
|
||||
@@ -32,7 +30,7 @@ import { ProductCard } from "/snippets/product-card.mdx";
|
||||
|
||||
Vercel automatically detects this configuration and runs your application on Bun. The value has to be `"1.x"`, Vercel handles the minor version internally.
|
||||
|
||||
For best results, match your local Bun version with the version used by Vercel. **Currently, Bun version `1.2.23` is supported**.
|
||||
For best results, match your local Bun version with the version used by Vercel.
|
||||
</Step>
|
||||
|
||||
<Step title="Next.js configuration">
|
||||
@@ -81,7 +79,7 @@ import { ProductCard } from "/snippets/product-card.mdx";
|
||||
console.log("runtime", process.versions.bun);
|
||||
```
|
||||
```txt
|
||||
runtime 1.2.23
|
||||
runtime 1.3.3
|
||||
```
|
||||
|
||||
[See the Vercel Bun Runtime documentation for feature support →](https://vercel.com/docs/functions/runtimes/bun#feature-support)
|
||||
|
||||
@@ -22,7 +22,7 @@ bun add discord.js
|
||||
|
||||
---
|
||||
|
||||
Before we go further, we need to go to the [Discord developer portal](https://discord.com/developers/applications), login/signup, create a new _Application_, then create a new _Bot_ within that application. Follow the [official guide](https://discordjs.guide/preparations/setting-up-a-bot-application.html#creating-your-bot) for step-by-step instructions.
|
||||
Before we go further, we need to go to the [Discord developer portal](https://discord.com/developers/applications), login/signup, create a new _Application_, then create a new _Bot_ within that application. Follow the [official guide](https://discordjs.guide/legacy/preparations/app-setup#creating-your-bot) for step-by-step instructions.
|
||||
|
||||
---
|
||||
|
||||
@@ -30,7 +30,7 @@ Once complete, you'll be presented with your bot's _private key_. Let's add this
|
||||
|
||||
<Note>This is an example token that has already been invalidated.</Note>
|
||||
|
||||
```txt .env.local icon="settings"
|
||||
```ini .env.local icon="settings"
|
||||
DISCORD_TOKEN=NzkyNzE1NDU0MTk2MDg4ODQy.X-hvzA.Ovy4MCQywSkoMRRclStW4xAYK7I
|
||||
```
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ mode: center
|
||||
Express and other major Node.js HTTP libraries should work out of the box. Bun implements the [`node:http`](https://nodejs.org/api/http.html) and [`node:https`](https://nodejs.org/api/https.html) modules that these libraries rely on.
|
||||
|
||||
<Note>
|
||||
Refer to the [Runtime > Node.js APIs](https://bun.com/docs/runtime/nodejs-apis#node-http) page for more detailed
|
||||
compatibility information.
|
||||
Refer to the [Runtime > Node.js APIs](/runtime/nodejs-compat#node-http) page for more detailed compatibility
|
||||
information.
|
||||
</Note>
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
---
|
||||
title: Use EdgeDB with Bun
|
||||
sidebarTitle: EdgeDB with Bun
|
||||
title: Use Gel with Bun
|
||||
sidebarTitle: Gel with Bun
|
||||
mode: center
|
||||
---
|
||||
|
||||
EdgeDB is a graph-relational database powered by Postgres under the hood. It provides a declarative schema language, migrations system, and object-oriented query language, in addition to supporting raw SQL queries. It solves the object-relational mapping problem at the database layer, eliminating the need for an ORM library in your application code.
|
||||
Gel (formerly EdgeDB) is a graph-relational database powered by Postgres under the hood. It provides a declarative schema language, migrations system, and object-oriented query language, in addition to supporting raw SQL queries. It solves the object-relational mapping problem at the database layer, eliminating the need for an ORM library in your application code.
|
||||
|
||||
---
|
||||
|
||||
First, [install EdgeDB](https://www.edgedb.com/install) if you haven't already.
|
||||
First, [install Gel](https://docs.geldata.com/learn/installation) if you haven't already.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```sh Linux/macOS terminal icon="terminal"
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.edgedb.com | sh
|
||||
curl https://www.geldata.com/sh --proto "=https" -sSf1 | sh
|
||||
```
|
||||
|
||||
```sh Windows terminal icon="windows"
|
||||
iwr https://ps1.edgedb.com -useb | iex
|
||||
irm https://www.geldata.com/ps1 | iex
|
||||
```
|
||||
|
||||
```sh Homebrew terminal icon="terminal"
|
||||
brew install geldata/tap/gel-cli
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
@@ -34,35 +38,35 @@ bun init -y
|
||||
|
||||
---
|
||||
|
||||
We'll use the EdgeDB CLI to initialize an EdgeDB instance for our project. This creates an `edgedb.toml` file in our project root.
|
||||
We'll use the Gel CLI to initialize a Gel instance for our project. This creates a `gel.toml` file in our project root.
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
edgedb project init
|
||||
gel project init
|
||||
```
|
||||
|
||||
```txt
|
||||
No `edgedb.toml` found in `/Users/colinmcd94/Documents/bun/fun/examples/my-edgedb-app` or above
|
||||
No `gel.toml` found in `/Users/colinmcd94/Documents/bun/fun/examples/my-gel-app` or above
|
||||
Do you want to initialize a new project? [Y/n]
|
||||
> Y
|
||||
Specify the name of EdgeDB instance to use with this project [default: my_edgedb_app]:
|
||||
> my_edgedb_app
|
||||
Checking EdgeDB versions...
|
||||
Specify the version of EdgeDB to use with this project [default: x.y]:
|
||||
Specify the name of Gel instance to use with this project [default: my_gel_app]:
|
||||
> my_gel_app
|
||||
Checking Gel versions...
|
||||
Specify the version of Gel to use with this project [default: x.y]:
|
||||
> x.y
|
||||
┌─────────────────────┬────────────────────────────────────────────────────────────────────────┐
|
||||
│ Project directory │ /Users/colinmcd94/Documents/bun/fun/examples/my-edgedb-app │
|
||||
│ Project config │ /Users/colinmcd94/Documents/bun/fun/examples/my-edgedb-app/edgedb.toml │
|
||||
│ Schema dir (empty) │ /Users/colinmcd94/Documents/bun/fun/examples/my-edgedb-app/dbschema │
|
||||
│ Installation method │ portable package │
|
||||
│ Version │ x.y+6d5921b │
|
||||
│ Instance name │ my_edgedb_app │
|
||||
└─────────────────────┴────────────────────────────────────────────────────────────────────────┘
|
||||
┌─────────────────────┬──────────────────────────────────────────────────────────────────┐
|
||||
│ Project directory │ /Users/colinmcd94/Documents/bun/fun/examples/my-gel-app │
|
||||
│ Project config │ /Users/colinmcd94/Documents/bun/fun/examples/my-gel-app/gel.toml│
|
||||
│ Schema dir (empty) │ /Users/colinmcd94/Documents/bun/fun/examples/my-gel-app/dbschema│
|
||||
│ Installation method │ portable package │
|
||||
│ Version │ x.y+6d5921b │
|
||||
│ Instance name │ my_gel_app │
|
||||
└─────────────────────┴──────────────────────────────────────────────────────────────────┘
|
||||
Version x.y+6d5921b is already downloaded
|
||||
Initializing EdgeDB instance...
|
||||
Initializing Gel instance...
|
||||
Applying migrations...
|
||||
Everything is up to date. Revision initial
|
||||
Project initialized.
|
||||
To connect to my_edgedb_app, run `edgedb`
|
||||
To connect to my_gel_app, run `gel`
|
||||
```
|
||||
|
||||
---
|
||||
@@ -70,8 +74,8 @@ To connect to my_edgedb_app, run `edgedb`
|
||||
To see if the database is running, let's open a REPL and run a simple query.
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
edgedb
|
||||
edgedb> select 1 + 1;
|
||||
gel
|
||||
gel> select 1 + 1;
|
||||
```
|
||||
|
||||
```txt
|
||||
@@ -81,12 +85,12 @@ edgedb> select 1 + 1;
|
||||
Then run `\quit` to exit the REPL.
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
edgedb> \quit
|
||||
gel> \quit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
With the project initialized, we can define a schema. The `edgedb project init` command already created a `dbschema/default.esdl` file to contain our schema.
|
||||
With the project initialized, we can define a schema. The `gel project init` command already created a `dbschema/default.esdl` file to contain our schema.
|
||||
|
||||
```txt File Tree icon="folder-tree"
|
||||
dbschema
|
||||
@@ -112,15 +116,15 @@ module default {
|
||||
Then generate and apply an initial migration.
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
edgedb migration create
|
||||
gel migration create
|
||||
```
|
||||
|
||||
```txt
|
||||
Created /Users/colinmcd94/Documents/bun/fun/examples/my-edgedb-app/dbschema/migrations/00001.edgeql, id: m1uwekrn4ni4qs7ul7hfar4xemm5kkxlpswolcoyqj3xdhweomwjrq
|
||||
Created /Users/colinmcd94/Documents/bun/fun/examples/my-gel-app/dbschema/migrations/00001.edgeql, id: m1uwekrn4ni4qs7ul7hfar4xemm5kkxlpswolcoyqj3xdhweomwjrq
|
||||
```
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
edgedb migrate
|
||||
gel migrate
|
||||
```
|
||||
|
||||
```txt
|
||||
@@ -129,11 +133,11 @@ Applied m1uwekrn4ni4qs7ul7hfar4xemm5kkxlpswolcoyqj3xdhweomwjrq (00001.edgeql)
|
||||
|
||||
---
|
||||
|
||||
With our schema applied, let's execute some queries using EdgeDB's JavaScript client library. We'll install the client library and EdgeDB's codegen CLI, and create a `seed.ts`.file.
|
||||
With our schema applied, let's execute some queries using Gel's JavaScript client library. We'll install the client library and Gel's codegen CLI, and create a `seed.ts`.file.
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
bun add edgedb
|
||||
bun add -D @edgedb/generate
|
||||
bun add gel
|
||||
bun add -D @gel/generate
|
||||
touch seed.ts
|
||||
```
|
||||
|
||||
@@ -144,7 +148,7 @@ Paste the following code into `seed.ts`.
|
||||
The client auto-connects to the database. We insert a couple movies using the `.execute()` method. We will use EdgeQL's `for` expression to turn this bulk insert into a single optimized query.
|
||||
|
||||
```ts seed.ts icon="/icons/typescript.svg"
|
||||
import { createClient } from "edgedb";
|
||||
import { createClient } from "gel";
|
||||
|
||||
const client = createClient();
|
||||
|
||||
@@ -184,10 +188,10 @@ Seeding complete.
|
||||
|
||||
---
|
||||
|
||||
EdgeDB implements a number of code generation tools for TypeScript. To query our newly seeded database in a typesafe way, we'll use `@edgedb/generate` to code-generate the EdgeQL query builder.
|
||||
Gel implements a number of code generation tools for TypeScript. To query our newly seeded database in a typesafe way, we'll use `@gel/generate` to code-generate the EdgeQL query builder.
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
bunx @edgedb/generate edgeql-js
|
||||
bunx @gel/generate edgeql-js
|
||||
```
|
||||
|
||||
```txt
|
||||
@@ -213,7 +217,7 @@ the query builder directory? The following line will be added:
|
||||
In `index.ts`, we can import the generated query builder from `./dbschema/edgeql-js` and write a simple select query.
|
||||
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
import { createClient } from "edgedb";
|
||||
import { createClient } from "gel";
|
||||
import e from "./dbschema/edgeql-js";
|
||||
|
||||
const client = createClient();
|
||||
@@ -254,4 +258,4 @@ bun run index.ts
|
||||
|
||||
---
|
||||
|
||||
For complete documentation, refer to the [EdgeDB docs](https://www.edgedb.com/docs).
|
||||
For complete documentation, refer to the [Gel docs](https://docs.geldata.com/).
|
||||
@@ -89,4 +89,4 @@ Moo!
|
||||
|
||||
---
|
||||
|
||||
This is a simple introduction to using Mongoose with TypeScript and Bun. As you build your application, refer to the official [MongoDB](https://docs.mongodb.com/) and [Mongoose](https://mongoosejs.com/docs/) sites for complete documentation.
|
||||
This is a simple introduction to using Mongoose with TypeScript and Bun. As you build your application, refer to the official [MongoDB](https://www.mongodb.com/docs) and [Mongoose](https://mongoosejs.com/docs/) sites for complete documentation.
|
||||
|
||||
@@ -20,7 +20,7 @@ bun add -D drizzle-kit
|
||||
|
||||
Create a `.env.local` file and add your [Neon Postgres connection string](https://neon.tech/docs/connect/connect-from-any-app) to it.
|
||||
|
||||
```txt .env.local icon="settings"
|
||||
```ini .env.local icon="settings"
|
||||
DATABASE_URL=postgresql://usertitle:password@ep-adj-noun-guid.us-east-1.aws.neon.tech/neondb?sslmode=require
|
||||
```
|
||||
|
||||
@@ -33,7 +33,7 @@ import { neon } from "@neondatabase/serverless";
|
||||
import { drizzle } from "drizzle-orm/neon-http";
|
||||
|
||||
// Bun automatically loads the DATABASE_URL from .env.local
|
||||
// Refer to: https://bun.com/docs/runtime/env for more information
|
||||
// Refer to: https://bun.com/docs/runtime/environment-variables for more information
|
||||
const sql = neon(process.env.DATABASE_URL!);
|
||||
|
||||
export const db = drizzle(sql);
|
||||
|
||||
@@ -21,7 +21,7 @@ bun add @neondatabase/serverless
|
||||
|
||||
Create a `.env.local` file and add your [Neon Postgres connection string](https://neon.tech/docs/connect/connect-from-any-app) to it.
|
||||
|
||||
```sh .env.local icon="settings"
|
||||
```ini .env.local icon="settings"
|
||||
DATABASE_URL=postgresql://usertitle:password@ep-adj-noun-guid.us-east-1.aws.neon.tech/neondb?sslmode=require
|
||||
```
|
||||
|
||||
@@ -33,7 +33,7 @@ Paste the following code into your project's `index.ts` file.
|
||||
import { neon } from "@neondatabase/serverless";
|
||||
|
||||
// Bun automatically loads the DATABASE_URL from .env.local
|
||||
// Refer to: https://bun.com/docs/runtime/env for more information
|
||||
// Refer to: https://bun.com/docs/runtime/environment-variables for more information
|
||||
const sql = neon(process.env.DATABASE_URL);
|
||||
|
||||
const rows = await sql`SELECT version()`;
|
||||
|
||||
@@ -4,54 +4,100 @@ sidebarTitle: Next.js with Bun
|
||||
mode: center
|
||||
---
|
||||
|
||||
Initialize a Next.js app with `create-next-app`. This will scaffold a new Next.js project and automatically install dependencies.
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
bun create next-app
|
||||
```
|
||||
|
||||
```txt
|
||||
✔ What is your project named? … my-app
|
||||
✔ Would you like to use TypeScript with this project? … No / Yes
|
||||
✔ Would you like to use ESLint with this project? … No / Yes
|
||||
✔ Would you like to use Tailwind CSS? ... No / Yes
|
||||
✔ Would you like to use `src/` directory with this project? … No / Yes
|
||||
✔ Would you like to use App Router? (recommended) ... No / Yes
|
||||
✔ What import alias would you like configured? … @/*
|
||||
Creating a new Next.js app in /path/to/my-app.
|
||||
```
|
||||
[Next.js](https://nextjs.org/) is a React framework for building full-stack web applications. It supports server-side rendering, static site generation, API routes, and more. Bun provides fast package installation and can run Next.js development and production servers.
|
||||
|
||||
---
|
||||
|
||||
You can specify a starter template using the `--example` flag.
|
||||
<Steps>
|
||||
<Step title="Create a new Next.js app">
|
||||
Use the interactive CLI to create a new Next.js app. This will scaffold a new Next.js project and automatically install dependencies.
|
||||
|
||||
```sh
|
||||
bun create next-app --example with-supabase
|
||||
```
|
||||
```sh terminal icon="terminal"
|
||||
bun create next-app@latest my-bun-app
|
||||
```
|
||||
|
||||
```txt
|
||||
✔ What is your project named? … my-app
|
||||
...
|
||||
```
|
||||
</Step>
|
||||
<Step title="Start the dev server">
|
||||
Change to the project directory and run the dev server with Bun.
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
cd my-bun-app
|
||||
bun --bun run dev
|
||||
```
|
||||
|
||||
This starts the Next.js dev server with Bun's runtime.
|
||||
|
||||
Open [`http://localhost:3000`](http://localhost:3000) with your browser to see the result. Any changes you make to `app/page.tsx` will be hot-reloaded in the browser.
|
||||
|
||||
</Step>
|
||||
<Step title="Update scripts in package.json">
|
||||
Modify the scripts field in your `package.json` by prefixing the Next.js CLI commands with `bun --bun`. This ensures that Bun executes the Next.js CLI for common tasks like `dev`, `build`, and `start`.
|
||||
|
||||
```json package.json icon="file-json"
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "bun --bun next dev", // [!code ++]
|
||||
"build": "bun --bun next build", // [!code ++]
|
||||
"start": "bun --bun next start", // [!code ++]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
To start the dev server with Bun, run `bun --bun run dev` from the project root.
|
||||
## Hosting
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
cd my-app
|
||||
bun --bun run dev
|
||||
```
|
||||
Next.js applications on Bun can be deployed to various platforms.
|
||||
|
||||
<Columns cols={3}>
|
||||
<Card title="Vercel" href="/guides/deployment/vercel" icon="/icons/ecosystem/vercel.svg">
|
||||
Deploy on Vercel
|
||||
</Card>
|
||||
<Card title="Railway" href="/guides/deployment/railway" icon="/icons/ecosystem/railway.svg">
|
||||
Deploy on Railway
|
||||
</Card>
|
||||
<Card title="DigitalOcean" href="/guides/deployment/digital-ocean" icon="/icons/ecosystem/digitalocean.svg">
|
||||
Deploy on DigitalOcean
|
||||
</Card>
|
||||
<Card title="AWS Lambda" href="/guides/deployment/aws-lambda" icon="/icons/ecosystem/aws.svg">
|
||||
Deploy on AWS Lambda
|
||||
</Card>
|
||||
<Card title="Google Cloud Run" href="/guides/deployment/google-cloud-run" icon="/icons/ecosystem/gcp.svg">
|
||||
Deploy on Google Cloud Run
|
||||
</Card>
|
||||
<Card title="Render" href="/guides/deployment/render" icon="/icons/ecosystem/render.svg">
|
||||
Deploy on Render
|
||||
</Card>
|
||||
</Columns>
|
||||
|
||||
---
|
||||
|
||||
To run the dev server with Node.js instead, omit `--bun`.
|
||||
## Templates
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
cd my-app
|
||||
bun run dev
|
||||
```
|
||||
<Columns cols={2}>
|
||||
<Card
|
||||
title="Bun + Next.js Basic Starter"
|
||||
img="/images/templates/bun-nextjs-basic.png"
|
||||
href="https://github.com/bun-templates/bun-nextjs-basic"
|
||||
arrow="true"
|
||||
cta="Go to template"
|
||||
>
|
||||
A simple App Router starter with Bun, Next.js, and Tailwind CSS.
|
||||
</Card>
|
||||
<Card
|
||||
title="Todo App with Next.js + Bun"
|
||||
img="/images/templates/bun-nextjs-todo.png"
|
||||
href="https://github.com/bun-templates/bun-nextjs-todo"
|
||||
arrow="true"
|
||||
cta="Go to template"
|
||||
>
|
||||
A full-stack todo application built with Bun, Next.js, and PostgreSQL.
|
||||
</Card>
|
||||
</Columns>
|
||||
|
||||
---
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. Any changes you make to `(pages/app)/index.tsx` will be hot-reloaded in the browser.
|
||||
[→ See Next.js's official documentation](https://nextjs.org/docs) for more information on building and deploying Next.js applications.
|
||||
|
||||
@@ -14,7 +14,7 @@ bunx nuxi init my-nuxt-app
|
||||
✔ Which package manager would you like to use?
|
||||
bun
|
||||
◐ Installing dependencies...
|
||||
bun install v1.3.1 (16b4bf34)
|
||||
bun install v1.3.3 (16b4bf34)
|
||||
+ @nuxt/devtools@0.8.2
|
||||
+ nuxt@3.7.0
|
||||
785 packages installed [2.67s]
|
||||
|
||||
@@ -62,7 +62,7 @@ mode: center
|
||||
<Step title="Configure database connection">
|
||||
Set up your Postgres database URL in the `.env` file.
|
||||
|
||||
```env .env icon="settings"
|
||||
```ini .env icon="settings"
|
||||
DATABASE_URL="postgresql://username:password@localhost:5432/mydb?schema=public"
|
||||
```
|
||||
</Step>
|
||||
|
||||
@@ -161,4 +161,4 @@ mode: center
|
||||
|
||||
---
|
||||
|
||||
That's it! Now that you've set up Prisma using Bun, we recommend referring to the [official Prisma docs](https://www.prisma.io/docs/concepts/components/prisma-client) as you continue to develop your application.
|
||||
That's it! Now that you've set up Prisma using Bun, we recommend referring to the [official Prisma docs](https://www.prisma.io/docs/orm/prisma-client) as you continue to develop your application.
|
||||
|
||||
@@ -65,14 +65,14 @@ bun create qwik
|
||||
│ bun qwik add │
|
||||
│ │
|
||||
│ Relevant docs: │
|
||||
│ https://qwik.builder.io/docs/getting-started/ │
|
||||
│ https://qwik.dev/docs/getting-started/ │
|
||||
│ │
|
||||
│ Questions? Start the conversation at: │
|
||||
│ https://qwik.builder.io/chat │
|
||||
│ https://qwik.dev/chat │
|
||||
│ https://twitter.com/QwikDev │
|
||||
│ │
|
||||
│ Presentations, Podcasts and Videos: │
|
||||
│ https://qwik.builder.io/media/ │
|
||||
│ https://qwik.dev/media/ │
|
||||
│ │
|
||||
│ Next steps: │
|
||||
│ cd my-app │
|
||||
@@ -111,4 +111,4 @@ Open [http://localhost:5173](http://localhost:5173) with your browser to see the
|
||||
|
||||
---
|
||||
|
||||
Refer to the [Qwik docs](https://qwik.builder.io/docs/getting-started/) for complete documentation.
|
||||
Refer to the [Qwik docs](https://qwik.dev/docs/getting-started/) for complete documentation.
|
||||
|
||||
@@ -20,7 +20,7 @@ bun add @sentry/bun
|
||||
|
||||
Then, initialize the Sentry SDK with your Sentry DSN in your app's entry file. You can find your DSN in your Sentry project settings.
|
||||
|
||||
```js sentry.ts icon="/icons/typescript.svg"
|
||||
```ts sentry.ts icon="/icons/typescript.svg"
|
||||
import * as Sentry from "@sentry/bun";
|
||||
|
||||
// Ensure to call this before importing any other modules!
|
||||
@@ -37,7 +37,7 @@ Sentry.init({
|
||||
|
||||
You can verify that Sentry is working by capturing a test error:
|
||||
|
||||
```js sentry.ts icon="/icons/typescript.svg"
|
||||
```ts sentry.ts icon="/icons/typescript.svg"
|
||||
setTimeout(() => {
|
||||
try {
|
||||
foo();
|
||||
|
||||
@@ -88,7 +88,7 @@ To build for production, you'll need to add the right SvelteKit adapter. Current
|
||||
|
||||
Now, make the following changes to your `svelte.config.js`.
|
||||
|
||||
```ts svelte.config.js icon="file-code"
|
||||
```js svelte.config.js icon="file-code"
|
||||
import adapter from "@sveltejs/adapter-auto"; // [!code --]
|
||||
import adapter from "svelte-adapter-bun"; // [!code ++]
|
||||
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
|
||||
|
||||
791
docs/guides/ecosystem/tanstack-start.mdx
Normal file
791
docs/guides/ecosystem/tanstack-start.mdx
Normal file
@@ -0,0 +1,791 @@
|
||||
---
|
||||
title: Use TanStack Start with Bun
|
||||
sidebarTitle: TanStack Start with Bun
|
||||
mode: center
|
||||
---
|
||||
|
||||
[TanStack Start](https://tanstack.com/start/latest) is a full-stack framework powered by TanStack Router. It supports full-document SSR, streaming, server functions, bundling and more, powered by TanStack Router and [Vite](https://vite.dev/).
|
||||
|
||||
---
|
||||
|
||||
<Steps>
|
||||
<Step title="Create a new TanStack Start app">
|
||||
Use the interactive CLI to create a new TanStack Start app.
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
bun create @tanstack/start@latest my-tanstack-app
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step title="Start the dev server">
|
||||
Change to the project directory and run the dev server with Bun.
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
cd my-tanstack-app
|
||||
bun --bun run dev
|
||||
```
|
||||
|
||||
This starts the Vite dev server with Bun.
|
||||
|
||||
</Step>
|
||||
<Step title="Update scripts in package.json">
|
||||
Modify the scripts field in your `package.json` by prefixing the Vite CLI commands with `bun --bun`. This ensures that Bun executes the Vite CLI for common tasks like `dev`, `build`, and `preview`.
|
||||
|
||||
```json package.json icon="file-json"
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "bun --bun vite dev", // [!code ++]
|
||||
"build": "bun --bun vite build", // [!code ++]
|
||||
"serve": "bun --bun vite preview" // [!code ++]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
## Hosting
|
||||
|
||||
To host your TanStack Start app, you can use [Nitro](https://nitro.build/) or a custom Bun server for production deployments.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Nitro">
|
||||
<Steps>
|
||||
<Step title="Add Nitro to your project">
|
||||
Add [Nitro](https://nitro.build/) to your project. This tool allows you to deploy your TanStack Start app to different platforms.
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
bun add nitro
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step title={<span>Update your <code>vite.config.ts</code> file</span>}>
|
||||
Update your `vite.config.ts` file to include the necessary plugins for TanStack Start with Bun.
|
||||
|
||||
```ts vite.config.ts icon="/icons/typescript.svg"
|
||||
// other imports...
|
||||
import { nitro } from "nitro/vite"; // [!code ++]
|
||||
|
||||
const config = defineConfig({
|
||||
plugins: [
|
||||
tanstackStart(),
|
||||
nitro({ preset: "bun" }), // [!code ++]
|
||||
// other plugins...
|
||||
],
|
||||
});
|
||||
|
||||
export default config;
|
||||
```
|
||||
|
||||
<Note>
|
||||
The `bun` preset is optional, but it configures the build output specifically for Bun's runtime.
|
||||
</Note>
|
||||
|
||||
</Step>
|
||||
<Step title="Update the start command">
|
||||
Make sure `build` and `start` scripts are present in your `package.json` file:
|
||||
|
||||
```json package.json icon="file-json"
|
||||
{
|
||||
"scripts": {
|
||||
"build": "bun --bun vite build", // [!code ++]
|
||||
// The .output files are created by Nitro when you run `bun run build`.
|
||||
// Not necessary when deploying to Vercel.
|
||||
"start": "bun run .output/server/index.mjs" // [!code ++]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Note>
|
||||
You do **not** need the custom `start` script when deploying to Vercel.
|
||||
</Note>
|
||||
|
||||
</Step>
|
||||
<Step title="Deploy your app">
|
||||
Check out one of our guides to deploy your app to a hosting provider.
|
||||
|
||||
<Note>
|
||||
When deploying to Vercel, you can either add the `"bunVersion": "1.x"` to your `vercel.json` file, or add it to the `nitro` config in your `vite.config.ts` file:
|
||||
|
||||
<Warning>
|
||||
Do **not** use the `bun` Nitro preset when deploying to Vercel.
|
||||
</Warning>
|
||||
|
||||
```ts vite.config.ts icon="/icons/typescript.svg"
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
tanstackStart(),
|
||||
nitro({
|
||||
preset: "bun", // [!code --]
|
||||
vercel: { // [!code ++]
|
||||
functions: { // [!code ++]
|
||||
runtime: "bun1.x", // [!code ++]
|
||||
}, // [!code ++]
|
||||
}, // [!code ++]
|
||||
}),
|
||||
],
|
||||
});
|
||||
```
|
||||
</Note>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
</Tab>
|
||||
<Tab title="Custom Server">
|
||||
<Note>
|
||||
This custom server implementation is based on [TanStack's Bun template](https://github.com/TanStack/router/blob/main/examples/react/start-bun/server.ts). It provides fine-grained control over static asset serving, including configurable memory management that preloads small files into memory for fast serving while serving larger files on-demand. This approach is useful when you need precise control over resource usage and asset loading behavior in production deployments.
|
||||
</Note>
|
||||
|
||||
<Steps>
|
||||
<Step title="Create the production server">
|
||||
Create a `server.ts` file in your project root with the following custom server implementation:
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg" expandable
|
||||
/**
|
||||
* TanStack Start Production Server with Bun
|
||||
*
|
||||
* A high-performance production server for TanStack Start applications that
|
||||
* implements intelligent static asset loading with configurable memory management.
|
||||
*
|
||||
* Features:
|
||||
* - Hybrid loading strategy (preload small files, serve large files on-demand)
|
||||
* - Configurable file filtering with include/exclude patterns
|
||||
* - Memory-efficient response generation
|
||||
* - Production-ready caching headers
|
||||
*
|
||||
* Environment Variables:
|
||||
*
|
||||
* PORT (number)
|
||||
* - Server port number
|
||||
* - Default: 3000
|
||||
*
|
||||
* ASSET_PRELOAD_MAX_SIZE (number)
|
||||
* - Maximum file size in bytes to preload into memory
|
||||
* - Files larger than this will be served on-demand from disk
|
||||
* - Default: 5242880 (5MB)
|
||||
* - Example: ASSET_PRELOAD_MAX_SIZE=5242880 (5MB)
|
||||
*
|
||||
* ASSET_PRELOAD_INCLUDE_PATTERNS (string)
|
||||
* - Comma-separated list of glob patterns for files to include
|
||||
* - If specified, only matching files are eligible for preloading
|
||||
* - Patterns are matched against filenames only, not full paths
|
||||
* - Example: ASSET_PRELOAD_INCLUDE_PATTERNS="*.js,*.css,*.woff2"
|
||||
*
|
||||
* ASSET_PRELOAD_EXCLUDE_PATTERNS (string)
|
||||
* - Comma-separated list of glob patterns for files to exclude
|
||||
* - Applied after include patterns
|
||||
* - Patterns are matched against filenames only, not full paths
|
||||
* - Example: ASSET_PRELOAD_EXCLUDE_PATTERNS="*.map,*.txt"
|
||||
*
|
||||
* ASSET_PRELOAD_VERBOSE_LOGGING (boolean)
|
||||
* - Enable detailed logging of loaded and skipped files
|
||||
* - Default: false
|
||||
* - Set to "true" to enable verbose output
|
||||
*
|
||||
* ASSET_PRELOAD_ENABLE_ETAG (boolean)
|
||||
* - Enable ETag generation for preloaded assets
|
||||
* - Default: true
|
||||
* - Set to "false" to disable ETag support
|
||||
*
|
||||
* ASSET_PRELOAD_ENABLE_GZIP (boolean)
|
||||
* - Enable Gzip compression for eligible assets
|
||||
* - Default: true
|
||||
* - Set to "false" to disable Gzip compression
|
||||
*
|
||||
* ASSET_PRELOAD_GZIP_MIN_SIZE (number)
|
||||
* - Minimum file size in bytes required for Gzip compression
|
||||
* - Files smaller than this will not be compressed
|
||||
* - Default: 1024 (1KB)
|
||||
*
|
||||
* ASSET_PRELOAD_GZIP_MIME_TYPES (string)
|
||||
* - Comma-separated list of MIME types eligible for Gzip compression
|
||||
* - Supports partial matching for types ending with "/"
|
||||
* - Default: text/,application/javascript,application/json,application/xml,image/svg+xml
|
||||
*
|
||||
* Usage:
|
||||
* bun run server.ts
|
||||
*/
|
||||
|
||||
import path from 'node:path'
|
||||
|
||||
// Configuration
|
||||
const SERVER_PORT = Number(process.env.PORT ?? 3000)
|
||||
const CLIENT_DIRECTORY = './dist/client'
|
||||
const SERVER_ENTRY_POINT = './dist/server/server.js'
|
||||
|
||||
// Logging utilities for professional output
|
||||
const log = {
|
||||
info: (message: string) => {
|
||||
console.log(`[INFO] ${message}`)
|
||||
},
|
||||
success: (message: string) => {
|
||||
console.log(`[SUCCESS] ${message}`)
|
||||
},
|
||||
warning: (message: string) => {
|
||||
console.log(`[WARNING] ${message}`)
|
||||
},
|
||||
error: (message: string) => {
|
||||
console.log(`[ERROR] ${message}`)
|
||||
},
|
||||
header: (message: string) => {
|
||||
console.log(`\n${message}\n`)
|
||||
},
|
||||
}
|
||||
|
||||
// Preloading configuration from environment variables
|
||||
const MAX_PRELOAD_BYTES = Number(
|
||||
process.env.ASSET_PRELOAD_MAX_SIZE ?? 5 * 1024 * 1024, // 5MB default
|
||||
)
|
||||
|
||||
// Parse comma-separated include patterns (no defaults)
|
||||
const INCLUDE_PATTERNS = (process.env.ASSET_PRELOAD_INCLUDE_PATTERNS ?? '')
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
.map((pattern: string) => convertGlobToRegExp(pattern))
|
||||
|
||||
// Parse comma-separated exclude patterns (no defaults)
|
||||
const EXCLUDE_PATTERNS = (process.env.ASSET_PRELOAD_EXCLUDE_PATTERNS ?? '')
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
.map((pattern: string) => convertGlobToRegExp(pattern))
|
||||
|
||||
// Verbose logging flag
|
||||
const VERBOSE = process.env.ASSET_PRELOAD_VERBOSE_LOGGING === 'true'
|
||||
|
||||
// Optional ETag feature
|
||||
const ENABLE_ETAG = (process.env.ASSET_PRELOAD_ENABLE_ETAG ?? 'true') === 'true'
|
||||
|
||||
// Optional Gzip feature
|
||||
const ENABLE_GZIP = (process.env.ASSET_PRELOAD_ENABLE_GZIP ?? 'true') === 'true'
|
||||
const GZIP_MIN_BYTES = Number(process.env.ASSET_PRELOAD_GZIP_MIN_SIZE ?? 1024) // 1KB
|
||||
const GZIP_TYPES = (
|
||||
process.env.ASSET_PRELOAD_GZIP_MIME_TYPES ??
|
||||
'text/,application/javascript,application/json,application/xml,image/svg+xml'
|
||||
)
|
||||
.split(',')
|
||||
.map((v) => v.trim())
|
||||
.filter(Boolean)
|
||||
|
||||
/**
|
||||
* Convert a simple glob pattern to a regular expression
|
||||
* Supports * wildcard for matching any characters
|
||||
*/
|
||||
function convertGlobToRegExp(globPattern: string): RegExp {
|
||||
// Escape regex special chars except *, then replace * with .*
|
||||
const escapedPattern = globPattern
|
||||
.replace(/[-/\\^$+?.()|[\]{}]/g, '\\$&')
|
||||
.replace(/\*/g, '.*')
|
||||
return new RegExp(`^${escapedPattern}$`, 'i')
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute ETag for a given data buffer
|
||||
*/
|
||||
function computeEtag(data: Uint8Array): string {
|
||||
const hash = Bun.hash(data)
|
||||
return `W/"${hash.toString(16)}-${data.byteLength.toString()}"`
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata for preloaded static assets
|
||||
*/
|
||||
interface AssetMetadata {
|
||||
route: string
|
||||
size: number
|
||||
type: string
|
||||
}
|
||||
|
||||
/**
|
||||
* In-memory asset with ETag and Gzip support
|
||||
*/
|
||||
interface InMemoryAsset {
|
||||
raw: Uint8Array
|
||||
gz?: Uint8Array
|
||||
etag?: string
|
||||
type: string
|
||||
immutable: boolean
|
||||
size: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of static asset preloading process
|
||||
*/
|
||||
interface PreloadResult {
|
||||
routes: Record<string, (req: Request) => Response | Promise<Response>>
|
||||
loaded: AssetMetadata[]
|
||||
skipped: AssetMetadata[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a file is eligible for preloading based on configured patterns
|
||||
*/
|
||||
function isFileEligibleForPreloading(relativePath: string): boolean {
|
||||
const fileName = relativePath.split(/[/\\]/).pop() ?? relativePath
|
||||
|
||||
// If include patterns are specified, file must match at least one
|
||||
if (INCLUDE_PATTERNS.length > 0) {
|
||||
if (!INCLUDE_PATTERNS.some((pattern) => pattern.test(fileName))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// If exclude patterns are specified, file must not match any
|
||||
if (EXCLUDE_PATTERNS.some((pattern) => pattern.test(fileName))) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a MIME type is compressible
|
||||
*/
|
||||
function isMimeTypeCompressible(mimeType: string): boolean {
|
||||
return GZIP_TYPES.some((type) =>
|
||||
type.endsWith('/') ? mimeType.startsWith(type) : mimeType === type,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Conditionally compress data based on size and MIME type
|
||||
*/
|
||||
function compressDataIfAppropriate(
|
||||
data: Uint8Array,
|
||||
mimeType: string,
|
||||
): Uint8Array | undefined {
|
||||
if (!ENABLE_GZIP) return undefined
|
||||
if (data.byteLength < GZIP_MIN_BYTES) return undefined
|
||||
if (!isMimeTypeCompressible(mimeType)) return undefined
|
||||
try {
|
||||
return Bun.gzipSync(data.buffer as ArrayBuffer)
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create response handler function with ETag and Gzip support
|
||||
*/
|
||||
function createResponseHandler(
|
||||
asset: InMemoryAsset,
|
||||
): (req: Request) => Response {
|
||||
return (req: Request) => {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': asset.type,
|
||||
'Cache-Control': asset.immutable
|
||||
? 'public, max-age=31536000, immutable'
|
||||
: 'public, max-age=3600',
|
||||
}
|
||||
|
||||
if (ENABLE_ETAG && asset.etag) {
|
||||
const ifNone = req.headers.get('if-none-match')
|
||||
if (ifNone && ifNone === asset.etag) {
|
||||
return new Response(null, {
|
||||
status: 304,
|
||||
headers: { ETag: asset.etag },
|
||||
})
|
||||
}
|
||||
headers.ETag = asset.etag
|
||||
}
|
||||
|
||||
if (
|
||||
ENABLE_GZIP &&
|
||||
asset.gz &&
|
||||
req.headers.get('accept-encoding')?.includes('gzip')
|
||||
) {
|
||||
headers['Content-Encoding'] = 'gzip'
|
||||
headers['Content-Length'] = String(asset.gz.byteLength)
|
||||
const gzCopy = new Uint8Array(asset.gz)
|
||||
return new Response(gzCopy, { status: 200, headers })
|
||||
}
|
||||
|
||||
headers['Content-Length'] = String(asset.raw.byteLength)
|
||||
const rawCopy = new Uint8Array(asset.raw)
|
||||
return new Response(rawCopy, { status: 200, headers })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create composite glob pattern from include patterns
|
||||
*/
|
||||
function createCompositeGlobPattern(): Bun.Glob {
|
||||
const raw = (process.env.ASSET_PRELOAD_INCLUDE_PATTERNS ?? '')
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
if (raw.length === 0) return new Bun.Glob('**/*')
|
||||
if (raw.length === 1) return new Bun.Glob(raw[0])
|
||||
return new Bun.Glob(`{${raw.join(',')}}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize static routes with intelligent preloading strategy
|
||||
* Small files are loaded into memory, large files are served on-demand
|
||||
*/
|
||||
async function initializeStaticRoutes(
|
||||
clientDirectory: string,
|
||||
): Promise<PreloadResult> {
|
||||
const routes: Record<string, (req: Request) => Response | Promise<Response>> =
|
||||
{}
|
||||
const loaded: AssetMetadata[] = []
|
||||
const skipped: AssetMetadata[] = []
|
||||
|
||||
log.info(`Loading static assets from ${clientDirectory}...`)
|
||||
if (VERBOSE) {
|
||||
console.log(
|
||||
`Max preload size: ${(MAX_PRELOAD_BYTES / 1024 / 1024).toFixed(2)} MB`,
|
||||
)
|
||||
if (INCLUDE_PATTERNS.length > 0) {
|
||||
console.log(
|
||||
`Include patterns: ${process.env.ASSET_PRELOAD_INCLUDE_PATTERNS ?? ''}`,
|
||||
)
|
||||
}
|
||||
if (EXCLUDE_PATTERNS.length > 0) {
|
||||
console.log(
|
||||
`Exclude patterns: ${process.env.ASSET_PRELOAD_EXCLUDE_PATTERNS ?? ''}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let totalPreloadedBytes = 0
|
||||
|
||||
try {
|
||||
const glob = createCompositeGlobPattern()
|
||||
for await (const relativePath of glob.scan({ cwd: clientDirectory })) {
|
||||
const filepath = path.join(clientDirectory, relativePath)
|
||||
const route = `/${relativePath.split(path.sep).join(path.posix.sep)}`
|
||||
|
||||
try {
|
||||
// Get file metadata
|
||||
const file = Bun.file(filepath)
|
||||
|
||||
// Skip if file doesn't exist or is empty
|
||||
if (!(await file.exists()) || file.size === 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
const metadata: AssetMetadata = {
|
||||
route,
|
||||
size: file.size,
|
||||
type: file.type || 'application/octet-stream',
|
||||
}
|
||||
|
||||
// Determine if file should be preloaded
|
||||
const matchesPattern = isFileEligibleForPreloading(relativePath)
|
||||
const withinSizeLimit = file.size <= MAX_PRELOAD_BYTES
|
||||
|
||||
if (matchesPattern && withinSizeLimit) {
|
||||
// Preload small files into memory with ETag and Gzip support
|
||||
const bytes = new Uint8Array(await file.arrayBuffer())
|
||||
const gz = compressDataIfAppropriate(bytes, metadata.type)
|
||||
const etag = ENABLE_ETAG ? computeEtag(bytes) : undefined
|
||||
const asset: InMemoryAsset = {
|
||||
raw: bytes,
|
||||
gz,
|
||||
etag,
|
||||
type: metadata.type,
|
||||
immutable: true,
|
||||
size: bytes.byteLength,
|
||||
}
|
||||
routes[route] = createResponseHandler(asset)
|
||||
|
||||
loaded.push({ ...metadata, size: bytes.byteLength })
|
||||
totalPreloadedBytes += bytes.byteLength
|
||||
} else {
|
||||
// Serve large or filtered files on-demand
|
||||
routes[route] = () => {
|
||||
const fileOnDemand = Bun.file(filepath)
|
||||
return new Response(fileOnDemand, {
|
||||
headers: {
|
||||
'Content-Type': metadata.type,
|
||||
'Cache-Control': 'public, max-age=3600',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
skipped.push(metadata)
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error && error.name !== 'EISDIR') {
|
||||
log.error(`Failed to load ${filepath}: ${error.message}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show detailed file overview only when verbose mode is enabled
|
||||
if (VERBOSE && (loaded.length > 0 || skipped.length > 0)) {
|
||||
const allFiles = [...loaded, ...skipped].sort((a, b) =>
|
||||
a.route.localeCompare(b.route),
|
||||
)
|
||||
|
||||
// Calculate max path length for alignment
|
||||
const maxPathLength = Math.min(
|
||||
Math.max(...allFiles.map((f) => f.route.length)),
|
||||
60,
|
||||
)
|
||||
|
||||
// Format file size with KB and actual gzip size
|
||||
const formatFileSize = (bytes: number, gzBytes?: number) => {
|
||||
const kb = bytes / 1024
|
||||
const sizeStr = kb < 100 ? kb.toFixed(2) : kb.toFixed(1)
|
||||
|
||||
if (gzBytes !== undefined) {
|
||||
const gzKb = gzBytes / 1024
|
||||
const gzStr = gzKb < 100 ? gzKb.toFixed(2) : gzKb.toFixed(1)
|
||||
return {
|
||||
size: sizeStr,
|
||||
gzip: gzStr,
|
||||
}
|
||||
}
|
||||
|
||||
// Rough gzip estimation (typically 30-70% compression) if no actual gzip data
|
||||
const gzipKb = kb * 0.35
|
||||
return {
|
||||
size: sizeStr,
|
||||
gzip: gzipKb < 100 ? gzipKb.toFixed(2) : gzipKb.toFixed(1),
|
||||
}
|
||||
}
|
||||
|
||||
if (loaded.length > 0) {
|
||||
console.log('\n📁 Preloaded into memory:')
|
||||
console.log(
|
||||
'Path │ Size │ Gzip Size',
|
||||
)
|
||||
loaded
|
||||
.sort((a, b) => a.route.localeCompare(b.route))
|
||||
.forEach((file) => {
|
||||
const { size, gzip } = formatFileSize(file.size)
|
||||
const paddedPath = file.route.padEnd(maxPathLength)
|
||||
const sizeStr = `${size.padStart(7)} kB`
|
||||
const gzipStr = `${gzip.padStart(7)} kB`
|
||||
console.log(`${paddedPath} │ ${sizeStr} │ ${gzipStr}`)
|
||||
})
|
||||
}
|
||||
|
||||
if (skipped.length > 0) {
|
||||
console.log('\n💾 Served on-demand:')
|
||||
console.log(
|
||||
'Path │ Size │ Gzip Size',
|
||||
)
|
||||
skipped
|
||||
.sort((a, b) => a.route.localeCompare(b.route))
|
||||
.forEach((file) => {
|
||||
const { size, gzip } = formatFileSize(file.size)
|
||||
const paddedPath = file.route.padEnd(maxPathLength)
|
||||
const sizeStr = `${size.padStart(7)} kB`
|
||||
const gzipStr = `${gzip.padStart(7)} kB`
|
||||
console.log(`${paddedPath} │ ${sizeStr} │ ${gzipStr}`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Show detailed verbose info if enabled
|
||||
if (VERBOSE) {
|
||||
if (loaded.length > 0 || skipped.length > 0) {
|
||||
const allFiles = [...loaded, ...skipped].sort((a, b) =>
|
||||
a.route.localeCompare(b.route),
|
||||
)
|
||||
console.log('\n📊 Detailed file information:')
|
||||
console.log(
|
||||
'Status │ Path │ MIME Type │ Reason',
|
||||
)
|
||||
allFiles.forEach((file) => {
|
||||
const isPreloaded = loaded.includes(file)
|
||||
const status = isPreloaded ? 'MEMORY' : 'ON-DEMAND'
|
||||
const reason =
|
||||
!isPreloaded && file.size > MAX_PRELOAD_BYTES
|
||||
? 'too large'
|
||||
: !isPreloaded
|
||||
? 'filtered'
|
||||
: 'preloaded'
|
||||
const route =
|
||||
file.route.length > 30
|
||||
? file.route.substring(0, 27) + '...'
|
||||
: file.route
|
||||
console.log(
|
||||
`${status.padEnd(12)} │ ${route.padEnd(30)} │ ${file.type.padEnd(28)} │ ${reason.padEnd(10)}`,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
console.log('\n📊 No files found to display')
|
||||
}
|
||||
}
|
||||
|
||||
// Log summary after the file list
|
||||
console.log() // Empty line for separation
|
||||
if (loaded.length > 0) {
|
||||
log.success(
|
||||
`Preloaded ${String(loaded.length)} files (${(totalPreloadedBytes / 1024 / 1024).toFixed(2)} MB) into memory`,
|
||||
)
|
||||
} else {
|
||||
log.info('No files preloaded into memory')
|
||||
}
|
||||
|
||||
if (skipped.length > 0) {
|
||||
const tooLarge = skipped.filter((f) => f.size > MAX_PRELOAD_BYTES).length
|
||||
const filtered = skipped.length - tooLarge
|
||||
log.info(
|
||||
`${String(skipped.length)} files will be served on-demand (${String(tooLarge)} too large, ${String(filtered)} filtered)`,
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(
|
||||
`Failed to load static files from ${clientDirectory}: ${String(error)}`,
|
||||
)
|
||||
}
|
||||
|
||||
return { routes, loaded, skipped }
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the server
|
||||
*/
|
||||
async function initializeServer() {
|
||||
log.header('Starting Production Server')
|
||||
|
||||
// Load TanStack Start server handler
|
||||
let handler: { fetch: (request: Request) => Response | Promise<Response> }
|
||||
try {
|
||||
const serverModule = (await import(SERVER_ENTRY_POINT)) as {
|
||||
default: { fetch: (request: Request) => Response | Promise<Response> }
|
||||
}
|
||||
handler = serverModule.default
|
||||
log.success('TanStack Start application handler initialized')
|
||||
} catch (error) {
|
||||
log.error(`Failed to load server handler: ${String(error)}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Build static routes with intelligent preloading
|
||||
const { routes } = await initializeStaticRoutes(CLIENT_DIRECTORY)
|
||||
|
||||
// Create Bun server
|
||||
const server = Bun.serve({
|
||||
port: SERVER_PORT,
|
||||
|
||||
routes: {
|
||||
// Serve static assets (preloaded or on-demand)
|
||||
...routes,
|
||||
|
||||
// Fallback to TanStack Start handler for all other routes
|
||||
'/*': (req: Request) => {
|
||||
try {
|
||||
return handler.fetch(req)
|
||||
} catch (error) {
|
||||
log.error(`Server handler error: ${String(error)}`)
|
||||
return new Response('Internal Server Error', { status: 500 })
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// Global error handler
|
||||
error(error) {
|
||||
log.error(
|
||||
`Uncaught server error: ${error instanceof Error ? error.message : String(error)}`,
|
||||
)
|
||||
return new Response('Internal Server Error', { status: 500 })
|
||||
},
|
||||
})
|
||||
|
||||
log.success(`Server listening on http://localhost:${String(server.port)}`)
|
||||
}
|
||||
|
||||
// Initialize the server
|
||||
initializeServer().catch((error: unknown) => {
|
||||
log.error(`Failed to start server: ${String(error)}`)
|
||||
process.exit(1)
|
||||
})
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step title="Update package.json scripts">
|
||||
Add a `start` script to run the custom server:
|
||||
|
||||
```json package.json icon="file-json"
|
||||
{
|
||||
"scripts": {
|
||||
"build": "bun --bun vite build",
|
||||
"start": "bun run server.ts" // [!code ++]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step title="Build and run">
|
||||
Build your application and start the server:
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
bun run build
|
||||
bun run start
|
||||
```
|
||||
|
||||
The server will start on port 3000 by default (configurable via `PORT` environment variable).
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Columns cols={3}>
|
||||
<Card title="Vercel" href="/guides/deployment/vercel" icon="/icons/ecosystem/vercel.svg">
|
||||
Deploy on Vercel
|
||||
</Card>
|
||||
<Card title="Render" href="/guides/deployment/render" icon="/icons/ecosystem/render.svg">
|
||||
Deploy on Render
|
||||
</Card>
|
||||
<Card title="Railway" href="/guides/deployment/railway" icon="/icons/ecosystem/railway.svg">
|
||||
Deploy on Railway
|
||||
</Card>
|
||||
<Card title="DigitalOcean" href="/guides/deployment/digital-ocean" icon="/icons/ecosystem/digitalocean.svg">
|
||||
Deploy on DigitalOcean
|
||||
</Card>
|
||||
<Card title="AWS Lambda" href="/guides/deployment/aws-lambda" icon="/icons/ecosystem/aws.svg">
|
||||
Deploy on AWS Lambda
|
||||
</Card>
|
||||
<Card title="Google Cloud Run" href="/guides/deployment/google-cloud-run" icon="/icons/ecosystem/gcp.svg">
|
||||
Deploy on Google Cloud Run
|
||||
</Card>
|
||||
</Columns>
|
||||
|
||||
---
|
||||
|
||||
## Templates
|
||||
|
||||
<Columns cols={2}>
|
||||
<Card
|
||||
title="Todo App with Tanstack + Bun"
|
||||
img="/images/templates/bun-tanstack-todo.png"
|
||||
href="https://github.com/bun-templates/bun-tanstack-todo"
|
||||
arrow="true"
|
||||
cta="Go to template"
|
||||
>
|
||||
A Todo application built with Bun, TanStack Start, and PostgreSQL.
|
||||
</Card>
|
||||
<Card
|
||||
title="Bun + TanStack Start Application"
|
||||
img="/images/templates/bun-tanstack-basic.png"
|
||||
href="https://github.com/bun-templates/bun-tanstack-basic"
|
||||
arrow="true"
|
||||
cta="Go to template"
|
||||
>
|
||||
A TanStack Start template using Bun with SSR and file-based routing.
|
||||
</Card>
|
||||
<Card
|
||||
title="Basic Bun + Tanstack Starter"
|
||||
img="/images/templates/bun-tanstack-start.png"
|
||||
href="https://github.com/bun-templates/bun-tanstack-start"
|
||||
arrow="true"
|
||||
cta="Go to template"
|
||||
>
|
||||
The basic TanStack starter using the Bun runtime and Bun's file APIs.
|
||||
</Card>
|
||||
</Columns>
|
||||
|
||||
---
|
||||
|
||||
[→ See TanStack Start's official documentation](https://tanstack.com/start/latest/docs/framework/react/guide/hosting) for more information on hosting.
|
||||
@@ -34,7 +34,7 @@ mode: center
|
||||
|
||||
Set the `REDIS_URL` environment variable in your `.env` file using the Redis endpoint (not the REST URL):
|
||||
|
||||
```env .env icon="settings"
|
||||
```ini .env icon="settings"
|
||||
REDIS_URL=rediss://********@********.upstash.io:6379
|
||||
```
|
||||
|
||||
|
||||
@@ -74,4 +74,4 @@ bunx --bun vite build
|
||||
|
||||
---
|
||||
|
||||
This is a stripped down guide to get you started with Vite + Bun. For more information, see the [Vite documentation](https://vitejs.dev/guide/).
|
||||
This is a stripped down guide to get you started with Vite + Bun. For more information, see the [Vite documentation](https://vite.dev/guide/).
|
||||
|
||||
@@ -6,7 +6,7 @@ mode: center
|
||||
|
||||
## Extract links from a webpage
|
||||
|
||||
Bun's [HTMLRewriter](https://bun.com/docs/api/html-rewriter) API can be used to efficiently extract links from HTML content. It works by chaining together CSS selectors to match the elements, text, and attributes you want to process. This is a simple example of how to extract links from a webpage. You can pass `.transform` a `Response`, `Blob`, or `string`.
|
||||
Bun's [HTMLRewriter](/runtime/html-rewriter) API can be used to efficiently extract links from HTML content. It works by chaining together CSS selectors to match the elements, text, and attributes you want to process. This is a simple example of how to extract links from a webpage. You can pass `.transform` a `Response`, `Blob`, or `string`.
|
||||
|
||||
```ts extract-links.ts icon="/icons/typescript.svg"
|
||||
async function extractLinks(url: string) {
|
||||
@@ -37,6 +37,7 @@ await extractLinks("https://bun.com");
|
||||
|
||||
When scraping websites, you often want to convert relative URLs (like `/docs`) to absolute URLs. Here's how to handle URL resolution:
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```ts extract-links.ts icon="/icons/typescript.svg"
|
||||
async function extractLinksFromURL(url: string) {
|
||||
const response = await fetch(url);
|
||||
@@ -47,13 +48,11 @@ async function extractLinksFromURL(url: string) {
|
||||
const href = el.getAttribute("href");
|
||||
if (href) {
|
||||
// Convert relative URLs to absolute // [!code ++]
|
||||
try {
|
||||
// [!code ++]
|
||||
try { // [!code ++]
|
||||
const absoluteURL = new URL(href, url).href; // [!code ++]
|
||||
links.add(absoluteURL); // [!code ++]
|
||||
} catch {
|
||||
// [!code ++]
|
||||
links.add(href);
|
||||
links.add(absoluteURL);
|
||||
} catch { // [!code ++]
|
||||
links.add(href); // [!code ++]
|
||||
} // [!code ++]
|
||||
}
|
||||
},
|
||||
@@ -69,4 +68,4 @@ const websiteLinks = await extractLinksFromURL("https://example.com");
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > HTMLRewriter](https://bun.com/docs/api/html-rewriter) for complete documentation on HTML transformation with Bun.
|
||||
See [Docs > API > HTMLRewriter](/runtime/html-rewriter) for complete documentation on HTML transformation with Bun.
|
||||
|
||||
@@ -6,7 +6,7 @@ mode: center
|
||||
|
||||
## Extract social share images and Open Graph tags
|
||||
|
||||
Bun's [HTMLRewriter](https://bun.com/docs/api/html-rewriter) API can be used to efficiently extract social share images and Open Graph metadata from HTML content. This is particularly useful for building link preview features, social media cards, or web scrapers. We can use HTMLRewriter to match CSS selectors to HTML elements, text, and attributes we want to process.
|
||||
Bun's [HTMLRewriter](/runtime/html-rewriter) API can be used to efficiently extract social share images and Open Graph metadata from HTML content. This is particularly useful for building link preview features, social media cards, or web scrapers. We can use HTMLRewriter to match CSS selectors to HTML elements, text, and attributes we want to process.
|
||||
|
||||
```ts extract-social-meta.ts icon="/icons/typescript.svg"
|
||||
interface SocialMetadata {
|
||||
|
||||
@@ -63,8 +63,9 @@ Our form will send a `POST` request to the `/action` endpoint with the form data
|
||||
|
||||
First we use the [`.formData()`](https://developer.mozilla.org/en-US/docs/Web/API/Request/formData) method on the incoming `Request` to asynchronously parse its contents to a `FormData` instance. Then we can use the [`.get()`](https://developer.mozilla.org/en-US/docs/Web/API/FormData/get) method to extract the value of the `name` and `profilePicture` fields. Here `name` corresponds to a `string` and `profilePicture` is a `Blob`.
|
||||
|
||||
Finally, we write the `Blob` to disk using [`Bun.write()`](https://bun.com/docs/api/file-io#writing-files-bun-write).
|
||||
Finally, we write the `Blob` to disk using [`Bun.write()`](/runtime/file-io#writing-files-bun-write).
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```ts index.ts icon="/icons/typescript.svg"
|
||||
const server = Bun.serve({
|
||||
port: 4000,
|
||||
@@ -80,8 +81,7 @@ const server = Bun.serve({
|
||||
});
|
||||
|
||||
// parse formdata at /action // [!code ++]
|
||||
if (url.pathname === "/action") {
|
||||
// [!code ++]
|
||||
if (url.pathname === "/action") { // [!code ++]
|
||||
const formdata = await req.formData(); // [!code ++]
|
||||
const name = formdata.get("name"); // [!code ++]
|
||||
const profilePicture = formdata.get("profilePicture"); // [!code ++]
|
||||
|
||||
@@ -4,7 +4,7 @@ sidebarTitle: Hot reload an HTTP server
|
||||
mode: center
|
||||
---
|
||||
|
||||
Bun supports the [`--hot`](https://bun.com/docs/runtime/hot#hot-mode) flag to run a file with hot reloading enabled. When any module or file changes, Bun re-runs the file.
|
||||
Bun supports the [`--hot`](/runtime/watch-mode#hot-mode) flag to run a file with hot reloading enabled. When any module or file changes, Bun re-runs the file.
|
||||
|
||||
```sh terminal icon="terminal"
|
||||
bun --hot run index.ts
|
||||
|
||||
@@ -6,7 +6,7 @@ mode: center
|
||||
|
||||
This starts an HTTP server listening on port `3000`. It demonstrates basic routing with a number of common responses and also handles POST data from standard forms or as JSON.
|
||||
|
||||
See [`Bun.serve`](https://bun.com/docs/api/http) for details.
|
||||
See [`Bun.serve`](/runtime/http/server) for details.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
const server = Bun.serve({
|
||||
|
||||
@@ -6,7 +6,7 @@ mode: center
|
||||
|
||||
This starts an HTTP server listening on port `3000`. It responds to all requests with a `Response` with status `200` and body `"Welcome to Bun!"`.
|
||||
|
||||
See [`Bun.serve`](https://bun.com/docs/api/http) for details.
|
||||
See [`Bun.serve`](/runtime/http/server) for details.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
const server = Bun.serve({
|
||||
|
||||
@@ -4,7 +4,7 @@ sidebarTitle: Stream file response
|
||||
mode: center
|
||||
---
|
||||
|
||||
This snippet reads a file from disk using [`Bun.file()`](https://bun.com/docs/api/file-io#reading-files-bun-file). This returns a `BunFile` instance, which can be passed directly into the `new Response` constructor.
|
||||
This snippet reads a file from disk using [`Bun.file()`](/runtime/file-io#reading-files-bun-file). This returns a `BunFile` instance, which can be passed directly into the `new Response` constructor.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
const path = "/path/to/file.txt";
|
||||
@@ -32,7 +32,7 @@ new Response(Bun.file("./img.png")).headers.get("Content-Type");
|
||||
|
||||
---
|
||||
|
||||
Putting it all together with [`Bun.serve()`](https://bun.com/docs/api/http#bun-serve).
|
||||
Putting it all together with [`Bun.serve()`](/runtime/http/server).
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
// static file server
|
||||
@@ -47,4 +47,4 @@ Bun.serve({
|
||||
|
||||
---
|
||||
|
||||
See [Docs > API > File I/O](https://bun.com/docs/api/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`.
|
||||
See [Docs > API > File I/O](/runtime/file-io#writing-files-bun-write) for complete documentation of `Bun.write()`.
|
||||
|
||||
@@ -4,7 +4,7 @@ sidebarTitle: Configure TLS
|
||||
mode: center
|
||||
---
|
||||
|
||||
Set the `tls` key to configure TLS. Both `key` and `cert` are required. The `key` should be the contents of your private key; `cert` should be the contents of your issued certificate. Use [`Bun.file()`](https://bun.com/docs/api/file-io#reading-files-bun-file) to read the contents.
|
||||
Set the `tls` key to configure TLS. Both `key` and `cert` are required. The `key` should be the contents of your private key; `cert` should be the contents of your issued certificate. Use [`Bun.file()`](/runtime/file-io#reading-files-bun-file) to read the contents.
|
||||
|
||||
```ts server.ts icon="/icons/typescript.svg"
|
||||
const server = Bun.serve({
|
||||
|
||||
@@ -25,4 +25,4 @@ This will add the package to `devDependencies` in `package.json`.
|
||||
|
||||
---
|
||||
|
||||
See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager.
|
||||
See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager.
|
||||
|
||||
@@ -33,6 +33,8 @@ bun add git@github.com:lodash/lodash.git
|
||||
bun add github:colinhacks/zod
|
||||
```
|
||||
|
||||
**Note:** GitHub dependencies download via HTTP tarball when possible for faster installation.
|
||||
|
||||
---
|
||||
|
||||
See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager.
|
||||
See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager.
|
||||
|
||||
@@ -24,4 +24,4 @@ This will add the package to `optionalDependencies` in `package.json`.
|
||||
|
||||
---
|
||||
|
||||
See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager.
|
||||
See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager.
|
||||
|
||||
@@ -17,7 +17,7 @@ This will add the package to `peerDependencies` in `package.json`.
|
||||
```json package.json icon="file-json"
|
||||
{
|
||||
"peerDependencies": {
|
||||
"@types/bun": "^1.3.1" // [!code ++]
|
||||
"@types/bun": "^1.3.3" // [!code ++]
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -26,14 +26,14 @@ This will add the package to `peerDependencies` in `package.json`.
|
||||
|
||||
Running `bun install` will install peer dependencies by default, unless marked optional in `peerDependenciesMeta`.
|
||||
|
||||
{/* prettier-ignore */}
|
||||
```json package.json icon="file-json"
|
||||
{
|
||||
"peerDependencies": {
|
||||
"@types/bun": "^1.3.1"
|
||||
"@types/bun": "^1.3.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/bun": {
|
||||
// [!code ++]
|
||||
"@types/bun": { // [!code ++]
|
||||
"optional": true // [!code ++]
|
||||
} // [!code ++]
|
||||
}
|
||||
@@ -42,4 +42,4 @@ Running `bun install` will install peer dependencies by default, unless marked o
|
||||
|
||||
---
|
||||
|
||||
See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager.
|
||||
See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager.
|
||||
|
||||
@@ -32,4 +32,4 @@ import { z } from "zod";
|
||||
|
||||
---
|
||||
|
||||
See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager.
|
||||
See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager.
|
||||
|
||||
@@ -41,4 +41,4 @@ bun add zod@next
|
||||
|
||||
---
|
||||
|
||||
See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager.
|
||||
See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager.
|
||||
|
||||
@@ -25,15 +25,15 @@ To use it with `bun install`, add a `bunfig.toml` file to your project with the
|
||||
[install.registry]
|
||||
url = "https://pkgs.dev.azure.com/my-azure-artifacts-user/_packaging/my-azure-artifacts-user/npm/registry"
|
||||
username = "my-azure-artifacts-user"
|
||||
# Bun v1.0.3+ supports using an environment variable here
|
||||
# You can use an environment variable here
|
||||
password = "$NPM_PASSWORD"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Then assign your Azure Personal Access Token to the `NPM_PASSWORD` environment variable. Bun [automatically reads](https://bun.com/docs/runtime/env) `.env` files, so create a file called `.env` in your project root. There is no need to base-64 encode this token! Bun will do this for you.
|
||||
Then assign your Azure Personal Access Token to the `NPM_PASSWORD` environment variable. Bun [automatically reads](/runtime/environment-variables) `.env` files, so create a file called `.env` in your project root. There is no need to base-64 encode this token! Bun will do this for you.
|
||||
|
||||
```txt .env icon="settings"
|
||||
```ini .env icon="settings"
|
||||
NPM_PASSWORD=<paste token here>
|
||||
```
|
||||
|
||||
@@ -43,7 +43,7 @@ NPM_PASSWORD=<paste token here>
|
||||
|
||||
---
|
||||
|
||||
To configure Azure Artifacts without `bunfig.toml`, you can set the `NPM_CONFIG_REGISTRY` environment variable. The URL should include `:username` and `:_password` as query parameters. Replace `<USERNAME>` and `<PASSWORD>` with the apprropriate values.
|
||||
To configure Azure Artifacts without `bunfig.toml`, you can set the `NPM_CONFIG_REGISTRY` environment variable. The URL should include `:username` and `:_password` as query parameters. Replace `<USERNAME>` and `<PASSWORD>` with the appropriate values.
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
NPM_CONFIG_REGISTRY=https://pkgs.dev.azure.com/my-azure-artifacts-user/_packaging/my-azure-artifacts-user/npm/registry/:username=<USERNAME>:_password=<PASSWORD>
|
||||
|
||||
@@ -20,7 +20,7 @@ registry = "https://usertitle:password@registry.npmjs.org"
|
||||
|
||||
---
|
||||
|
||||
Your `bunfig.toml` can reference environment variables. Bun automatically loads environment variables from `.env.local`, `.env.[NODE_ENV]`, and `.env`. See [Docs > Environment variables](https://bun.com/docs/runtime/env) for more information.
|
||||
Your `bunfig.toml` can reference environment variables. Bun automatically loads environment variables from `.env.local`, `.env.[NODE_ENV]`, and `.env`. See [Docs > Environment variables](/runtime/environment-variables) for more information.
|
||||
|
||||
```toml bunfig.toml icon="settings"
|
||||
[install]
|
||||
@@ -29,4 +29,4 @@ registry = { url = "https://registry.npmjs.org", token = "$npm_token" }
|
||||
|
||||
---
|
||||
|
||||
See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager.
|
||||
See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager.
|
||||
|
||||
@@ -99,7 +99,7 @@ bun update
|
||||
bun update @types/bun --latest
|
||||
|
||||
# Update a dependency to a specific version
|
||||
bun update @types/bun@1.3.1
|
||||
bun update @types/bun@1.3.3
|
||||
|
||||
# Update all dependencies to the latest versions
|
||||
bun update --latest
|
||||
|
||||
@@ -6,7 +6,8 @@ mode: center
|
||||
|
||||
<Note>
|
||||
Bun v1.1.39 introduced `bun.lock`, a JSONC formatted lockfile. `bun.lock` is human-readable and git-diffable without
|
||||
configuration, at no cost to performance. [**Learn more.**](https://bun.com/docs/install/lockfile#text-based-lockfile)
|
||||
configuration, at no cost to performance. In 1.2.0+ it is the default format used for new projects. [**Learn
|
||||
more.**](/pm/lockfile#text-based-lockfile)
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
@@ -17,7 +17,7 @@ Make sure to replace `MY_SUBDOMAIN` with your JFrog Artifactory subdomain, such
|
||||
```toml bunfig.toml icon="settings"
|
||||
[install.registry]
|
||||
url = "https://MY_SUBDOMAIN.jfrog.io/artifactory/api/npm/npm/_auth=MY_TOKEN"
|
||||
# Bun v1.0.3+ supports using an environment variable here
|
||||
# You can use an environment variable here
|
||||
# url = "$NPM_CONFIG_REGISTRY"
|
||||
```
|
||||
|
||||
|
||||
@@ -22,4 +22,4 @@ z.string();
|
||||
|
||||
---
|
||||
|
||||
See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager.
|
||||
See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager.
|
||||
|
||||
@@ -4,7 +4,7 @@ sidebarTitle: Configure a scoped registry
|
||||
mode: center
|
||||
---
|
||||
|
||||
Private registries can be configured using either [`.npmrc`](https://bun.com/docs/install/npmrc) or [`bunfig.toml`](https://bun.com/docs/runtime/bunfig#install-registry). While both are supported, we recommend using **bunfig.toml** for enhanced flexibility and Bun-specific options.
|
||||
Private registries can be configured using either [`.npmrc`](/pm/npmrc) or [`bunfig.toml`](/runtime/bunfig#install-registry). While both are supported, we recommend using **bunfig.toml** for enhanced flexibility and Bun-specific options.
|
||||
|
||||
To configure a registry for a particular npm scope:
|
||||
|
||||
@@ -28,7 +28,7 @@ To configure a registry for a particular npm scope:
|
||||
|
||||
---
|
||||
|
||||
Your `bunfig.toml` can reference environment variables. Bun automatically loads environment variables from `.env.local`, `.env.[NODE_ENV]`, and `.env`. See [Docs > Environment variables](https://bun.com/docs/runtime/env) for more information.
|
||||
Your `bunfig.toml` can reference environment variables. Bun automatically loads environment variables from `.env.local`, `.env.[NODE_ENV]`, and `.env`. See [Docs > Environment variables](/runtime/environment-variables) for more information.
|
||||
|
||||
```toml bunfig.toml icon="settings"
|
||||
[install.scopes]
|
||||
@@ -37,4 +37,4 @@ Your `bunfig.toml` can reference environment variables. Bun automatically loads
|
||||
|
||||
---
|
||||
|
||||
See [Docs > Package manager](https://bun.com/docs/cli/install) for complete documentation of Bun's package manager.
|
||||
See [Docs > Package manager](/pm/cli/install) for complete documentation of Bun's package manager.
|
||||
|
||||
@@ -47,4 +47,4 @@ bun install
|
||||
|
||||
---
|
||||
|
||||
See [Docs > Package manager > Trusted dependencies](https://bun.com/docs/install/lifecycle) for complete documentation of trusted dependencies.
|
||||
See [Docs > Package manager > Trusted dependencies](/pm/lifecycle) for complete documentation of trusted dependencies.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user