mirror of
https://github.com/oven-sh/bun
synced 2026-02-16 22:01:47 +00:00
Compare commits
8 Commits
claude/fix
...
claude/fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46ee6da96d | ||
|
|
91a22fa674 | ||
|
|
ddefa11070 | ||
|
|
35f8154319 | ||
|
|
9d68ec882a | ||
|
|
1337f5dba4 | ||
|
|
56b5be4ba4 | ||
|
|
6c119d608e |
@@ -259,18 +259,13 @@ $ git clone https://github.com/oven-sh/WebKit vendor/WebKit
|
||||
# Check out the commit hash specified in `set(WEBKIT_VERSION <commit_hash>)` in cmake/tools/SetupWebKit.cmake
|
||||
$ git -C vendor/WebKit checkout <commit_hash>
|
||||
|
||||
# Make a debug build of JSC. This will output build artifacts in ./vendor/WebKit/WebKitBuild/Debug
|
||||
# Optionally, you can use `bun run jsc:build` for a release build
|
||||
$ bun run jsc:build:debug && rm vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/DerivedSources/inspector/InspectorProtocolObjects.h
|
||||
|
||||
# After an initial run of `make jsc-debug`, you can rebuild JSC with:
|
||||
$ cmake --build vendor/WebKit/WebKitBuild/Debug --target jsc && rm vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/DerivedSources/inspector/InspectorProtocolObjects.h
|
||||
|
||||
# Build bun with the local JSC build
|
||||
# Build bun with the local JSC build — this automatically configures and builds JSC
|
||||
$ bun run build:local
|
||||
```
|
||||
|
||||
Using `bun run build:local` will build Bun in the `./build/debug-local` directory (instead of `./build/debug`), you'll have to change a couple of places to use this new directory:
|
||||
`bun run build:local` handles everything: configuring JSC, building JSC, and building Bun. On subsequent runs, JSC will incrementally rebuild if any WebKit sources changed. `ninja -Cbuild/debug-local` also works after the first build, and will build Bun+JSC.
|
||||
|
||||
The build output goes to `./build/debug-local` (instead of `./build/debug`), so you'll need to update a couple of places:
|
||||
|
||||
- The first line in [`src/js/builtins.d.ts`](/src/js/builtins.d.ts)
|
||||
- The `CompilationDatabase` line in [`.clangd` config](/.clangd) should be `CompilationDatabase: build/debug-local`
|
||||
@@ -281,7 +276,7 @@ Note that the WebKit folder, including build artifacts, is 8GB+ in size.
|
||||
|
||||
If you are using a JSC debug build and using VScode, make sure to run the `C/C++: Select a Configuration` command to configure intellisense to find the debug headers.
|
||||
|
||||
Note that if you change make changes to our [WebKit fork](https://github.com/oven-sh/WebKit), you will also have to change [`SetupWebKit.cmake`](/cmake/tools/SetupWebKit.cmake) to point to the commit hash.
|
||||
Note that if you make changes to our [WebKit fork](https://github.com/oven-sh/WebKit), you will also have to change [`SetupWebKit.cmake`](/cmake/tools/SetupWebKit.cmake) to point to the commit hash.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -1273,13 +1273,18 @@ else()
|
||||
${WEBKIT_LIB_PATH}/libWTF.a
|
||||
${WEBKIT_LIB_PATH}/libJavaScriptCore.a
|
||||
)
|
||||
if(NOT APPLE OR EXISTS ${WEBKIT_LIB_PATH}/libbmalloc.a)
|
||||
if(WEBKIT_LOCAL OR NOT APPLE OR EXISTS ${WEBKIT_LIB_PATH}/libbmalloc.a)
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libbmalloc.a)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include_directories(${WEBKIT_INCLUDE_PATH})
|
||||
|
||||
# When building with a local WebKit, ensure JSC is built before compiling Bun's C++ sources.
|
||||
if(WEBKIT_LOCAL AND TARGET jsc)
|
||||
add_dependencies(${bun} jsc)
|
||||
endif()
|
||||
|
||||
# Include the generated dependency versions header
|
||||
include_directories(${CMAKE_BINARY_DIR})
|
||||
|
||||
@@ -1324,9 +1329,14 @@ if(LINUX)
|
||||
target_link_libraries(${bun} PUBLIC libatomic.so)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicudata.a)
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicui18n.a)
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicuuc.a)
|
||||
if(WEBKIT_LOCAL)
|
||||
find_package(ICU REQUIRED COMPONENTS data i18n uc)
|
||||
target_link_libraries(${bun} PRIVATE ICU::data ICU::i18n ICU::uc)
|
||||
else()
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicudata.a)
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicui18n.a)
|
||||
target_link_libraries(${bun} PRIVATE ${WEBKIT_LIB_PATH}/libicuuc.a)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
|
||||
@@ -3,6 +3,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")
|
||||
option(WEBKIT_BUILD_TYPE "The build type for local WebKit (defaults to CMAKE_BUILD_TYPE)")
|
||||
|
||||
if(NOT WEBKIT_VERSION)
|
||||
set(WEBKIT_VERSION 515344bc5d65aa2d4f9ff277b5fb944f0e051dcd)
|
||||
@@ -15,7 +16,10 @@ string(SUBSTRING ${WEBKIT_VERSION} 0 16 WEBKIT_VERSION_PREFIX)
|
||||
string(SUBSTRING ${WEBKIT_VERSION} 0 8 WEBKIT_VERSION_SHORT)
|
||||
|
||||
if(WEBKIT_LOCAL)
|
||||
set(DEFAULT_WEBKIT_PATH ${VENDOR_PATH}/WebKit/WebKitBuild/${CMAKE_BUILD_TYPE})
|
||||
if(NOT WEBKIT_BUILD_TYPE)
|
||||
set(WEBKIT_BUILD_TYPE ${CMAKE_BUILD_TYPE})
|
||||
endif()
|
||||
set(DEFAULT_WEBKIT_PATH ${VENDOR_PATH}/WebKit/WebKitBuild/${WEBKIT_BUILD_TYPE})
|
||||
else()
|
||||
set(DEFAULT_WEBKIT_PATH ${CACHE_PATH}/webkit-${WEBKIT_VERSION_PREFIX})
|
||||
endif()
|
||||
@@ -30,35 +34,153 @@ set(WEBKIT_INCLUDE_PATH ${WEBKIT_PATH}/include)
|
||||
set(WEBKIT_LIB_PATH ${WEBKIT_PATH}/lib)
|
||||
|
||||
if(WEBKIT_LOCAL)
|
||||
if(EXISTS ${WEBKIT_PATH}/cmakeconfig.h)
|
||||
# You may need to run:
|
||||
# make jsc-compile-debug jsc-copy-headers
|
||||
include_directories(
|
||||
${WEBKIT_PATH}
|
||||
${WEBKIT_PATH}/JavaScriptCore/Headers
|
||||
${WEBKIT_PATH}/JavaScriptCore/Headers/JavaScriptCore
|
||||
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders
|
||||
${WEBKIT_PATH}/bmalloc/Headers
|
||||
${WEBKIT_PATH}/WTF/Headers
|
||||
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders/JavaScriptCore
|
||||
${WEBKIT_PATH}/JavaScriptCore/DerivedSources/inspector
|
||||
)
|
||||
set(WEBKIT_SOURCE_DIR ${VENDOR_PATH}/WebKit)
|
||||
|
||||
# On Windows, add ICU include path from vcpkg
|
||||
if(WIN32)
|
||||
# Auto-detect vcpkg triplet
|
||||
set(VCPKG_ARM64_PATH ${VENDOR_PATH}/WebKit/vcpkg_installed/arm64-windows-static)
|
||||
set(VCPKG_X64_PATH ${VENDOR_PATH}/WebKit/vcpkg_installed/x64-windows-static)
|
||||
if(EXISTS ${VCPKG_ARM64_PATH})
|
||||
set(VCPKG_ICU_PATH ${VCPKG_ARM64_PATH})
|
||||
if(WIN32)
|
||||
# --- Build ICU from source (Windows only) ---
|
||||
# On macOS, ICU is found automatically (Homebrew icu4c for headers, system for libs).
|
||||
# On Linux, ICU is found automatically from system packages (e.g. libicu-dev).
|
||||
# On Windows, there is no system ICU, so we build it from source.
|
||||
set(ICU_LOCAL_ROOT ${VENDOR_PATH}/WebKit/WebKitBuild/icu)
|
||||
if(NOT EXISTS ${ICU_LOCAL_ROOT}/lib/sicudt.lib)
|
||||
message(STATUS "Building ICU from source...")
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|ARM64|aarch64|AARCH64")
|
||||
set(ICU_PLATFORM "ARM64")
|
||||
else()
|
||||
set(VCPKG_ICU_PATH ${VCPKG_X64_PATH})
|
||||
set(ICU_PLATFORM "x64")
|
||||
endif()
|
||||
if(EXISTS ${VCPKG_ICU_PATH}/include)
|
||||
include_directories(${VCPKG_ICU_PATH}/include)
|
||||
message(STATUS "Using ICU from vcpkg: ${VCPKG_ICU_PATH}/include")
|
||||
execute_process(
|
||||
COMMAND powershell -ExecutionPolicy Bypass -File
|
||||
${WEBKIT_SOURCE_DIR}/build-icu.ps1
|
||||
-Platform ${ICU_PLATFORM}
|
||||
-BuildType ${WEBKIT_BUILD_TYPE}
|
||||
-OutputDir ${ICU_LOCAL_ROOT}
|
||||
RESULT_VARIABLE ICU_BUILD_RESULT
|
||||
)
|
||||
if(NOT ICU_BUILD_RESULT EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to build ICU (exit code: ${ICU_BUILD_RESULT}).")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Copy ICU libs to WEBKIT_LIB_PATH with the names BuildBun.cmake expects.
|
||||
# Prebuilt WebKit uses 's' prefix (static) and 'd' suffix (debug).
|
||||
file(MAKE_DIRECTORY ${WEBKIT_LIB_PATH})
|
||||
if(WEBKIT_BUILD_TYPE STREQUAL "Debug")
|
||||
set(ICU_SUFFIX "d")
|
||||
else()
|
||||
set(ICU_SUFFIX "")
|
||||
endif()
|
||||
file(COPY_FILE ${ICU_LOCAL_ROOT}/lib/sicudt.lib ${WEBKIT_LIB_PATH}/sicudt${ICU_SUFFIX}.lib ONLY_IF_DIFFERENT)
|
||||
file(COPY_FILE ${ICU_LOCAL_ROOT}/lib/icuin.lib ${WEBKIT_LIB_PATH}/sicuin${ICU_SUFFIX}.lib ONLY_IF_DIFFERENT)
|
||||
file(COPY_FILE ${ICU_LOCAL_ROOT}/lib/icuuc.lib ${WEBKIT_LIB_PATH}/sicuuc${ICU_SUFFIX}.lib ONLY_IF_DIFFERENT)
|
||||
endif()
|
||||
|
||||
# --- Configure JSC ---
|
||||
message(STATUS "Configuring JSC from local WebKit source at ${WEBKIT_SOURCE_DIR}...")
|
||||
|
||||
set(JSC_CMAKE_ARGS
|
||||
-S ${WEBKIT_SOURCE_DIR}
|
||||
-B ${WEBKIT_PATH}
|
||||
-G ${CMAKE_GENERATOR}
|
||||
-DPORT=JSCOnly
|
||||
-DENABLE_STATIC_JSC=ON
|
||||
-DUSE_THIN_ARCHIVES=OFF
|
||||
-DENABLE_FTL_JIT=ON
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||||
-DUSE_BUN_JSC_ADDITIONS=ON
|
||||
-DUSE_BUN_EVENT_LOOP=ON
|
||||
-DENABLE_BUN_SKIP_FAILING_ASSERTIONS=ON
|
||||
-DALLOW_LINE_AND_COLUMN_NUMBER_IN_BUILTINS=ON
|
||||
-DCMAKE_BUILD_TYPE=${WEBKIT_BUILD_TYPE}
|
||||
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
|
||||
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
|
||||
-DENABLE_REMOTE_INSPECTOR=ON
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
# ICU paths and Windows-specific compiler/linker settings
|
||||
list(APPEND JSC_CMAKE_ARGS
|
||||
-DICU_ROOT=${ICU_LOCAL_ROOT}
|
||||
-DICU_LIBRARY=${ICU_LOCAL_ROOT}/lib
|
||||
-DICU_INCLUDE_DIR=${ICU_LOCAL_ROOT}/include
|
||||
-DCMAKE_LINKER=lld-link
|
||||
)
|
||||
# Static CRT and U_STATIC_IMPLEMENTATION
|
||||
if(WEBKIT_BUILD_TYPE STREQUAL "Debug")
|
||||
set(JSC_MSVC_RUNTIME "MultiThreadedDebug")
|
||||
else()
|
||||
set(JSC_MSVC_RUNTIME "MultiThreaded")
|
||||
endif()
|
||||
list(APPEND JSC_CMAKE_ARGS
|
||||
-DCMAKE_MSVC_RUNTIME_LIBRARY=${JSC_MSVC_RUNTIME}
|
||||
"-DCMAKE_C_FLAGS=/DU_STATIC_IMPLEMENTATION"
|
||||
"-DCMAKE_CXX_FLAGS=/DU_STATIC_IMPLEMENTATION /clang:-fno-c++-static-destructors"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENABLE_ASAN)
|
||||
list(APPEND JSC_CMAKE_ARGS -DENABLE_SANITIZERS=address)
|
||||
endif()
|
||||
|
||||
# Pass through ccache if available
|
||||
if(CMAKE_C_COMPILER_LAUNCHER)
|
||||
list(APPEND JSC_CMAKE_ARGS -DCMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER})
|
||||
endif()
|
||||
if(CMAKE_CXX_COMPILER_LAUNCHER)
|
||||
list(APPEND JSC_CMAKE_ARGS -DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER})
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} ${JSC_CMAKE_ARGS}
|
||||
RESULT_VARIABLE JSC_CONFIGURE_RESULT
|
||||
)
|
||||
if(NOT JSC_CONFIGURE_RESULT EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to configure JSC (exit code: ${JSC_CONFIGURE_RESULT}). "
|
||||
"Check the output above for errors.")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(JSC_BYPRODUCTS
|
||||
${WEBKIT_LIB_PATH}/JavaScriptCore.lib
|
||||
${WEBKIT_LIB_PATH}/WTF.lib
|
||||
${WEBKIT_LIB_PATH}/bmalloc.lib
|
||||
)
|
||||
else()
|
||||
set(JSC_BYPRODUCTS
|
||||
${WEBKIT_LIB_PATH}/libJavaScriptCore.a
|
||||
${WEBKIT_LIB_PATH}/libWTF.a
|
||||
${WEBKIT_LIB_PATH}/libbmalloc.a
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_custom_target(jsc ALL
|
||||
COMMAND ${CMAKE_COMMAND} --build ${WEBKIT_PATH} --config ${WEBKIT_BUILD_TYPE} --target jsc
|
||||
BYPRODUCTS ${JSC_BYPRODUCTS}
|
||||
COMMENT "Building JSC (${WEBKIT_PATH})"
|
||||
)
|
||||
else()
|
||||
add_custom_target(jsc ALL
|
||||
COMMAND ${CMAKE_COMMAND} --build ${WEBKIT_PATH} --config ${WEBKIT_BUILD_TYPE} --target jsc
|
||||
BYPRODUCTS ${JSC_BYPRODUCTS}
|
||||
COMMENT "Building JSC (${WEBKIT_PATH})"
|
||||
USES_TERMINAL
|
||||
)
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
${WEBKIT_PATH}
|
||||
${WEBKIT_PATH}/JavaScriptCore/Headers
|
||||
${WEBKIT_PATH}/JavaScriptCore/Headers/JavaScriptCore
|
||||
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders
|
||||
${WEBKIT_PATH}/bmalloc/Headers
|
||||
${WEBKIT_PATH}/WTF/Headers
|
||||
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders/JavaScriptCore
|
||||
)
|
||||
|
||||
# On Windows, add ICU headers from the local ICU build
|
||||
if(WIN32)
|
||||
include_directories(${ICU_LOCAL_ROOT}/include)
|
||||
endif()
|
||||
|
||||
# After this point, only prebuilt WebKit is supported
|
||||
|
||||
@@ -7,9 +7,9 @@ Bytecode caching is a build-time optimization that dramatically improves applica
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic usage
|
||||
### Basic usage (CommonJS)
|
||||
|
||||
Enable bytecode caching with the `--bytecode` flag:
|
||||
Enable bytecode caching with the `--bytecode` flag. Without `--format`, this defaults to CommonJS:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
bun build ./index.ts --target=bun --bytecode --outdir=./dist
|
||||
@@ -17,7 +17,7 @@ bun build ./index.ts --target=bun --bytecode --outdir=./dist
|
||||
|
||||
This generates two files:
|
||||
|
||||
- `dist/index.js` - Your bundled JavaScript
|
||||
- `dist/index.js` - Your bundled JavaScript (CommonJS)
|
||||
- `dist/index.jsc` - The bytecode cache file
|
||||
|
||||
At runtime, Bun automatically detects and uses the `.jsc` file:
|
||||
@@ -28,14 +28,24 @@ bun ./dist/index.js # Automatically uses index.jsc
|
||||
|
||||
### With standalone executables
|
||||
|
||||
When creating executables with `--compile`, bytecode is embedded into the binary:
|
||||
When creating executables with `--compile`, bytecode is embedded into the binary. Both ESM and CommonJS formats are supported:
|
||||
|
||||
```bash terminal icon="terminal"
|
||||
# ESM (requires --compile)
|
||||
bun build ./cli.ts --compile --bytecode --format=esm --outfile=mycli
|
||||
|
||||
# CommonJS (works with or without --compile)
|
||||
bun build ./cli.ts --compile --bytecode --outfile=mycli
|
||||
```
|
||||
|
||||
The resulting executable contains both the code and bytecode, giving you maximum performance in a single file.
|
||||
|
||||
### ESM bytecode
|
||||
|
||||
ESM bytecode requires `--compile` because Bun embeds module metadata (import/export information) in the compiled binary. This metadata allows the JavaScript engine to skip parsing entirely at runtime.
|
||||
|
||||
Without `--compile`, ESM bytecode would still require parsing the source to analyze module dependencies—defeating the purpose of bytecode caching.
|
||||
|
||||
### Combining with other optimizations
|
||||
|
||||
Bytecode works great with minification and source maps:
|
||||
@@ -90,35 +100,9 @@ Larger applications benefit more because they have more code to parse.
|
||||
- ❌ **Code that runs once**
|
||||
- ❌ **Development builds**
|
||||
- ❌ **Size-constrained environments**
|
||||
- ❌ **Code with top-level await** (not supported)
|
||||
|
||||
## Limitations
|
||||
|
||||
### CommonJS only
|
||||
|
||||
Bytecode caching currently works with CommonJS output format. Bun's bundler automatically converts most ESM code to CommonJS, but **top-level await** is the exception:
|
||||
|
||||
```js
|
||||
// This prevents bytecode caching
|
||||
const data = await fetch("https://api.example.com");
|
||||
export default data;
|
||||
```
|
||||
|
||||
**Why**: Top-level await requires async module evaluation, which can't be represented in CommonJS. The module graph becomes asynchronous, and the CommonJS wrapper function model breaks down.
|
||||
|
||||
**Workaround**: Move async initialization into a function:
|
||||
|
||||
```js
|
||||
async function init() {
|
||||
const data = await fetch("https://api.example.com");
|
||||
return data;
|
||||
}
|
||||
|
||||
export default init;
|
||||
```
|
||||
|
||||
Now the module exports a function that the consumer can await when needed.
|
||||
|
||||
### Version compatibility
|
||||
|
||||
Bytecode is **not portable across Bun versions**. The bytecode format is tied to JavaScriptCore's internal representation, which changes between versions.
|
||||
@@ -236,8 +220,6 @@ It's normal for it it to log a cache miss multiple times since Bun doesn't curre
|
||||
- Compressing `.jsc` files for network transfer (gzip/brotli)
|
||||
- Evaluating if the startup performance gain is worth the size increase
|
||||
|
||||
**Top-level await**: Not supported. Refactor to use async initialization functions.
|
||||
|
||||
## What is bytecode?
|
||||
|
||||
When you run JavaScript, the JavaScript engine doesn't execute your source code directly. Instead, it goes through several steps:
|
||||
|
||||
@@ -322,10 +322,7 @@ Using bytecode compilation, `tsc` starts 2x faster:
|
||||
|
||||
Bytecode compilation moves parsing overhead for large input files from runtime to bundle time. Your app starts faster, in exchange for making the `bun build` command a little slower. It doesn't obscure source code.
|
||||
|
||||
<Warning>
|
||||
**Experimental:** Bytecode compilation is an experimental feature. Only `cjs` format is supported (which means no
|
||||
top-level-await). Let us know if you run into any issues!
|
||||
</Warning>
|
||||
<Note>Bytecode compilation supports both `cjs` and `esm` formats when used with `--compile`.</Note>
|
||||
|
||||
### What do these flags do?
|
||||
|
||||
|
||||
@@ -1508,22 +1508,43 @@ BuildArtifact (entry-point) {
|
||||
|
||||
## Bytecode
|
||||
|
||||
The `bytecode: boolean` option can be used to generate bytecode for any JavaScript/TypeScript entrypoints. This can greatly improve startup times for large applications. Only supported for `"cjs"` format, only supports `"target": "bun"` and dependent on a matching version of Bun. This adds a corresponding `.jsc` file for each entrypoint.
|
||||
The `bytecode: boolean` option can be used to generate bytecode for any JavaScript/TypeScript entrypoints. This can greatly improve startup times for large applications. Requires `"target": "bun"` and is dependent on a matching version of Bun.
|
||||
|
||||
- **CommonJS**: Works with or without `compile: true`. Generates a `.jsc` file alongside each entrypoint.
|
||||
- **ESM**: Requires `compile: true`. Bytecode and module metadata are embedded in the standalone executable.
|
||||
|
||||
Without an explicit `format`, bytecode defaults to CommonJS.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="JavaScript">
|
||||
```ts title="build.ts" icon="/icons/typescript.svg"
|
||||
// CommonJS bytecode (generates .jsc files)
|
||||
await Bun.build({
|
||||
entrypoints: ["./index.tsx"],
|
||||
outdir: "./out",
|
||||
bytecode: true,
|
||||
})
|
||||
|
||||
// ESM bytecode (requires compile)
|
||||
await Bun.build({
|
||||
entrypoints: ["./index.tsx"],
|
||||
outfile: "./mycli",
|
||||
bytecode: true,
|
||||
format: "esm",
|
||||
compile: true,
|
||||
})
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="CLI">
|
||||
```bash terminal icon="terminal"
|
||||
# CommonJS bytecode
|
||||
bun build ./index.tsx --outdir ./out --bytecode
|
||||
|
||||
# ESM bytecode (requires --compile)
|
||||
bun build ./index.tsx --outfile ./mycli --bytecode --format=esm --compile
|
||||
```
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -1690,7 +1711,10 @@ interface BuildConfig {
|
||||
* start times, but will make the final output larger and slightly increase
|
||||
* memory usage.
|
||||
*
|
||||
* Bytecode is currently only supported for CommonJS (`format: "cjs"`).
|
||||
* - CommonJS: works with or without `compile: true`
|
||||
* - ESM: requires `compile: true`
|
||||
*
|
||||
* Without an explicit `format`, defaults to CommonJS.
|
||||
*
|
||||
* Must be `target: "bun"`
|
||||
* @default false
|
||||
|
||||
@@ -266,18 +266,13 @@ git clone https://github.com/oven-sh/WebKit vendor/WebKit
|
||||
# Check out the commit hash specified in `set(WEBKIT_VERSION <commit_hash>)` in cmake/tools/SetupWebKit.cmake
|
||||
git -C vendor/WebKit checkout <commit_hash>
|
||||
|
||||
# Make a debug build of JSC. This will output build artifacts in ./vendor/WebKit/WebKitBuild/Debug
|
||||
# Optionally, you can use `bun run jsc:build` for a release build
|
||||
bun run jsc:build:debug && rm vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/DerivedSources/inspector/InspectorProtocolObjects.h
|
||||
|
||||
# After an initial run of `make jsc-debug`, you can rebuild JSC with:
|
||||
cmake --build vendor/WebKit/WebKitBuild/Debug --target jsc && rm vendor/WebKit/WebKitBuild/Debug/JavaScriptCore/DerivedSources/inspector/InspectorProtocolObjects.h
|
||||
|
||||
# Build bun with the local JSC build
|
||||
# Build bun with the local JSC build — this automatically configures and builds JSC
|
||||
bun run build:local
|
||||
```
|
||||
|
||||
Using `bun run build:local` will build Bun in the `./build/debug-local` directory (instead of `./build/debug`), you'll have to change a couple of places to use this new directory:
|
||||
`bun run build:local` handles everything: configuring JSC, building JSC, and building Bun. On subsequent runs, JSC will incrementally rebuild if any WebKit sources changed. `ninja -Cbuild/debug-local` also works after the first build, and will build Bun+JSC.
|
||||
|
||||
The build output goes to `./build/debug-local` (instead of `./build/debug`), so you'll need to update a couple of places:
|
||||
|
||||
- The first line in `src/js/builtins.d.ts`
|
||||
- The `CompilationDatabase` line in `.clangd` config should be `CompilationDatabase: build/debug-local`
|
||||
@@ -288,7 +283,7 @@ Note that the WebKit folder, including build artifacts, is 8GB+ in size.
|
||||
|
||||
If you are using a JSC debug build and using VScode, make sure to run the `C/C++: Select a Configuration` command to configure intellisense to find the debug headers.
|
||||
|
||||
Note that if you change make changes to our [WebKit fork](https://github.com/oven-sh/WebKit), you will also have to change `SetupWebKit.cmake` to point to the commit hash.
|
||||
Note that if you make changes to our [WebKit fork](https://github.com/oven-sh/WebKit), you will also have to change `SetupWebKit.cmake` to point to the commit hash.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -50,7 +50,8 @@ bun build <entry points>
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="--format" type="string" default="esm">
|
||||
Module format of the output bundle. One of <code>esm</code>, <code>cjs</code>, or <code>iife</code>
|
||||
Module format of the output bundle. One of <code>esm</code>, <code>cjs</code>, or <code>iife</code>. Defaults to{" "}
|
||||
<code>cjs</code> when <code>--bytecode</code> is used.
|
||||
</ParamField>
|
||||
|
||||
### File Naming
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "bun",
|
||||
"version": "1.3.8",
|
||||
"version": "1.3.9",
|
||||
"workspaces": [
|
||||
"./packages/bun-types",
|
||||
"./packages/@types/bun"
|
||||
|
||||
5
packages/bun-types/bun.d.ts
vendored
5
packages/bun-types/bun.d.ts
vendored
@@ -2594,7 +2594,10 @@ declare module "bun" {
|
||||
* start times, but will make the final output larger and slightly increase
|
||||
* memory usage.
|
||||
*
|
||||
* Bytecode is currently only supported for CommonJS (`format: "cjs"`).
|
||||
* - CommonJS: works with or without `compile: true`
|
||||
* - ESM: requires `compile: true`
|
||||
*
|
||||
* Without an explicit `format`, defaults to CommonJS.
|
||||
*
|
||||
* Must be `target: "bun"`
|
||||
* @default false
|
||||
|
||||
10
packages/bun-types/s3.d.ts
vendored
10
packages/bun-types/s3.d.ts
vendored
@@ -1284,7 +1284,10 @@ declare module "bun" {
|
||||
*/
|
||||
list(
|
||||
input?: S3ListObjectsOptions | null,
|
||||
options?: Pick<S3Options, "accessKeyId" | "secretAccessKey" | "sessionToken" | "region" | "bucket" | "endpoint">,
|
||||
options?: Pick<
|
||||
S3Options,
|
||||
"accessKeyId" | "secretAccessKey" | "sessionToken" | "region" | "bucket" | "endpoint" | "requestPayer"
|
||||
>,
|
||||
): Promise<S3ListObjectsResponse>;
|
||||
|
||||
/**
|
||||
@@ -1320,7 +1323,10 @@ declare module "bun" {
|
||||
*/
|
||||
static list(
|
||||
input?: S3ListObjectsOptions | null,
|
||||
options?: Pick<S3Options, "accessKeyId" | "secretAccessKey" | "sessionToken" | "region" | "bucket" | "endpoint">,
|
||||
options?: Pick<
|
||||
S3Options,
|
||||
"accessKeyId" | "secretAccessKey" | "sessionToken" | "region" | "bucket" | "endpoint" | "requestPayer"
|
||||
>,
|
||||
): Promise<S3ListObjectsResponse>;
|
||||
}
|
||||
|
||||
|
||||
@@ -285,7 +285,9 @@ pub const Run = struct {
|
||||
.dir = cpu_prof_opts.dir,
|
||||
.md_format = cpu_prof_opts.md_format,
|
||||
.json_format = cpu_prof_opts.json_format,
|
||||
.interval = cpu_prof_opts.interval,
|
||||
};
|
||||
CPUProfiler.setSamplingInterval(cpu_prof_opts.interval);
|
||||
CPUProfiler.startCPUProfiler(vm.jsc_vm);
|
||||
bun.analytics.Features.cpu_profile += 1;
|
||||
}
|
||||
|
||||
@@ -1019,6 +1019,13 @@ pub const JSBundler = struct {
|
||||
}
|
||||
}
|
||||
|
||||
// ESM bytecode requires compile because module_info (import/export metadata)
|
||||
// is only available in compiled binaries. Without it, JSC must parse the file
|
||||
// twice (once for module analysis, once for bytecode), which is a deopt.
|
||||
if (this.bytecode and this.format == .esm and this.compile == null) {
|
||||
return globalThis.throwInvalidArguments("ESM bytecode requires compile: true. Use format: 'cjs' for bytecode without compile.", .{});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,12 @@
|
||||
|
||||
extern "C" void Bun__startCPUProfiler(JSC::VM* vm);
|
||||
extern "C" void Bun__stopCPUProfiler(JSC::VM* vm, BunString* outJSON, BunString* outText);
|
||||
extern "C" void Bun__setSamplingInterval(int intervalMicroseconds);
|
||||
|
||||
void Bun__setSamplingInterval(int intervalMicroseconds)
|
||||
{
|
||||
Bun::setSamplingInterval(intervalMicroseconds);
|
||||
}
|
||||
|
||||
namespace Bun {
|
||||
|
||||
|
||||
@@ -3,11 +3,17 @@ pub const CPUProfilerConfig = struct {
|
||||
dir: []const u8,
|
||||
md_format: bool = false,
|
||||
json_format: bool = false,
|
||||
interval: u32 = 1000,
|
||||
};
|
||||
|
||||
// C++ function declarations
|
||||
extern fn Bun__startCPUProfiler(vm: *jsc.VM) void;
|
||||
extern fn Bun__stopCPUProfiler(vm: *jsc.VM, outJSON: ?*bun.String, outText: ?*bun.String) void;
|
||||
extern fn Bun__setSamplingInterval(intervalMicroseconds: c_int) void;
|
||||
|
||||
pub fn setSamplingInterval(interval: u32) void {
|
||||
Bun__setSamplingInterval(@intCast(interval));
|
||||
}
|
||||
|
||||
pub fn startCPUProfiler(vm: *jsc.VM) void {
|
||||
Bun__startCPUProfiler(vm);
|
||||
|
||||
@@ -649,6 +649,10 @@ pub const PathLike = union(enum) {
|
||||
const normal = path_handler.normalizeBuf(resolve, b, .windows);
|
||||
return strings.toKernel32Path(@alignCast(std.mem.bytesAsSlice(u16, buf)), normal);
|
||||
}
|
||||
// Handle "." specially since normalizeStringBuf strips it to an empty string
|
||||
if (s.len == 1 and s[0] == '.') {
|
||||
return strings.toKernel32Path(@alignCast(std.mem.bytesAsSlice(u16, buf)), ".");
|
||||
}
|
||||
const normal = path_handler.normalizeStringBuf(s, b, true, .windows, false);
|
||||
return strings.toKernel32Path(@alignCast(std.mem.bytesAsSlice(u16, buf)), normal);
|
||||
}
|
||||
|
||||
@@ -427,7 +427,7 @@ pub const S3 = struct {
|
||||
.store = store, // store is needed in case of not found error
|
||||
.resolvedlistOptions = options,
|
||||
.global = globalThis,
|
||||
}), proxy);
|
||||
}), proxy, aws_options.request_payer);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -392,6 +392,7 @@ pub const Command = struct {
|
||||
enabled: bool = false,
|
||||
name: []const u8 = "",
|
||||
dir: []const u8 = "",
|
||||
interval: u32 = 1000,
|
||||
md_format: bool = false,
|
||||
json_format: bool = false,
|
||||
} = .{},
|
||||
|
||||
@@ -91,6 +91,7 @@ pub const runtime_params_ = [_]ParamType{
|
||||
clap.parseParam("--cpu-prof-name <STR> Specify the name of the CPU profile file") catch unreachable,
|
||||
clap.parseParam("--cpu-prof-dir <STR> Specify the directory where the CPU profile will be saved") catch unreachable,
|
||||
clap.parseParam("--cpu-prof-md Output CPU profile in markdown format (grep-friendly, designed for LLM analysis)") catch unreachable,
|
||||
clap.parseParam("--cpu-prof-interval <STR> Specify the sampling interval in microseconds for CPU profiling (default: 1000)") catch unreachable,
|
||||
clap.parseParam("--heap-prof Generate V8 heap snapshot on exit (.heapsnapshot)") catch unreachable,
|
||||
clap.parseParam("--heap-prof-name <STR> Specify the name of the heap profile file") catch unreachable,
|
||||
clap.parseParam("--heap-prof-dir <STR> Specify the directory where the heap profile will be saved") catch unreachable,
|
||||
@@ -178,7 +179,7 @@ pub const build_only_params = [_]ParamType{
|
||||
clap.parseParam("--sourcemap <STR>? Build with sourcemaps - 'linked', 'inline', 'external', or 'none'") catch unreachable,
|
||||
clap.parseParam("--banner <STR> Add a banner to the bundled output such as \"use client\"; for a bundle being used with RSCs") catch unreachable,
|
||||
clap.parseParam("--footer <STR> Add a footer to the bundled output such as // built with bun!") catch unreachable,
|
||||
clap.parseParam("--format <STR> Specifies the module format to build to. \"esm\", \"cjs\" and \"iife\" are supported. Defaults to \"esm\".") catch unreachable,
|
||||
clap.parseParam("--format <STR> Specifies the module format to build to. \"esm\", \"cjs\" and \"iife\" are supported. Defaults to \"esm\", or \"cjs\" with --bytecode.") catch unreachable,
|
||||
clap.parseParam("--root <STR> Root directory used for multiple entry points") catch unreachable,
|
||||
clap.parseParam("--splitting Enable code splitting") catch unreachable,
|
||||
clap.parseParam("--public-path <STR> A prefix to be appended to any import paths in bundled code") catch unreachable,
|
||||
@@ -864,6 +865,9 @@ pub fn parse(allocator: std.mem.Allocator, ctx: Command.Context, comptime cmd: C
|
||||
ctx.runtime_options.cpu_prof.md_format = cpu_prof_md_flag;
|
||||
// json_format is true if --cpu-prof is passed (regardless of --cpu-prof-md)
|
||||
ctx.runtime_options.cpu_prof.json_format = cpu_prof_flag;
|
||||
if (args.option("--cpu-prof-interval")) |interval_str| {
|
||||
ctx.runtime_options.cpu_prof.interval = std.fmt.parseInt(u32, interval_str, 10) catch 1000;
|
||||
}
|
||||
} else {
|
||||
// Warn if --cpu-prof-name or --cpu-prof-dir is used without a profiler flag
|
||||
if (args.option("--cpu-prof-name")) |_| {
|
||||
@@ -872,6 +876,9 @@ pub fn parse(allocator: std.mem.Allocator, ctx: Command.Context, comptime cmd: C
|
||||
if (args.option("--cpu-prof-dir")) |_| {
|
||||
Output.warn("--cpu-prof-dir requires --cpu-prof or --cpu-prof-md to be enabled", .{});
|
||||
}
|
||||
if (args.option("--cpu-prof-interval")) |_| {
|
||||
Output.warn("--cpu-prof-interval requires --cpu-prof or --cpu-prof-md to be enabled", .{});
|
||||
}
|
||||
}
|
||||
|
||||
const heap_prof_v8 = args.flag("--heap-prof");
|
||||
@@ -1339,10 +1346,18 @@ pub fn parse(allocator: std.mem.Allocator, ctx: Command.Context, comptime cmd: C
|
||||
}
|
||||
|
||||
ctx.bundler_options.output_format = format;
|
||||
// ESM bytecode is supported for --compile builds (module_info is embedded in binary)
|
||||
if (format != .cjs and format != .esm and ctx.bundler_options.bytecode) {
|
||||
Output.errGeneric("format must be 'cjs' or 'esm' when bytecode is true.", .{});
|
||||
Global.exit(1);
|
||||
if (ctx.bundler_options.bytecode) {
|
||||
if (format != .cjs and format != .esm) {
|
||||
Output.errGeneric("format must be 'cjs' or 'esm' when bytecode is true.", .{});
|
||||
Global.exit(1);
|
||||
}
|
||||
// ESM bytecode requires --compile because module_info (import/export metadata)
|
||||
// is only available in compiled binaries. Without it, JSC must parse the file
|
||||
// twice (once for module analysis, once for bytecode), which is a deopt.
|
||||
if (format == .esm and !ctx.bundler_options.compile) {
|
||||
Output.errGeneric("ESM bytecode requires --compile. Use --format=cjs for bytecode without --compile.", .{});
|
||||
Global.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,7 @@ pub fn listObjects(
|
||||
callback: *const fn (S3ListObjectsResult, *anyopaque) bun.JSTerminated!void,
|
||||
callback_context: *anyopaque,
|
||||
proxy_url: ?[]const u8,
|
||||
request_payer: bool,
|
||||
) bun.JSTerminated!void {
|
||||
var search_params: bun.ByteList = .{};
|
||||
|
||||
@@ -177,6 +178,7 @@ pub fn listObjects(
|
||||
.path = "",
|
||||
.method = .GET,
|
||||
.search_params = search_params.slice(),
|
||||
.request_payer = request_payer,
|
||||
}, true, null) catch |sign_err| {
|
||||
search_params.deinit(bun.default_allocator);
|
||||
|
||||
|
||||
@@ -280,6 +280,17 @@ pub const Interpreter = struct {
|
||||
exit_code: ?ExitCode = 0,
|
||||
this_jsvalue: JSValue = .zero,
|
||||
|
||||
/// Tracks which resources have been cleaned up to avoid double-free.
|
||||
/// When the interpreter finishes normally via finish(), it cleans up
|
||||
/// the runtime resources (IO, shell env) and sets this to .runtime_cleaned.
|
||||
/// The GC finalizer then only cleans up what remains (args, interpreter itself).
|
||||
cleanup_state: enum(u8) {
|
||||
/// Nothing has been cleaned up yet - need full cleanup
|
||||
needs_full_cleanup,
|
||||
/// Runtime resources (IO, shell env) have been cleaned up via finish()
|
||||
runtime_cleaned,
|
||||
} = .needs_full_cleanup,
|
||||
|
||||
__alloc_scope: if (bun.Environment.enableAllocScopes) bun.AllocationScope else void,
|
||||
estimated_size_for_gc: usize = 0,
|
||||
|
||||
@@ -1222,6 +1233,11 @@ pub const Interpreter = struct {
|
||||
}
|
||||
|
||||
fn #derefRootShellAndIOIfNeeded(this: *ThisInterpreter, free_buffered_io: bool) void {
|
||||
// Check if already cleaned up to prevent double-free
|
||||
if (this.cleanup_state == .runtime_cleaned) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (free_buffered_io) {
|
||||
// Can safely be called multiple times.
|
||||
if (this.root_shell._buffered_stderr == .owned) {
|
||||
@@ -1240,10 +1256,26 @@ pub const Interpreter = struct {
|
||||
}
|
||||
|
||||
this.this_jsvalue = .zero;
|
||||
// Mark that runtime resources have been cleaned up
|
||||
this.cleanup_state = .runtime_cleaned;
|
||||
}
|
||||
|
||||
fn deinitFromFinalizer(this: *ThisInterpreter) void {
|
||||
this.#derefRootShellAndIOIfNeeded(true);
|
||||
log("Interpreter(0x{x}) deinitFromFinalizer (cleanup_state={s})", .{ @intFromPtr(this), @tagName(this.cleanup_state) });
|
||||
|
||||
switch (this.cleanup_state) {
|
||||
.needs_full_cleanup => {
|
||||
// The interpreter never finished normally (e.g., early error or never started),
|
||||
// so we need to clean up IO and shell env here
|
||||
this.root_io.deref();
|
||||
this.root_shell.deinitImpl(false, true);
|
||||
},
|
||||
.runtime_cleaned => {
|
||||
// finish() already cleaned up IO and shell env via #derefRootShellAndIOIfNeeded,
|
||||
// nothing more to do for those resources
|
||||
},
|
||||
}
|
||||
|
||||
this.keep_alive.disable();
|
||||
this.args.deinit();
|
||||
this.allocator.destroy(this);
|
||||
|
||||
@@ -248,4 +248,131 @@ describe("s3 - Requester Pays", () => {
|
||||
|
||||
expect(url.searchParams.get("x-amz-request-payer")).toBeNull();
|
||||
});
|
||||
|
||||
it("should include x-amz-request-payer header in list() requests when requestPayer is true", async () => {
|
||||
let reqHeaders: Headers | undefined = undefined;
|
||||
let reqMethod: string | undefined = undefined;
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
async fetch(req) {
|
||||
reqHeaders = req.headers;
|
||||
reqMethod = req.method;
|
||||
return new Response(
|
||||
`<?xml version="1.0" encoding="UTF-8"?><ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
<Name>my_bucket</Name>
|
||||
</ListBucketResult>`,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/xml",
|
||||
},
|
||||
status: 200,
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const client = new S3Client({
|
||||
...s3Options,
|
||||
endpoint: server.url.href,
|
||||
requestPayer: true,
|
||||
});
|
||||
|
||||
await client.list({ prefix: "test/" });
|
||||
|
||||
expect(reqMethod).toBe("GET");
|
||||
expect(reqHeaders!.get("authorization")).toInclude("x-amz-request-payer");
|
||||
expect(reqHeaders!.get("x-amz-request-payer")).toBe("requester");
|
||||
});
|
||||
|
||||
it("should NOT include x-amz-request-payer header in list() requests when requestPayer is false", async () => {
|
||||
let reqHeaders: Headers | undefined = undefined;
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
async fetch(req) {
|
||||
reqHeaders = req.headers;
|
||||
return new Response(
|
||||
`<?xml version="1.0" encoding="UTF-8"?><ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
<Name>my_bucket</Name>
|
||||
</ListBucketResult>`,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/xml",
|
||||
},
|
||||
status: 200,
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const client = new S3Client({
|
||||
...s3Options,
|
||||
endpoint: server.url.href,
|
||||
requestPayer: false,
|
||||
});
|
||||
|
||||
await client.list();
|
||||
|
||||
expect(reqHeaders!.get("authorization")).not.toInclude("x-amz-request-payer");
|
||||
expect(reqHeaders!.get("x-amz-request-payer")).toBeNull();
|
||||
});
|
||||
|
||||
it("should include x-amz-request-payer header in static list() when requestPayer is true", async () => {
|
||||
let reqHeaders: Headers | undefined = undefined;
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
async fetch(req) {
|
||||
reqHeaders = req.headers;
|
||||
return new Response(
|
||||
`<?xml version="1.0" encoding="UTF-8"?><ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
<Name>my_bucket</Name>
|
||||
</ListBucketResult>`,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/xml",
|
||||
},
|
||||
status: 200,
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
await S3Client.list(null, {
|
||||
...s3Options,
|
||||
endpoint: server.url.href,
|
||||
requestPayer: true,
|
||||
});
|
||||
|
||||
expect(reqHeaders!.get("authorization")).toInclude("x-amz-request-payer");
|
||||
expect(reqHeaders!.get("x-amz-request-payer")).toBe("requester");
|
||||
});
|
||||
|
||||
it("should NOT include x-amz-request-payer header in static list() when requestPayer is false", async () => {
|
||||
let reqHeaders: Headers | undefined = undefined;
|
||||
using server = Bun.serve({
|
||||
port: 0,
|
||||
async fetch(req) {
|
||||
reqHeaders = req.headers;
|
||||
return new Response(
|
||||
`<?xml version="1.0" encoding="UTF-8"?><ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||
<Name>my_bucket</Name>
|
||||
</ListBucketResult>`,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/xml",
|
||||
},
|
||||
status: 200,
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
await S3Client.list(null, {
|
||||
...s3Options,
|
||||
endpoint: server.url.href,
|
||||
requestPayer: false,
|
||||
});
|
||||
|
||||
expect(reqHeaders!.get("authorization")).not.toInclude("x-amz-request-payer");
|
||||
expect(reqHeaders!.get("x-amz-request-payer")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
42
test/regression/issue/26631.test.ts
Normal file
42
test/regression/issue/26631.test.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { existsSync, statSync } from "node:fs";
|
||||
import { exists, stat } from "node:fs/promises";
|
||||
|
||||
// https://github.com/oven-sh/bun/issues/26631
|
||||
// Path resolution fails for current directory '.' on Windows
|
||||
|
||||
test("existsSync('.') should return true", () => {
|
||||
expect(existsSync(".")).toBe(true);
|
||||
});
|
||||
|
||||
test("exists('.') should return true", async () => {
|
||||
expect(await exists(".")).toBe(true);
|
||||
});
|
||||
|
||||
test("statSync('.') should return directory stats", () => {
|
||||
const stats = statSync(".");
|
||||
expect(stats.isDirectory()).toBe(true);
|
||||
});
|
||||
|
||||
test("stat('.') should return directory stats", async () => {
|
||||
const stats = await stat(".");
|
||||
expect(stats.isDirectory()).toBe(true);
|
||||
});
|
||||
|
||||
test("existsSync('..') should return true", () => {
|
||||
expect(existsSync("..")).toBe(true);
|
||||
});
|
||||
|
||||
test("exists('..') should return true", async () => {
|
||||
expect(await exists("..")).toBe(true);
|
||||
});
|
||||
|
||||
test("statSync('..') should return directory stats", () => {
|
||||
const stats = statSync("..");
|
||||
expect(stats.isDirectory()).toBe(true);
|
||||
});
|
||||
|
||||
test("stat('..') should return directory stats", async () => {
|
||||
const stats = await stat("..");
|
||||
expect(stats.isDirectory()).toBe(true);
|
||||
});
|
||||
Reference in New Issue
Block a user