mirror of
https://github.com/oven-sh/bun
synced 2026-02-23 18:21:41 +01:00
Compare commits
2 Commits
claude/ai-
...
claude/cjs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d34604d63 | ||
|
|
9dc4216062 |
@@ -1,88 +0,0 @@
|
||||
#!/usr/bin/env bun
|
||||
import { extname } from "path";
|
||||
import { spawnSync } from "child_process";
|
||||
|
||||
const input = await Bun.stdin.json();
|
||||
|
||||
const toolName = input.tool_name;
|
||||
const toolInput = input.tool_input || {};
|
||||
const filePath = toolInput.file_path;
|
||||
|
||||
// Only process Write, Edit, and MultiEdit tools
|
||||
if (!["Write", "Edit", "MultiEdit"].includes(toolName)) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const ext = extname(filePath);
|
||||
|
||||
// Only format known files
|
||||
if (!filePath) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
function formatZigFile() {
|
||||
try {
|
||||
// Format the Zig file
|
||||
const result = spawnSync("vendor/zig/zig.exe", ["fmt", filePath], {
|
||||
cwd: process.env.CLAUDE_PROJECT_DIR || process.cwd(),
|
||||
encoding: "utf-8",
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
console.error(`Failed to format ${filePath}: ${result.error.message}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (result.status !== 0) {
|
||||
console.error(`zig fmt failed for ${filePath}:`);
|
||||
if (result.stderr) {
|
||||
console.error(result.stderr);
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
function formatTypeScriptFile() {
|
||||
try {
|
||||
// Format the TypeScript file
|
||||
const result = spawnSync(
|
||||
"./node_modules/.bin/prettier",
|
||||
["--plugin=prettier-plugin-organize-imports", "--config", ".prettierrc", "--write", filePath],
|
||||
{
|
||||
cwd: process.env.CLAUDE_PROJECT_DIR || process.cwd(),
|
||||
encoding: "utf-8",
|
||||
},
|
||||
);
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
if (ext === ".zig") {
|
||||
formatZigFile();
|
||||
} else if (
|
||||
[
|
||||
".cjs",
|
||||
".css",
|
||||
".html",
|
||||
".js",
|
||||
".json",
|
||||
".jsonc",
|
||||
".jsx",
|
||||
".less",
|
||||
".mjs",
|
||||
".pcss",
|
||||
".postcss",
|
||||
".sass",
|
||||
".scss",
|
||||
".styl",
|
||||
".stylus",
|
||||
".toml",
|
||||
".ts",
|
||||
".tsx",
|
||||
".yaml",
|
||||
].includes(ext)
|
||||
) {
|
||||
formatTypeScriptFile();
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
@@ -1,175 +0,0 @@
|
||||
#!/usr/bin/env bun
|
||||
import { basename, extname } from "path";
|
||||
|
||||
const input = await Bun.stdin.json();
|
||||
|
||||
const toolName = input.tool_name;
|
||||
const toolInput = input.tool_input || {};
|
||||
const command = toolInput.command || "";
|
||||
const timeout = toolInput.timeout;
|
||||
const cwd = input.cwd || "";
|
||||
|
||||
// Get environment variables from the hook context
|
||||
// Note: We check process.env directly as env vars are inherited
|
||||
let useSystemBun = process.env.USE_SYSTEM_BUN;
|
||||
|
||||
if (toolName !== "Bash" || !command) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
function denyWithReason(reason) {
|
||||
const output = {
|
||||
hookSpecificOutput: {
|
||||
hookEventName: "PreToolUse",
|
||||
permissionDecision: "deny",
|
||||
permissionDecisionReason: reason,
|
||||
},
|
||||
};
|
||||
console.log(JSON.stringify(output));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Parse the command to extract argv0 and positional args
|
||||
let tokens;
|
||||
try {
|
||||
// Simple shell parsing - split on spaces but respect quotes (both single and double)
|
||||
tokens = command.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)?.map(t => t.replace(/^['"]|['"]$/g, "")) || [];
|
||||
} catch {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (tokens.length === 0) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Strip inline environment variable assignments (e.g., FOO=1 bun test)
|
||||
const inlineEnv = new Map();
|
||||
let commandStart = 0;
|
||||
while (
|
||||
commandStart < tokens.length &&
|
||||
/^[A-Za-z_][A-Za-z0-9_]*=/.test(tokens[commandStart]) &&
|
||||
!tokens[commandStart].includes("/")
|
||||
) {
|
||||
const [name, value = ""] = tokens[commandStart].split("=", 2);
|
||||
inlineEnv.set(name, value);
|
||||
commandStart++;
|
||||
}
|
||||
if (commandStart >= tokens.length) {
|
||||
process.exit(0);
|
||||
}
|
||||
tokens = tokens.slice(commandStart);
|
||||
useSystemBun = inlineEnv.get("USE_SYSTEM_BUN") ?? useSystemBun;
|
||||
|
||||
// Get the executable name (argv0)
|
||||
const argv0 = basename(tokens[0], extname(tokens[0]));
|
||||
|
||||
// Check if it's zig or zig.exe
|
||||
if (argv0 === "zig") {
|
||||
// Filter out flags (starting with -) to get positional arguments
|
||||
const positionalArgs = tokens.slice(1).filter(arg => !arg.startsWith("-"));
|
||||
|
||||
// Check if the positional args contain "build" followed by "obj"
|
||||
if (positionalArgs.length >= 2 && positionalArgs[0] === "build" && positionalArgs[1] === "obj") {
|
||||
denyWithReason("error: Use `bun bd` to build Bun and wait patiently");
|
||||
}
|
||||
}
|
||||
|
||||
// Check if argv0 is timeout and the command is "bun bd"
|
||||
if (argv0 === "timeout") {
|
||||
// Find the actual command after timeout and its arguments
|
||||
const timeoutArgEndIndex = tokens.slice(1).findIndex(t => !t.startsWith("-") && !/^\d/.test(t));
|
||||
if (timeoutArgEndIndex === -1) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const actualCommandIndex = timeoutArgEndIndex + 1;
|
||||
if (actualCommandIndex >= tokens.length) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const actualCommand = basename(tokens[actualCommandIndex]);
|
||||
const restArgs = tokens.slice(actualCommandIndex + 1);
|
||||
|
||||
// Check if it's "bun bd" or "bun-debug bd" without other positional args
|
||||
if (actualCommand === "bun" || actualCommand.includes("bun-debug")) {
|
||||
const positionalArgs = restArgs.filter(arg => !arg.startsWith("-"));
|
||||
if (positionalArgs.length === 1 && positionalArgs[0] === "bd") {
|
||||
denyWithReason("error: Run `bun bd` without a timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if command is "bun .* test" or "bun-debug test" with -u/--update-snapshots AND -t/--test-name-pattern
|
||||
if (argv0 === "bun" || argv0.includes("bun-debug")) {
|
||||
const allArgs = tokens.slice(1);
|
||||
|
||||
// Check if "test" is in positional args or "bd" followed by "test"
|
||||
const positionalArgs = allArgs.filter(arg => !arg.startsWith("-"));
|
||||
const hasTest = positionalArgs.includes("test") || (positionalArgs[0] === "bd" && positionalArgs[1] === "test");
|
||||
|
||||
if (hasTest) {
|
||||
const hasUpdateSnapshots = allArgs.some(arg => arg === "-u" || arg === "--update-snapshots");
|
||||
const hasTestNamePattern = allArgs.some(arg => arg === "-t" || arg === "--test-name-pattern");
|
||||
|
||||
if (hasUpdateSnapshots && hasTestNamePattern) {
|
||||
denyWithReason("error: Cannot use -u/--update-snapshots with -t/--test-name-pattern");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if timeout option is set for "bun bd" command
|
||||
if (timeout !== undefined && (argv0 === "bun" || argv0.includes("bun-debug"))) {
|
||||
const positionalArgs = tokens.slice(1).filter(arg => !arg.startsWith("-"));
|
||||
if (positionalArgs.length === 1 && positionalArgs[0] === "bd") {
|
||||
denyWithReason("error: Run `bun bd` without a timeout");
|
||||
}
|
||||
}
|
||||
|
||||
// Check if running "bun test <file>" without USE_SYSTEM_BUN=1
|
||||
if ((argv0 === "bun" || argv0.includes("bun-debug")) && useSystemBun !== "1") {
|
||||
const allArgs = tokens.slice(1);
|
||||
const positionalArgs = allArgs.filter(arg => !arg.startsWith("-"));
|
||||
|
||||
// Check if it's "test" (not "bd test")
|
||||
if (positionalArgs.length >= 1 && positionalArgs[0] === "test" && positionalArgs[0] !== "bd") {
|
||||
denyWithReason(
|
||||
"error: In development, use `bun bd test <file>` to test your changes. If you meant to use a release version, set USE_SYSTEM_BUN=1",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if running "bun bd test" from bun repo root or test folder without a file path
|
||||
if (argv0 === "bun" || argv0.includes("bun-debug")) {
|
||||
const allArgs = tokens.slice(1);
|
||||
const positionalArgs = allArgs.filter(arg => !arg.startsWith("-"));
|
||||
|
||||
// Check if it's "bd test"
|
||||
if (positionalArgs.length >= 2 && positionalArgs[0] === "bd" && positionalArgs[1] === "test") {
|
||||
// Check if cwd is the bun repo root or test folder
|
||||
const isBunRepoRoot = cwd === "/workspace/bun" || cwd.endsWith("/bun");
|
||||
const isTestFolder = cwd.endsWith("/bun/test");
|
||||
|
||||
if (isBunRepoRoot || isTestFolder) {
|
||||
// Check if there's a file path argument (looks like a path: contains / or has test extension)
|
||||
const hasFilePath = positionalArgs
|
||||
.slice(2)
|
||||
.some(
|
||||
arg =>
|
||||
arg.includes("/") ||
|
||||
arg.endsWith(".test.ts") ||
|
||||
arg.endsWith(".test.js") ||
|
||||
arg.endsWith(".test.tsx") ||
|
||||
arg.endsWith(".test.jsx"),
|
||||
);
|
||||
|
||||
if (!hasFilePath) {
|
||||
denyWithReason(
|
||||
"error: `bun bd test` from repo root or test folder will run all tests. Use `bun bd test <path>` with a specific test file.",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allow the command to proceed
|
||||
process.exit(0);
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/pre-bash-zig-build.js"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit|MultiEdit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/post-edit-zig-format.js"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ bun bd <file> <...args>
|
||||
Debug logs look like this:
|
||||
|
||||
```zig
|
||||
const log = bun.Output.scoped(.${SCOPE}, .hidden);
|
||||
const log = bun.Output.scoped(.${SCOPE}, false);
|
||||
|
||||
// ...later
|
||||
log("MY DEBUG LOG", .{})
|
||||
|
||||
19
.github/workflows/update-sqlite3.yml
vendored
19
.github/workflows/update-sqlite3.yml
vendored
@@ -70,7 +70,24 @@ jobs:
|
||||
- name: Update SQLite if needed
|
||||
if: success() && steps.check-version.outputs.current_num < steps.check-version.outputs.latest_num
|
||||
run: |
|
||||
./scripts/update-sqlite-amalgamation.sh ${{ steps.check-version.outputs.latest_num }} ${{ steps.check-version.outputs.latest_year }}
|
||||
set -euo pipefail
|
||||
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
cd $TEMP_DIR
|
||||
|
||||
echo "Downloading from: https://sqlite.org/${{ steps.check-version.outputs.latest_year }}/sqlite-amalgamation-${{ steps.check-version.outputs.latest_num }}.zip"
|
||||
|
||||
# Download and extract latest version
|
||||
wget "https://sqlite.org/${{ steps.check-version.outputs.latest_year }}/sqlite-amalgamation-${{ steps.check-version.outputs.latest_num }}.zip"
|
||||
unzip "sqlite-amalgamation-${{ steps.check-version.outputs.latest_num }}.zip"
|
||||
cd "sqlite-amalgamation-${{ steps.check-version.outputs.latest_num }}"
|
||||
|
||||
# Add header comment and copy files
|
||||
echo "// clang-format off" > $GITHUB_WORKSPACE/src/bun.js/bindings/sqlite/sqlite3.c
|
||||
cat sqlite3.c >> $GITHUB_WORKSPACE/src/bun.js/bindings/sqlite/sqlite3.c
|
||||
|
||||
echo "// clang-format off" > $GITHUB_WORKSPACE/src/bun.js/bindings/sqlite/sqlite3_local.h
|
||||
cat sqlite3.h >> $GITHUB_WORKSPACE/src/bun.js/bindings/sqlite/sqlite3_local.h
|
||||
|
||||
- name: Create Pull Request
|
||||
if: success() && steps.check-version.outputs.current_num < steps.check-version.outputs.latest_num
|
||||
|
||||
5
.github/workflows/vscode-release.yml
vendored
5
.github/workflows/vscode-release.yml
vendored
@@ -45,8 +45,3 @@ jobs:
|
||||
env:
|
||||
VSCE_PAT: ${{ secrets.VSCODE_EXTENSION }}
|
||||
working-directory: packages/bun-vscode/extension
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: bun-vscode-${{ github.event.inputs.version }}.vsix
|
||||
path: packages/bun-vscode/extension/bun-vscode-${{ github.event.inputs.version }}.vsix
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,9 +1,7 @@
|
||||
.claude/settings.local.json
|
||||
.DS_Store
|
||||
.env
|
||||
.envrc
|
||||
.eslintcache
|
||||
.gdb_history
|
||||
.idea
|
||||
.next
|
||||
.ninja_deps
|
||||
@@ -191,4 +189,4 @@ scratch*.{js,ts,tsx,cjs,mjs}
|
||||
scripts/lldb-inline
|
||||
|
||||
# We regenerate these in all the build scripts
|
||||
cmake/sources/*.txt
|
||||
cmake/sources/*.txt
|
||||
@@ -19,12 +19,6 @@
|
||||
"options": {
|
||||
"printWidth": 80
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["src/codegen/bindgenv2/**/*.ts", "*.bindv2.ts"],
|
||||
"options": {
|
||||
"printWidth": 100
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
69
CLAUDE.md
69
CLAUDE.md
@@ -143,6 +143,19 @@ When implementing JavaScript classes in C++:
|
||||
3. Add iso subspaces for classes with C++ fields
|
||||
4. Cache structures in ZigGlobalObject
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Code Formatting
|
||||
|
||||
- `bun run prettier` - Format JS/TS files
|
||||
- `bun run zig-format` - Format Zig files
|
||||
- `bun run clang-format` - Format C++ files
|
||||
|
||||
### Watching for Changes
|
||||
|
||||
- `bun run watch` - Incremental Zig compilation with error checking
|
||||
- `bun run watch-windows` - Windows-specific watch mode
|
||||
|
||||
### Code Generation
|
||||
|
||||
Code generation happens automatically as part of the build process. The main scripts are:
|
||||
@@ -164,6 +177,47 @@ Built-in JavaScript modules use special syntax and are organized as:
|
||||
- `internal/` - Internal modules not exposed to users
|
||||
- `builtins/` - Core JavaScript builtins (streams, console, etc.)
|
||||
|
||||
### Special Syntax in Built-in Modules
|
||||
|
||||
1. **`$` prefix** - Access to private properties and JSC intrinsics:
|
||||
|
||||
```js
|
||||
const arr = $Array.from(...); // Private global
|
||||
map.$set(...); // Private method
|
||||
const arr2 = $newArrayWithSize(5); // JSC intrinsic
|
||||
```
|
||||
|
||||
2. **`require()`** - Must use string literals, resolved at compile time:
|
||||
|
||||
```js
|
||||
const fs = require("fs"); // Directly loads by numeric ID
|
||||
```
|
||||
|
||||
3. **Debug helpers**:
|
||||
- `$debug()` - Like console.log but stripped in release builds
|
||||
- `$assert()` - Assertions stripped in release builds
|
||||
- `if($debug) {}` - Check if debug env var is set
|
||||
|
||||
4. **Platform detection**: `process.platform` and `process.arch` are inlined and dead-code eliminated
|
||||
|
||||
5. **Export syntax**: Use `export default` which gets converted to a return statement:
|
||||
```js
|
||||
export default {
|
||||
readFile,
|
||||
writeFile,
|
||||
};
|
||||
```
|
||||
|
||||
Note: These are NOT ES modules. The preprocessor converts `$` to `@` (JSC's actual syntax) and handles the special functions.
|
||||
|
||||
## CI
|
||||
|
||||
Bun uses BuildKite for CI. To get the status of a PR, you can use the following command:
|
||||
|
||||
```bash
|
||||
bun ci
|
||||
```
|
||||
|
||||
## Important Development Notes
|
||||
|
||||
1. **Never use `bun test` or `bun <file>` directly** - always use `bun bd test` or `bun bd <command>`. `bun bd` compiles & runs the debug build.
|
||||
@@ -175,6 +229,19 @@ Built-in JavaScript modules use special syntax and are organized as:
|
||||
7. **Avoid shell commands** - Don't use `find` or `grep` in tests; use Bun's Glob and built-in tools
|
||||
8. **Memory management** - In Zig code, be careful with allocators and use defer for cleanup
|
||||
9. **Cross-platform** - Run `bun run zig:check-all` to compile the Zig code on all platforms when making platform-specific changes
|
||||
10. **Debug builds** - Use `BUN_DEBUG_QUIET_LOGS=1` to disable debug logging, or `BUN_DEBUG_<scopeName>=1` to enable specific `Output.scoped(.${scopeName}, .visible)`s
|
||||
10. **Debug builds** - Use `BUN_DEBUG_QUIET_LOGS=1` to disable debug logging, or `BUN_DEBUG_<scope>=1` to enable specific scopes
|
||||
11. **Be humble & honest** - NEVER overstate what you got done or what actually works in commits, PRs or in messages to the user.
|
||||
12. **Branch names must start with `claude/`** - This is a requirement for the CI to work.
|
||||
|
||||
## Key APIs and Features
|
||||
|
||||
### Bun-Specific APIs
|
||||
|
||||
- **Bun.serve()** - High-performance HTTP server
|
||||
- **Bun.spawn()** - Process spawning with better performance than Node.js
|
||||
- **Bun.file()** - Fast file I/O operations
|
||||
- **Bun.write()** - Unified API for writing to files, stdout, etc.
|
||||
- **Bun.$ (Shell)** - Cross-platform shell scripting
|
||||
- **Bun.SQLite** - Native SQLite integration
|
||||
- **Bun.FFI** - Call native libraries from JavaScript
|
||||
- **Bun.Glob** - Fast file pattern matching
|
||||
|
||||
@@ -149,7 +149,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>, false)` 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.
|
||||
- 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
|
||||
|
||||
17
build.zig
17
build.zig
@@ -49,7 +49,6 @@ const BunBuildOptions = struct {
|
||||
enable_logs: bool = false,
|
||||
enable_asan: bool,
|
||||
enable_valgrind: bool,
|
||||
use_mimalloc: bool,
|
||||
tracy_callstack_depth: u16,
|
||||
reported_nodejs_version: Version,
|
||||
/// To make iterating on some '@embedFile's faster, we load them at runtime
|
||||
@@ -69,7 +68,6 @@ const BunBuildOptions = struct {
|
||||
|
||||
cached_options_module: ?*Module = null,
|
||||
windows_shim: ?WindowsShim = null,
|
||||
llvm_codegen_threads: ?u32 = null,
|
||||
|
||||
pub fn isBaseline(this: *const BunBuildOptions) bool {
|
||||
return this.arch.isX86() and
|
||||
@@ -98,7 +96,6 @@ const BunBuildOptions = struct {
|
||||
opts.addOption(bool, "enable_logs", this.enable_logs);
|
||||
opts.addOption(bool, "enable_asan", this.enable_asan);
|
||||
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(bool, "zig_self_hosted_backend", this.no_llvm);
|
||||
opts.addOption(bool, "override_no_export_cpp_apis", this.override_no_export_cpp_apis);
|
||||
@@ -272,8 +269,6 @@ pub fn build(b: *Build) !void {
|
||||
.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_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,
|
||||
};
|
||||
|
||||
// zig build obj
|
||||
@@ -503,7 +498,6 @@ fn addMultiCheck(
|
||||
.no_llvm = root_build_options.no_llvm,
|
||||
.enable_asan = root_build_options.enable_asan,
|
||||
.enable_valgrind = root_build_options.enable_valgrind,
|
||||
.use_mimalloc = root_build_options.use_mimalloc,
|
||||
.override_no_export_cpp_apis = root_build_options.override_no_export_cpp_apis,
|
||||
};
|
||||
|
||||
@@ -609,15 +603,7 @@ fn configureObj(b: *Build, opts: *BunBuildOptions, obj: *Compile) void {
|
||||
|
||||
// Object options
|
||||
obj.use_llvm = !opts.no_llvm;
|
||||
obj.use_lld = if (opts.os == .mac or opts.os == .linux) false else !opts.no_llvm;
|
||||
|
||||
if (opts.optimize == .Debug) {
|
||||
if (@hasField(std.meta.Child(@TypeOf(obj)), "llvm_codegen_threads"))
|
||||
obj.llvm_codegen_threads = opts.llvm_codegen_threads orelse 0;
|
||||
}
|
||||
|
||||
obj.no_link_obj = true;
|
||||
|
||||
obj.use_lld = if (opts.os == .mac) false else !opts.no_llvm;
|
||||
if (opts.enable_asan and !enableFastBuild(b)) {
|
||||
if (@hasField(Build.Module, "sanitize_address")) {
|
||||
obj.root_module.sanitize_address = true;
|
||||
@@ -724,7 +710,6 @@ fn addInternalImports(b: *Build, mod: *Module, opts: *BunBuildOptions) void {
|
||||
// Generated code exposed as individual modules.
|
||||
inline for (.{
|
||||
.{ .file = "ZigGeneratedClasses.zig", .import = "ZigGeneratedClasses" },
|
||||
.{ .file = "bindgen_generated.zig", .import = "bindgen_generated" },
|
||||
.{ .file = "ResolvedSourceTag.zig", .import = "ResolvedSourceTag" },
|
||||
.{ .file = "ErrorCode.zig", .import = "ErrorCode" },
|
||||
.{ .file = "runtime.out.js", .enable = opts.shouldEmbedCode() },
|
||||
|
||||
12
bun.lock
12
bun.lock
@@ -8,14 +8,14 @@
|
||||
"@lezer/cpp": "^1.1.3",
|
||||
"@types/bun": "workspace:*",
|
||||
"bun-tracestrings": "github:oven-sh/bun.report#912ca63e26c51429d3e6799aa2a6ab079b188fd8",
|
||||
"esbuild": "^0.21.5",
|
||||
"mitata": "^0.1.14",
|
||||
"esbuild": "^0.21.4",
|
||||
"mitata": "^0.1.11",
|
||||
"peechy": "0.4.34",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-organize-imports": "^4.3.0",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-organize-imports": "^4.0.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"source-map-js": "^1.2.1",
|
||||
"source-map-js": "^1.2.0",
|
||||
"typescript": "5.9.2",
|
||||
},
|
||||
},
|
||||
@@ -284,7 +284,7 @@
|
||||
|
||||
"prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
|
||||
|
||||
"prettier-plugin-organize-imports": ["prettier-plugin-organize-imports@4.3.0", "", { "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", "vue-tsc": "^2.1.0 || 3" }, "optionalPeers": ["vue-tsc"] }, "sha512-FxFz0qFhyBsGdIsb697f/EkvHzi5SZOhWAjxcx2dLt+Q532bAlhswcXGYB1yzjZ69kW8UoadFBw7TyNwlq96Iw=="],
|
||||
"prettier-plugin-organize-imports": ["prettier-plugin-organize-imports@4.2.0", "", { "peerDependencies": { "prettier": ">=2.0", "typescript": ">=2.9", "vue-tsc": "^2.1.0 || 3" }, "optionalPeers": ["vue-tsc"] }, "sha512-Zdy27UhlmyvATZi67BTnLcKTo8fm6Oik59Sz6H64PgZJVs6NJpPD1mT240mmJn62c98/QaL+r3kx9Q3gRpDajg=="],
|
||||
|
||||
"react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="],
|
||||
|
||||
|
||||
@@ -10,4 +10,3 @@ preload = "./test/preload.ts"
|
||||
|
||||
[install]
|
||||
linker = "isolated"
|
||||
minimumReleaseAge = 1
|
||||
|
||||
@@ -202,9 +202,4 @@ optionx(USE_WEBKIT_ICU BOOL "Use the ICU libraries from WebKit" DEFAULT ${DEFAUL
|
||||
|
||||
optionx(ERROR_LIMIT STRING "Maximum number of errors to show when compiling C++ code" DEFAULT "100")
|
||||
|
||||
# This is not an `option` because setting this variable to OFF is experimental
|
||||
# and unsupported. This replaces the `use_mimalloc` variable previously in
|
||||
# bun.zig, and enables C++ code to also be aware of the option.
|
||||
set(USE_MIMALLOC_AS_DEFAULT_ALLOCATOR ON)
|
||||
|
||||
list(APPEND CMAKE_ARGS -DCMAKE_EXPORT_COMPILE_COMMANDS=ON)
|
||||
|
||||
@@ -31,14 +31,6 @@
|
||||
"output": "BindgenSources.txt",
|
||||
"paths": ["src/**/*.bind.ts"]
|
||||
},
|
||||
{
|
||||
"output": "BindgenV2Sources.txt",
|
||||
"paths": ["src/**/*.bindv2.ts"]
|
||||
},
|
||||
{
|
||||
"output": "BindgenV2InternalSources.txt",
|
||||
"paths": ["src/codegen/bindgenv2/**/*.ts"]
|
||||
},
|
||||
{
|
||||
"output": "ZigSources.txt",
|
||||
"paths": ["src/**/*.zig"]
|
||||
|
||||
@@ -44,14 +44,6 @@ else()
|
||||
set(CONFIGURE_DEPENDS "")
|
||||
endif()
|
||||
|
||||
set(LLVM_ZIG_CODEGEN_THREADS 0)
|
||||
# This makes the build slower, so we turn it off for now.
|
||||
# if (DEBUG)
|
||||
# include(ProcessorCount)
|
||||
# ProcessorCount(CPU_COUNT)
|
||||
# set(LLVM_ZIG_CODEGEN_THREADS ${CPU_COUNT})
|
||||
# endif()
|
||||
|
||||
# --- Dependencies ---
|
||||
|
||||
set(BUN_DEPENDENCIES
|
||||
@@ -395,54 +387,6 @@ register_command(
|
||||
${BUN_BAKE_RUNTIME_OUTPUTS}
|
||||
)
|
||||
|
||||
set(BUN_BINDGENV2_SCRIPT ${CWD}/src/codegen/bindgenv2/script.ts)
|
||||
|
||||
absolute_sources(BUN_BINDGENV2_SOURCES ${CWD}/cmake/sources/BindgenV2Sources.txt)
|
||||
# These sources include the script itself.
|
||||
absolute_sources(BUN_BINDGENV2_INTERNAL_SOURCES
|
||||
${CWD}/cmake/sources/BindgenV2InternalSources.txt)
|
||||
string(REPLACE ";" "," BUN_BINDGENV2_SOURCES_COMMA_SEPARATED
|
||||
"${BUN_BINDGENV2_SOURCES}")
|
||||
|
||||
execute_process(
|
||||
COMMAND ${BUN_EXECUTABLE} run ${BUN_BINDGENV2_SCRIPT}
|
||||
--command=list-outputs
|
||||
--sources=${BUN_BINDGENV2_SOURCES_COMMA_SEPARATED}
|
||||
--codegen-path=${CODEGEN_PATH}
|
||||
RESULT_VARIABLE bindgen_result
|
||||
OUTPUT_VARIABLE bindgen_outputs
|
||||
)
|
||||
if(${bindgen_result})
|
||||
message(FATAL_ERROR "bindgenv2/script.ts exited with non-zero status")
|
||||
endif()
|
||||
foreach(output IN LISTS bindgen_outputs)
|
||||
if(output MATCHES "\.cpp$")
|
||||
list(APPEND BUN_BINDGENV2_CPP_OUTPUTS ${output})
|
||||
elseif(output MATCHES "\.zig$")
|
||||
list(APPEND BUN_BINDGENV2_ZIG_OUTPUTS ${output})
|
||||
else()
|
||||
message(FATAL_ERROR "unexpected bindgen output: [${output}]")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
register_command(
|
||||
TARGET
|
||||
bun-bindgen-v2
|
||||
COMMENT
|
||||
"Generating bindings (v2)"
|
||||
COMMAND
|
||||
${BUN_EXECUTABLE} run ${BUN_BINDGENV2_SCRIPT}
|
||||
--command=generate
|
||||
--codegen-path=${CODEGEN_PATH}
|
||||
--sources=${BUN_BINDGENV2_SOURCES_COMMA_SEPARATED}
|
||||
SOURCES
|
||||
${BUN_BINDGENV2_SOURCES}
|
||||
${BUN_BINDGENV2_INTERNAL_SOURCES}
|
||||
OUTPUTS
|
||||
${BUN_BINDGENV2_CPP_OUTPUTS}
|
||||
${BUN_BINDGENV2_ZIG_OUTPUTS}
|
||||
)
|
||||
|
||||
set(BUN_BINDGEN_SCRIPT ${CWD}/src/codegen/bindgen.ts)
|
||||
|
||||
absolute_sources(BUN_BINDGEN_SOURCES ${CWD}/cmake/sources/BindgenSources.txt)
|
||||
@@ -621,7 +565,6 @@ set(BUN_ZIG_GENERATED_SOURCES
|
||||
${BUN_ZIG_GENERATED_CLASSES_OUTPUTS}
|
||||
${BUN_JAVASCRIPT_OUTPUTS}
|
||||
${BUN_CPP_OUTPUTS}
|
||||
${BUN_BINDGENV2_ZIG_OUTPUTS}
|
||||
)
|
||||
|
||||
# In debug builds, these are not embedded, but rather referenced at runtime.
|
||||
@@ -635,13 +578,7 @@ if (TEST)
|
||||
set(BUN_ZIG_OUTPUT ${BUILD_PATH}/bun-test.o)
|
||||
set(ZIG_STEPS test)
|
||||
else()
|
||||
if (LLVM_ZIG_CODEGEN_THREADS GREATER 1)
|
||||
foreach(i RANGE ${LLVM_ZIG_CODEGEN_THREADS})
|
||||
list(APPEND BUN_ZIG_OUTPUT ${BUILD_PATH}/bun-zig.${i}.o)
|
||||
endforeach()
|
||||
else()
|
||||
set(BUN_ZIG_OUTPUT ${BUILD_PATH}/bun-zig.o)
|
||||
endif()
|
||||
set(BUN_ZIG_OUTPUT ${BUILD_PATH}/bun-zig.o)
|
||||
set(ZIG_STEPS obj)
|
||||
endif()
|
||||
|
||||
@@ -685,8 +622,6 @@ register_command(
|
||||
-Denable_logs=$<IF:$<BOOL:${ENABLE_LOGS}>,true,false>
|
||||
-Denable_asan=$<IF:$<BOOL:${ENABLE_ZIG_ASAN}>,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}
|
||||
-Dversion=${VERSION}
|
||||
-Dreported_nodejs_version=${NODEJS_VERSION}
|
||||
-Dcanary=${CANARY_REVISION}
|
||||
@@ -762,7 +697,6 @@ list(APPEND BUN_CPP_SOURCES
|
||||
${BUN_JAVASCRIPT_OUTPUTS}
|
||||
${BUN_OBJECT_LUT_OUTPUTS}
|
||||
${BUN_BINDGEN_CPP_OUTPUTS}
|
||||
${BUN_BINDGENV2_CPP_OUTPUTS}
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
@@ -900,10 +834,6 @@ if(WIN32)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(USE_MIMALLOC_AS_DEFAULT_ALLOCATOR)
|
||||
target_compile_definitions(${bun} PRIVATE USE_MIMALLOC=1)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(${bun} PRIVATE
|
||||
_HAS_EXCEPTIONS=0
|
||||
LIBUS_USE_OPENSSL=1
|
||||
|
||||
@@ -2,7 +2,7 @@ option(WEBKIT_VERSION "The version of WebKit to use")
|
||||
option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of downloading")
|
||||
|
||||
if(NOT WEBKIT_VERSION)
|
||||
set(WEBKIT_VERSION 6d0f3aac0b817cc01a846b3754b21271adedac12)
|
||||
set(WEBKIT_VERSION 69fa2714ab5f917c2d15501ff8cfdccfaea78882)
|
||||
endif()
|
||||
|
||||
string(SUBSTRING ${WEBKIT_VERSION} 0 16 WEBKIT_VERSION_PREFIX)
|
||||
|
||||
@@ -20,7 +20,7 @@ else()
|
||||
unsupported(CMAKE_SYSTEM_NAME)
|
||||
endif()
|
||||
|
||||
set(ZIG_COMMIT "55fdbfa0c86be86b68d43a4ba761e6909eb0d7b2")
|
||||
set(ZIG_COMMIT "e0b7c318f318196c5f81fdf3423816a7b5bb3112")
|
||||
optionx(ZIG_TARGET STRING "The zig target to use" DEFAULT ${DEFAULT_ZIG_TARGET})
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
|
||||
@@ -233,7 +233,6 @@ In addition to the standard fetch options, Bun provides several extensions:
|
||||
```ts
|
||||
const response = await fetch("http://example.com", {
|
||||
// Control automatic response decompression (default: true)
|
||||
// Supports gzip, deflate, brotli (br), and zstd
|
||||
decompress: true,
|
||||
|
||||
// Disable connection reuse for this request
|
||||
@@ -340,7 +339,7 @@ This will print the request and response headers to your terminal:
|
||||
[fetch] > User-Agent: Bun/$BUN_LATEST_VERSION
|
||||
[fetch] > Accept: */*
|
||||
[fetch] > Host: example.com
|
||||
[fetch] > Accept-Encoding: gzip, deflate, br, zstd
|
||||
[fetch] > Accept-Encoding: gzip, deflate, br
|
||||
|
||||
[fetch] < 200 OK
|
||||
[fetch] < Content-Encoding: gzip
|
||||
|
||||
@@ -155,24 +155,3 @@ const glob = new Glob("\\!index.ts");
|
||||
glob.match("!index.ts"); // => true
|
||||
glob.match("index.ts"); // => false
|
||||
```
|
||||
|
||||
## Node.js `fs.glob()` compatibility
|
||||
|
||||
Bun also implements Node.js's `fs.glob()` functions with additional features:
|
||||
|
||||
```ts
|
||||
import { glob, globSync, promises } from "node:fs";
|
||||
|
||||
// Array of patterns
|
||||
const files = await promises.glob(["**/*.ts", "**/*.js"]);
|
||||
|
||||
// Exclude patterns
|
||||
const filtered = await promises.glob("**/*", {
|
||||
exclude: ["node_modules/**", "*.test.*"],
|
||||
});
|
||||
```
|
||||
|
||||
All three functions (`fs.glob()`, `fs.globSync()`, `fs.promises.glob()`) support:
|
||||
|
||||
- Array of patterns as the first argument
|
||||
- `exclude` option to filter results
|
||||
|
||||
@@ -88,9 +88,6 @@ await redis.set("user:1:name", "Alice");
|
||||
// Get a key
|
||||
const name = await redis.get("user:1:name");
|
||||
|
||||
// Get a key as Uint8Array
|
||||
const buffer = await redis.getBuffer("user:1:name");
|
||||
|
||||
// Delete a key
|
||||
await redis.del("user:1:name");
|
||||
|
||||
@@ -135,10 +132,6 @@ await redis.hmset("user:123", [
|
||||
const userFields = await redis.hmget("user:123", ["name", "email"]);
|
||||
console.log(userFields); // ["Alice", "alice@example.com"]
|
||||
|
||||
// Get single field from hash (returns value directly, null if missing)
|
||||
const userName = await redis.hget("user:123", "name");
|
||||
console.log(userName); // "Alice"
|
||||
|
||||
// Increment a numeric field in a hash
|
||||
await redis.hincrby("user:123", "visits", 1);
|
||||
|
||||
|
||||
@@ -377,22 +377,6 @@ const users = [
|
||||
await sql`SELECT * FROM users WHERE id IN ${sql(users, "id")}`;
|
||||
```
|
||||
|
||||
### `sql.array` helper
|
||||
|
||||
The `sql.array` helper creates PostgreSQL array literals from JavaScript arrays:
|
||||
|
||||
```ts
|
||||
// Create array literals for PostgreSQL
|
||||
await sql`INSERT INTO tags (items) VALUES (${sql.array(["red", "blue", "green"])})`;
|
||||
// Generates: INSERT INTO tags (items) VALUES (ARRAY['red', 'blue', 'green'])
|
||||
|
||||
// Works with numeric arrays too
|
||||
await sql`SELECT * FROM products WHERE ids = ANY(${sql.array([1, 2, 3])})`;
|
||||
// Generates: SELECT * FROM products WHERE ids = ANY(ARRAY[1, 2, 3])
|
||||
```
|
||||
|
||||
**Note**: `sql.array` is PostgreSQL-only. Multi-dimensional arrays and NULL elements may not be supported yet.
|
||||
|
||||
## `sql``.simple()`
|
||||
|
||||
The PostgreSQL wire protocol supports two types of queries: "simple" and "extended". Simple queries can contain multiple statements but don't support parameters, while extended queries (the default) support parameters but only allow one statement.
|
||||
|
||||
@@ -663,8 +663,6 @@ class Statement<Params, ReturnType> {
|
||||
toString(): string; // serialize to SQL
|
||||
|
||||
columnNames: string[]; // the column names of the result set
|
||||
columnTypes: string[]; // types based on actual values in first row (call .get()/.all() first)
|
||||
declaredTypes: (string | null)[]; // types from CREATE TABLE schema (call .get()/.all() first)
|
||||
paramsCount: number; // the number of parameters expected by the statement
|
||||
native: any; // the native object representing the statement
|
||||
|
||||
|
||||
@@ -28,20 +28,6 @@ for await (const chunk of stream) {
|
||||
}
|
||||
```
|
||||
|
||||
`ReadableStream` also provides convenience methods for consuming the entire stream:
|
||||
|
||||
```ts
|
||||
const stream = new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue("hello world");
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
|
||||
const data = await stream.text(); // => "hello world"
|
||||
// Also available: .json(), .bytes(), .blob()
|
||||
```
|
||||
|
||||
## Direct `ReadableStream`
|
||||
|
||||
Bun implements an optimized version of `ReadableStream` that avoid unnecessary data copying & queue management logic. With a traditional `ReadableStream`, chunks of data are _enqueued_. Each chunk is copied into a queue, where it sits until the stream is ready to send more data.
|
||||
|
||||
@@ -602,40 +602,6 @@ dec.decode(decompressed);
|
||||
// => "hellohellohello..."
|
||||
```
|
||||
|
||||
## `Bun.zstdCompress()` / `Bun.zstdCompressSync()`
|
||||
|
||||
Compresses a `Uint8Array` using the Zstandard algorithm.
|
||||
|
||||
```ts
|
||||
const buf = Buffer.from("hello".repeat(100));
|
||||
|
||||
// Synchronous
|
||||
const compressedSync = Bun.zstdCompressSync(buf);
|
||||
// Asynchronous
|
||||
const compressedAsync = await Bun.zstdCompress(buf);
|
||||
|
||||
// With compression level (1-22, default: 3)
|
||||
const compressedLevel = Bun.zstdCompressSync(buf, { level: 6 });
|
||||
```
|
||||
|
||||
## `Bun.zstdDecompress()` / `Bun.zstdDecompressSync()`
|
||||
|
||||
Decompresses a `Uint8Array` using the Zstandard algorithm.
|
||||
|
||||
```ts
|
||||
const buf = Buffer.from("hello".repeat(100));
|
||||
const compressed = Bun.zstdCompressSync(buf);
|
||||
|
||||
// Synchronous
|
||||
const decompressedSync = Bun.zstdDecompressSync(compressed);
|
||||
// Asynchronous
|
||||
const decompressedAsync = await Bun.zstdDecompress(compressed);
|
||||
|
||||
const dec = new TextDecoder();
|
||||
dec.decode(decompressedSync);
|
||||
// => "hellohellohello..."
|
||||
```
|
||||
|
||||
## `Bun.inspect()`
|
||||
|
||||
Serializes an object to a `string` exactly as it would be printed by `console.log`.
|
||||
|
||||
@@ -114,7 +114,8 @@ type WebSocketData = {
|
||||
authToken: string;
|
||||
};
|
||||
|
||||
Bun.serve({
|
||||
// TypeScript: specify the type of `data`
|
||||
Bun.serve<WebSocketData>({
|
||||
fetch(req, server) {
|
||||
const cookies = new Bun.CookieMap(req.headers.get("cookie")!);
|
||||
|
||||
@@ -130,12 +131,8 @@ Bun.serve({
|
||||
return undefined;
|
||||
},
|
||||
websocket: {
|
||||
// TypeScript: specify the type of ws.data like this
|
||||
data: {} as WebSocketData,
|
||||
|
||||
// handler called when a message is received
|
||||
async message(ws, message) {
|
||||
// ws.data is now properly typed as WebSocketData
|
||||
const user = getUserFromToken(ws.data.authToken);
|
||||
|
||||
await saveMessageToDatabase({
|
||||
@@ -167,7 +164,7 @@ socket.addEventListener("message", event => {
|
||||
Bun's `ServerWebSocket` implementation implements a native publish-subscribe API for topic-based broadcasting. Individual sockets can `.subscribe()` to a topic (specified with a string identifier) and `.publish()` messages to all other subscribers to that topic (excluding itself). This topic-based broadcast API is similar to [MQTT](https://en.wikipedia.org/wiki/MQTT) and [Redis Pub/Sub](https://redis.io/topics/pubsub).
|
||||
|
||||
```ts
|
||||
const server = Bun.serve({
|
||||
const server = Bun.serve<{ username: string }>({
|
||||
fetch(req, server) {
|
||||
const url = new URL(req.url);
|
||||
if (url.pathname === "/chat") {
|
||||
@@ -182,9 +179,6 @@ const server = Bun.serve({
|
||||
return new Response("Hello world");
|
||||
},
|
||||
websocket: {
|
||||
// TypeScript: specify the type of ws.data like this
|
||||
data: {} as { username: string },
|
||||
|
||||
open(ws) {
|
||||
const msg = `${ws.data.username} has entered the chat`;
|
||||
ws.subscribe("the-group-chat");
|
||||
@@ -285,9 +279,6 @@ Bun implements the `WebSocket` class. To create a WebSocket client that connects
|
||||
|
||||
```ts
|
||||
const socket = new WebSocket("ws://localhost:3000");
|
||||
|
||||
// With subprotocol negotiation
|
||||
const socket2 = new WebSocket("ws://localhost:3000", ["soap", "wamp"]);
|
||||
```
|
||||
|
||||
In browsers, the cookies that are currently set on the page will be sent with the WebSocket upgrade request. This is a standard feature of the `WebSocket` API.
|
||||
@@ -302,17 +293,6 @@ const socket = new WebSocket("ws://localhost:3000", {
|
||||
});
|
||||
```
|
||||
|
||||
### Client compression
|
||||
|
||||
WebSocket clients support permessage-deflate compression. The `extensions` property shows negotiated compression:
|
||||
|
||||
```ts
|
||||
const socket = new WebSocket("wss://echo.websocket.org");
|
||||
socket.addEventListener("open", () => {
|
||||
console.log(socket.extensions); // => "permessage-deflate"
|
||||
});
|
||||
```
|
||||
|
||||
To add event listeners to the socket:
|
||||
|
||||
```ts
|
||||
|
||||
@@ -282,31 +282,6 @@ const worker = new Worker("./i-am-smol.ts", {
|
||||
Setting `smol: true` sets `JSC::HeapSize` to be `Small` instead of the default `Large`.
|
||||
{% /details %}
|
||||
|
||||
## Environment Data
|
||||
|
||||
Share data between the main thread and workers using `setEnvironmentData()` and `getEnvironmentData()`.
|
||||
|
||||
```js
|
||||
import { setEnvironmentData, getEnvironmentData } from "worker_threads";
|
||||
|
||||
// In main thread
|
||||
setEnvironmentData("config", { apiUrl: "https://api.example.com" });
|
||||
|
||||
// In worker
|
||||
const config = getEnvironmentData("config");
|
||||
console.log(config); // => { apiUrl: "https://api.example.com" }
|
||||
```
|
||||
|
||||
## Worker Events
|
||||
|
||||
Listen for worker creation events using `process.emit()`:
|
||||
|
||||
```js
|
||||
process.on("worker", worker => {
|
||||
console.log("New worker created:", worker.threadId);
|
||||
});
|
||||
```
|
||||
|
||||
## `Bun.isMainThread`
|
||||
|
||||
You can check if you're in the main thread by checking `Bun.isMainThread`.
|
||||
|
||||
@@ -140,19 +140,6 @@ The `--sourcemap` argument embeds a sourcemap compressed with zstd, so that erro
|
||||
|
||||
The `--bytecode` argument enables bytecode compilation. Every time you run JavaScript code in Bun, JavaScriptCore (the engine) will compile your source code into bytecode. We can move this parsing work from runtime to bundle time, saving you startup time.
|
||||
|
||||
## Embedding runtime arguments
|
||||
|
||||
**`--compile-exec-argv="args"`** - Embed runtime arguments that are available via `process.execArgv`:
|
||||
|
||||
```bash
|
||||
bun build --compile --compile-exec-argv="--smol --user-agent=MyBot" ./app.ts --outfile myapp
|
||||
```
|
||||
|
||||
```js
|
||||
// In the compiled app
|
||||
console.log(process.execArgv); // ["--smol", "--user-agent=MyBot"]
|
||||
```
|
||||
|
||||
## Act as the Bun CLI
|
||||
|
||||
{% note %}
|
||||
|
||||
@@ -313,14 +313,6 @@ $ bun build --entrypoints ./index.ts --outdir ./out --target browser
|
||||
|
||||
Depending on the target, Bun will apply different module resolution rules and optimizations.
|
||||
|
||||
### Module resolution
|
||||
|
||||
Bun supports the `NODE_PATH` environment variable for additional module resolution paths:
|
||||
|
||||
```bash
|
||||
NODE_PATH=./src bun build ./entry.js --outdir ./dist
|
||||
```
|
||||
|
||||
<!-- - Module resolution. For example, when bundling for the browser, Bun will prioritize the `"browser"` export condition when resolving imports. An error will be thrown if any Node.js or Bun built-ins are imported or used, e.g. `node:fs` or `Bun.serve`. -->
|
||||
|
||||
{% table %}
|
||||
@@ -400,55 +392,6 @@ $ bun build ./index.tsx --outdir ./out --format cjs
|
||||
|
||||
TODO: document IIFE once we support globalNames.
|
||||
|
||||
### `jsx`
|
||||
|
||||
Configure JSX transform behavior. Allows fine-grained control over how JSX is compiled.
|
||||
|
||||
**Classic runtime example** (uses `factory` and `fragment`):
|
||||
|
||||
{% codetabs %}
|
||||
|
||||
```ts#JavaScript
|
||||
await Bun.build({
|
||||
entrypoints: ['./app.tsx'],
|
||||
outdir: './out',
|
||||
jsx: {
|
||||
factory: 'h',
|
||||
fragment: 'Fragment',
|
||||
runtime: 'classic',
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
```bash#CLI
|
||||
# JSX configuration is handled via bunfig.toml or tsconfig.json
|
||||
$ bun build ./app.tsx --outdir ./out
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
|
||||
**Automatic runtime example** (uses `importSource`):
|
||||
|
||||
{% codetabs %}
|
||||
|
||||
```ts#JavaScript
|
||||
await Bun.build({
|
||||
entrypoints: ['./app.tsx'],
|
||||
outdir: './out',
|
||||
jsx: {
|
||||
importSource: 'preact',
|
||||
runtime: 'automatic',
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
```bash#CLI
|
||||
# JSX configuration is handled via bunfig.toml or tsconfig.json
|
||||
$ bun build ./app.tsx --outdir ./out
|
||||
```
|
||||
|
||||
{% /codetabs %}
|
||||
|
||||
### `splitting`
|
||||
|
||||
Whether to enable code splitting.
|
||||
@@ -1576,15 +1519,6 @@ interface BuildConfig {
|
||||
* @default "esm"
|
||||
*/
|
||||
format?: "esm" | "cjs" | "iife";
|
||||
/**
|
||||
* JSX configuration object for controlling JSX transform behavior
|
||||
*/
|
||||
jsx?: {
|
||||
factory?: string;
|
||||
fragment?: string;
|
||||
importSource?: string;
|
||||
runtime?: "automatic" | "classic";
|
||||
};
|
||||
naming?:
|
||||
| string
|
||||
| {
|
||||
|
||||
@@ -176,21 +176,7 @@ When a `bun.lock` exists and `package.json` hasn’t changed, Bun downloads miss
|
||||
|
||||
## Platform-specific dependencies?
|
||||
|
||||
bun stores normalized `cpu` and `os` values from npm in the lockfile, along with the resolved packages. It skips downloading, extracting, and installing packages disabled for the current target at runtime. This means the lockfile won't change between platforms/architectures even if the packages ultimately installed do change.
|
||||
|
||||
### `--cpu` and `--os` flags
|
||||
|
||||
You can override the target platform for package selection:
|
||||
|
||||
```bash
|
||||
bun install --cpu=x64 --os=linux
|
||||
```
|
||||
|
||||
This installs packages for the specified platform instead of the current system. Useful for cross-platform builds or when preparing deployments for different environments.
|
||||
|
||||
**Accepted values for `--cpu`**: `arm64`, `x64`, `ia32`, `ppc64`, `s390x`
|
||||
|
||||
**Accepted values for `--os`**: `linux`, `darwin`, `win32`, `freebsd`, `openbsd`, `sunos`, `aix`
|
||||
bun stores normalized `cpu` and `os` values from npm in the lockfile, along with the resolved packages. It skips downloading, extracting, and installing packages disabled for the current target at runtime. This means the lockfile won’t change between platforms/architectures even if the packages ultimately installed do change.
|
||||
|
||||
## Peer dependencies?
|
||||
|
||||
@@ -259,91 +245,3 @@ bun uses a binary format for caching NPM registry responses. This loads much fas
|
||||
You will see these files in `~/.bun/install/cache/*.npm`. The filename pattern is `${hash(packageName)}.npm`. It’s a hash so that extra directories don’t need to be created for scoped packages.
|
||||
|
||||
Bun's usage of `Cache-Control` ignores `Age`. This improves performance, but means bun may be about 5 minutes out of date to receive the latest package version metadata from npm.
|
||||
|
||||
## pnpm migration
|
||||
|
||||
Bun automatically migrates projects from pnpm to bun. When a `pnpm-lock.yaml` file is detected and no `bun.lock` file exists, Bun will automatically migrate the lockfile to `bun.lock` during installation. The original `pnpm-lock.yaml` file remains unmodified.
|
||||
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
**Note**: Migration only runs when `bun.lock` is absent. There is currently no opt-out flag for pnpm migration.
|
||||
|
||||
The migration process handles:
|
||||
|
||||
### Lockfile Migration
|
||||
|
||||
- Converts `pnpm-lock.yaml` to `bun.lock` format
|
||||
- Preserves package versions and resolution information
|
||||
- Maintains dependency relationships and peer dependencies
|
||||
- Handles patched dependencies with integrity hashes
|
||||
|
||||
### Workspace Configuration
|
||||
|
||||
When a `pnpm-workspace.yaml` file exists, Bun migrates workspace settings to your root `package.json`:
|
||||
|
||||
```yaml
|
||||
# pnpm-workspace.yaml
|
||||
packages:
|
||||
- "apps/*"
|
||||
- "packages/*"
|
||||
|
||||
catalog:
|
||||
react: ^18.0.0
|
||||
typescript: ^5.0.0
|
||||
|
||||
catalogs:
|
||||
build:
|
||||
webpack: ^5.0.0
|
||||
babel: ^7.0.0
|
||||
```
|
||||
|
||||
The workspace packages list and catalogs are moved to the `workspaces` field in `package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"workspaces": {
|
||||
"packages": ["apps/*", "packages/*"],
|
||||
"catalog": {
|
||||
"react": "^18.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"catalogs": {
|
||||
"build": {
|
||||
"webpack": "^5.0.0",
|
||||
"babel": "^7.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Catalog Dependencies
|
||||
|
||||
Dependencies using pnpm's `catalog:` protocol are preserved:
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"react": "catalog:",
|
||||
"webpack": "catalog:build"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration Migration
|
||||
|
||||
The following pnpm configuration is migrated from both `pnpm-lock.yaml` and `pnpm-workspace.yaml`:
|
||||
|
||||
- **Overrides**: Moved from `pnpm.overrides` to root-level `overrides` in `package.json`
|
||||
- **Patched Dependencies**: Moved from `pnpm.patchedDependencies` to root-level `patchedDependencies` in `package.json`
|
||||
- **Workspace Overrides**: Applied from `pnpm-workspace.yaml` to root `package.json`
|
||||
|
||||
### Requirements
|
||||
|
||||
- Requires pnpm lockfile version 7 or higher
|
||||
- Workspace packages must have a `name` field in their `package.json`
|
||||
- All catalog entries referenced by dependencies must exist in the catalogs definition
|
||||
|
||||
After migration, you can safely remove `pnpm-lock.yaml` and `pnpm-workspace.yaml` files.
|
||||
|
||||
@@ -63,15 +63,6 @@ $ bunx --bun my-cli # good
|
||||
$ bunx my-cli --bun # bad
|
||||
```
|
||||
|
||||
## Package flag
|
||||
|
||||
**`--package <pkg>` or `-p <pkg>`** - Run binary from specific package. Useful when binary name differs from package name:
|
||||
|
||||
```bash
|
||||
bunx -p renovate renovate-config-validator
|
||||
bunx --package @angular/cli ng
|
||||
```
|
||||
|
||||
To force bun to always be used with a script, use a shebang.
|
||||
|
||||
```
|
||||
|
||||
@@ -33,11 +33,6 @@ It creates:
|
||||
- an entry point which defaults to `index.ts` unless any of `index.{tsx, jsx, js, mts, mjs}` exist or the `package.json` specifies a `module` or `main` field
|
||||
- a `README.md` file
|
||||
|
||||
AI Agent rules (disable with `$BUN_AGENT_RULE_DISABLED=1`):
|
||||
|
||||
- a `CLAUDE.md` file when Claude CLI is detected (disable with `CLAUDE_CODE_AGENT_RULE_DISABLED` env var)
|
||||
- a `.cursor/rules/*.mdc` file to guide [Cursor AI](https://cursor.sh) to use Bun instead of Node.js and npm when Cursor is detected
|
||||
|
||||
If you pass `-y` or `--yes`, it will assume you want to continue without asking questions.
|
||||
|
||||
At the end, it runs `bun install` to install `@types/bun`.
|
||||
|
||||
@@ -221,38 +221,6 @@ Bun uses a global cache at `~/.bun/install/cache/` to minimize disk usage. Packa
|
||||
|
||||
For complete documentation refer to [Package manager > Global cache](https://bun.com/docs/install/cache).
|
||||
|
||||
## Minimum release age
|
||||
|
||||
To protect against supply chain attacks where malicious packages are quickly published, you can configure a minimum age requirement for npm packages. Package versions published more recently than the specified threshold (in seconds) will be filtered out during installation.
|
||||
|
||||
```bash
|
||||
# Only install package versions published at least 3 days ago
|
||||
$ bun add @types/bun --minimum-release-age 259200 # seconds
|
||||
```
|
||||
|
||||
You can also configure this in `bunfig.toml`:
|
||||
|
||||
```toml
|
||||
[install]
|
||||
# Only install package versions published at least 3 days ago
|
||||
minimumReleaseAge = 259200 # seconds
|
||||
|
||||
# Exclude trusted packages from the age gate
|
||||
minimumReleaseAgeExcludes = ["@types/node", "typescript"]
|
||||
```
|
||||
|
||||
When the minimum age filter is active:
|
||||
|
||||
- Only affects new package resolution - existing packages in `bun.lock` remain unchanged
|
||||
- All dependencies (direct and transitive) are filtered to meet the age requirement when being resolved
|
||||
- When versions are blocked by the age gate, a stability check detects rapid bugfix patterns
|
||||
- If multiple versions were published close together just outside your age gate, it extends the filter to skip those potentially unstable versions and selects an older, more mature version
|
||||
- Searches up to 7 days after the age gate, however if still finding rapid releases it ignores stability check
|
||||
- Exact version requests (like `package@1.1.1`) still respect the age gate but bypass the stability check
|
||||
- Versions without a `time` field are treated as passing the age check (npm registry should always provide timestamps)
|
||||
|
||||
For more advanced security scanning, including integration with services & custom filtering, see [Package manager > Security Scanner API](https://bun.com/docs/install/security-scanner-api).
|
||||
|
||||
## Configuration
|
||||
|
||||
The default behavior of `bun install` can be configured in `bunfig.toml`. The default values are shown below.
|
||||
@@ -287,10 +255,6 @@ concurrentScripts = 16 # (cpu count or GOMAXPROCS) x2
|
||||
# installation strategy: "hoisted" or "isolated"
|
||||
# default: "hoisted"
|
||||
linker = "hoisted"
|
||||
|
||||
# minimum age config
|
||||
minimumReleaseAge = 259200 # seconds
|
||||
minimumReleaseAgeExcludes = ["@types/node", "typescript"]
|
||||
```
|
||||
|
||||
## CI/CD
|
||||
|
||||
@@ -44,47 +44,4 @@ You can also pass glob patterns to filter by workspace names:
|
||||
|
||||
{% bunOutdatedTerminal glob="{e,t}*" displayGlob="--filter='@monorepo/{types,cli}'" /%}
|
||||
|
||||
### Catalog Dependencies
|
||||
|
||||
`bun outdated` supports checking catalog dependencies defined in `package.json`:
|
||||
|
||||
```sh
|
||||
$ bun outdated -r
|
||||
┌────────────────────┬─────────┬─────────┬─────────┬────────────────────────────────┐
|
||||
│ Package │ Current │ Update │ Latest │ Workspace │
|
||||
├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤
|
||||
│ body-parser │ 1.19.0 │ 1.19.0 │ 2.2.0 │ @test/shared │
|
||||
├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤
|
||||
│ cors │ 2.8.0 │ 2.8.0 │ 2.8.5 │ @test/shared │
|
||||
├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤
|
||||
│ chalk │ 4.0.0 │ 4.0.0 │ 5.6.2 │ @test/utils │
|
||||
├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤
|
||||
│ uuid │ 8.0.0 │ 8.0.0 │ 13.0.0 │ @test/utils │
|
||||
├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤
|
||||
│ axios │ 0.21.0 │ 0.21.0 │ 1.12.2 │ catalog (@test/app) │
|
||||
├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤
|
||||
│ lodash │ 4.17.15 │ 4.17.15 │ 4.17.21 │ catalog (@test/app, @test/app) │
|
||||
├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤
|
||||
│ react │ 17.0.0 │ 17.0.0 │ 19.1.1 │ catalog (@test/app) │
|
||||
├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤
|
||||
│ react-dom │ 17.0.0 │ 17.0.0 │ 19.1.1 │ catalog (@test/app) │
|
||||
├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤
|
||||
│ express │ 4.17.0 │ 4.17.0 │ 5.1.0 │ catalog (@test/shared) │
|
||||
├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤
|
||||
│ moment │ 2.24.0 │ 2.24.0 │ 2.30.1 │ catalog (@test/utils) │
|
||||
├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤
|
||||
│ @types/node (dev) │ 14.0.0 │ 14.0.0 │ 24.5.2 │ @test/shared │
|
||||
├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤
|
||||
│ @types/react (dev) │ 17.0.0 │ 17.0.0 │ 19.1.15 │ catalog:testing (@test/app) │
|
||||
├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤
|
||||
│ eslint (dev) │ 7.0.0 │ 7.0.0 │ 9.36.0 │ catalog:testing (@test/app) │
|
||||
├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤
|
||||
│ typescript (dev) │ 4.9.5 │ 4.9.5 │ 5.9.2 │ catalog:build (@test/app) │
|
||||
├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤
|
||||
│ jest (dev) │ 26.0.0 │ 26.0.0 │ 30.2.0 │ catalog:testing (@test/shared) │
|
||||
├────────────────────┼─────────┼─────────┼─────────┼────────────────────────────────┤
|
||||
│ prettier (dev) │ 2.0.0 │ 2.0.0 │ 3.6.2 │ catalog:build (@test/utils) │
|
||||
└────────────────────┴─────────┴─────────┴─────────┴────────────────────────────────┘
|
||||
```
|
||||
|
||||
{% bunCLIUsage command="outdated" /%}
|
||||
|
||||
@@ -82,16 +82,6 @@ The `--dry-run` flag can be used to simulate the publish process without actuall
|
||||
$ bun publish --dry-run
|
||||
```
|
||||
|
||||
### `--tolerate-republish`
|
||||
|
||||
The `--tolerate-republish` flag makes `bun publish` exit with code 0 instead of code 1 when attempting to republish over an existing version number. This is useful in automated workflows where republishing the same version might occur and should not be treated as an error.
|
||||
|
||||
```sh
|
||||
$ bun publish --tolerate-republish
|
||||
```
|
||||
|
||||
Without this flag, attempting to publish a version that already exists will result in an error and exit code 1. With this flag, the command will exit successfully even when trying to republish an existing version.
|
||||
|
||||
### `--gzip-level`
|
||||
|
||||
Specify the level of gzip compression to use when packing the package. Only applies to `bun publish` without a tarball path argument. Values range from `0` to `9` (default is `9`).
|
||||
|
||||
@@ -151,14 +151,6 @@ By default, Bun respects this shebang and executes the script with `node`. Howev
|
||||
$ bun run --bun vite
|
||||
```
|
||||
|
||||
### `--no-addons`
|
||||
|
||||
Disable native addons and use the `node-addons` export condition.
|
||||
|
||||
```bash
|
||||
$ bun --no-addons run server.js
|
||||
```
|
||||
|
||||
### Filtering
|
||||
|
||||
In monorepos containing multiple packages, you can use the `--filter` argument to execute scripts in many packages at once.
|
||||
@@ -174,14 +166,6 @@ will execute `<script>` in both `bar` and `baz`, but not in `foo`.
|
||||
|
||||
Find more details in the docs page for [filter](https://bun.com/docs/cli/filter#running-scripts-with-filter).
|
||||
|
||||
### `--workspaces`
|
||||
|
||||
Run scripts across all workspaces in the monorepo:
|
||||
|
||||
```bash
|
||||
bun run --workspaces test
|
||||
```
|
||||
|
||||
## `bun run -` to pipe code from stdin
|
||||
|
||||
`bun run -` lets you read JavaScript, TypeScript, TSX, or JSX from stdin and execute it without writing to a temporary file first.
|
||||
@@ -228,14 +212,6 @@ $ bun --smol run index.tsx
|
||||
|
||||
This causes the garbage collector to run more frequently, which can slow down execution. However, it can be useful in environments with limited memory. Bun automatically adjusts the garbage collector's heap size based on the available memory (accounting for cgroups and other memory limits) with and without the `--smol` flag, so this is mostly useful for cases where you want to make the heap size grow more slowly.
|
||||
|
||||
## `--user-agent`
|
||||
|
||||
**`--user-agent <string>`** - Set User-Agent header for all `fetch()` requests:
|
||||
|
||||
```bash
|
||||
bun --user-agent "MyBot/1.0" run index.tsx
|
||||
```
|
||||
|
||||
## Resolution order
|
||||
|
||||
Absolute paths and paths starting with `./` or `.\\` are always executed as source files. Unless using `bun run`, running a file with an allowed extension will prefer the file over a package.json script.
|
||||
@@ -247,15 +223,4 @@ When there is a package.json script and a file with the same name, `bun run` pri
|
||||
3. Binaries from project packages, eg `bun add eslint && bun run eslint`
|
||||
4. (`bun run` only) System commands, eg `bun run ls`
|
||||
|
||||
### `--unhandled-rejections`
|
||||
|
||||
Configure how unhandled promise rejections are handled:
|
||||
|
||||
```bash
|
||||
$ bun --unhandled-rejections=throw script.js # Throw exception (terminate immediately)
|
||||
$ bun --unhandled-rejections=strict script.js # Throw exception (emit rejectionHandled if handled later)
|
||||
$ bun --unhandled-rejections=warn script.js # Print warning to stderr (default in Node.js)
|
||||
$ bun --unhandled-rejections=none script.js # Silently ignore
|
||||
```
|
||||
|
||||
{% bunCLIUsage command="run" /%}
|
||||
|
||||
@@ -47,8 +47,6 @@ To filter by _test name_, use the `-t`/`--test-name-pattern` flag.
|
||||
$ bun test --test-name-pattern addition
|
||||
```
|
||||
|
||||
When no tests match the filter, `bun test` exits with code 1.
|
||||
|
||||
To run a specific file in the test runner, make sure the path starts with `./` or `/` to distinguish it from a filter name.
|
||||
|
||||
```bash
|
||||
@@ -111,90 +109,6 @@ Use the `--timeout` flag to specify a _per-test_ timeout in milliseconds. If a t
|
||||
$ bun test --timeout 20
|
||||
```
|
||||
|
||||
## Concurrent test execution
|
||||
|
||||
By default, Bun runs all tests sequentially within each test file. You can enable concurrent execution to run async tests in parallel, significantly speeding up test suites with independent tests.
|
||||
|
||||
### `--concurrent` flag
|
||||
|
||||
Use the `--concurrent` flag to run all tests concurrently within their respective files:
|
||||
|
||||
```sh
|
||||
$ bun test --concurrent
|
||||
```
|
||||
|
||||
When this flag is enabled, all tests will run in parallel unless explicitly marked with `test.serial`.
|
||||
|
||||
### `--max-concurrency` flag
|
||||
|
||||
Control the maximum number of tests running simultaneously with the `--max-concurrency` flag:
|
||||
|
||||
```sh
|
||||
# Limit to 4 concurrent tests
|
||||
$ bun test --concurrent --max-concurrency 4
|
||||
|
||||
# Default: 20
|
||||
$ bun test --concurrent
|
||||
```
|
||||
|
||||
This helps prevent resource exhaustion when running many concurrent tests. The default value is 20.
|
||||
|
||||
### `test.concurrent`
|
||||
|
||||
Mark individual tests to run concurrently, even when the `--concurrent` flag is not used:
|
||||
|
||||
```ts
|
||||
import { test, expect } from "bun:test";
|
||||
|
||||
// These tests run in parallel with each other
|
||||
test.concurrent("concurrent test 1", async () => {
|
||||
await fetch("/api/endpoint1");
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
test.concurrent("concurrent test 2", async () => {
|
||||
await fetch("/api/endpoint2");
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
// This test runs sequentially
|
||||
test("sequential test", () => {
|
||||
expect(1 + 1).toBe(2);
|
||||
});
|
||||
```
|
||||
|
||||
### `test.serial`
|
||||
|
||||
Force tests to run sequentially, even when the `--concurrent` flag is enabled:
|
||||
|
||||
```ts
|
||||
import { test, expect } from "bun:test";
|
||||
|
||||
let sharedState = 0;
|
||||
|
||||
// These tests must run in order
|
||||
test.serial("first serial test", () => {
|
||||
sharedState = 1;
|
||||
expect(sharedState).toBe(1);
|
||||
});
|
||||
|
||||
test.serial("second serial test", () => {
|
||||
// Depends on the previous test
|
||||
expect(sharedState).toBe(1);
|
||||
sharedState = 2;
|
||||
});
|
||||
|
||||
// This test can run concurrently if --concurrent is enabled
|
||||
test("independent test", () => {
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
// Chaining test qualifiers
|
||||
test.failing.each([1, 2, 3])("chained qualifiers %d", input => {
|
||||
expect(input).toBe(0); // This test is expected to fail for each input
|
||||
});
|
||||
```
|
||||
|
||||
## Rerun tests
|
||||
|
||||
Use the `--rerun-each` flag to run each test multiple times. This is useful for detecting flaky or non-deterministic test failures.
|
||||
|
||||
@@ -90,17 +90,6 @@ Packages are organized in sections by dependency type:
|
||||
|
||||
Within each section, individual packages may have additional suffixes (` dev`, ` peer`, ` optional`) for extra clarity.
|
||||
|
||||
## `--recursive`
|
||||
|
||||
Use the `--recursive` flag with `--interactive` to update dependencies across all workspaces in a monorepo:
|
||||
|
||||
```sh
|
||||
$ bun update --interactive --recursive
|
||||
$ bun update -i -r
|
||||
```
|
||||
|
||||
This displays an additional "Workspace" column showing which workspace each dependency belongs to.
|
||||
|
||||
## `--latest`
|
||||
|
||||
By default, `bun update` will update to the latest version of a dependency that satisfies the version range specified in your `package.json`.
|
||||
|
||||
@@ -7,7 +7,7 @@ When building a WebSocket server, it's typically necessary to store some identif
|
||||
With [Bun.serve()](https://bun.com/docs/api/websockets#contextual-data), this "contextual data" is set when the connection is initially upgraded by passing a `data` parameter in the `server.upgrade()` call.
|
||||
|
||||
```ts
|
||||
Bun.serve({
|
||||
Bun.serve<{ socketId: number }>({
|
||||
fetch(req, server) {
|
||||
const success = server.upgrade(req, {
|
||||
data: {
|
||||
@@ -20,9 +20,6 @@ Bun.serve({
|
||||
// ...
|
||||
},
|
||||
websocket: {
|
||||
// TypeScript: specify the type of ws.data like this
|
||||
data: {} as { socketId: number },
|
||||
|
||||
// define websocket handlers
|
||||
async message(ws, message) {
|
||||
// the contextual data is available as the `data` property
|
||||
@@ -44,7 +41,8 @@ type WebSocketData = {
|
||||
userId: string;
|
||||
};
|
||||
|
||||
Bun.serve({
|
||||
// TypeScript: specify the type of `data`
|
||||
Bun.serve<WebSocketData>({
|
||||
async fetch(req, server) {
|
||||
// use a library to parse cookies
|
||||
const cookies = parseCookies(req.headers.get("Cookie"));
|
||||
@@ -62,9 +60,6 @@ Bun.serve({
|
||||
if (upgraded) return undefined;
|
||||
},
|
||||
websocket: {
|
||||
// TypeScript: specify the type of ws.data like this
|
||||
data: {} as WebSocketData,
|
||||
|
||||
async message(ws, message) {
|
||||
// save the message to a database
|
||||
await saveMessageToDatabase({
|
||||
|
||||
@@ -7,7 +7,7 @@ Bun's server-side `WebSocket` API provides a native pub-sub API. Sockets can be
|
||||
This code snippet implements a simple single-channel chat server.
|
||||
|
||||
```ts
|
||||
const server = Bun.serve({
|
||||
const server = Bun.serve<{ username: string }>({
|
||||
fetch(req, server) {
|
||||
const cookies = req.headers.get("cookie");
|
||||
const username = getUsernameFromCookies(cookies);
|
||||
@@ -17,9 +17,6 @@ const server = Bun.serve({
|
||||
return new Response("Hello world");
|
||||
},
|
||||
websocket: {
|
||||
// TypeScript: specify the type of ws.data like this
|
||||
data: {} as { username: string },
|
||||
|
||||
open(ws) {
|
||||
const msg = `${ws.data.username} has entered the chat`;
|
||||
ws.subscribe("the-group-chat");
|
||||
|
||||
@@ -7,7 +7,7 @@ Start a simple WebSocket server using [`Bun.serve`](https://bun.com/docs/api/htt
|
||||
Inside `fetch`, we attempt to upgrade incoming `ws:` or `wss:` requests to WebSocket connections.
|
||||
|
||||
```ts
|
||||
const server = Bun.serve({
|
||||
const server = Bun.serve<{ authToken: string }>({
|
||||
fetch(req, server) {
|
||||
const success = server.upgrade(req);
|
||||
if (success) {
|
||||
|
||||
@@ -24,26 +24,6 @@ To update all dependencies to the latest versions (including breaking changes):
|
||||
bun update --latest
|
||||
```
|
||||
|
||||
### Filtering options
|
||||
|
||||
**`--audit-level=<low|moderate|high|critical>`** - Only show vulnerabilities at this severity level or higher:
|
||||
|
||||
```bash
|
||||
bun audit --audit-level=high
|
||||
```
|
||||
|
||||
**`--prod`** - Audit only production dependencies (excludes devDependencies):
|
||||
|
||||
```bash
|
||||
bun audit --prod
|
||||
```
|
||||
|
||||
**`--ignore <CVE>`** - Ignore specific CVEs (can be used multiple times):
|
||||
|
||||
```bash
|
||||
bun audit --ignore CVE-2022-25883 --ignore CVE-2023-26136
|
||||
```
|
||||
|
||||
### `--json`
|
||||
|
||||
Use the `--json` flag to print the raw JSON response from the registry instead of the formatted report:
|
||||
|
||||
@@ -46,13 +46,3 @@ print = "yarn"
|
||||
Bun v1.2 changed the default lockfile format to the text-based `bun.lock`. Existing binary `bun.lockb` lockfiles can be migrated to the new format by running `bun install --save-text-lockfile --frozen-lockfile --lockfile-only` and deleting `bun.lockb`.
|
||||
|
||||
More information about the new lockfile format can be found on [our blogpost](https://bun.com/blog/bun-lock-text-lockfile).
|
||||
|
||||
#### Automatic lockfile migration
|
||||
|
||||
When running `bun install` in a project without a `bun.lock`, Bun automatically migrates existing lockfiles:
|
||||
|
||||
- `yarn.lock` (v1)
|
||||
- `package-lock.json` (npm)
|
||||
- `pnpm-lock.yaml` (pnpm)
|
||||
|
||||
The original lockfile is preserved and can be removed manually after verification.
|
||||
|
||||
@@ -73,33 +73,3 @@ The equivalent `bunfig.toml` option is to add a key in [`install.scopes`](https:
|
||||
[install.scopes]
|
||||
myorg = { url = "http://localhost:4873/", username = "myusername", password = "$NPM_PASSWORD" }
|
||||
```
|
||||
|
||||
### `link-workspace-packages`: Control workspace package installation
|
||||
|
||||
Controls how workspace packages are installed when available locally:
|
||||
|
||||
```ini
|
||||
link-workspace-packages=true
|
||||
```
|
||||
|
||||
The equivalent `bunfig.toml` option is [`install.linkWorkspacePackages`](https://bun.com/docs/runtime/bunfig#install-linkworkspacepackages):
|
||||
|
||||
```toml
|
||||
[install]
|
||||
linkWorkspacePackages = true
|
||||
```
|
||||
|
||||
### `save-exact`: Save exact versions
|
||||
|
||||
Always saves exact versions without the `^` prefix:
|
||||
|
||||
```ini
|
||||
save-exact=true
|
||||
```
|
||||
|
||||
The equivalent `bunfig.toml` option is [`install.exact`](https://bun.com/docs/runtime/bunfig#install-exact):
|
||||
|
||||
```toml
|
||||
[install]
|
||||
exact = true
|
||||
```
|
||||
|
||||
@@ -81,7 +81,7 @@ Workspaces have a couple major benefits.
|
||||
|
||||
- **Code can be split into logical parts.** If one package relies on another, you can simply add it as a dependency in `package.json`. If package `b` depends on `a`, `bun install` will install your local `packages/a` directory into `node_modules` instead of downloading it from the npm registry.
|
||||
- **Dependencies can be de-duplicated.** If `a` and `b` share a common dependency, it will be _hoisted_ to the root `node_modules` directory. This reduces redundant disk usage and minimizes "dependency hell" issues associated with having multiple versions of a package installed simultaneously.
|
||||
- **Run scripts in multiple packages.** You can use the [`--filter` flag](https://bun.com/docs/cli/filter) to easily run `package.json` scripts in multiple packages in your workspace, or `--workspaces` to run scripts across all workspaces.
|
||||
- **Run scripts in multiple packages.** You can use the [`--filter` flag](https://bun.com/docs/cli/filter) to easily run `package.json` scripts in multiple packages in your workspace.
|
||||
|
||||
## Share versions with Catalogs
|
||||
|
||||
|
||||
@@ -249,46 +249,6 @@ This is useful for:
|
||||
|
||||
The `--concurrent` CLI flag will override this setting when specified.
|
||||
|
||||
### `test.randomize`
|
||||
|
||||
Run tests in random order. Default `false`.
|
||||
|
||||
```toml
|
||||
[test]
|
||||
randomize = true
|
||||
```
|
||||
|
||||
This helps catch bugs related to test interdependencies by running tests in a different order each time. When combined with `seed`, the random order becomes reproducible.
|
||||
|
||||
The `--randomize` CLI flag will override this setting when specified.
|
||||
|
||||
### `test.seed`
|
||||
|
||||
Set the random seed for test randomization. This option requires `randomize` to be `true`.
|
||||
|
||||
```toml
|
||||
[test]
|
||||
randomize = true
|
||||
seed = 2444615283
|
||||
```
|
||||
|
||||
Using a seed makes the randomized test order reproducible across runs, which is useful for debugging flaky tests. When you encounter a test failure with randomization enabled, you can use the same seed to reproduce the exact test order.
|
||||
|
||||
The `--seed` CLI flag will override this setting when specified.
|
||||
|
||||
### `test.rerunEach`
|
||||
|
||||
Re-run each test file a specified number of times. Default `0` (run once).
|
||||
|
||||
```toml
|
||||
[test]
|
||||
rerunEach = 3
|
||||
```
|
||||
|
||||
This is useful for catching flaky tests or non-deterministic behavior. Each test file will be executed the specified number of times.
|
||||
|
||||
The `--rerun-each` CLI flag will override this setting when specified.
|
||||
|
||||
## Package manager
|
||||
|
||||
Package management is a complex issue; to support a range of use cases, the behavior of `bun install` can be configured under the `[install]` section.
|
||||
@@ -610,20 +570,6 @@ Valid values are:
|
||||
|
||||
{% /table %}
|
||||
|
||||
### `install.minimumReleaseAge`
|
||||
|
||||
Configure a minimum age (in seconds) for npm package versions. Package versions published more recently than this threshold will be filtered out during installation. Default is `null` (disabled).
|
||||
|
||||
```toml
|
||||
[install]
|
||||
# Only install package versions published at least 3 days ago
|
||||
minimumReleaseAge = 259200
|
||||
# These packages will bypass the 3-day minimum age requirement
|
||||
minimumReleaseAgeExcludes = ["@types/bun", "typescript"]
|
||||
```
|
||||
|
||||
For more details see [Minimum release age](https://bun.com/docs/cli/install#minimum-release-age) in the install documentation.
|
||||
|
||||
<!-- ## Debugging -->
|
||||
|
||||
<!--
|
||||
@@ -651,7 +597,7 @@ editor = "code"
|
||||
|
||||
The `bun run` command can be configured under the `[run]` section. These apply to the `bun run` command and the `bun` command when running a file or executable or script.
|
||||
|
||||
Currently, `bunfig.toml` is only automatically loaded for `bun run` in a local project (it doesn't check for a global `.bunfig.toml`).
|
||||
Currently, `bunfig.toml` isn't always automatically loaded for `bun run` in a local project (it does check for a global `bunfig.toml`), so you might still need to pass `-c` or `-c=bunfig.toml` to use these settings.
|
||||
|
||||
### `run.shell` - use the system shell or Bun's shell
|
||||
|
||||
|
||||
@@ -220,11 +220,6 @@ These environment variables are read by Bun and configure aspects of its behavio
|
||||
- `DO_NOT_TRACK`
|
||||
- Disable uploading crash reports to `bun.report` on crash. On macOS & Windows, crash report uploads are enabled by default. Otherwise, telemetry is not sent yet as of May 21st, 2024, but we are planning to add telemetry in the coming weeks. If `DO_NOT_TRACK=1`, then auto-uploading crash reports and telemetry are both [disabled](https://do-not-track.dev/).
|
||||
|
||||
---
|
||||
|
||||
- `BUN_OPTIONS`
|
||||
- Prepends command-line arguments to any Bun execution. For example, `BUN_OPTIONS="--hot"` makes `bun run dev` behave like `bun --hot run dev`.
|
||||
|
||||
{% /table %}
|
||||
|
||||
## Runtime transpiler caching
|
||||
|
||||
@@ -124,7 +124,7 @@ This page is updated regularly to reflect compatibility status of the latest ver
|
||||
|
||||
### [`node:perf_hooks`](https://nodejs.org/api/perf_hooks.html)
|
||||
|
||||
🟡 APIs are implemented, but Node.js test suite does not pass yet for this module.
|
||||
🟡 Missing `createHistogram` `monitorEventLoopDelay`. It's recommended to use `performance` global instead of `perf_hooks.performance`.
|
||||
|
||||
### [`node:process`](https://nodejs.org/api/process.html)
|
||||
|
||||
@@ -156,7 +156,7 @@ This page is updated regularly to reflect compatibility status of the latest ver
|
||||
|
||||
### [`node:worker_threads`](https://nodejs.org/api/worker_threads.html)
|
||||
|
||||
🟡 `Worker` doesn't support the following options: `stdin` `stdout` `stderr` `trackedUnmanagedFds` `resourceLimits`. Missing `markAsUntransferable` `moveMessagePortToContext`.
|
||||
🟡 `Worker` doesn't support the following options: `stdin` `stdout` `stderr` `trackedUnmanagedFds` `resourceLimits`. Missing `markAsUntransferable` `moveMessagePortToContext` `getHeapSnapshot`.
|
||||
|
||||
### [`node:inspector`](https://nodejs.org/api/inspector.html)
|
||||
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
# Thread::initializePlatformThreading() in ThreadingPOSIX.cpp) to the JS thread to suspend or resume
|
||||
# it. So stopping the process would just create noise when debugging any long-running script.
|
||||
process handle -p true -s false -n false SIGPWR
|
||||
process handle -p true -s false -n false SIGUSR1
|
||||
process handle -p true -s false -n false SIGUSR2
|
||||
|
||||
command script import -c lldb_pretty_printers.py
|
||||
type category enable zig.lang
|
||||
|
||||
@@ -78,12 +78,6 @@
|
||||
"no-empty-file": "off",
|
||||
"no-unnecessary-await": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["src/js/builtins/**"],
|
||||
"rules": {
|
||||
"no-unused-expressions": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
12
package.json
12
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "bun",
|
||||
"version": "1.3.0",
|
||||
"version": "1.2.23",
|
||||
"workspaces": [
|
||||
"./packages/bun-types",
|
||||
"./packages/@types/bun"
|
||||
@@ -11,14 +11,14 @@
|
||||
"@lezer/cpp": "^1.1.3",
|
||||
"@types/bun": "workspace:*",
|
||||
"bun-tracestrings": "github:oven-sh/bun.report#912ca63e26c51429d3e6799aa2a6ab079b188fd8",
|
||||
"esbuild": "^0.21.5",
|
||||
"mitata": "^0.1.14",
|
||||
"esbuild": "^0.21.4",
|
||||
"mitata": "^0.1.11",
|
||||
"peechy": "0.4.34",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-organize-imports": "^4.3.0",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-organize-imports": "^4.0.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"source-map-js": "^1.2.1",
|
||||
"source-map-js": "^1.2.0",
|
||||
"typescript": "5.9.2"
|
||||
},
|
||||
"resolutions": {
|
||||
|
||||
1471
packages/bun-types/bun.d.ts
vendored
1471
packages/bun-types/bun.d.ts
vendored
File diff suppressed because it is too large
Load Diff
2
packages/bun-types/bun.ns.d.ts
vendored
2
packages/bun-types/bun.ns.d.ts
vendored
@@ -3,3 +3,5 @@ import * as BunModule from "bun";
|
||||
declare global {
|
||||
export import Bun = BunModule;
|
||||
}
|
||||
|
||||
export {};
|
||||
|
||||
5
packages/bun-types/deprecated.d.ts
vendored
5
packages/bun-types/deprecated.d.ts
vendored
@@ -98,11 +98,6 @@ declare module "bun" {
|
||||
): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link Serve.Options Bun.Serve.Options<T, R>} instead
|
||||
*/
|
||||
type ServeOptions<T = undefined, R extends string = never> = Serve.Options<T, R>;
|
||||
|
||||
/** @deprecated Use {@link SQL.Query Bun.SQL.Query} */
|
||||
type SQLQuery<T = any> = SQL.Query<T>;
|
||||
|
||||
|
||||
76
packages/bun-types/globals.d.ts
vendored
76
packages/bun-types/globals.d.ts
vendored
@@ -7,13 +7,6 @@ declare module "bun" {
|
||||
type LibWorkerOrBunWorker = LibDomIsLoaded extends true ? {} : Bun.Worker;
|
||||
type LibEmptyOrBunWebSocket = LibDomIsLoaded extends true ? {} : Bun.WebSocket;
|
||||
|
||||
type LibEmptyOrNodeStreamWebCompressionStream = LibDomIsLoaded extends true
|
||||
? {}
|
||||
: import("node:stream/web").CompressionStream;
|
||||
type LibEmptyOrNodeStreamWebDecompressionStream = LibDomIsLoaded extends true
|
||||
? {}
|
||||
: import("node:stream/web").DecompressionStream;
|
||||
|
||||
type LibPerformanceOrNodePerfHooksPerformance = LibDomIsLoaded extends true ? {} : import("perf_hooks").Performance;
|
||||
type LibEmptyOrPerformanceEntry = LibDomIsLoaded extends true ? {} : import("node:perf_hooks").PerformanceEntry;
|
||||
type LibEmptyOrPerformanceMark = LibDomIsLoaded extends true ? {} : import("node:perf_hooks").PerformanceMark;
|
||||
@@ -278,30 +271,6 @@ declare var Event: {
|
||||
new (type: string, eventInitDict?: Bun.EventInit): Event;
|
||||
};
|
||||
|
||||
/**
|
||||
* Unimplemented in Bun
|
||||
*/
|
||||
interface CompressionStream extends Bun.__internal.LibEmptyOrNodeStreamWebCompressionStream {}
|
||||
/**
|
||||
* Unimplemented in Bun
|
||||
*/
|
||||
declare var CompressionStream: Bun.__internal.UseLibDomIfAvailable<
|
||||
"CompressionStream",
|
||||
typeof import("node:stream/web").CompressionStream
|
||||
>;
|
||||
|
||||
/**
|
||||
* Unimplemented in Bun
|
||||
*/
|
||||
interface DecompressionStream extends Bun.__internal.LibEmptyOrNodeStreamWebCompressionStream {}
|
||||
/**
|
||||
* Unimplemented in Bun
|
||||
*/
|
||||
declare var DecompressionStream: Bun.__internal.UseLibDomIfAvailable<
|
||||
"DecompressionStream",
|
||||
typeof import("node:stream/web").DecompressionStream
|
||||
>;
|
||||
|
||||
interface EventTarget {
|
||||
/**
|
||||
* Adds a new handler for the `type` event. Any given `listener` is added only once per `type` and per `capture` option value.
|
||||
@@ -891,10 +860,7 @@ interface ErrnoException extends Error {
|
||||
syscall?: string | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* An abnormal event (called an exception) which occurs as a result of calling a
|
||||
* method or accessing a property of a web API
|
||||
*/
|
||||
/** An abnormal event (called an exception) which occurs as a result of calling a method or accessing a property of a web API. */
|
||||
interface DOMException extends Error {
|
||||
readonly message: string;
|
||||
readonly name: string;
|
||||
@@ -924,35 +890,11 @@ interface DOMException extends Error {
|
||||
readonly INVALID_NODE_TYPE_ERR: 24;
|
||||
readonly DATA_CLONE_ERR: 25;
|
||||
}
|
||||
declare var DOMException: {
|
||||
prototype: DOMException;
|
||||
new (message?: string, name?: string): DOMException;
|
||||
readonly INDEX_SIZE_ERR: 1;
|
||||
readonly DOMSTRING_SIZE_ERR: 2;
|
||||
readonly HIERARCHY_REQUEST_ERR: 3;
|
||||
readonly WRONG_DOCUMENT_ERR: 4;
|
||||
readonly INVALID_CHARACTER_ERR: 5;
|
||||
readonly NO_DATA_ALLOWED_ERR: 6;
|
||||
readonly NO_MODIFICATION_ALLOWED_ERR: 7;
|
||||
readonly NOT_FOUND_ERR: 8;
|
||||
readonly NOT_SUPPORTED_ERR: 9;
|
||||
readonly INUSE_ATTRIBUTE_ERR: 10;
|
||||
readonly INVALID_STATE_ERR: 11;
|
||||
readonly SYNTAX_ERR: 12;
|
||||
readonly INVALID_MODIFICATION_ERR: 13;
|
||||
readonly NAMESPACE_ERR: 14;
|
||||
readonly INVALID_ACCESS_ERR: 15;
|
||||
readonly VALIDATION_ERR: 16;
|
||||
readonly TYPE_MISMATCH_ERR: 17;
|
||||
readonly SECURITY_ERR: 18;
|
||||
readonly NETWORK_ERR: 19;
|
||||
readonly ABORT_ERR: 20;
|
||||
readonly URL_MISMATCH_ERR: 21;
|
||||
readonly QUOTA_EXCEEDED_ERR: 22;
|
||||
readonly TIMEOUT_ERR: 23;
|
||||
readonly INVALID_NODE_TYPE_ERR: 24;
|
||||
readonly DATA_CLONE_ERR: 25;
|
||||
};
|
||||
|
||||
// declare var DOMException: {
|
||||
// prototype: DOMException;
|
||||
// new (message?: string, name?: string): DOMException;
|
||||
// };
|
||||
|
||||
declare function alert(message?: string): void;
|
||||
declare function confirm(message?: string): boolean;
|
||||
@@ -1663,6 +1605,12 @@ declare var AbortSignal: Bun.__internal.UseLibDomIfAvailable<
|
||||
}
|
||||
>;
|
||||
|
||||
interface DOMException {}
|
||||
declare var DOMException: Bun.__internal.UseLibDomIfAvailable<
|
||||
"DOMException",
|
||||
{ prototype: DOMException; new (): DOMException }
|
||||
>;
|
||||
|
||||
interface FormData {
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/append) */
|
||||
append(name: string, value: string | Blob): void;
|
||||
|
||||
1
packages/bun-types/index.d.ts
vendored
1
packages/bun-types/index.d.ts
vendored
@@ -21,7 +21,6 @@
|
||||
/// <reference path="./redis.d.ts" />
|
||||
/// <reference path="./shell.d.ts" />
|
||||
/// <reference path="./experimental.d.ts" />
|
||||
/// <reference path="./serve.d.ts" />
|
||||
/// <reference path="./sql.d.ts" />
|
||||
/// <reference path="./security.d.ts" />
|
||||
|
||||
|
||||
2593
packages/bun-types/redis.d.ts
vendored
2593
packages/bun-types/redis.d.ts
vendored
File diff suppressed because it is too large
Load Diff
1272
packages/bun-types/serve.d.ts
vendored
1272
packages/bun-types/serve.d.ts
vendored
File diff suppressed because it is too large
Load Diff
77
packages/bun-types/sql.d.ts
vendored
77
packages/bun-types/sql.d.ts
vendored
@@ -12,68 +12,6 @@ declare module "bun" {
|
||||
release(): void;
|
||||
}
|
||||
|
||||
type ArrayType =
|
||||
| "BOOLEAN"
|
||||
| "BYTEA"
|
||||
| "CHAR"
|
||||
| "NAME"
|
||||
| "TEXT"
|
||||
| "CHAR"
|
||||
| "VARCHAR"
|
||||
| "SMALLINT"
|
||||
| "INT2VECTOR"
|
||||
| "INTEGER"
|
||||
| "INT"
|
||||
| "BIGINT"
|
||||
| "REAL"
|
||||
| "DOUBLE PRECISION"
|
||||
| "NUMERIC"
|
||||
| "MONEY"
|
||||
| "OID"
|
||||
| "TID"
|
||||
| "XID"
|
||||
| "CID"
|
||||
| "JSON"
|
||||
| "JSONB"
|
||||
| "JSONPATH"
|
||||
| "XML"
|
||||
| "POINT"
|
||||
| "LSEG"
|
||||
| "PATH"
|
||||
| "BOX"
|
||||
| "POLYGON"
|
||||
| "LINE"
|
||||
| "CIRCLE"
|
||||
| "CIDR"
|
||||
| "MACADDR"
|
||||
| "INET"
|
||||
| "MACADDR8"
|
||||
| "DATE"
|
||||
| "TIME"
|
||||
| "TIMESTAMP"
|
||||
| "TIMESTAMPTZ"
|
||||
| "INTERVAL"
|
||||
| "TIMETZ"
|
||||
| "BIT"
|
||||
| "VARBIT"
|
||||
| "ACLITEM"
|
||||
| "PG_DATABASE"
|
||||
| (string & {});
|
||||
|
||||
/**
|
||||
* Represents a SQL array parameter
|
||||
*/
|
||||
interface SQLArrayParameter {
|
||||
/**
|
||||
* The serialized values of the array parameter
|
||||
*/
|
||||
serializedValues: string;
|
||||
/**
|
||||
* The type of the array parameter
|
||||
*/
|
||||
arrayType: ArrayType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a client within a transaction context Extends SQL with savepoint
|
||||
* functionality
|
||||
@@ -692,21 +630,6 @@ declare module "bun" {
|
||||
*/
|
||||
reserve(): Promise<ReservedSQL>;
|
||||
|
||||
/**
|
||||
* Creates a new SQL array parameter
|
||||
* @param values - The values to create the array parameter from
|
||||
* @param typeNameOrTypeID - The type name or type ID to create the array parameter from, if omitted it will default to JSON
|
||||
* @returns A new SQL array parameter
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const array = sql.array([1, 2, 3], "INT");
|
||||
* await sql`CREATE TABLE users_posts (user_id INT, posts_id INT[])`;
|
||||
* await sql`INSERT INTO users_posts (user_id, posts_id) VALUES (${user.id}, ${array})`;
|
||||
* ```
|
||||
*/
|
||||
array(values: any[], typeNameOrTypeID?: number | ArrayType): SQLArrayParameter;
|
||||
|
||||
/**
|
||||
* Begins a new transaction.
|
||||
*
|
||||
|
||||
33
packages/bun-types/test.d.ts
vendored
33
packages/bun-types/test.d.ts
vendored
@@ -390,20 +390,11 @@ declare module "bun:test" {
|
||||
*/
|
||||
repeats?: number;
|
||||
}
|
||||
|
||||
namespace __internal {
|
||||
type IsTuple<T> = T extends readonly unknown[]
|
||||
? number extends T["length"]
|
||||
? false // It's an array with unknown length, not a tuple
|
||||
: true // It's an array with a fixed length (a tuple)
|
||||
: false; // Not an array at all
|
||||
|
||||
/**
|
||||
* Accepts `[1, 2, 3] | ["a", "b", "c"]` and returns `[1 | "a", 2 | "b", 3 | "c"]`
|
||||
*/
|
||||
type Flatten<T, Copy extends T = T> = { [Key in keyof T]: Copy[Key] };
|
||||
}
|
||||
|
||||
type IsTuple<T> = T extends readonly unknown[]
|
||||
? number extends T["length"]
|
||||
? false // It's an array with unknown length, not a tuple
|
||||
: true // It's an array with a fixed length (a tuple)
|
||||
: false; // Not an array at all
|
||||
/**
|
||||
* Runs a test.
|
||||
*
|
||||
@@ -427,16 +418,10 @@ declare module "bun:test" {
|
||||
*
|
||||
* @category Testing
|
||||
*/
|
||||
export interface Test<T extends ReadonlyArray<unknown>> {
|
||||
export interface Test<T extends Readonly<any[]>> {
|
||||
(
|
||||
label: string,
|
||||
|
||||
fn: (
|
||||
...args: __internal.IsTuple<T> extends true
|
||||
? [...table: __internal.Flatten<T>, done: (err?: unknown) => void]
|
||||
: T
|
||||
) => void | Promise<unknown>,
|
||||
|
||||
fn: (...args: IsTuple<T> extends true ? [...T, (err?: unknown) => void] : T) => void | Promise<unknown>,
|
||||
/**
|
||||
* - If a `number`, sets the timeout for the test in milliseconds.
|
||||
* - If an `object`, sets the options for the test.
|
||||
@@ -528,8 +513,8 @@ declare module "bun:test" {
|
||||
*
|
||||
* @param table Array of Arrays with the arguments that are passed into the test fn for each row.
|
||||
*/
|
||||
each<T extends Readonly<[unknown, ...unknown[]]>>(table: readonly T[]): Test<T>;
|
||||
each<T extends unknown[]>(table: readonly T[]): Test<T>;
|
||||
each<T extends Readonly<[any, ...any[]]>>(table: readonly T[]): Test<[...T]>;
|
||||
each<T extends any[]>(table: readonly T[]): Test<[...T]>;
|
||||
each<T>(table: T[]): Test<[T]>;
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -717,25 +717,6 @@ LIBUS_SOCKET_DESCRIPTOR bsd_accept_socket(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd
|
||||
return LIBUS_SOCKET_ERROR;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
/* A bug in XNU (the macOS kernel) can cause accept() to return a socket but addrlen=0.
|
||||
* This happens when an IPv4 connection is made to an IPv6 dual-stack listener
|
||||
* and the connection is immediately aborted (sends RST packet).
|
||||
* However, there might be buffered data from connectx() before the abort. */
|
||||
if (addr->len == 0) {
|
||||
/* Check if there's any pending data before discarding the socket */
|
||||
char peek_buf[1];
|
||||
ssize_t has_data = recv(accepted_fd, peek_buf, 1, MSG_PEEK | MSG_DONTWAIT);
|
||||
|
||||
if (has_data <= 0) {
|
||||
/* No data available, socket is truly dead - discard it */
|
||||
bsd_close_socket(accepted_fd);
|
||||
continue; /* Try to accept the next connection */
|
||||
}
|
||||
/* If has_data > 0, let the socket through - there's buffered data to read */
|
||||
}
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -226,11 +226,11 @@ struct us_bun_socket_context_options_t {
|
||||
const char *ca_file_name;
|
||||
const char *ssl_ciphers;
|
||||
int ssl_prefer_low_memory_usage; /* Todo: rename to prefer_low_memory_usage and apply for TCP as well */
|
||||
const char * const *key;
|
||||
const char **key;
|
||||
unsigned int key_count;
|
||||
const char * const *cert;
|
||||
const char **cert;
|
||||
unsigned int cert_count;
|
||||
const char * const *ca;
|
||||
const char **ca;
|
||||
unsigned int ca_count;
|
||||
unsigned int secure_options;
|
||||
int reject_unauthorized;
|
||||
|
||||
@@ -303,10 +303,10 @@ public:
|
||||
auto context = (struct us_socket_context_t *)this->httpContext;
|
||||
struct us_socket_t *s = context->head_sockets;
|
||||
while (s) {
|
||||
// no matter the type of socket will always contain the AsyncSocketData
|
||||
auto *data = ((AsyncSocket<SSL> *) s)->getAsyncSocketData();
|
||||
HttpResponseData<SSL> *httpResponseData = HttpResponse<SSL>::getHttpResponseDataS(s);
|
||||
httpResponseData->shouldCloseOnceIdle = true;
|
||||
struct us_socket_t *next = s->next;
|
||||
if (data->isIdle) {
|
||||
if (httpResponseData->isIdle) {
|
||||
us_socket_close(SSL, s, LIBUS_SOCKET_CLOSE_CODE_CLEAN_SHUTDOWN, 0);
|
||||
}
|
||||
s = next;
|
||||
@@ -641,10 +641,6 @@ public:
|
||||
httpContext->getSocketContextData()->onClientError = std::move(onClientError);
|
||||
}
|
||||
|
||||
void setOnSocketUpgraded(HttpContextData<SSL>::OnSocketUpgradedCallback onUpgraded) {
|
||||
httpContext->getSocketContextData()->onSocketUpgraded = onUpgraded;
|
||||
}
|
||||
|
||||
TemplatedApp &&run() {
|
||||
uWS::run();
|
||||
return std::move(*this);
|
||||
|
||||
@@ -83,7 +83,6 @@ struct AsyncSocketData {
|
||||
|
||||
/* Or empty */
|
||||
AsyncSocketData() = default;
|
||||
bool isIdle = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -253,7 +253,6 @@ private:
|
||||
/* Mark that we are inside the parser now */
|
||||
httpContextData->flags.isParsingHttp = true;
|
||||
httpResponseData->isIdle = false;
|
||||
|
||||
// clients need to know the cursor after http parse, not servers!
|
||||
// how far did we read then? we need to know to continue with websocket parsing data? or?
|
||||
|
||||
|
||||
@@ -43,11 +43,11 @@ struct alignas(16) HttpContextData {
|
||||
template <bool> friend struct TemplatedApp;
|
||||
private:
|
||||
std::vector<MoveOnlyFunction<void(HttpResponse<SSL> *, int)>> filterHandlers;
|
||||
using OnSocketClosedCallback = void (*)(void* userData, int is_ssl, struct us_socket_t *rawSocket);
|
||||
using OnSocketDataCallback = void (*)(void* userData, int is_ssl, struct us_socket_t *rawSocket, const char *data, int length, bool last);
|
||||
using OnSocketDrainCallback = void (*)(void* userData, int is_ssl, struct us_socket_t *rawSocket);
|
||||
using OnSocketUpgradedCallback = void (*)(void* userData, int is_ssl, struct us_socket_t *rawSocket);
|
||||
using OnClientErrorCallback = MoveOnlyFunction<void(int is_ssl, struct us_socket_t *rawSocket, uWS::HttpParserError errorCode, char *rawPacket, int rawPacketLength)>;
|
||||
using OnSocketClosedCallback = void (*)(void* userData, int is_ssl, struct us_socket_t *rawSocket);
|
||||
|
||||
|
||||
MoveOnlyFunction<void(const char *hostname)> missingServerNameHandler;
|
||||
|
||||
@@ -66,7 +66,6 @@ private:
|
||||
OnSocketClosedCallback onSocketClosed = nullptr;
|
||||
OnSocketDrainCallback onSocketDrain = nullptr;
|
||||
OnSocketDataCallback onSocketData = nullptr;
|
||||
OnSocketUpgradedCallback onSocketUpgraded = nullptr;
|
||||
OnClientErrorCallback onClientError = nullptr;
|
||||
|
||||
uint64_t maxHeaderSize = 0; // 0 means no limit
|
||||
@@ -79,7 +78,6 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
HttpFlags flags;
|
||||
};
|
||||
|
||||
|
||||
@@ -316,20 +316,14 @@ public:
|
||||
HttpContext<SSL> *httpContext = (HttpContext<SSL> *) us_socket_context(SSL, (struct us_socket_t *) this);
|
||||
|
||||
/* Move any backpressure out of HttpResponse */
|
||||
auto* responseData = getHttpResponseData();
|
||||
BackPressure backpressure(std::move(((AsyncSocketData<SSL> *) responseData)->buffer));
|
||||
|
||||
auto* socketData = responseData->socketData;
|
||||
HttpContextData<SSL> *httpContextData = httpContext->getSocketContextData();
|
||||
|
||||
BackPressure backpressure(std::move(((AsyncSocketData<SSL> *) getHttpResponseData())->buffer));
|
||||
|
||||
/* Destroy HttpResponseData */
|
||||
responseData->~HttpResponseData();
|
||||
getHttpResponseData()->~HttpResponseData();
|
||||
|
||||
/* Before we adopt and potentially change socket, check if we are corked */
|
||||
bool wasCorked = Super::isCorked();
|
||||
|
||||
|
||||
|
||||
/* Adopting a socket invalidates it, do not rely on it directly to carry any data */
|
||||
us_socket_t *usSocket = us_socket_context_adopt_socket(SSL, (us_socket_context_t *) webSocketContext, (us_socket_t *) this, sizeof(WebSocketData) + sizeof(UserData));
|
||||
WebSocket<SSL, true, UserData> *webSocket = (WebSocket<SSL, true, UserData> *) usSocket;
|
||||
@@ -340,12 +334,10 @@ public:
|
||||
}
|
||||
|
||||
/* Initialize websocket with any moved backpressure intact */
|
||||
webSocket->init(perMessageDeflate, compressOptions, std::move(backpressure), socketData, httpContextData->onSocketClosed);
|
||||
if (httpContextData->onSocketUpgraded) {
|
||||
httpContextData->onSocketUpgraded(socketData, SSL, usSocket);
|
||||
}
|
||||
webSocket->init(perMessageDeflate, compressOptions, std::move(backpressure));
|
||||
|
||||
/* We should only mark this if inside the parser; if upgrading "async" we cannot set this */
|
||||
HttpContextData<SSL> *httpContextData = httpContext->getSocketContextData();
|
||||
if (httpContextData->flags.isParsingHttp) {
|
||||
/* We need to tell the Http parser that we changed socket */
|
||||
httpContextData->upgradedWebSocket = webSocket;
|
||||
@@ -359,6 +351,7 @@ public:
|
||||
|
||||
/* Move construct the UserData right before calling open handler */
|
||||
new (webSocket->getUserData()) UserData(std::forward<UserData>(userData));
|
||||
|
||||
|
||||
/* Emit open event and start the timeout */
|
||||
if (webSocketContextData->openHandler) {
|
||||
|
||||
@@ -109,6 +109,9 @@ struct HttpResponseData : AsyncSocketData<SSL>, HttpParser {
|
||||
uint8_t idleTimeout = 10; // default HTTP_TIMEOUT 10 seconds
|
||||
bool fromAncientRequest = false;
|
||||
bool isConnectRequest = false;
|
||||
bool isIdle = true;
|
||||
bool shouldCloseOnceIdle = false;
|
||||
|
||||
|
||||
#ifdef UWS_WITH_PROXY
|
||||
ProxyParser proxyParser;
|
||||
|
||||
@@ -34,8 +34,8 @@ struct WebSocket : AsyncSocket<SSL> {
|
||||
private:
|
||||
typedef AsyncSocket<SSL> Super;
|
||||
|
||||
void *init(bool perMessageDeflate, CompressOptions compressOptions, BackPressure &&backpressure, void *socketData, WebSocketData::OnSocketClosedCallback onSocketClosed) {
|
||||
new (us_socket_ext(SSL, (us_socket_t *) this)) WebSocketData(perMessageDeflate, compressOptions, std::move(backpressure), socketData, onSocketClosed);
|
||||
void *init(bool perMessageDeflate, CompressOptions compressOptions, BackPressure &&backpressure) {
|
||||
new (us_socket_ext(SSL, (us_socket_t *) this)) WebSocketData(perMessageDeflate, compressOptions, std::move(backpressure));
|
||||
return this;
|
||||
}
|
||||
public:
|
||||
|
||||
@@ -256,9 +256,6 @@ private:
|
||||
|
||||
/* For whatever reason, if we already have emitted close event, do not emit it again */
|
||||
WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s));
|
||||
if (webSocketData->socketData && webSocketData->onSocketClosed) {
|
||||
webSocketData->onSocketClosed(webSocketData->socketData, SSL, (us_socket_t *) s);
|
||||
}
|
||||
if (!webSocketData->isShuttingDown) {
|
||||
/* Emit close event */
|
||||
auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s));
|
||||
|
||||
@@ -52,6 +52,7 @@ struct WebSocketContextData {
|
||||
private:
|
||||
|
||||
public:
|
||||
|
||||
/* This one points to the App's shared topicTree */
|
||||
TopicTree<TopicTreeMessage, TopicTreeBigMessage> *topicTree;
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ private:
|
||||
unsigned int controlTipLength = 0;
|
||||
bool isShuttingDown = 0;
|
||||
bool hasTimedOut = false;
|
||||
|
||||
enum CompressionStatus : char {
|
||||
DISABLED,
|
||||
ENABLED,
|
||||
@@ -53,12 +52,7 @@ private:
|
||||
/* We could be a subscriber */
|
||||
Subscriber *subscriber = nullptr;
|
||||
public:
|
||||
using OnSocketClosedCallback = void (*)(void* userData, int is_ssl, struct us_socket_t *rawSocket);
|
||||
void *socketData = nullptr;
|
||||
/* node http compatibility callbacks */
|
||||
OnSocketClosedCallback onSocketClosed = nullptr;
|
||||
|
||||
WebSocketData(bool perMessageDeflate, CompressOptions compressOptions, BackPressure &&backpressure, void *socketData, OnSocketClosedCallback onSocketClosed) : AsyncSocketData<false>(std::move(backpressure)), WebSocketState<true>() {
|
||||
WebSocketData(bool perMessageDeflate, CompressOptions compressOptions, BackPressure &&backpressure) : AsyncSocketData<false>(std::move(backpressure)), WebSocketState<true>() {
|
||||
compressionStatus = perMessageDeflate ? ENABLED : DISABLED;
|
||||
|
||||
/* Initialize the dedicated sliding window(s) */
|
||||
@@ -70,10 +64,6 @@ public:
|
||||
inflationStream = new InflationStream(compressOptions);
|
||||
}
|
||||
}
|
||||
// never close websocket sockets when closing idle connections
|
||||
this->isIdle = false;
|
||||
this->socketData = socketData;
|
||||
this->onSocketClosed = onSocketClosed;
|
||||
}
|
||||
|
||||
~WebSocketData() {
|
||||
|
||||
@@ -22,7 +22,7 @@ At its core is the _Bun runtime_, a fast JavaScript runtime designed as a drop-i
|
||||
## Features:
|
||||
|
||||
- Live in-editor error messages (gif below)
|
||||
- Vscode test runner support
|
||||
- Test runner codelens
|
||||
- Debugger support
|
||||
- Run scripts from package.json
|
||||
- Visual lockfile viewer for old binary lockfiles (`bun.lockb`)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bun-vscode",
|
||||
"version": "0.0.31",
|
||||
"version": "0.0.29",
|
||||
"author": "oven",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -116,6 +116,20 @@
|
||||
"category": "Bun",
|
||||
"enablement": "!inDebugMode && resourceLangId =~ /^(javascript|typescript|javascriptreact|typescriptreact)$/ && !isInDiffEditor && resourceScheme == 'untitled'",
|
||||
"icon": "$(play-circle)"
|
||||
},
|
||||
{
|
||||
"command": "extension.bun.runTest",
|
||||
"title": "Run all tests",
|
||||
"shortTitle": "Run Test",
|
||||
"category": "Bun",
|
||||
"icon": "$(play)"
|
||||
},
|
||||
{
|
||||
"command": "extension.bun.watchTest",
|
||||
"title": "Run all tests in watch mode",
|
||||
"shortTitle": "Run Test Watch",
|
||||
"category": "Bun",
|
||||
"icon": "$(sync)"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
|
||||
@@ -80,7 +80,6 @@ function getNodeParallelTestTimeout(testPath) {
|
||||
if (testPath.includes("test-dns")) {
|
||||
return 90_000;
|
||||
}
|
||||
if (!isCI) return 60_000; // everything slower in debug mode
|
||||
return 20_000;
|
||||
}
|
||||
|
||||
@@ -450,7 +449,7 @@ async function runTests() {
|
||||
|
||||
if (parallelism > 1) {
|
||||
console.log(grouptitle);
|
||||
result = await fn(index);
|
||||
result = await fn();
|
||||
} else {
|
||||
result = await startGroup(grouptitle, fn);
|
||||
}
|
||||
@@ -470,7 +469,6 @@ async function runTests() {
|
||||
const label = `${getAnsi(color)}[${index}/${total}] ${title} - ${error}${getAnsi("reset")}`;
|
||||
startGroup(label, () => {
|
||||
if (parallelism > 1) return;
|
||||
if (!isCI) return;
|
||||
process.stderr.write(stdoutPreview);
|
||||
});
|
||||
|
||||
@@ -581,11 +579,8 @@ async function runTests() {
|
||||
const title = relative(cwd, absoluteTestPath).replaceAll(sep, "/");
|
||||
if (isNodeTest(testPath)) {
|
||||
const testContent = readFileSync(absoluteTestPath, "utf-8");
|
||||
let runWithBunTest = title.includes("needs-test") || testContent.includes("node:test");
|
||||
// don't wanna have a filter for includes("bun:test") but these need our mocks
|
||||
runWithBunTest ||= title === "test/js/node/test/parallel/test-fs-append-file-flush.js";
|
||||
runWithBunTest ||= title === "test/js/node/test/parallel/test-fs-write-file-flush.js";
|
||||
runWithBunTest ||= title === "test/js/node/test/parallel/test-fs-write-stream-flush.js";
|
||||
const runWithBunTest =
|
||||
title.includes("needs-test") || testContent.includes("bun:test") || testContent.includes("node:test");
|
||||
const subcommand = runWithBunTest ? "test" : "run";
|
||||
const env = {
|
||||
FORCE_COLOR: "0",
|
||||
@@ -673,9 +668,7 @@ async function runTests() {
|
||||
const title = join(relative(cwd, vendorPath), testPath).replace(/\\/g, "/");
|
||||
|
||||
if (testRunner === "bun") {
|
||||
await runTest(title, index =>
|
||||
spawnBunTest(execPath, testPath, { cwd: vendorPath, env: { TEST_SERIAL_ID: index } }),
|
||||
);
|
||||
await runTest(title, () => spawnBunTest(execPath, testPath, { cwd: vendorPath }));
|
||||
} else {
|
||||
const testRunnerPath = join(cwd, "test", "runners", `${testRunner}.ts`);
|
||||
if (!existsSync(testRunnerPath)) {
|
||||
@@ -1302,7 +1295,6 @@ async function spawnBun(execPath, { args, cwd, timeout, env, stdout, stderr }) {
|
||||
* @param {object} [opts]
|
||||
* @param {string} [opts.cwd]
|
||||
* @param {string[]} [opts.args]
|
||||
* @param {object} [opts.env]
|
||||
* @returns {Promise<TestResult>}
|
||||
*/
|
||||
async function spawnBunTest(execPath, testPath, opts = { cwd }) {
|
||||
@@ -1336,7 +1328,6 @@ async function spawnBunTest(execPath, testPath, opts = { cwd }) {
|
||||
|
||||
const env = {
|
||||
GITHUB_ACTIONS: "true", // always true so annotations are parsed
|
||||
...opts["env"],
|
||||
};
|
||||
if ((basename(execPath).includes("asan") || !isCI) && shouldValidateExceptions(relative(cwd, absPath))) {
|
||||
env.BUN_JSC_validateExceptionChecks = "1";
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# This script updates SQLite amalgamation files with the required compiler flags.
|
||||
# It downloads the SQLite source, configures it with necessary flags, builds the
|
||||
# amalgamation, and copies the generated files to the Bun source tree.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/update-sqlite-amalgamation.sh <version_number> <year>
|
||||
#
|
||||
# Example:
|
||||
# ./scripts/update-sqlite-amalgamation.sh 3500400 2025
|
||||
#
|
||||
# The version number is a 7-digit SQLite version (e.g., 3500400 for 3.50.4)
|
||||
# The year is the release year found in the download URL
|
||||
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Usage: $0 <version_number> <year>"
|
||||
echo "Example: $0 3500400 2025"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION_NUM="$1"
|
||||
YEAR="$2"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
# Create temporary directory
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$TEMP_DIR"' EXIT
|
||||
|
||||
cd "$TEMP_DIR"
|
||||
|
||||
echo "Downloading SQLite source version $VERSION_NUM from year $YEAR..."
|
||||
DOWNLOAD_URL="https://sqlite.org/$YEAR/sqlite-src-$VERSION_NUM.zip"
|
||||
echo "URL: $DOWNLOAD_URL"
|
||||
|
||||
wget -q "$DOWNLOAD_URL"
|
||||
unzip -q "sqlite-src-$VERSION_NUM.zip"
|
||||
cd "sqlite-src-$VERSION_NUM"
|
||||
|
||||
echo "Configuring SQLite with required flags..."
|
||||
# These flags must be set during amalgamation generation for them to take effect
|
||||
# in the parser and other compile-time generated code
|
||||
CFLAGS="-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 -DSQLITE_ENABLE_COLUMN_METADATA=1"
|
||||
./configure CFLAGS="$CFLAGS" > /dev/null 2>&1
|
||||
|
||||
echo "Building amalgamation..."
|
||||
make sqlite3.c > /dev/null 2>&1
|
||||
|
||||
echo "Copying files to Bun source tree..."
|
||||
# Add clang-format off directive and copy the amalgamation
|
||||
echo "// clang-format off" > "$REPO_ROOT/src/bun.js/bindings/sqlite/sqlite3.c"
|
||||
cat sqlite3.c >> "$REPO_ROOT/src/bun.js/bindings/sqlite/sqlite3.c"
|
||||
|
||||
echo "// clang-format off" > "$REPO_ROOT/src/bun.js/bindings/sqlite/sqlite3_local.h"
|
||||
cat sqlite3.h >> "$REPO_ROOT/src/bun.js/bindings/sqlite/sqlite3_local.h"
|
||||
|
||||
echo "✓ Successfully updated SQLite amalgamation files"
|
||||
@@ -1 +0,0 @@
|
||||
CLAUDE.md
|
||||
@@ -1,12 +0,0 @@
|
||||
## Zig
|
||||
|
||||
Syntax reminders:
|
||||
|
||||
- Private fields are fully supported in Zig with the `#` prefix. `struct { #foo: u32 };` makes a struct with a private field named `#foo`.
|
||||
- Decl literals in Zig are recommended. `const decl: Decl = .{ .binding = 0, .value = 0 };`
|
||||
|
||||
Conventions:
|
||||
|
||||
- Prefer `@import` at the **bottom** of the file.
|
||||
- It's `@import("bun")` not `@import("root").bun`
|
||||
- You must be patient with the build.
|
||||
@@ -199,6 +199,7 @@ pub const StandaloneModuleGraph = struct {
|
||||
store.ref();
|
||||
|
||||
const b = bun.webcore.Blob.initWithStore(store, globalObject).new();
|
||||
b.allocator = bun.default_allocator;
|
||||
|
||||
if (bun.http.MimeType.byExtensionNoDefault(bun.strings.trimLeadingChar(std.fs.path.extension(this.name), '.'))) |mime| {
|
||||
store.mime_type = mime;
|
||||
@@ -431,27 +432,6 @@ pub const StandaloneModuleGraph = struct {
|
||||
}
|
||||
};
|
||||
|
||||
if (comptime bun.Environment.is_canary or bun.Environment.isDebug) {
|
||||
if (bun.getenvZ("BUN_FEATURE_FLAG_DUMP_CODE")) |dump_code_dir| {
|
||||
const buf = bun.path_buffer_pool.get();
|
||||
defer bun.path_buffer_pool.put(buf);
|
||||
const dest_z = bun.path.joinAbsStringBufZ(dump_code_dir, buf, &.{dest_path}, .auto);
|
||||
|
||||
// Scoped block to handle dump failures without skipping module emission
|
||||
dump: {
|
||||
const file = bun.sys.File.makeOpen(dest_z, bun.O.WRONLY | bun.O.CREAT | bun.O.TRUNC, 0o664).unwrap() catch |err| {
|
||||
Output.prettyErrorln("<r><red>error<r><d>:<r> failed to open {s}: {s}", .{ dest_path, @errorName(err) });
|
||||
break :dump;
|
||||
};
|
||||
defer file.close();
|
||||
file.writeAll(output_file.value.buffer.bytes).unwrap() catch |err| {
|
||||
Output.prettyErrorln("<r><red>error<r><d>:<r> failed to write {s}: {s}", .{ dest_path, @errorName(err) });
|
||||
break :dump;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var module = CompiledModuleGraphFile{
|
||||
.name = string_builder.fmtAppendCountZ("{s}{s}", .{
|
||||
prefix,
|
||||
@@ -540,10 +520,10 @@ pub const StandaloneModuleGraph = struct {
|
||||
|
||||
pub fn inject(bytes: []const u8, self_exe: [:0]const u8, inject_options: InjectOptions, target: *const CompileTarget) bun.FileDescriptor {
|
||||
var buf: bun.PathBuffer = undefined;
|
||||
var zname: [:0]const u8 = bun.fs.FileSystem.tmpname("bun-build", &buf, @as(u64, @bitCast(std.time.milliTimestamp()))) catch |err| {
|
||||
var zname: [:0]const u8 = bun.span(bun.fs.FileSystem.instance.tmpname("bun-build", &buf, @as(u64, @bitCast(std.time.milliTimestamp()))) catch |err| {
|
||||
Output.prettyErrorln("<r><red>error<r><d>:<r> failed to get temporary file name: {s}", .{@errorName(err)});
|
||||
return bun.invalid_fd;
|
||||
};
|
||||
});
|
||||
|
||||
const cleanup = struct {
|
||||
pub fn toClean(name: [:0]const u8, fd: bun.FileDescriptor) void {
|
||||
|
||||
@@ -919,8 +919,6 @@ pub const Default = struct {
|
||||
_ = self;
|
||||
return c_allocator;
|
||||
}
|
||||
|
||||
pub const deinit = void;
|
||||
};
|
||||
|
||||
const basic = if (bun.use_mimalloc)
|
||||
|
||||
@@ -94,8 +94,6 @@ const BorrowedHeap = if (safety_checks) *DebugHeap else *mimalloc.Heap;
|
||||
const DebugHeap = struct {
|
||||
inner: *mimalloc.Heap,
|
||||
thread_lock: bun.safety.ThreadLock,
|
||||
|
||||
pub const deinit = void;
|
||||
};
|
||||
|
||||
threadlocal var thread_heap: if (safety_checks) ?DebugHeap else void = if (safety_checks) null;
|
||||
|
||||
@@ -506,12 +506,6 @@ pub fn AllocationScopeIn(comptime Allocator: type) type {
|
||||
pub fn setPointerExtra(self: Self, ptr: *anyopaque, extra: Extra) void {
|
||||
return self.borrow().setPointerExtra(ptr, extra);
|
||||
}
|
||||
|
||||
pub fn leakSlice(self: Self, memory: anytype) void {
|
||||
if (comptime !Self.enabled) return;
|
||||
_ = @typeInfo(@TypeOf(memory)).pointer;
|
||||
self.trackExternalFree(memory, null) catch @panic("tried to free memory that was not allocated by the allocation scope");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -216,7 +216,8 @@ pub extern fn mi_new_reallocn(p: ?*anyopaque, newcount: usize, size: usize) ?*an
|
||||
pub const MI_SMALL_WSIZE_MAX = @as(c_int, 128);
|
||||
pub const MI_SMALL_SIZE_MAX = MI_SMALL_WSIZE_MAX * @import("std").zig.c_translation.sizeof(?*anyopaque);
|
||||
pub const MI_ALIGNMENT_MAX = (@as(c_int, 16) * @as(c_int, 1024)) * @as(c_ulong, 1024);
|
||||
pub const MI_MAX_ALIGN_SIZE = 16;
|
||||
|
||||
const MI_MAX_ALIGN_SIZE = 16;
|
||||
|
||||
pub fn mustUseAlignedAlloc(alignment: std.mem.Alignment) bool {
|
||||
return alignment.toByteUnits() > MI_MAX_ALIGN_SIZE;
|
||||
|
||||
@@ -112,7 +112,6 @@ pub const Features = struct {
|
||||
pub var unsupported_uv_function: usize = 0;
|
||||
pub var exited: usize = 0;
|
||||
pub var yarn_migration: usize = 0;
|
||||
pub var pnpm_migration: usize = 0;
|
||||
pub var yaml_parse: usize = 0;
|
||||
|
||||
comptime {
|
||||
|
||||
@@ -321,9 +321,8 @@ pub const ByteWriter = Writer(*std.io.FixedBufferStream([]u8));
|
||||
pub const FileWriter = Writer(std.fs.File);
|
||||
|
||||
pub const api = struct {
|
||||
// these are in sync with BunLoaderType in headers-handwritten.h
|
||||
pub const Loader = enum(u8) {
|
||||
_none = 254,
|
||||
_none = 255,
|
||||
jsx = 1,
|
||||
js = 2,
|
||||
ts = 3,
|
||||
@@ -3053,8 +3052,173 @@ pub const api = struct {
|
||||
|
||||
security_scanner: ?[]const u8 = null,
|
||||
|
||||
minimum_release_age_ms: ?f64 = null,
|
||||
minimum_release_age_excludes: ?[]const []const u8 = null,
|
||||
pub fn decode(reader: anytype) anyerror!BunInstall {
|
||||
var this = std.mem.zeroes(BunInstall);
|
||||
|
||||
while (true) {
|
||||
switch (try reader.readByte()) {
|
||||
0 => {
|
||||
return this;
|
||||
},
|
||||
|
||||
1 => {
|
||||
this.default_registry = try reader.readValue(NpmRegistry);
|
||||
},
|
||||
2 => {
|
||||
this.scoped = try reader.readValue(NpmRegistryMap);
|
||||
},
|
||||
3 => {
|
||||
this.lockfile_path = try reader.readValue([]const u8);
|
||||
},
|
||||
4 => {
|
||||
this.save_lockfile_path = try reader.readValue([]const u8);
|
||||
},
|
||||
5 => {
|
||||
this.cache_directory = try reader.readValue([]const u8);
|
||||
},
|
||||
6 => {
|
||||
this.dry_run = try reader.readValue(bool);
|
||||
},
|
||||
7 => {
|
||||
this.force = try reader.readValue(bool);
|
||||
},
|
||||
8 => {
|
||||
this.save_dev = try reader.readValue(bool);
|
||||
},
|
||||
9 => {
|
||||
this.save_optional = try reader.readValue(bool);
|
||||
},
|
||||
10 => {
|
||||
this.save_peer = try reader.readValue(bool);
|
||||
},
|
||||
11 => {
|
||||
this.save_lockfile = try reader.readValue(bool);
|
||||
},
|
||||
12 => {
|
||||
this.production = try reader.readValue(bool);
|
||||
},
|
||||
13 => {
|
||||
this.save_yarn_lockfile = try reader.readValue(bool);
|
||||
},
|
||||
14 => {
|
||||
this.native_bin_links = try reader.readArray([]const u8);
|
||||
},
|
||||
15 => {
|
||||
this.disable_cache = try reader.readValue(bool);
|
||||
},
|
||||
16 => {
|
||||
this.disable_manifest_cache = try reader.readValue(bool);
|
||||
},
|
||||
17 => {
|
||||
this.global_dir = try reader.readValue([]const u8);
|
||||
},
|
||||
18 => {
|
||||
this.global_bin_dir = try reader.readValue([]const u8);
|
||||
},
|
||||
19 => {
|
||||
this.frozen_lockfile = try reader.readValue(bool);
|
||||
},
|
||||
20 => {
|
||||
this.exact = try reader.readValue(bool);
|
||||
},
|
||||
21 => {
|
||||
this.concurrent_scripts = try reader.readValue(u32);
|
||||
},
|
||||
else => {
|
||||
return error.InvalidMessage;
|
||||
},
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub fn encode(this: *const @This(), writer: anytype) anyerror!void {
|
||||
if (this.default_registry) |default_registry| {
|
||||
try writer.writeFieldID(1);
|
||||
try writer.writeValue(@TypeOf(default_registry), default_registry);
|
||||
}
|
||||
if (this.scoped) |scoped| {
|
||||
try writer.writeFieldID(2);
|
||||
try writer.writeValue(@TypeOf(scoped), scoped);
|
||||
}
|
||||
if (this.lockfile_path) |lockfile_path| {
|
||||
try writer.writeFieldID(3);
|
||||
try writer.writeValue(@TypeOf(lockfile_path), lockfile_path);
|
||||
}
|
||||
if (this.save_lockfile_path) |save_lockfile_path| {
|
||||
try writer.writeFieldID(4);
|
||||
try writer.writeValue(@TypeOf(save_lockfile_path), save_lockfile_path);
|
||||
}
|
||||
if (this.cache_directory) |cache_directory| {
|
||||
try writer.writeFieldID(5);
|
||||
try writer.writeValue(@TypeOf(cache_directory), cache_directory);
|
||||
}
|
||||
if (this.dry_run) |dry_run| {
|
||||
try writer.writeFieldID(6);
|
||||
try writer.writeInt(@as(u8, @intFromBool(dry_run)));
|
||||
}
|
||||
if (this.force) |force| {
|
||||
try writer.writeFieldID(7);
|
||||
try writer.writeInt(@as(u8, @intFromBool(force)));
|
||||
}
|
||||
if (this.save_dev) |save_dev| {
|
||||
try writer.writeFieldID(8);
|
||||
try writer.writeInt(@as(u8, @intFromBool(save_dev)));
|
||||
}
|
||||
if (this.save_optional) |save_optional| {
|
||||
try writer.writeFieldID(9);
|
||||
try writer.writeInt(@as(u8, @intFromBool(save_optional)));
|
||||
}
|
||||
if (this.save_peer) |save_peer| {
|
||||
try writer.writeFieldID(10);
|
||||
try writer.writeInt(@as(u8, @intFromBool(save_peer)));
|
||||
}
|
||||
if (this.save_lockfile) |save_lockfile| {
|
||||
try writer.writeFieldID(11);
|
||||
try writer.writeInt(@as(u8, @intFromBool(save_lockfile)));
|
||||
}
|
||||
if (this.production) |production| {
|
||||
try writer.writeFieldID(12);
|
||||
try writer.writeInt(@as(u8, @intFromBool(production)));
|
||||
}
|
||||
if (this.save_yarn_lockfile) |save_yarn_lockfile| {
|
||||
try writer.writeFieldID(13);
|
||||
try writer.writeInt(@as(u8, @intFromBool(save_yarn_lockfile)));
|
||||
}
|
||||
if (this.native_bin_links) |native_bin_links| {
|
||||
try writer.writeFieldID(14);
|
||||
try writer.writeArray([]const u8, native_bin_links);
|
||||
}
|
||||
if (this.disable_cache) |disable_cache| {
|
||||
try writer.writeFieldID(15);
|
||||
try writer.writeInt(@as(u8, @intFromBool(disable_cache)));
|
||||
}
|
||||
if (this.disable_manifest_cache) |disable_manifest_cache| {
|
||||
try writer.writeFieldID(16);
|
||||
try writer.writeInt(@as(u8, @intFromBool(disable_manifest_cache)));
|
||||
}
|
||||
if (this.global_dir) |global_dir| {
|
||||
try writer.writeFieldID(17);
|
||||
try writer.writeValue(@TypeOf(global_dir), global_dir);
|
||||
}
|
||||
if (this.global_bin_dir) |global_bin_dir| {
|
||||
try writer.writeFieldID(18);
|
||||
try writer.writeValue(@TypeOf(global_bin_dir), global_bin_dir);
|
||||
}
|
||||
if (this.frozen_lockfile) |frozen_lockfile| {
|
||||
try writer.writeFieldID(19);
|
||||
try writer.writeInt(@as(u8, @intFromBool(frozen_lockfile)));
|
||||
}
|
||||
if (this.exact) |exact| {
|
||||
try writer.writeFieldID(20);
|
||||
try writer.writeInt(@as(u8, @intFromBool(exact)));
|
||||
}
|
||||
if (this.concurrent_scripts) |concurrent_scripts| {
|
||||
try writer.writeFieldID(21);
|
||||
try writer.writeInt(concurrent_scripts);
|
||||
}
|
||||
try writer.endMessage();
|
||||
}
|
||||
};
|
||||
|
||||
pub const ClientServerModule = struct {
|
||||
|
||||
@@ -752,7 +752,7 @@ pub const Object = struct {
|
||||
pub fn hasProperty(obj: *const Object, name: string) bool {
|
||||
for (obj.properties.slice()) |prop| {
|
||||
const key = prop.key orelse continue;
|
||||
if (key.data != .e_string) continue;
|
||||
if (std.meta.activeTag(key.data) != .e_string) continue;
|
||||
if (key.data.e_string.eql(string, name)) return true;
|
||||
}
|
||||
return false;
|
||||
@@ -762,7 +762,7 @@ pub const Object = struct {
|
||||
for (obj.properties.slice(), 0..) |prop, i| {
|
||||
const value = prop.value orelse continue;
|
||||
const key = prop.key orelse continue;
|
||||
if (key.data != .e_string) continue;
|
||||
if (std.meta.activeTag(key.data) != .e_string) continue;
|
||||
const key_str = key.data.e_string;
|
||||
if (key_str.eql(string, name)) {
|
||||
return Expr.Query{
|
||||
|
||||
@@ -132,14 +132,14 @@ pub fn isEmpty(expr: Expr) bool {
|
||||
pub const Query = struct { expr: Expr, loc: logger.Loc, i: u32 = 0 };
|
||||
|
||||
pub fn hasAnyPropertyNamed(expr: *const Expr, comptime names: []const string) bool {
|
||||
if (expr.data != .e_object) return false;
|
||||
if (std.meta.activeTag(expr.data) != .e_object) return false;
|
||||
const obj = expr.data.e_object;
|
||||
if (obj.properties.len == 0) return false;
|
||||
|
||||
for (obj.properties.slice()) |prop| {
|
||||
if (prop.value == null) continue;
|
||||
const key = prop.key orelse continue;
|
||||
if (key.data != .e_string) continue;
|
||||
if (std.meta.activeTag(key.data) != .e_string) continue;
|
||||
const key_str = key.data.e_string;
|
||||
if (strings.eqlAnyComptime(key_str.data, names)) return true;
|
||||
}
|
||||
@@ -266,7 +266,7 @@ pub fn set(expr: *Expr, allocator: std.mem.Allocator, name: string, value: Expr)
|
||||
for (0..expr.data.e_object.properties.len) |i| {
|
||||
const prop = &expr.data.e_object.properties.ptr[i];
|
||||
const key = prop.key orelse continue;
|
||||
if (key.data != .e_string) continue;
|
||||
if (std.meta.activeTag(key.data) != .e_string) continue;
|
||||
if (key.data.e_string.eql(string, name)) {
|
||||
prop.value = value;
|
||||
return;
|
||||
@@ -288,7 +288,7 @@ pub fn setString(expr: *Expr, allocator: std.mem.Allocator, name: string, value:
|
||||
for (0..expr.data.e_object.properties.len) |i| {
|
||||
const prop = &expr.data.e_object.properties.ptr[i];
|
||||
const key = prop.key orelse continue;
|
||||
if (key.data != .e_string) continue;
|
||||
if (std.meta.activeTag(key.data) != .e_string) continue;
|
||||
if (key.data.e_string.eql(string, name)) {
|
||||
prop.value = Expr.init(E.String, .{ .data = value }, logger.Loc.Empty);
|
||||
return;
|
||||
@@ -310,15 +310,6 @@ pub fn getObject(expr: *const Expr, name: string) ?Expr {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn getBoolean(expr: *const Expr, name: string) ?bool {
|
||||
if (expr.asProperty(name)) |query| {
|
||||
if (query.expr.data == .e_boolean) {
|
||||
return query.expr.data.e_boolean.value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn getString(expr: *const Expr, allocator: std.mem.Allocator, name: string) OOM!?struct { string, logger.Loc } {
|
||||
if (asProperty(expr, name)) |q| {
|
||||
if (q.expr.asString(allocator)) |str| {
|
||||
@@ -394,7 +385,7 @@ pub fn getRope(self: *const Expr, rope: *const E.Object.Rope) ?E.Object.RopeQuer
|
||||
|
||||
// Making this comptime bloats the binary and doesn't seem to impact runtime performance.
|
||||
pub fn asProperty(expr: *const Expr, name: string) ?Query {
|
||||
if (expr.data != .e_object) return null;
|
||||
if (std.meta.activeTag(expr.data) != .e_object) return null;
|
||||
const obj = expr.data.e_object;
|
||||
if (obj.properties.len == 0) return null;
|
||||
|
||||
@@ -402,7 +393,7 @@ pub fn asProperty(expr: *const Expr, name: string) ?Query {
|
||||
}
|
||||
|
||||
pub fn asPropertyStringMap(expr: *const Expr, name: string, allocator: std.mem.Allocator) ?*bun.StringArrayHashMap(string) {
|
||||
if (expr.data != .e_object) return null;
|
||||
if (std.meta.activeTag(expr.data) != .e_object) return null;
|
||||
const obj_ = expr.data.e_object;
|
||||
if (obj_.properties.len == 0) return null;
|
||||
const query = obj_.asProperty(name) orelse return null;
|
||||
@@ -448,7 +439,7 @@ pub const ArrayIterator = struct {
|
||||
};
|
||||
|
||||
pub fn asArray(expr: *const Expr) ?ArrayIterator {
|
||||
if (expr.data != .e_array) return null;
|
||||
if (std.meta.activeTag(expr.data) != .e_array) return null;
|
||||
const array = expr.data.e_array;
|
||||
if (array.items.len == 0) return null;
|
||||
|
||||
@@ -464,7 +455,7 @@ pub inline fn asUtf8StringLiteral(expr: *const Expr) ?string {
|
||||
}
|
||||
|
||||
pub inline fn asStringLiteral(expr: *const Expr, allocator: std.mem.Allocator) ?string {
|
||||
if (expr.data != .e_string) return null;
|
||||
if (std.meta.activeTag(expr.data) != .e_string) return null;
|
||||
return expr.data.e_string.string(allocator) catch null;
|
||||
}
|
||||
|
||||
@@ -510,7 +501,7 @@ pub inline fn asStringZ(expr: *const Expr, allocator: std.mem.Allocator) OOM!?st
|
||||
pub fn asBool(
|
||||
expr: *const Expr,
|
||||
) ?bool {
|
||||
if (expr.data != .e_boolean) return null;
|
||||
if (std.meta.activeTag(expr.data) != .e_boolean) return null;
|
||||
|
||||
return expr.data.e_boolean.value;
|
||||
}
|
||||
@@ -531,7 +522,7 @@ const Serializable = struct {
|
||||
};
|
||||
|
||||
pub fn isMissing(a: *const Expr) bool {
|
||||
return a.data == Expr.Tag.e_missing;
|
||||
return std.meta.activeTag(a.data) == Expr.Tag.e_missing;
|
||||
}
|
||||
|
||||
// The goal of this function is to "rotate" the AST if it's possible to use the
|
||||
|
||||
@@ -325,7 +325,7 @@ pub const Runner = struct {
|
||||
return _entry.value_ptr.*;
|
||||
}
|
||||
|
||||
var blob_: ?*const jsc.WebCore.Blob = null;
|
||||
var blob_: ?jsc.WebCore.Blob = null;
|
||||
const mime_type: ?MimeType = null;
|
||||
|
||||
if (value.jsType() == .DOMWrapper) {
|
||||
@@ -334,23 +334,30 @@ pub const Runner = struct {
|
||||
} else if (value.as(jsc.WebCore.Request)) |resp| {
|
||||
return this.run(try resp.getBlobWithoutCallFrame(this.global));
|
||||
} else if (value.as(jsc.WebCore.Blob)) |resp| {
|
||||
blob_ = resp;
|
||||
blob_ = resp.*;
|
||||
blob_.?.allocator = null;
|
||||
} else if (value.as(bun.api.ResolveMessage) != null or value.as(bun.api.BuildMessage) != null) {
|
||||
_ = this.macro.vm.uncaughtException(this.global, value, false);
|
||||
return error.MacroFailed;
|
||||
}
|
||||
}
|
||||
|
||||
if (blob_) |blob| {
|
||||
return Expr.fromBlob(
|
||||
if (blob_) |*blob| {
|
||||
const out_expr = Expr.fromBlob(
|
||||
blob,
|
||||
this.allocator,
|
||||
mime_type,
|
||||
this.log,
|
||||
this.caller.loc,
|
||||
) catch {
|
||||
blob.deinit();
|
||||
return error.MacroFailed;
|
||||
};
|
||||
if (out_expr.data == .e_string) {
|
||||
blob.deinit();
|
||||
}
|
||||
|
||||
return out_expr;
|
||||
}
|
||||
|
||||
return Expr.init(E.String, E.String.empty, this.caller.loc);
|
||||
|
||||
123
src/ast/P.zig
123
src/ast/P.zig
@@ -170,21 +170,6 @@ pub fn NewParser_(
|
||||
dirname_ref: Ref = Ref.None,
|
||||
import_meta_ref: Ref = Ref.None,
|
||||
hmr_api_ref: Ref = Ref.None,
|
||||
|
||||
/// If bake is enabled and this is a server-side file, we want to use
|
||||
/// special `Response` class inside the `bun:app` built-in module to
|
||||
/// support syntax like `return Response(<jsx />, {...})` or `return Response.render("/my-page")`
|
||||
/// or `return Response.redirect("/other")`.
|
||||
///
|
||||
/// So we'll need to add a `import { Response } from 'bun:app'` to the
|
||||
/// top of the file
|
||||
///
|
||||
/// We need to declare this `response_ref` upfront
|
||||
response_ref: Ref = Ref.None,
|
||||
/// We also need to declare the namespace ref for `bun:app` and attach
|
||||
/// it to the symbol so the code generated `e_import_identifier`'s
|
||||
bun_app_namespace_ref: Ref = Ref.None,
|
||||
|
||||
scopes_in_order_visitor_index: usize = 0,
|
||||
has_classic_runtime_warned: bool = false,
|
||||
macro_call_count: MacroCallCountType = 0,
|
||||
@@ -969,7 +954,7 @@ pub fn NewParser_(
|
||||
switch (call.target.data) {
|
||||
.e_identifier => |ident| {
|
||||
// is this a require("something")
|
||||
if (strings.eqlComptime(p.loadNameFromRef(ident.ref), "require") and call.args.len == 1 and call.args.ptr[0].data == .e_string) {
|
||||
if (strings.eqlComptime(p.loadNameFromRef(ident.ref), "require") and call.args.len == 1 and std.meta.activeTag(call.args.ptr[0].data) == .e_string) {
|
||||
_ = p.addImportRecord(.require, loc, call.args.at(0).data.e_string.string(p.allocator) catch unreachable);
|
||||
}
|
||||
},
|
||||
@@ -985,7 +970,7 @@ pub fn NewParser_(
|
||||
switch (call.target.data) {
|
||||
.e_identifier => |ident| {
|
||||
// is this a require("something")
|
||||
if (strings.eqlComptime(p.loadNameFromRef(ident.ref), "require") and call.args.len == 1 and call.args.ptr[0].data == .e_string) {
|
||||
if (strings.eqlComptime(p.loadNameFromRef(ident.ref), "require") and call.args.len == 1 and std.meta.activeTag(call.args.ptr[0].data) == .e_string) {
|
||||
_ = p.addImportRecord(.require, loc, call.args.at(0).data.e_string.string(p.allocator) catch unreachable);
|
||||
}
|
||||
},
|
||||
@@ -1235,81 +1220,6 @@ pub fn NewParser_(
|
||||
};
|
||||
}
|
||||
|
||||
pub fn generateImportStmtForBakeResponse(
|
||||
noalias p: *P,
|
||||
parts: *ListManaged(js_ast.Part),
|
||||
) !void {
|
||||
bun.assert(!p.response_ref.isNull());
|
||||
bun.assert(!p.bun_app_namespace_ref.isNull());
|
||||
const allocator = p.allocator;
|
||||
|
||||
const import_path = "bun:app";
|
||||
|
||||
const import_record_i = p.addImportRecordByRange(.stmt, logger.Range.None, import_path);
|
||||
|
||||
var declared_symbols = DeclaredSymbol.List{};
|
||||
try declared_symbols.ensureTotalCapacity(allocator, 2);
|
||||
|
||||
var stmts = try allocator.alloc(Stmt, 1);
|
||||
|
||||
declared_symbols.appendAssumeCapacity(
|
||||
DeclaredSymbol{ .ref = p.bun_app_namespace_ref, .is_top_level = true },
|
||||
);
|
||||
try p.module_scope.generated.append(allocator, p.bun_app_namespace_ref);
|
||||
|
||||
const clause_items = try allocator.dupe(js_ast.ClauseItem, &.{
|
||||
js_ast.ClauseItem{
|
||||
.alias = "Response",
|
||||
.original_name = "Response",
|
||||
.alias_loc = logger.Loc{},
|
||||
.name = LocRef{ .ref = p.response_ref, .loc = logger.Loc{} },
|
||||
},
|
||||
});
|
||||
|
||||
declared_symbols.appendAssumeCapacity(DeclaredSymbol{
|
||||
.ref = p.response_ref,
|
||||
.is_top_level = true,
|
||||
});
|
||||
|
||||
// ensure every e_import_identifier holds the namespace
|
||||
if (p.options.features.hot_module_reloading) {
|
||||
const symbol = &p.symbols.items[p.response_ref.inner_index];
|
||||
bun.assert(symbol.namespace_alias != null);
|
||||
symbol.namespace_alias.?.import_record_index = import_record_i;
|
||||
}
|
||||
|
||||
try p.is_import_item.put(allocator, p.response_ref, {});
|
||||
try p.named_imports.put(allocator, p.response_ref, js_ast.NamedImport{
|
||||
.alias = "Response",
|
||||
.alias_loc = logger.Loc{},
|
||||
.namespace_ref = p.bun_app_namespace_ref,
|
||||
.import_record_index = import_record_i,
|
||||
});
|
||||
|
||||
stmts[0] = p.s(
|
||||
S.Import{
|
||||
.namespace_ref = p.bun_app_namespace_ref,
|
||||
.items = clause_items,
|
||||
.import_record_index = import_record_i,
|
||||
.is_single_line = true,
|
||||
},
|
||||
logger.Loc{},
|
||||
);
|
||||
|
||||
var import_records = try allocator.alloc(u32, 1);
|
||||
import_records[0] = import_record_i;
|
||||
|
||||
// This import is placed in a part before the main code, however
|
||||
// the bundler ends up re-ordering this to be after... The order
|
||||
// does not matter as ESM imports are always hoisted.
|
||||
parts.append(js_ast.Part{
|
||||
.stmts = stmts,
|
||||
.declared_symbols = declared_symbols,
|
||||
.import_record_indices = bun.BabyList(u32).fromOwnedSlice(import_records),
|
||||
.tag = .runtime,
|
||||
}) catch unreachable;
|
||||
}
|
||||
|
||||
pub fn generateImportStmt(
|
||||
noalias p: *P,
|
||||
import_path: string,
|
||||
@@ -1317,7 +1227,7 @@ pub fn NewParser_(
|
||||
parts: *ListManaged(js_ast.Part),
|
||||
symbols: anytype,
|
||||
additional_stmt: ?Stmt,
|
||||
comptime prefix: string,
|
||||
comptime suffix: string,
|
||||
comptime is_internal: bool,
|
||||
) anyerror!void {
|
||||
const allocator = p.allocator;
|
||||
@@ -1327,13 +1237,13 @@ pub fn NewParser_(
|
||||
import_record.path.namespace = "runtime";
|
||||
import_record.is_internal = is_internal;
|
||||
const import_path_identifier = try import_record.path.name.nonUniqueNameString(allocator);
|
||||
var namespace_identifier = try allocator.alloc(u8, import_path_identifier.len + prefix.len);
|
||||
var namespace_identifier = try allocator.alloc(u8, import_path_identifier.len + suffix.len);
|
||||
const clause_items = try allocator.alloc(js_ast.ClauseItem, imports.len);
|
||||
var stmts = try allocator.alloc(Stmt, 1 + if (additional_stmt != null) @as(usize, 1) else @as(usize, 0));
|
||||
var declared_symbols = DeclaredSymbol.List{};
|
||||
try declared_symbols.ensureTotalCapacity(allocator, imports.len + 1);
|
||||
bun.copy(u8, namespace_identifier, prefix);
|
||||
bun.copy(u8, namespace_identifier[prefix.len..], import_path_identifier);
|
||||
bun.copy(u8, namespace_identifier, suffix);
|
||||
bun.copy(u8, namespace_identifier[suffix.len..], import_path_identifier);
|
||||
|
||||
const namespace_ref = try p.newSymbol(.other, namespace_identifier);
|
||||
declared_symbols.appendAssumeCapacity(.{
|
||||
@@ -2104,25 +2014,6 @@ pub fn NewParser_(
|
||||
.wrap_exports_for_server_reference => {},
|
||||
}
|
||||
|
||||
// Server-side components:
|
||||
// Declare upfront the symbols for "Response" and "bun:app"
|
||||
switch (p.options.features.server_components) {
|
||||
.none, .client_side => {},
|
||||
else => {
|
||||
p.response_ref = try p.declareGeneratedSymbol(.import, "Response");
|
||||
p.bun_app_namespace_ref = try p.newSymbol(
|
||||
.other,
|
||||
"import_bun_app",
|
||||
);
|
||||
const symbol = &p.symbols.items[p.response_ref.inner_index];
|
||||
symbol.namespace_alias = .{
|
||||
.namespace_ref = p.bun_app_namespace_ref,
|
||||
.alias = "Response",
|
||||
.import_record_index = std.math.maxInt(u32),
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
if (p.options.features.hot_module_reloading) {
|
||||
p.hmr_api_ref = try p.declareCommonJSSymbol(.unbound, "hmr");
|
||||
}
|
||||
@@ -3180,7 +3071,7 @@ pub fn NewParser_(
|
||||
return ref;
|
||||
}
|
||||
|
||||
pub fn declareGeneratedSymbol(p: *P, kind: Symbol.Kind, comptime name: string) !Ref {
|
||||
fn declareGeneratedSymbol(p: *P, kind: Symbol.Kind, comptime name: string) !Ref {
|
||||
// The bundler runs the renamer, so it is ok to not append a hash
|
||||
if (p.options.bundle) {
|
||||
return try declareSymbolMaybeGenerated(p, kind, logger.Loc.Empty, name, true);
|
||||
|
||||
@@ -1357,16 +1357,6 @@ pub const Parser = struct {
|
||||
);
|
||||
}
|
||||
|
||||
// Bake: transform global `Response` to use `import { Response } from 'bun:app'`
|
||||
if (!p.response_ref.isNull() and is_used_and_has_no_links: {
|
||||
// We only want to do this if the symbol is used and didn't get
|
||||
// bound to some other value
|
||||
const symbol: *const Symbol = &p.symbols.items[p.response_ref.innerIndex()];
|
||||
break :is_used_and_has_no_links !symbol.hasLink() and symbol.use_count_estimate > 0;
|
||||
}) {
|
||||
try p.generateImportStmtForBakeResponse(&before);
|
||||
}
|
||||
|
||||
if (before.items.len > 0 or after.items.len > 0) {
|
||||
try parts.ensureUnusedCapacity(before.items.len + after.items.len);
|
||||
const parts_len = parts.items.len;
|
||||
|
||||
@@ -205,18 +205,9 @@ pub const SideEffects = enum(u1) {
|
||||
.bin_ge,
|
||||
=> {
|
||||
if (isPrimitiveWithSideEffects(bin.left.data) and isPrimitiveWithSideEffects(bin.right.data)) {
|
||||
const left_simplified = simplifyUnusedExpr(p, bin.left);
|
||||
const right_simplified = simplifyUnusedExpr(p, bin.right);
|
||||
|
||||
// If both sides would be removed entirely, we can return null to remove the whole expression
|
||||
if (left_simplified == null and right_simplified == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Otherwise, preserve at least the structure
|
||||
return Expr.joinWithComma(
|
||||
left_simplified orelse bin.left.toEmpty(),
|
||||
right_simplified orelse bin.right.toEmpty(),
|
||||
simplifyUnusedExpr(p, bin.left) orelse bin.left.toEmpty(),
|
||||
simplifyUnusedExpr(p, bin.right) orelse bin.right.toEmpty(),
|
||||
p.allocator,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -194,11 +194,11 @@ pub fn isTSArrowFnJSX(p: anytype) !bool {
|
||||
}
|
||||
if (p.lexer.token == .t_identifier) {
|
||||
try p.lexer.next();
|
||||
if (p.lexer.token == .t_comma or p.lexer.token == .t_equals) {
|
||||
if (p.lexer.token == .t_comma) {
|
||||
is_ts_arrow_fn = true;
|
||||
} else if (p.lexer.token == .t_extends) {
|
||||
try p.lexer.next();
|
||||
is_ts_arrow_fn = p.lexer.token != .t_equals and p.lexer.token != .t_greater_than and p.lexer.token != .t_slash;
|
||||
is_ts_arrow_fn = p.lexer.token != .t_equals and p.lexer.token != .t_greater_than;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1223,9 +1223,8 @@ pub fn ParseStmt(
|
||||
// "module Foo {}"
|
||||
// "declare module 'fs' {}"
|
||||
// "declare module 'fs';"
|
||||
if (!p.lexer.has_newline_before and
|
||||
(opts.is_module_scope or opts.is_namespace_scope) and
|
||||
(p.lexer.token == .t_identifier or (p.lexer.token == .t_string_literal and opts.is_typescript_declare)))
|
||||
if (((opts.is_module_scope or opts.is_namespace_scope) and (p.lexer.token == .t_identifier or
|
||||
(p.lexer.token == .t_string_literal and opts.is_typescript_declare))))
|
||||
{
|
||||
return p.parseTypeScriptNamespaceStmt(loc, opts);
|
||||
}
|
||||
|
||||
@@ -827,25 +827,24 @@ pub fn ParseSuffix(
|
||||
const optional_chain = &optional_chain_;
|
||||
while (true) {
|
||||
if (p.lexer.loc().start == p.after_arrow_body_loc.start) {
|
||||
defer left_and_out.* = left_value;
|
||||
next_token: switch (p.lexer.token) {
|
||||
.t_comma => {
|
||||
if (level.gte(.comma)) {
|
||||
return;
|
||||
}
|
||||
while (true) {
|
||||
switch (p.lexer.token) {
|
||||
.t_comma => {
|
||||
if (level.gte(.comma)) {
|
||||
break;
|
||||
}
|
||||
|
||||
try p.lexer.next();
|
||||
left.* = p.newExpr(E.Binary{
|
||||
.op = .bin_comma,
|
||||
.left = left.*,
|
||||
.right = try p.parseExpr(.comma),
|
||||
}, left.loc);
|
||||
|
||||
continue :next_token p.lexer.token;
|
||||
},
|
||||
else => {
|
||||
return;
|
||||
},
|
||||
try p.lexer.next();
|
||||
left.* = p.newExpr(E.Binary{
|
||||
.op = .bin_comma,
|
||||
.left = left.*,
|
||||
.right = try p.parseExpr(.comma),
|
||||
}, left.loc);
|
||||
},
|
||||
else => {
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -210,7 +210,7 @@ pub fn ParseTypescript(
|
||||
p.popScope();
|
||||
|
||||
if (!opts.is_typescript_declare) {
|
||||
name.ref = try p.declareSymbol(.ts_namespace, name_loc, name_text);
|
||||
name.ref = bun.handleOom(p.declareSymbol(.ts_namespace, name_loc, name_text));
|
||||
try p.ref_to_ts_namespace_member.put(p.allocator, name.ref.?, ns_member_data);
|
||||
}
|
||||
|
||||
|
||||
@@ -136,9 +136,6 @@ pub fn CreateBinaryExpressionVisitor(
|
||||
// "(0, this.fn)()" => "(0, this.fn)()"
|
||||
if (p.options.features.minify_syntax) {
|
||||
if (SideEffects.simplifyUnusedExpr(p, e_.left)) |simplified_left| {
|
||||
if (simplified_left.isEmpty()) {
|
||||
return e_.right;
|
||||
}
|
||||
e_.left = simplified_left;
|
||||
} else {
|
||||
// The left operand has no side effects, but we need to preserve
|
||||
|
||||
@@ -64,7 +64,7 @@ pub fn VisitExpr(
|
||||
}
|
||||
pub fn e_import_meta(p: *P, expr: Expr, in: ExprIn) Expr {
|
||||
// TODO: delete import.meta might not work
|
||||
const is_delete_target = p.delete_target == .e_import_meta;
|
||||
const is_delete_target = std.meta.activeTag(p.delete_target) == .e_import_meta;
|
||||
|
||||
if (p.define.dots.get("meta")) |meta| {
|
||||
for (meta) |define| {
|
||||
@@ -609,8 +609,7 @@ pub fn VisitExpr(
|
||||
p.delete_target = dot.data;
|
||||
}
|
||||
|
||||
// don't call visitExprInOut on `dot` because we've already visited `target` above!
|
||||
return dot;
|
||||
return p.visitExprInOut(dot, in);
|
||||
}
|
||||
|
||||
// Handle property rewrites to ensure things
|
||||
@@ -1445,20 +1444,9 @@ pub fn VisitExpr(
|
||||
// Why? Because we *don't* want to check for uses of
|
||||
// `useState` _inside_ React, and we know React uses
|
||||
// commonjs so it will never be `.e_import_identifier`.
|
||||
check_for_usestate: {
|
||||
if (e_.target.data == .e_import_identifier) break :check_for_usestate true;
|
||||
// Also check for `React.useState(...)`
|
||||
if (e_.target.data == .e_dot and e_.target.data.e_dot.target.data == .e_import_identifier) {
|
||||
const id = e_.target.data.e_dot.target.data.e_import_identifier;
|
||||
const name = p.symbols.items[id.ref.innerIndex()].original_name;
|
||||
break :check_for_usestate bun.strings.eqlComptime(name, "React");
|
||||
}
|
||||
break :check_for_usestate false;
|
||||
}) {
|
||||
e_.target.data == .e_import_identifier) {
|
||||
bun.assert(p.options.features.server_components.isServerSide());
|
||||
if (!bun.strings.startsWith(p.source.path.pretty, "node_modules") and
|
||||
bun.strings.eqlComptime(original_name, "useState"))
|
||||
{
|
||||
if (bun.strings.eqlComptime(original_name, "useState")) {
|
||||
p.log.addError(
|
||||
p.source,
|
||||
expr.loc,
|
||||
|
||||
@@ -1315,10 +1315,10 @@ pub fn VisitStmt(
|
||||
try p.top_level_enums.append(p.allocator, data.name.ref.?);
|
||||
}
|
||||
|
||||
try p.recordDeclaredSymbol(data.name.ref.?);
|
||||
try p.pushScopeForVisitPass(.entry, stmt.loc);
|
||||
bun.handleOom(p.recordDeclaredSymbol(data.name.ref.?));
|
||||
bun.handleOom(p.pushScopeForVisitPass(.entry, stmt.loc));
|
||||
defer p.popScope();
|
||||
try p.recordDeclaredSymbol(data.arg);
|
||||
bun.handleOom(p.recordDeclaredSymbol(data.arg));
|
||||
|
||||
const allocator = p.allocator;
|
||||
// Scan ahead for any variables inside this namespace. This must be done
|
||||
@@ -1327,7 +1327,7 @@ pub fn VisitStmt(
|
||||
// We need to convert the uses into property accesses on the namespace.
|
||||
for (data.values) |value| {
|
||||
if (value.ref.isValid()) {
|
||||
try p.is_exported_inside_namespace.put(allocator, value.ref, data.arg);
|
||||
bun.handleOom(p.is_exported_inside_namespace.put(allocator, value.ref, data.arg));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1336,7 +1336,7 @@ pub fn VisitStmt(
|
||||
// without initializers are initialized to undefined.
|
||||
var next_numeric_value: ?f64 = 0.0;
|
||||
|
||||
var value_exprs = try ListManaged(Expr).initCapacity(allocator, data.values.len);
|
||||
var value_exprs = bun.handleOom(ListManaged(Expr).initCapacity(allocator, data.values.len));
|
||||
|
||||
var all_values_are_pure = true;
|
||||
|
||||
|
||||
35
src/bake.zig
35
src/bake.zig
@@ -27,6 +27,9 @@ pub const UserOptions = struct {
|
||||
|
||||
/// Currently, this function must run at the top of the event loop.
|
||||
pub fn fromJS(config: JSValue, global: *jsc.JSGlobalObject) !UserOptions {
|
||||
if (!config.isObject()) {
|
||||
return global.throwInvalidArguments("'" ++ api_name ++ "' is not an object", .{});
|
||||
}
|
||||
var arena = std.heap.ArenaAllocator.init(bun.default_allocator);
|
||||
errdefer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
@@ -35,38 +38,6 @@ pub const UserOptions = struct {
|
||||
errdefer allocations.free();
|
||||
var bundler_options = SplitBundlerOptions.empty;
|
||||
|
||||
if (!config.isObject()) {
|
||||
// Allow users to do `export default { app: 'react' }` for convenience
|
||||
if (config.isString()) {
|
||||
const bunstr = try config.toBunString(global);
|
||||
defer bunstr.deref();
|
||||
const utf8_string = bunstr.toUTF8(bun.default_allocator);
|
||||
defer utf8_string.deinit();
|
||||
|
||||
if (bun.strings.eql(utf8_string.byteSlice(), "react")) {
|
||||
const root = bun.getcwdAlloc(alloc) catch |err| switch (err) {
|
||||
error.OutOfMemory => {
|
||||
return global.throwOutOfMemory();
|
||||
},
|
||||
else => {
|
||||
return global.throwError(err, "while querying current working directory");
|
||||
},
|
||||
};
|
||||
|
||||
const framework = try Framework.react(alloc);
|
||||
|
||||
return UserOptions{
|
||||
.arena = arena,
|
||||
.allocations = allocations,
|
||||
.root = root,
|
||||
.framework = framework,
|
||||
.bundler_options = bundler_options,
|
||||
};
|
||||
}
|
||||
}
|
||||
return global.throwInvalidArguments("'" ++ api_name ++ "' is not an object", .{});
|
||||
}
|
||||
|
||||
if (try config.getOptional(global, "bundlerOptions", JSValue)) |js_options| {
|
||||
if (try js_options.getOptional(global, "server", JSValue)) |server_options| {
|
||||
bundler_options.server = try BuildConfigSubset.fromJS(global, server_options);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user