mirror of
https://github.com/oven-sh/bun
synced 2026-02-25 11:07:19 +01:00
Compare commits
28 Commits
ciro/fix-h
...
ci-osxcros
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
237a15851b | ||
|
|
387800fe70 | ||
|
|
d109a9eabe | ||
|
|
83f430482c | ||
|
|
b7959cc1e2 | ||
|
|
ef092602c1 | ||
|
|
38d55fface | ||
|
|
06f9ea853e | ||
|
|
da9e842014 | ||
|
|
0f9f993d5e | ||
|
|
1b9041d1ab | ||
|
|
23afa08cb0 | ||
|
|
90beb5350d | ||
|
|
b0dd79e0a7 | ||
|
|
90a60c2c11 | ||
|
|
af4f1c7d39 | ||
|
|
2c1dea818c | ||
|
|
cc125b475f | ||
|
|
cbbf88f3a6 | ||
|
|
8064a55a48 | ||
|
|
0531d6756c | ||
|
|
6135b3dec9 | ||
|
|
b08dd8795e | ||
|
|
c1eba5886f | ||
|
|
fcca2cc398 | ||
|
|
dd32e6b416 | ||
|
|
b453360dff | ||
|
|
1476e4c958 |
@@ -743,6 +743,10 @@ function(register_cmake_command)
|
||||
list(APPEND MAKE_EFFECTIVE_ARGS --fresh)
|
||||
endif()
|
||||
|
||||
if(CMAKE_TOOLCHAIN_FILE)
|
||||
list(APPEND MAKE_EFFECTIVE_ARGS --toolchain=${CMAKE_TOOLCHAIN_FILE})
|
||||
endif()
|
||||
|
||||
register_command(
|
||||
COMMENT "Configuring ${MAKE_TARGET}"
|
||||
TARGET configure-${MAKE_TARGET}
|
||||
|
||||
@@ -26,11 +26,31 @@ if(RELEASE)
|
||||
list(APPEND LOLHTML_BUILD_ARGS --release)
|
||||
endif()
|
||||
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
if(ARCH STREQUAL "x64")
|
||||
set(RUST_ARCH x86_64)
|
||||
elseif(ARCH STREQUAL "arm64")
|
||||
set(RUST_ARCH aarch64)
|
||||
else()
|
||||
unsupported(ARCH)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
set(RUST_TARGET ${RUST_ARCH}-apple-darwin)
|
||||
elseif(LINUX)
|
||||
set(RUST_TARGET ${RUST_ARCH}-unknown-linux-gnu)
|
||||
elseif(WIN32)
|
||||
set(RUST_TARGET ${RUST_ARCH}-pc-windows-msvc)
|
||||
else()
|
||||
unsupported(CMAKE_SYSTEM_NAME)
|
||||
endif()
|
||||
|
||||
list(APPEND LOLHTML_BUILD_ARGS --target=${RUST_TARGET})
|
||||
endif()
|
||||
|
||||
# Windows requires unwind tables, apparently.
|
||||
if (NOT WIN32)
|
||||
# The encoded escape sequences are intentional. They're how you delimit multiple arguments in a single environment variable.
|
||||
# Also add rust optimization flag for smaller binary size, but not huge speed penalty.
|
||||
set(RUSTFLAGS "-Cpanic=abort-Cdebuginfo=0-Cforce-unwind-tables=no-Copt-level=s")
|
||||
if(NOT WIN32)
|
||||
set(RUST_FLAGS "-Cpanic=abort -Cdebuginfo=0 -Cforce-unwind-tables=no -Copt-level=s")
|
||||
endif()
|
||||
|
||||
register_command(
|
||||
@@ -48,12 +68,23 @@ register_command(
|
||||
CARGO_TERM_COLOR=always
|
||||
CARGO_TERM_VERBOSE=true
|
||||
CARGO_TERM_DIAGNOSTIC=true
|
||||
CARGO_ENCODED_RUSTFLAGS=${RUSTFLAGS}
|
||||
CARGO_HOME=${CARGO_HOME}
|
||||
RUSTUP_HOME=${RUSTUP_HOME}
|
||||
CC=${CMAKE_C_COMPILER}
|
||||
CFLAGS=${CMAKE_C_FLAGS}
|
||||
CXX=${CMAKE_CXX_COMPILER}
|
||||
CXXFLAGS=${CMAKE_CXX_FLAGS}
|
||||
AR=${CMAKE_AR}
|
||||
RUSTFLAGS=${RUST_FLAGS}
|
||||
)
|
||||
|
||||
target_link_libraries(${bun} PRIVATE ${LOLHTML_LIBRARY})
|
||||
if(BUN_LINK_ONLY)
|
||||
target_sources(${bun} PRIVATE ${LOLHTML_LIBRARY})
|
||||
endif()
|
||||
|
||||
# Notes for OSXCross, which doesn't work yet:
|
||||
# CFLAGS += --sysroot=${CMAKE_OSX_SYSROOT}
|
||||
# CXXFLAGS += --sysroot=${CMAKE_OSX_SYSROOT}
|
||||
# LDFLAGS += -F${CMAKE_OSX_SYSROOT}/System/Library/Frameworks
|
||||
# RUSTFLAGS += -C linker=${CMAKE_LINKER} -C link-arg=-F${CMAKE_OSX_SYSROOT}/System/Library/Frameworks -C link-arg=-L${CMAKE_OSX_SYSROOT}/usr/lib
|
||||
|
||||
28
cmake/toolchains/darwin-aarch64-osxcross.cmake
Normal file
28
cmake/toolchains/darwin-aarch64-osxcross.cmake
Normal file
@@ -0,0 +1,28 @@
|
||||
set(CMAKE_SYSTEM_NAME Darwin)
|
||||
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||
|
||||
set(CMAKE_C_COMPILER_WORKS ON)
|
||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||
|
||||
set(OSXCROSS_TARGET_DIR "/opt/osxcross")
|
||||
set(OSXCROSS_SDK "${OSXCROSS_TARGET_DIR}/SDK/MacOSX14.5.sdk")
|
||||
set(OSXCROSS_HOST "aarch64-apple-darwin23.5")
|
||||
|
||||
set(CMAKE_C_COMPILER "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-clang" CACHE FILEPATH "clang")
|
||||
set(CMAKE_CXX_COMPILER "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-clang++" CACHE FILEPATH "clang++")
|
||||
set(CMAKE_LINKER "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-ld" CACHE FILEPATH "ld")
|
||||
set(CMAKE_AR "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-ar" CACHE FILEPATH "ar")
|
||||
set(CMAKE_STRIP "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-strip" CACHE FILEPATH "strip")
|
||||
set(CMAKE_RANLIB "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-ranlib" CACHE FILEPATH "ranlib")
|
||||
set(CMAKE_NM "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-nm" CACHE FILEPATH "nm")
|
||||
set(CMAKE_DSYMUTIL "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-dsymutil" CACHE FILEPATH "dsymutil")
|
||||
set(CMAKE_INSTALL_NAME_TOOL "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-install_name_tool" CACHE FILEPATH "install_name_tool")
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH "${CMAKE_FIND_ROOT_PATH}" "${OSXCROSS_SDK}" "${OSXCROSS_TARGET_DIR}/macports/pkgs/opt/local")
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||
|
||||
set(ENV{PKG_CONFIG_LIBDIR} "${OSXCROSS_TARGET_DIR}/macports/pkgs/opt/local/lib/pkgconfig")
|
||||
set(ENV{PKG_CONFIG_SYSROOT_DIR} "${OSXCROSS_TARGET_DIR}/macports/pkgs")
|
||||
37
cmake/toolchains/darwin-x64-osxcross.cmake
Normal file
37
cmake/toolchains/darwin-x64-osxcross.cmake
Normal file
@@ -0,0 +1,37 @@
|
||||
set(CMAKE_SYSTEM_NAME Darwin)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x64)
|
||||
set(CMAKE_OSX_ARCHITECTURES x86_64)
|
||||
set(CMAKE_OSX_SDK_VERSION 14.5)
|
||||
set(CMAKE_OSX_KERNEL_VERSION 23.5)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 13.0)
|
||||
|
||||
set(CMAKE_C_COMPILER_WORKS ON)
|
||||
set(CMAKE_CXX_COMPILER_WORKS ON)
|
||||
|
||||
set(OSXCROSS_TARGET_DIR "/opt/osxcross")
|
||||
set(OSXCROSS_SDK "${OSXCROSS_TARGET_DIR}/SDK/MacOSX${CMAKE_OSX_SDK_VERSION}.sdk")
|
||||
set(OSXCROSS_HOST "x86_64-apple-darwin${CMAKE_OSX_KERNEL_VERSION}")
|
||||
|
||||
set(CMAKE_C_COMPILER "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-clang" CACHE FILEPATH "clang")
|
||||
set(CMAKE_CXX_COMPILER "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-clang++" CACHE FILEPATH "clang++")
|
||||
set(CMAKE_LINKER "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-ld" CACHE FILEPATH "ld")
|
||||
set(CMAKE_AR "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-ar" CACHE FILEPATH "ar")
|
||||
set(CMAKE_STRIP "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-strip" CACHE FILEPATH "strip")
|
||||
set(CMAKE_RANLIB "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-ranlib" CACHE FILEPATH "ranlib")
|
||||
set(CMAKE_NM "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-nm" CACHE FILEPATH "nm")
|
||||
set(CMAKE_DSYMUTIL "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-dsymutil" CACHE FILEPATH "dsymutil")
|
||||
set(CMAKE_INSTALL_NAME_TOOL "${OSXCROSS_TARGET_DIR}/bin/${OSXCROSS_HOST}-install_name_tool" CACHE FILEPATH "install_name_tool")
|
||||
|
||||
set(CMAKE_SYSROOT "${OSXCROSS_SDK}")
|
||||
set(CMAKE_OSX_SYSROOT "${OSXCROSS_SDK}")
|
||||
set(CMAKE_FIND_ROOT_PATH "${CMAKE_FIND_ROOT_PATH}" "${OSXCROSS_SDK}" "${OSXCROSS_TARGET_DIR}/macports/pkgs/opt/local")
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||
|
||||
# This fixes an issue where pthreads is not found with c-ares
|
||||
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
|
||||
|
||||
set(ENV{PKG_CONFIG_LIBDIR} "${OSXCROSS_TARGET_DIR}/macports/pkgs/opt/local/lib/pkgconfig")
|
||||
set(ENV{PKG_CONFIG_SYSROOT_DIR} "${OSXCROSS_TARGET_DIR}/macports/pkgs")
|
||||
@@ -671,7 +671,7 @@ _bun() {
|
||||
cmd)
|
||||
local -a scripts_list
|
||||
IFS=$'\n' scripts_list=($(SHELL=zsh bun getcompletes i))
|
||||
scripts="scripts:scripts:(($scripts_list))"
|
||||
scripts="scripts:scripts:((${scripts_list//:/\\\\:}))"
|
||||
IFS=$'\n' files_list=($(SHELL=zsh bun getcompletes j))
|
||||
|
||||
main_commands=(
|
||||
@@ -871,8 +871,8 @@ _bun_run_param_script_completion() {
|
||||
IFS=$'\n' scripts_list=($(SHELL=zsh bun getcompletes s))
|
||||
IFS=$'\n' bins=($(SHELL=zsh bun getcompletes b))
|
||||
|
||||
_alternative "scripts:scripts:(($scripts_list))"
|
||||
_alternative "bin:bin:(($bins))"
|
||||
_alternative "scripts:scripts:((${scripts_list//:/\\\\:}))"
|
||||
_alternative "bin:bin:((${bins//:/\\\\:}))"
|
||||
_alternative "files:file:_files -g '*.(js|ts|jsx|tsx|wasm)'"
|
||||
}
|
||||
|
||||
|
||||
@@ -536,12 +536,12 @@ Bun implements the following matchers. Full Jest compatibility is on the roadmap
|
||||
|
||||
---
|
||||
|
||||
- ❌
|
||||
- ✅
|
||||
- [`.toThrowErrorMatchingSnapshot()`](https://jestjs.io/docs/expect#tothrowerrormatchingsnapshothint)
|
||||
|
||||
---
|
||||
|
||||
- ❌
|
||||
- ✅
|
||||
- [`.toThrowErrorMatchingInlineSnapshot()`](https://jestjs.io/docs/expect#tothrowerrormatchinginlinesnapshotinlinesnapshot)
|
||||
|
||||
{% /table %}
|
||||
|
||||
34
packages/bun-types/test.d.ts
vendored
34
packages/bun-types/test.d.ts
vendored
@@ -1299,15 +1299,17 @@ declare module "bun:test" {
|
||||
* Asserts that a value matches the most recent inline snapshot.
|
||||
*
|
||||
* @example
|
||||
* expect("Hello").toMatchInlineSnapshot();
|
||||
* expect("Hello").toMatchInlineSnapshot(`"Hello"`);
|
||||
* @param value The latest snapshot value.
|
||||
*
|
||||
* @param value The latest automatically-updated snapshot value.
|
||||
*/
|
||||
toMatchInlineSnapshot(value?: string): void;
|
||||
/**
|
||||
* Asserts that a value matches the most recent inline snapshot.
|
||||
*
|
||||
* @example
|
||||
* expect("Hello").toMatchInlineSnapshot(`"Hello"`);
|
||||
* expect({ c: new Date() }).toMatchInlineSnapshot({ c: expect.any(Date) });
|
||||
* expect({ c: new Date() }).toMatchInlineSnapshot({ c: expect.any(Date) }, `
|
||||
* {
|
||||
* "v": Any<Date>,
|
||||
@@ -1315,9 +1317,35 @@ declare module "bun:test" {
|
||||
* `);
|
||||
*
|
||||
* @param propertyMatchers Object containing properties to match against the value.
|
||||
* @param hint Hint used to identify the snapshot in the snapshot file.
|
||||
* @param value The latest automatically-updated snapshot value.
|
||||
*/
|
||||
toMatchInlineSnapshot(propertyMatchers?: object, value?: string): void;
|
||||
/**
|
||||
* Asserts that a function throws an error matching the most recent snapshot.
|
||||
*
|
||||
* @example
|
||||
* function fail() {
|
||||
* throw new Error("Oops!");
|
||||
* }
|
||||
* expect(fail).toThrowErrorMatchingSnapshot();
|
||||
* expect(fail).toThrowErrorMatchingSnapshot("This one should say Oops!");
|
||||
*
|
||||
* @param value The latest automatically-updated snapshot value.
|
||||
*/
|
||||
toThrowErrorMatchingSnapshot(hint?: string): void;
|
||||
/**
|
||||
* Asserts that a function throws an error matching the most recent snapshot.
|
||||
*
|
||||
* @example
|
||||
* function fail() {
|
||||
* throw new Error("Oops!");
|
||||
* }
|
||||
* expect(fail).toThrowErrorMatchingInlineSnapshot();
|
||||
* expect(fail).toThrowErrorMatchingInlineSnapshot(`"Oops!"`);
|
||||
*
|
||||
* @param value The latest automatically-updated snapshot value.
|
||||
*/
|
||||
toThrowErrorMatchingInlineSnapshot(value?: string): void;
|
||||
/**
|
||||
* Asserts that an object matches a subset of properties.
|
||||
*
|
||||
|
||||
@@ -965,8 +965,10 @@ int bsd_connect_udp_socket(LIBUS_SOCKET_DESCRIPTOR fd, const char *host, int por
|
||||
char port_string[16];
|
||||
snprintf(port_string, 16, "%d", port);
|
||||
|
||||
if (getaddrinfo(host, port_string, &hints, &result)) {
|
||||
return -1;
|
||||
int gai_error = getaddrinfo(host, port_string, &hints, &result);
|
||||
|
||||
if (gai_error != 0) {
|
||||
return gai_error;
|
||||
}
|
||||
|
||||
if (result == NULL) {
|
||||
|
||||
@@ -29,7 +29,6 @@ endif()
|
||||
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
|
||||
add_compile_options(
|
||||
-fheinous-gnu-extensions
|
||||
-Wno-string-plus-int
|
||||
-Wno-deprecated-declarations
|
||||
)
|
||||
@@ -57,81 +56,100 @@ if(WIN32)
|
||||
)
|
||||
endif()
|
||||
|
||||
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/VERSION TCC_VERSION)
|
||||
option(BUILD_C2STR "Build the c2str utility" ON)
|
||||
option(BUILD_TCC "Build the tcc compiler" ON)
|
||||
|
||||
add_compile_definitions(TCC_VERSION=\"${TCC_VERSION}\")
|
||||
if(BUILD_C2STR)
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/c2str)
|
||||
add_custom_target(c2str.exe
|
||||
COMMAND
|
||||
${CMAKE_COMMAND}
|
||||
-S ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
-B ${CMAKE_CURRENT_BINARY_DIR}/c2str
|
||||
-G${CMAKE_GENERATOR}
|
||||
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
|
||||
-DBUILD_TCC=OFF
|
||||
COMMAND
|
||||
${CMAKE_COMMAND}
|
||||
--build ${CMAKE_CURRENT_BINARY_DIR}/c2str
|
||||
--target c2str.exe
|
||||
WORKING_DIRECTORY
|
||||
${CMAKE_CURRENT_BINARY_DIR}/c2str
|
||||
)
|
||||
else()
|
||||
add_executable(c2str.exe conftest.c)
|
||||
target_compile_options(c2str.exe PRIVATE -DC2STR)
|
||||
|
||||
execute_process(
|
||||
COMMAND git rev-parse --short HEAD
|
||||
OUTPUT_VARIABLE TCC_GITHASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
if(TCC_GITHASH)
|
||||
add_compile_definitions(TCC_GITHASH=\"${TCC_GITHASH}\")
|
||||
add_custom_command(
|
||||
TARGET
|
||||
c2str.exe POST_BUILD
|
||||
COMMAND
|
||||
c2str.exe include/tccdefs.h tccdefs_.h
|
||||
WORKING_DIRECTORY
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(TCC_SOURCES
|
||||
libtcc.c
|
||||
tccpp.c
|
||||
tccgen.c
|
||||
tccdbg.c
|
||||
tccelf.c
|
||||
tccasm.c
|
||||
tccrun.c
|
||||
)
|
||||
if(BUILD_TCC)
|
||||
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/VERSION TCC_VERSION)
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|ARM64")
|
||||
list(APPEND TCC_SOURCES
|
||||
arm64-gen.c
|
||||
arm64-link.c
|
||||
arm64-asm.c
|
||||
add_compile_definitions(TCC_VERSION=\"${TCC_VERSION}\")
|
||||
|
||||
execute_process(
|
||||
COMMAND git rev-parse --short HEAD
|
||||
OUTPUT_VARIABLE TCC_GITHASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|x64|amd64|AMD64")
|
||||
list(APPEND TCC_SOURCES
|
||||
x86_64-gen.c
|
||||
x86_64-link.c
|
||||
i386-asm.c
|
||||
|
||||
if(TCC_GITHASH)
|
||||
add_compile_definitions(TCC_GITHASH=\"${TCC_GITHASH}\")
|
||||
endif()
|
||||
|
||||
set(TCC_SOURCES
|
||||
libtcc.c
|
||||
tccpp.c
|
||||
tccgen.c
|
||||
tccdbg.c
|
||||
tccelf.c
|
||||
tccasm.c
|
||||
tccrun.c
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND TCC_SOURCES tccmacho.c)
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|ARM64")
|
||||
list(APPEND TCC_SOURCES
|
||||
arm64-gen.c
|
||||
arm64-link.c
|
||||
arm64-asm.c
|
||||
)
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|x64|amd64|AMD64")
|
||||
list(APPEND TCC_SOURCES
|
||||
x86_64-gen.c
|
||||
x86_64-link.c
|
||||
i386-asm.c
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND TCC_SOURCES tccpe.c)
|
||||
endif()
|
||||
if(APPLE)
|
||||
list(APPEND TCC_SOURCES tccmacho.c)
|
||||
endif()
|
||||
|
||||
add_executable(c2str.exe conftest.c)
|
||||
target_compile_options(c2str.exe PRIVATE -DC2STR)
|
||||
if(WIN32)
|
||||
list(APPEND TCC_SOURCES tccpe.c)
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
TARGET
|
||||
c2str.exe POST_BUILD
|
||||
COMMAND
|
||||
c2str.exe include/tccdefs.h tccdefs_.h
|
||||
WORKING_DIRECTORY
|
||||
add_library(tcc STATIC ${TCC_SOURCES})
|
||||
|
||||
if(BUILD_C2STR)
|
||||
add_dependencies(tcc c2str.exe)
|
||||
endif()
|
||||
|
||||
target_include_directories(tcc PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
add_library(tcc STATIC ${TCC_SOURCES})
|
||||
|
||||
add_custom_command(
|
||||
TARGET
|
||||
tcc PRE_BUILD
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E touch config.h
|
||||
WORKING_DIRECTORY
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
add_dependencies(tcc c2str.exe)
|
||||
|
||||
target_include_directories(tcc PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
# Version: 7
|
||||
# Version: 9
|
||||
|
||||
# A script that installs the dependencies needed to build and test Bun.
|
||||
# This should work on macOS and Linux with a POSIX shell.
|
||||
@@ -11,15 +11,17 @@
|
||||
# increment the version comment to indicate that a new image should be built.
|
||||
# Otherwise, the existing image will be retroactively updated.
|
||||
|
||||
pid=$$
|
||||
pid="$$"
|
||||
|
||||
print() {
|
||||
echo "$@"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo "error: $@" >&2
|
||||
kill -s TERM "$pid"
|
||||
print "error: $@" >&2
|
||||
if ! [ "$$" = "$pid" ]; then
|
||||
kill -s TERM "$pid"
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -39,24 +41,44 @@ execute_sudo() {
|
||||
}
|
||||
|
||||
execute_as_user() {
|
||||
sh="$(require sh)"
|
||||
|
||||
if [ "$sudo" = "1" ] || [ "$can_sudo" = "1" ]; then
|
||||
if [ -f "$(which sudo)" ]; then
|
||||
execute sudo -n -u "$user" /bin/sh -c "$*"
|
||||
execute sudo -n -u "$user" "$sh" -c "$*"
|
||||
elif [ -f "$(which doas)" ]; then
|
||||
execute doas -u "$user" /bin/sh -c "$*"
|
||||
execute doas -u "$user" "$sh" -c "$*"
|
||||
elif [ -f "$(which su)" ]; then
|
||||
execute su -s /bin/sh "$user" -c "$*"
|
||||
execute su -s "$sh" "$user" -c "$*"
|
||||
else
|
||||
execute /bin/sh -c "$*"
|
||||
execute "$sh" -c "$*"
|
||||
fi
|
||||
else
|
||||
execute /bin/sh -c "$*"
|
||||
execute "$sh" -c "$*"
|
||||
fi
|
||||
}
|
||||
|
||||
grant_to_user() {
|
||||
path="$1"
|
||||
execute_sudo chown -R "$user:$group" "$path"
|
||||
if ! [ -f "$path" ] && ! [ -d "$path" ]; then
|
||||
error "Could not find file or directory: \"$path\""
|
||||
fi
|
||||
|
||||
chown="$(require chown)"
|
||||
execute_sudo "$chown" -R "$user:$group" "$path"
|
||||
if ! [ "$user" = "$current_user" ] || ! [ "$group" = "$current_group" ]; then
|
||||
execute_sudo "$chown" -R "$current_user:$current_group" "$path"
|
||||
fi
|
||||
}
|
||||
|
||||
grant_to_everyone() {
|
||||
path="$1"
|
||||
if ! [ -f "$path" ] && ! [ -d "$path" ]; then
|
||||
error "Could not find file or directory: \"$path\""
|
||||
fi
|
||||
|
||||
chmod="$(require chmod)"
|
||||
execute_sudo "$chmod" 777 "$path"
|
||||
}
|
||||
|
||||
which() {
|
||||
@@ -68,15 +90,15 @@ require() {
|
||||
if ! [ -f "$path" ]; then
|
||||
error "Command \"$1\" is required, but is not installed."
|
||||
fi
|
||||
echo "$path"
|
||||
print "$path"
|
||||
}
|
||||
|
||||
fetch() {
|
||||
curl=$(which curl)
|
||||
curl="$(which curl)"
|
||||
if [ -f "$curl" ]; then
|
||||
execute "$curl" -fsSL "$1"
|
||||
else
|
||||
wget=$(which wget)
|
||||
wget="$(which wget)"
|
||||
if [ -f "$wget" ]; then
|
||||
execute "$wget" -qO- "$1"
|
||||
else
|
||||
@@ -85,78 +107,115 @@ fetch() {
|
||||
fi
|
||||
}
|
||||
|
||||
download_file() {
|
||||
url="$1"
|
||||
filename="${2:-$(basename "$url")}"
|
||||
tmp="$(execute mktemp -d)"
|
||||
execute chmod 755 "$tmp"
|
||||
compare_version() {
|
||||
if [ "$1" = "$2" ]; then
|
||||
print "0"
|
||||
elif [ "$1" = "$(echo -e "$1\n$2" | sort -V | head -n1)" ]; then
|
||||
print "-1"
|
||||
else
|
||||
print "1"
|
||||
fi
|
||||
}
|
||||
|
||||
path="$tmp/$filename"
|
||||
fetch "$url" >"$path"
|
||||
execute chmod 644 "$path"
|
||||
create_directory() {
|
||||
path="$1"
|
||||
path_dir="$path"
|
||||
while ! [ -d "$path_dir" ]; do
|
||||
path_dir="$(dirname "$path_dir")"
|
||||
done
|
||||
|
||||
path_needs_sudo="0"
|
||||
if ! [ -r "$path_dir" ] || ! [ -w "$path_dir" ]; then
|
||||
path_needs_sudo="1"
|
||||
fi
|
||||
|
||||
mkdir="$(require mkdir)"
|
||||
if [ "$path_needs_sudo" = "1" ]; then
|
||||
execute_sudo "$mkdir" -p "$path"
|
||||
else
|
||||
execute "$mkdir" -p "$path"
|
||||
fi
|
||||
|
||||
grant_to_user "$path"
|
||||
}
|
||||
|
||||
create_tmp_directory() {
|
||||
mktemp="$(require mktemp)"
|
||||
path="$(execute "$mktemp" -d)"
|
||||
grant_to_everyone "$path"
|
||||
print "$path"
|
||||
}
|
||||
|
||||
compare_version() {
|
||||
if [ "$1" = "$2" ]; then
|
||||
echo "0"
|
||||
elif [ "$1" = "$(echo -e "$1\n$2" | sort -V | head -n1)" ]; then
|
||||
echo "-1"
|
||||
else
|
||||
echo "1"
|
||||
create_file() {
|
||||
path="$1"
|
||||
path_dir="$(dirname "$path")"
|
||||
if ! [ -d "$path_dir" ]; then
|
||||
create_directory "$path_dir"
|
||||
fi
|
||||
|
||||
path_needs_sudo="0"
|
||||
if ! [ -r "$path" ] || ! [ -w "$path" ]; then
|
||||
path_needs_sudo="1"
|
||||
fi
|
||||
|
||||
if [ "$path_needs_sudo" = "1" ]; then
|
||||
execute_sudo touch "$path"
|
||||
else
|
||||
execute touch "$path"
|
||||
fi
|
||||
|
||||
content="$2"
|
||||
if [ -n "$content" ]; then
|
||||
append_file "$path" "$content"
|
||||
fi
|
||||
|
||||
grant_to_user "$path"
|
||||
}
|
||||
|
||||
append_to_file() {
|
||||
file="$1"
|
||||
content="$2"
|
||||
|
||||
file_needs_sudo="0"
|
||||
if [ -f "$file" ]; then
|
||||
if ! [ -r "$file" ] || ! [ -w "$file" ]; then
|
||||
file_needs_sudo="1"
|
||||
fi
|
||||
else
|
||||
execute_as_user mkdir -p "$(dirname "$file")"
|
||||
execute_as_user touch "$file"
|
||||
append_file() {
|
||||
path="$1"
|
||||
if ! [ -f "$path" ]; then
|
||||
create_file "$path"
|
||||
fi
|
||||
|
||||
echo "$content" | while read -r line; do
|
||||
if ! grep -q "$line" "$file"; then
|
||||
if [ "$file_needs_sudo" = "1" ]; then
|
||||
execute_sudo sh -c "echo '$line' >> '$file'"
|
||||
path_needs_sudo="0"
|
||||
if ! [ -r "$path" ] || ! [ -w "$path" ]; then
|
||||
path_needs_sudo="1"
|
||||
fi
|
||||
|
||||
content="$2"
|
||||
print "$content" | while read -r line; do
|
||||
if ! grep -q "$line" "$path"; then
|
||||
sh="$(require sh)"
|
||||
if [ "$path_needs_sudo" = "1" ]; then
|
||||
execute_sudo "$sh" -c "echo '$line' >> '$path'"
|
||||
else
|
||||
echo "$line" >>"$file"
|
||||
execute "$sh" -c "echo '$line' >> '$path'"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
append_to_file_sudo() {
|
||||
file="$1"
|
||||
content="$2"
|
||||
download_file() {
|
||||
file_url="$1"
|
||||
file_tmp_dir="$(create_tmp_directory)"
|
||||
file_tmp_path="$file_tmp_dir/$(basename "$file_url")"
|
||||
|
||||
if ! [ -f "$file" ]; then
|
||||
execute_sudo mkdir -p "$(dirname "$file")"
|
||||
execute_sudo touch "$file"
|
||||
fi
|
||||
|
||||
echo "$content" | while read -r line; do
|
||||
if ! grep -q "$line" "$file"; then
|
||||
echo "$line" | execute_sudo tee "$file" >/dev/null
|
||||
fi
|
||||
done
|
||||
fetch "$file_url" >"$file_tmp_path"
|
||||
grant_to_everyone "$file_tmp_path"
|
||||
|
||||
print "$file_tmp_path"
|
||||
}
|
||||
|
||||
append_to_profile() {
|
||||
content="$1"
|
||||
profiles=".profile .zprofile .bash_profile .bashrc .zshrc"
|
||||
for profile in $profiles; do
|
||||
file="$home/$profile"
|
||||
if [ "$ci" = "1" ] || [ -f "$file" ]; then
|
||||
append_to_file "$file" "$content"
|
||||
fi
|
||||
for profile_path in "$current_home/$profile" "$home/$profile"; do
|
||||
if [ "$ci" = "1" ] || [ -f "$profile_path" ]; then
|
||||
append_file "$profile_path" "$content"
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
@@ -167,7 +226,7 @@ append_to_path() {
|
||||
fi
|
||||
|
||||
append_to_profile "export PATH=\"$path:\$PATH\""
|
||||
export PATH="$path:$PATH"
|
||||
# export PATH="$path:$PATH"
|
||||
}
|
||||
|
||||
move_to_bin() {
|
||||
@@ -190,19 +249,22 @@ move_to_bin() {
|
||||
check_features() {
|
||||
print "Checking features..."
|
||||
|
||||
case "$CI" in
|
||||
true | 1)
|
||||
ci=1
|
||||
print "CI: enabled"
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$@" in
|
||||
*--ci*)
|
||||
ci=1
|
||||
print "CI: enabled"
|
||||
;;
|
||||
esac
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
*--ci*)
|
||||
ci=1
|
||||
print "CI: enabled"
|
||||
;;
|
||||
*--osxcross*)
|
||||
osxcross=1
|
||||
print "Cross-compiling to macOS: enabled"
|
||||
;;
|
||||
*--gcc-13*)
|
||||
gcc_version="13"
|
||||
print "GCC 13: enabled"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
check_operating_system() {
|
||||
@@ -211,17 +273,29 @@ check_operating_system() {
|
||||
|
||||
os="$("$uname" -s)"
|
||||
case "$os" in
|
||||
Linux*) os="linux" ;;
|
||||
Darwin*) os="darwin" ;;
|
||||
*) error "Unsupported operating system: $os" ;;
|
||||
Linux*)
|
||||
os="linux"
|
||||
;;
|
||||
Darwin*)
|
||||
os="darwin"
|
||||
;;
|
||||
*)
|
||||
error "Unsupported operating system: $os"
|
||||
;;
|
||||
esac
|
||||
print "Operating System: $os"
|
||||
|
||||
arch="$("$uname" -m)"
|
||||
case "$arch" in
|
||||
x86_64 | x64 | amd64) arch="x64" ;;
|
||||
aarch64 | arm64) arch="aarch64" ;;
|
||||
*) error "Unsupported architecture: $arch" ;;
|
||||
x86_64 | x64 | amd64)
|
||||
arch="x64"
|
||||
;;
|
||||
aarch64 | arm64)
|
||||
arch="aarch64"
|
||||
;;
|
||||
*)
|
||||
error "Unsupported architecture: $arch"
|
||||
;;
|
||||
esac
|
||||
print "Architecture: $arch"
|
||||
|
||||
@@ -235,7 +309,7 @@ check_operating_system() {
|
||||
abi="musl"
|
||||
alpine="$(cat /etc/alpine-release)"
|
||||
if [ "$alpine" ~ "_" ]; then
|
||||
release="$(echo "$alpine" | cut -d_ -f1)-edge"
|
||||
release="$(print "$alpine" | cut -d_ -f1)-edge"
|
||||
else
|
||||
release="$alpine"
|
||||
fi
|
||||
@@ -255,6 +329,7 @@ check_operating_system() {
|
||||
distro="$("$sw_vers" -productName)"
|
||||
release="$("$sw_vers" -productVersion)"
|
||||
fi
|
||||
|
||||
case "$arch" in
|
||||
x64)
|
||||
sysctl="$(which sysctl)"
|
||||
@@ -277,7 +352,7 @@ check_operating_system() {
|
||||
ldd="$(which ldd)"
|
||||
if [ -f "$ldd" ]; then
|
||||
ldd_version="$($ldd --version 2>&1)"
|
||||
abi_version="$(echo "$ldd_version" | grep -o -E '[0-9]+\.[0-9]+(\.[0-9]+)?' | head -n 1)"
|
||||
abi_version="$(print "$ldd_version" | grep -o -E '[0-9]+\.[0-9]+(\.[0-9]+)?' | head -n 1)"
|
||||
case "$ldd_version" in
|
||||
*musl*)
|
||||
abi="musl"
|
||||
@@ -394,6 +469,10 @@ check_user() {
|
||||
can_sudo=1
|
||||
print "Sudo: can be used"
|
||||
fi
|
||||
|
||||
current_user="$user"
|
||||
current_group="$group"
|
||||
current_home="$home"
|
||||
}
|
||||
|
||||
check_ulimit() {
|
||||
@@ -405,15 +484,12 @@ check_ulimit() {
|
||||
systemd_conf="/etc/systemd/system.conf"
|
||||
if [ -f "$systemd_conf" ]; then
|
||||
limits_conf="/etc/security/limits.d/99-unlimited.conf"
|
||||
if ! [ -f "$limits_conf" ]; then
|
||||
execute_sudo mkdir -p "$(dirname "$limits_conf")"
|
||||
execute_sudo touch "$limits_conf"
|
||||
fi
|
||||
create_file "$limits_conf"
|
||||
fi
|
||||
|
||||
limits="core data fsize memlock nofile rss stack cpu nproc as locks sigpending msgqueue"
|
||||
for limit in $limits; do
|
||||
limit_upper="$(echo "$limit" | tr '[:lower:]' '[:upper:]')"
|
||||
limit_upper="$(print "$limit" | tr '[:lower:]' '[:upper:]')"
|
||||
|
||||
limit_value="unlimited"
|
||||
case "$limit" in
|
||||
@@ -425,13 +501,13 @@ check_ulimit() {
|
||||
if [ -f "$limits_conf" ]; then
|
||||
limit_users="root *"
|
||||
for limit_user in $limit_users; do
|
||||
append_to_file "$limits_conf" "$limit_user soft $limit $limit_value"
|
||||
append_to_file "$limits_conf" "$limit_user hard $limit $limit_value"
|
||||
append_file "$limits_conf" "$limit_user soft $limit $limit_value"
|
||||
append_file "$limits_conf" "$limit_user hard $limit $limit_value"
|
||||
done
|
||||
fi
|
||||
|
||||
if [ -f "$systemd_conf" ]; then
|
||||
append_to_file "$systemd_conf" "DefaultLimit$limit_upper=$limit_value"
|
||||
append_file "$systemd_conf" "DefaultLimit$limit_upper=$limit_value"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -448,13 +524,13 @@ check_ulimit() {
|
||||
esac
|
||||
rc_ulimit="$rc_ulimit -$limit_flag $limit_value"
|
||||
done
|
||||
append_to_file "$rc_conf" "rc_ulimit=\"$rc_ulimit\""
|
||||
append_file "$rc_conf" "rc_ulimit=\"$rc_ulimit\""
|
||||
fi
|
||||
|
||||
pam_confs="/etc/pam.d/common-session /etc/pam.d/common-session-noninteractive"
|
||||
for pam_conf in $pam_confs; do
|
||||
if [ -f "$pam_conf" ]; then
|
||||
append_to_file "$pam_conf" "session optional pam_limits.so"
|
||||
append_file "$pam_conf" "session optional pam_limits.so"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -557,7 +633,7 @@ install_brew() {
|
||||
|
||||
bash="$(require bash)"
|
||||
script=$(download_file "https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh")
|
||||
NONINTERACTIVE=1 execute_as_user "$bash" "$script"
|
||||
execute_as_user "$bash" -c "NONINTERACTIVE=1 $script"
|
||||
|
||||
case "$arch" in
|
||||
x64)
|
||||
@@ -638,7 +714,7 @@ nodejs_version_exact() {
|
||||
}
|
||||
|
||||
nodejs_version() {
|
||||
echo "$(nodejs_version_exact)" | cut -d. -f1
|
||||
print "$(nodejs_version_exact)" | cut -d. -f1
|
||||
}
|
||||
|
||||
install_nodejs() {
|
||||
@@ -674,14 +750,21 @@ install_nodejs() {
|
||||
}
|
||||
|
||||
install_nodejs_headers() {
|
||||
headers_tar="$(download_file "https://nodejs.org/download/release/v$(nodejs_version_exact)/node-v$(nodejs_version_exact)-headers.tar.gz")"
|
||||
headers_dir="$(dirname "$headers_tar")"
|
||||
execute tar -xzf "$headers_tar" -C "$headers_dir"
|
||||
headers_include="$headers_dir/node-v$(nodejs_version_exact)/include"
|
||||
execute_sudo cp -R "$headers_include/" "/usr"
|
||||
nodejs_headers_tar="$(download_file "https://nodejs.org/download/release/v$(nodejs_version_exact)/node-v$(nodejs_version_exact)-headers.tar.gz")"
|
||||
nodejs_headers_dir="$(dirname "$nodejs_headers_tar")"
|
||||
execute tar -xzf "$nodejs_headers_tar" -C "$nodejs_headers_dir"
|
||||
|
||||
nodejs_headers_include="$nodejs_headers_dir/node-v$(nodejs_version_exact)/include"
|
||||
execute_sudo cp -R "$nodejs_headers_include/" "/usr"
|
||||
}
|
||||
|
||||
bun_version_exact() {
|
||||
print "1.1.38"
|
||||
}
|
||||
|
||||
install_bun() {
|
||||
install_packages unzip
|
||||
|
||||
case "$pm" in
|
||||
apk)
|
||||
install_packages \
|
||||
@@ -690,23 +773,28 @@ install_bun() {
|
||||
;;
|
||||
esac
|
||||
|
||||
bash="$(require bash)"
|
||||
script=$(download_file "https://bun.sh/install")
|
||||
|
||||
version="${1:-"latest"}"
|
||||
case "$version" in
|
||||
latest)
|
||||
execute_as_user "$bash" "$script"
|
||||
case "$abi" in
|
||||
musl)
|
||||
bun_triplet="bun-$os-$arch-$abi"
|
||||
;;
|
||||
*)
|
||||
execute_as_user "$bash" "$script" -s "$version"
|
||||
bun_triplet="bun-$os-$arch"
|
||||
;;
|
||||
esac
|
||||
|
||||
move_to_bin "$home/.bun/bin/bun"
|
||||
bun_path="$(which bun)"
|
||||
bunx_path="$(dirname "$bun_path")/bunx"
|
||||
execute_sudo ln -sf "$bun_path" "$bunx_path"
|
||||
unzip="$(require unzip)"
|
||||
bun_download_url="https://pub-5e11e972747a44bf9aaf9394f185a982.r2.dev/releases/bun-v$(bun_version_exact)/$bun_triplet.zip"
|
||||
bun_zip="$(download_file "$bun_download_url")"
|
||||
bun_tmpdir="$(dirname "$bun_zip")"
|
||||
execute "$unzip" -o "$bun_zip" -d "$bun_tmpdir"
|
||||
|
||||
bun_path="/opt/bun"
|
||||
create_directory "$bun_path/bin"
|
||||
execute mv "$bun_tmpdir/$bun_triplet/bun" "$bun_path/bin/bun"
|
||||
execute ln -sf "$bun_path/bin/bun" "$bun_path/bin/bunx"
|
||||
|
||||
append_to_path "$bun_path/bin"
|
||||
append_to_profile "export BUN_INSTALL=$bun_path"
|
||||
}
|
||||
|
||||
install_cmake() {
|
||||
@@ -799,24 +887,19 @@ install_build_essentials() {
|
||||
|
||||
install_cmake
|
||||
install_llvm
|
||||
install_osxcross
|
||||
install_gcc
|
||||
install_ccache
|
||||
install_rust
|
||||
install_docker
|
||||
}
|
||||
|
||||
llvm_version_exact() {
|
||||
case "$os-$abi" in
|
||||
darwin-* | windows-* | linux-musl)
|
||||
print "18.1.8"
|
||||
;;
|
||||
linux-*)
|
||||
print "16.0.6"
|
||||
;;
|
||||
esac
|
||||
print "18.1.8"
|
||||
}
|
||||
|
||||
llvm_version() {
|
||||
echo "$(llvm_version_exact)" | cut -d. -f1
|
||||
print "$(llvm_version_exact)" | cut -d. -f1
|
||||
}
|
||||
|
||||
install_llvm() {
|
||||
@@ -824,14 +907,7 @@ install_llvm() {
|
||||
apt)
|
||||
bash="$(require bash)"
|
||||
llvm_script="$(download_file "https://apt.llvm.org/llvm.sh")"
|
||||
case "$distro-$release" in
|
||||
ubuntu-24*)
|
||||
execute_sudo "$bash" "$llvm_script" "$(llvm_version)" all -njammy
|
||||
;;
|
||||
*)
|
||||
execute_sudo "$bash" "$llvm_script" "$(llvm_version)" all
|
||||
;;
|
||||
esac
|
||||
execute_sudo "$bash" "$llvm_script" "$(llvm_version)" all
|
||||
;;
|
||||
brew)
|
||||
install_packages "llvm@$(llvm_version)"
|
||||
@@ -849,6 +925,55 @@ install_llvm() {
|
||||
esac
|
||||
}
|
||||
|
||||
install_gcc() {
|
||||
if ! [ "$os" = "linux" ] || ! [ "$distro" = "ubuntu" ] || [ -z "$gcc_version" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
# Taken from WebKit's Dockerfile.
|
||||
# https://github.com/oven-sh/WebKit/blob/816a3c02e0f8b53f8eec06b5ed911192589b51e2/Dockerfile
|
||||
|
||||
execute_sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
|
||||
execute_sudo apt update -y
|
||||
install_packages \
|
||||
"gcc-$gcc_version" \
|
||||
"g++-$gcc_version" \
|
||||
"libgcc-$gcc_version-dev" \
|
||||
"libstdc++-$gcc_version-dev" \
|
||||
libasan6 \
|
||||
libubsan1 \
|
||||
libatomic1 \
|
||||
libtsan0 \
|
||||
liblsan0 \
|
||||
libgfortran5 \
|
||||
libc6-dev
|
||||
|
||||
execute_sudo update-alternatives \
|
||||
--install /usr/bin/gcc gcc "/usr/bin/gcc-$gcc_version" 130 \
|
||||
--slave /usr/bin/g++ g++ "/usr/bin/g++-$gcc_version" \
|
||||
--slave /usr/bin/gcc-ar gcc-ar "/usr/bin/gcc-ar-$gcc_version" \
|
||||
--slave /usr/bin/gcc-nm gcc-nm "/usr/bin/gcc-nm-$gcc_version" \
|
||||
--slave /usr/bin/gcc-ranlib gcc-ranlib "/usr/bin/gcc-ranlib-$gcc_version"
|
||||
|
||||
case "$arch" in
|
||||
x64)
|
||||
arch_path="x86_64-linux-gnu"
|
||||
;;
|
||||
aarch64)
|
||||
arch_path="aarch64-linux-gnu"
|
||||
;;
|
||||
esac
|
||||
|
||||
gcc_path="/usr/lib/gcc/$arch_path/$gcc_version"
|
||||
create_directory "$gcc_path"
|
||||
execute_sudo ln -sf /usr/lib/$arch_path/libstdc++.so.6 "$gcc_path/libstdc++.so.6"
|
||||
|
||||
ld_conf_path="/etc/ld.so.conf.d/gcc-$gcc_version.conf"
|
||||
append_file "$ld_conf_path" "$gcc_path"
|
||||
append_file "$ld_conf_path" "/usr/lib/$arch_path"
|
||||
execute_sudo ldconfig
|
||||
}
|
||||
|
||||
install_ccache() {
|
||||
case "$pm" in
|
||||
apt | apk | brew)
|
||||
@@ -865,9 +990,23 @@ install_rust() {
|
||||
cargo
|
||||
;;
|
||||
*)
|
||||
rust_home="/opt/rust"
|
||||
create_directory "$rust_home"
|
||||
append_to_profile "export RUSTUP_HOME=$rust_home"
|
||||
append_to_profile "export CARGO_HOME=$rust_home"
|
||||
|
||||
sh="$(require sh)"
|
||||
script=$(download_file "https://sh.rustup.rs")
|
||||
execute_as_user "$sh" "$script" -y
|
||||
rustup_script=$(download_file "https://sh.rustup.rs")
|
||||
execute "$sh" -c "RUSTUP_HOME=$rust_home CARGO_HOME=$rust_home $rustup_script -y --no-modify-path"
|
||||
append_to_path "$rust_home/bin"
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$osxcross" in
|
||||
1)
|
||||
rustup="$(require rustup)"
|
||||
execute_as_user "$rustup" target add aarch64-apple-darwin
|
||||
execute_as_user "$rustup" target add x86_64-apple-darwin
|
||||
;;
|
||||
esac
|
||||
}
|
||||
@@ -910,6 +1049,46 @@ install_docker() {
|
||||
fi
|
||||
}
|
||||
|
||||
macos_sdk_version() {
|
||||
# https://github.com/alexey-lysiuk/macos-sdk/releases
|
||||
print "14.5"
|
||||
}
|
||||
|
||||
install_osxcross() {
|
||||
if ! [ "$os" = "linux" ] || ! [ "$osxcross" = "1" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
install_packages \
|
||||
libssl-dev \
|
||||
lzma-dev \
|
||||
libxml2-dev \
|
||||
zlib1g-dev \
|
||||
bzip2 \
|
||||
cpio
|
||||
|
||||
osxcross_path="/opt/osxcross"
|
||||
create_directory "$osxcross_path"
|
||||
|
||||
osxcross_commit="29fe6dd35522073c9df5800f8cd1feb4b9a993a8"
|
||||
osxcross_tar="$(download_file "https://github.com/tpoechtrager/osxcross/archive/$osxcross_commit.tar.gz")"
|
||||
execute tar -xzf "$osxcross_tar" -C "$osxcross_path"
|
||||
|
||||
osxcross_build_path="$osxcross_path/build"
|
||||
execute mv "$osxcross_path/osxcross-$osxcross_commit" "$osxcross_build_path"
|
||||
|
||||
osxcross_sdk_tar="$(download_file "https://github.com/alexey-lysiuk/macos-sdk/releases/download/$(macos_sdk_version)/MacOSX$(macos_sdk_version).tar.xz")"
|
||||
execute mv "$osxcross_sdk_tar" "$osxcross_build_path/tarballs/MacOSX$(macos_sdk_version).sdk.tar.xz"
|
||||
|
||||
bash="$(require bash)"
|
||||
execute_sudo ln -sf "$(which clang-$(llvm_version))" /usr/bin/clang
|
||||
execute_sudo ln -sf "$(which clang++-$(llvm_version))" /usr/bin/clang++
|
||||
execute_sudo "$bash" -c "UNATTENDED=1 TARGET_DIR='$osxcross_path' $osxcross_build_path/build.sh"
|
||||
|
||||
execute_sudo rm -rf "$osxcross_build_path"
|
||||
grant_to_user "$osxcross_path"
|
||||
}
|
||||
|
||||
install_tailscale() {
|
||||
if [ "$docker" = "1" ]; then
|
||||
return
|
||||
@@ -975,14 +1154,12 @@ create_buildkite_user() {
|
||||
|
||||
buildkite_paths="$home /var/cache/buildkite-agent /var/log/buildkite-agent /var/run/buildkite-agent /var/run/buildkite-agent/buildkite-agent.sock"
|
||||
for path in $buildkite_paths; do
|
||||
execute_sudo mkdir -p "$path"
|
||||
execute_sudo chown -R "$user:$group" "$path"
|
||||
create_directory "$path"
|
||||
done
|
||||
|
||||
buildkite_files="/var/run/buildkite-agent/buildkite-agent.pid"
|
||||
for file in $buildkite_files; do
|
||||
execute_sudo touch "$file"
|
||||
execute_sudo chown "$user:$group" "$file"
|
||||
create_file "$file"
|
||||
done
|
||||
}
|
||||
|
||||
@@ -992,27 +1169,22 @@ install_buildkite() {
|
||||
fi
|
||||
|
||||
buildkite_version="3.87.0"
|
||||
case "$os-$arch" in
|
||||
linux-aarch64)
|
||||
buildkite_filename="buildkite-agent-linux-arm64-$buildkite_version.tar.gz"
|
||||
case "$arch" in
|
||||
aarch64)
|
||||
buildkite_arch="arm64"
|
||||
;;
|
||||
linux-x64)
|
||||
buildkite_filename="buildkite-agent-linux-amd64-$buildkite_version.tar.gz"
|
||||
;;
|
||||
darwin-aarch64)
|
||||
buildkite_filename="buildkite-agent-darwin-arm64-$buildkite_version.tar.gz"
|
||||
;;
|
||||
darwin-x64)
|
||||
buildkite_filename="buildkite-agent-darwin-amd64-$buildkite_version.tar.gz"
|
||||
x64)
|
||||
buildkite_arch="amd64"
|
||||
;;
|
||||
esac
|
||||
buildkite_url="https://github.com/buildkite/agent/releases/download/v$buildkite_version/$buildkite_filename"
|
||||
buildkite_filepath="$(download_file "$buildkite_url" "$buildkite_filename")"
|
||||
buildkite_tmpdir="$(dirname "$buildkite_filepath")"
|
||||
|
||||
execute tar -xzf "$buildkite_filepath" -C "$buildkite_tmpdir"
|
||||
buildkite_filename="buildkite-agent-$os-$buildkite_arch-$buildkite_version.tar.gz"
|
||||
buildkite_url="https://github.com/buildkite/agent/releases/download/v$buildkite_version/$buildkite_filename"
|
||||
buildkite_tar="$(download_file "$buildkite_url")"
|
||||
buildkite_tmpdir="$(dirname "$buildkite_tar")"
|
||||
|
||||
execute tar -xzf "$buildkite_tar" -C "$buildkite_tmpdir"
|
||||
move_to_bin "$buildkite_tmpdir/buildkite-agent"
|
||||
execute rm -rf "$buildkite_tmpdir"
|
||||
}
|
||||
|
||||
install_chromium() {
|
||||
@@ -1103,6 +1275,19 @@ install_chromium() {
|
||||
esac
|
||||
}
|
||||
|
||||
clean_system() {
|
||||
if ! [ "$ci" = "1" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
print "Cleaning system..."
|
||||
|
||||
tmp_paths="/tmp /var/tmp"
|
||||
for path in $tmp_paths; do
|
||||
execute_sudo rm -rf "$path"/*
|
||||
done
|
||||
}
|
||||
|
||||
main() {
|
||||
check_features "$@"
|
||||
check_operating_system
|
||||
@@ -1114,6 +1299,7 @@ main() {
|
||||
install_common_software
|
||||
install_build_essentials
|
||||
install_chromium
|
||||
clean_system
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
@@ -2374,6 +2374,7 @@ function getCloud(name) {
|
||||
* @typedef {"linux" | "darwin" | "windows"} Os
|
||||
* @typedef {"aarch64" | "x64"} Arch
|
||||
* @typedef {"macos" | "windowsserver" | "debian" | "ubuntu" | "alpine" | "amazonlinux"} Distro
|
||||
* @typedef {"osxcross" | "gcc-13"} Feature
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -2383,6 +2384,7 @@ function getCloud(name) {
|
||||
* @property {Distro} distro
|
||||
* @property {string} release
|
||||
* @property {string} [eol]
|
||||
* @property {Feature[]} [features]
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -2416,6 +2418,7 @@ function getCloud(name) {
|
||||
* @property {Arch} arch
|
||||
* @property {Distro} distro
|
||||
* @property {string} [release]
|
||||
* @property {Feature[]} [features]
|
||||
* @property {string} [name]
|
||||
* @property {string} [instanceType]
|
||||
* @property {string} [imageId]
|
||||
@@ -2464,6 +2467,7 @@ async function main() {
|
||||
"detached": { type: "boolean" },
|
||||
"tag": { type: "string", multiple: true },
|
||||
"ci": { type: "boolean" },
|
||||
"feature": { type: "string", multiple: true },
|
||||
"rdp": { type: "boolean" },
|
||||
"vnc": { type: "boolean" },
|
||||
"authorized-user": { type: "string", multiple: true },
|
||||
@@ -2501,6 +2505,7 @@ async function main() {
|
||||
arch: parseArch(args["arch"]),
|
||||
distro: args["distro"],
|
||||
release: args["release"],
|
||||
features: args["feature"],
|
||||
name: args["name"],
|
||||
instanceType: args["instance-type"],
|
||||
imageId: args["image-id"],
|
||||
@@ -2517,7 +2522,7 @@ async function main() {
|
||||
sshKeys,
|
||||
};
|
||||
|
||||
const { detached, bootstrap, ci, os, arch, distro, release } = options;
|
||||
const { detached, bootstrap, ci, os, arch, distro, release, features } = options;
|
||||
const name = distro ? `${os}-${arch}-${distro}-${release}` : `${os}-${arch}-${release}`;
|
||||
|
||||
let bootstrapPath, agentPath;
|
||||
@@ -2627,6 +2632,9 @@ async function main() {
|
||||
} else {
|
||||
const remotePath = "/tmp/bootstrap.sh";
|
||||
const args = ci ? ["--ci"] : [];
|
||||
for (const feature of features || []) {
|
||||
args.push(`--${feature}`);
|
||||
}
|
||||
await startGroup("Running bootstrap...", async () => {
|
||||
await machine.upload(bootstrapPath, remotePath);
|
||||
await machine.spawnSafe(["sh", remotePath, ...args], { stdio: "inherit" });
|
||||
|
||||
@@ -1232,7 +1232,6 @@ pub const JSFrameworkRouter = struct {
|
||||
pub fn finalize(this: *JSFrameworkRouter) void {
|
||||
this.files.deinit(bun.default_allocator);
|
||||
this.router.deinit(bun.default_allocator);
|
||||
bun.default_allocator.free(this.router.types);
|
||||
for (this.stored_parse_errors.items) |i| bun.default_allocator.free(i.rel_path);
|
||||
this.stored_parse_errors.deinit(bun.default_allocator);
|
||||
bun.destroy(this);
|
||||
|
||||
@@ -290,14 +290,6 @@ pub const UDPSocket = struct {
|
||||
.vm = vm,
|
||||
});
|
||||
|
||||
// also cleans up config
|
||||
defer {
|
||||
if (globalThis.hasException()) {
|
||||
this.closed = true;
|
||||
this.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
if (uws.udp.Socket.create(
|
||||
this.loop,
|
||||
onData,
|
||||
@@ -309,15 +301,26 @@ pub const UDPSocket = struct {
|
||||
)) |socket| {
|
||||
this.socket = socket;
|
||||
} else {
|
||||
this.closed = true;
|
||||
this.deinit();
|
||||
return globalThis.throw("Failed to bind socket", .{});
|
||||
}
|
||||
|
||||
errdefer {
|
||||
this.socket.close();
|
||||
this.deinit();
|
||||
}
|
||||
|
||||
if (config.connect) |connect| {
|
||||
const ret = this.socket.connect(connect.address, connect.port);
|
||||
if (ret != 0) {
|
||||
if (JSC.Maybe(void).errnoSys(ret, .connect)) |err| {
|
||||
return globalThis.throwValue(err.toJS(globalThis));
|
||||
}
|
||||
|
||||
if (bun.c_ares.Error.initEAI(ret)) |err| {
|
||||
return globalThis.throwValue(err.toJS(globalThis));
|
||||
}
|
||||
}
|
||||
this.connect_info = .{ .port = connect.port };
|
||||
}
|
||||
@@ -645,7 +648,7 @@ pub const UDPSocket = struct {
|
||||
// finalize is only called when js_refcount reaches 0
|
||||
// js_refcount can only reach 0 when the socket is closed
|
||||
bun.assert(this.closed);
|
||||
|
||||
this.poll_ref.disable();
|
||||
this.config.deinit();
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ JSC::JSValue generateModule(JSC::JSGlobalObject* globalObject, JSC::VM& vm, cons
|
||||
return result;
|
||||
}
|
||||
|
||||
#if BUN_DYNAMIC_JS_LOAD_PATH
|
||||
#ifdef BUN_DYNAMIC_JS_LOAD_PATH
|
||||
JSValue initializeInternalModuleFromDisk(
|
||||
JSGlobalObject* globalObject,
|
||||
VM& vm,
|
||||
|
||||
@@ -6032,11 +6032,10 @@ CPP_DECL bool Bun__CallFrame__isFromBunMain(JSC::CallFrame* callFrame, JSC::VM*
|
||||
return source.string() == "builtin://bun/main"_s;
|
||||
}
|
||||
|
||||
CPP_DECL void Bun__CallFrame__getCallerSrcLoc(JSC::CallFrame* callFrame, JSC::JSGlobalObject* globalObject, unsigned int* outSourceID, unsigned int* outLine, unsigned int* outColumn)
|
||||
CPP_DECL void Bun__CallFrame__getCallerSrcLoc(JSC::CallFrame* callFrame, JSC::JSGlobalObject* globalObject, BunString* outSourceURL, unsigned int* outLine, unsigned int* outColumn)
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
JSC::LineColumn lineColumn;
|
||||
JSC::SourceID sourceID = 0;
|
||||
String sourceURL;
|
||||
|
||||
ZigStackFrame remappedFrame = {};
|
||||
@@ -6046,6 +6045,7 @@ CPP_DECL void Bun__CallFrame__getCallerSrcLoc(JSC::CallFrame* callFrame, JSC::JS
|
||||
return WTF::IterationStatus::Continue;
|
||||
|
||||
if (visitor->hasLineAndColumnInfo()) {
|
||||
|
||||
lineColumn = visitor->computeLineAndColumn();
|
||||
|
||||
String sourceURLForFrame = visitor->sourceURL();
|
||||
@@ -6071,8 +6071,6 @@ CPP_DECL void Bun__CallFrame__getCallerSrcLoc(JSC::CallFrame* callFrame, JSC::JS
|
||||
sourceURLForFrame = origin.string();
|
||||
}
|
||||
}
|
||||
|
||||
sourceID = provider->asID();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6099,7 +6097,7 @@ CPP_DECL void Bun__CallFrame__getCallerSrcLoc(JSC::CallFrame* callFrame, JSC::JS
|
||||
lineColumn.column = OrdinalNumber::fromZeroBasedInt(remappedFrame.position.column_zero_based).oneBasedInt();
|
||||
}
|
||||
|
||||
*outSourceID = sourceID;
|
||||
*outSourceURL = Bun::toStringRef(sourceURL);
|
||||
*outLine = lineColumn.line;
|
||||
*outColumn = lineColumn.column;
|
||||
}
|
||||
|
||||
@@ -6666,19 +6666,19 @@ pub const CallFrame = opaque {
|
||||
return value;
|
||||
}
|
||||
|
||||
extern fn Bun__CallFrame__getCallerSrcLoc(*const CallFrame, *JSGlobalObject, *c_uint, *c_uint, *c_uint) void;
|
||||
extern fn Bun__CallFrame__getCallerSrcLoc(*const CallFrame, *JSGlobalObject, *bun.String, *c_uint, *c_uint) void;
|
||||
pub const CallerSrcLoc = struct {
|
||||
source_file_id: c_uint,
|
||||
str: bun.String,
|
||||
line: c_uint,
|
||||
column: c_uint,
|
||||
};
|
||||
pub fn getCallerSrcLoc(call_frame: *const CallFrame, globalThis: *JSGlobalObject) CallerSrcLoc {
|
||||
var source_id: c_uint = undefined;
|
||||
var str: bun.String = undefined;
|
||||
var line: c_uint = undefined;
|
||||
var column: c_uint = undefined;
|
||||
Bun__CallFrame__getCallerSrcLoc(call_frame, globalThis, &source_id, &line, &column);
|
||||
Bun__CallFrame__getCallerSrcLoc(call_frame, globalThis, &str, &line, &column);
|
||||
return .{
|
||||
.source_file_id = source_id,
|
||||
.str = str,
|
||||
.line = line,
|
||||
.column = column,
|
||||
};
|
||||
|
||||
71
src/bun.js/bindings/sqlite/sqlite3.c
vendored
71
src/bun.js/bindings/sqlite/sqlite3.c
vendored
@@ -1,7 +1,7 @@
|
||||
// clang-format off
|
||||
/******************************************************************************
|
||||
** This file is an amalgamation of many separate C source files from SQLite
|
||||
** version 3.47.1. By combining all the individual C code files into this
|
||||
** version 3.47.2. By combining all the individual C code files into this
|
||||
** single large file, the entire code can be compiled as a single translation
|
||||
** unit. This allows many compilers to do optimizations that would not be
|
||||
** possible if the files were compiled separately. Performance improvements
|
||||
@@ -19,7 +19,7 @@
|
||||
** separate file. This file contains only code for the core SQLite library.
|
||||
**
|
||||
** The content in this amalgamation comes from Fossil check-in
|
||||
** b95d11e958643b969c47a8e5857f3793b9e6.
|
||||
** 2aabe05e2e8cae4847a802ee2daddc1d7413.
|
||||
*/
|
||||
#define SQLITE_CORE 1
|
||||
#define SQLITE_AMALGAMATION 1
|
||||
@@ -463,9 +463,9 @@ extern "C" {
|
||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.47.1"
|
||||
#define SQLITE_VERSION_NUMBER 3047001
|
||||
#define SQLITE_SOURCE_ID "2024-11-25 12:07:48 b95d11e958643b969c47a8e5857f3793b9e69700b8f1469371386369a26e577e"
|
||||
#define SQLITE_VERSION "3.47.2"
|
||||
#define SQLITE_VERSION_NUMBER 3047002
|
||||
#define SQLITE_SOURCE_ID "2024-12-07 20:39:59 2aabe05e2e8cae4847a802ee2daddc1d7413d8fc560254d93ee3e72c14685b6c"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
@@ -35698,8 +35698,8 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
|
||||
int eValid = 1; /* True exponent is either not used or is well-formed */
|
||||
int nDigit = 0; /* Number of digits processed */
|
||||
int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */
|
||||
u64 s2; /* round-tripped significand */
|
||||
double rr[2];
|
||||
u64 s2;
|
||||
|
||||
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
|
||||
*pResult = 0.0; /* Default return value, in case of an error */
|
||||
@@ -35802,7 +35802,7 @@ do_atof_calc:
|
||||
e = (e*esign) + d;
|
||||
|
||||
/* Try to adjust the exponent to make it smaller */
|
||||
while( e>0 && s<(LARGEST_UINT64/10) ){
|
||||
while( e>0 && s<((LARGEST_UINT64-0x7ff)/10) ){
|
||||
s *= 10;
|
||||
e--;
|
||||
}
|
||||
@@ -35812,11 +35812,16 @@ do_atof_calc:
|
||||
}
|
||||
|
||||
rr[0] = (double)s;
|
||||
s2 = (u64)rr[0];
|
||||
#if defined(_MSC_VER) && _MSC_VER<1700
|
||||
if( s2==0x8000000000000000LL ){ s2 = 2*(u64)(0.5*rr[0]); }
|
||||
#endif
|
||||
rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);
|
||||
assert( sizeof(s2)==sizeof(rr[0]) );
|
||||
memcpy(&s2, &rr[0], sizeof(s2));
|
||||
if( s2<=0x43efffffffffffffLL ){
|
||||
s2 = (u64)rr[0];
|
||||
rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);
|
||||
}else{
|
||||
rr[1] = 0.0;
|
||||
}
|
||||
assert( rr[1]<=1.0e-10*rr[0] ); /* Equal only when rr[0]==0.0 */
|
||||
|
||||
if( e>0 ){
|
||||
while( e>=100 ){
|
||||
e -= 100;
|
||||
@@ -147606,32 +147611,32 @@ static Expr *substExpr(
|
||||
if( pSubst->isOuterJoin ){
|
||||
ExprSetProperty(pNew, EP_CanBeNull);
|
||||
}
|
||||
if( pNew->op==TK_TRUEFALSE ){
|
||||
pNew->u.iValue = sqlite3ExprTruthValue(pNew);
|
||||
pNew->op = TK_INTEGER;
|
||||
ExprSetProperty(pNew, EP_IntValue);
|
||||
}
|
||||
|
||||
/* Ensure that the expression now has an implicit collation sequence,
|
||||
** just as it did when it was a column of a view or sub-query. */
|
||||
{
|
||||
CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pNew);
|
||||
CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse,
|
||||
pSubst->pCList->a[iColumn].pExpr
|
||||
);
|
||||
if( pNat!=pColl || (pNew->op!=TK_COLUMN && pNew->op!=TK_COLLATE) ){
|
||||
pNew = sqlite3ExprAddCollateString(pSubst->pParse, pNew,
|
||||
(pColl ? pColl->zName : "BINARY")
|
||||
);
|
||||
}
|
||||
}
|
||||
ExprClearProperty(pNew, EP_Collate);
|
||||
if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){
|
||||
sqlite3SetJoinExpr(pNew, pExpr->w.iJoin,
|
||||
pExpr->flags & (EP_OuterON|EP_InnerON));
|
||||
}
|
||||
sqlite3ExprDelete(db, pExpr);
|
||||
pExpr = pNew;
|
||||
if( pExpr->op==TK_TRUEFALSE ){
|
||||
pExpr->u.iValue = sqlite3ExprTruthValue(pExpr);
|
||||
pExpr->op = TK_INTEGER;
|
||||
ExprSetProperty(pExpr, EP_IntValue);
|
||||
}
|
||||
|
||||
/* Ensure that the expression now has an implicit collation sequence,
|
||||
** just as it did when it was a column of a view or sub-query. */
|
||||
{
|
||||
CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pExpr);
|
||||
CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse,
|
||||
pSubst->pCList->a[iColumn].pExpr
|
||||
);
|
||||
if( pNat!=pColl || (pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE) ){
|
||||
pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr,
|
||||
(pColl ? pColl->zName : "BINARY")
|
||||
);
|
||||
}
|
||||
}
|
||||
ExprClearProperty(pExpr, EP_Collate);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
@@ -254939,7 +254944,7 @@ static void fts5SourceIdFunc(
|
||||
){
|
||||
assert( nArg==0 );
|
||||
UNUSED_PARAM2(nArg, apUnused);
|
||||
sqlite3_result_text(pCtx, "fts5: 2024-11-25 12:07:48 b95d11e958643b969c47a8e5857f3793b9e69700b8f1469371386369a26e577e", -1, SQLITE_TRANSIENT);
|
||||
sqlite3_result_text(pCtx, "fts5: 2024-12-07 20:39:59 2aabe05e2e8cae4847a802ee2daddc1d7413d8fc560254d93ee3e72c14685b6c", -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
6
src/bun.js/bindings/sqlite/sqlite3_local.h
vendored
6
src/bun.js/bindings/sqlite/sqlite3_local.h
vendored
@@ -147,9 +147,9 @@ extern "C" {
|
||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.47.1"
|
||||
#define SQLITE_VERSION_NUMBER 3047001
|
||||
#define SQLITE_SOURCE_ID "2024-11-25 12:07:48 b95d11e958643b969c47a8e5857f3793b9e69700b8f1469371386369a26e577e"
|
||||
#define SQLITE_VERSION "3.47.2"
|
||||
#define SQLITE_VERSION_NUMBER 3047002
|
||||
#define SQLITE_SOURCE_ID "2024-12-07 20:39:59 2aabe05e2e8cae4847a802ee2daddc1d7413d8fc560254d93ee3e72c14685b6c"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
|
||||
@@ -2152,7 +2152,6 @@ pub const Expect = struct {
|
||||
pub fn toThrow(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) bun.JSError!JSValue {
|
||||
defer this.postMatch(globalThis);
|
||||
|
||||
const vm = globalThis.bunVM();
|
||||
const thisValue = callFrame.this();
|
||||
const arguments = callFrame.argumentsAsArray(1);
|
||||
|
||||
@@ -2178,63 +2177,9 @@ pub const Expect = struct {
|
||||
};
|
||||
expected_value.ensureStillAlive();
|
||||
|
||||
const value: JSValue = try this.getValue(globalThis, thisValue, "toThrow", "<green>expected<r>");
|
||||
|
||||
const not = this.flags.not;
|
||||
|
||||
var return_value_from_function: JSValue = .zero;
|
||||
const result_: ?JSValue = brk: {
|
||||
if (!value.jsType().isFunction()) {
|
||||
if (this.flags.promise != .none) {
|
||||
break :brk value;
|
||||
}
|
||||
|
||||
return globalThis.throw("Expected value must be a function", .{});
|
||||
}
|
||||
|
||||
var return_value: JSValue = .zero;
|
||||
|
||||
// Drain existing unhandled rejections
|
||||
vm.global.handleRejectedPromises();
|
||||
|
||||
var scope = vm.unhandledRejectionScope();
|
||||
const prev_unhandled_pending_rejection_to_capture = vm.unhandled_pending_rejection_to_capture;
|
||||
vm.unhandled_pending_rejection_to_capture = &return_value;
|
||||
vm.onUnhandledRejection = &VirtualMachine.onQuietUnhandledRejectionHandlerCaptureValue;
|
||||
return_value_from_function = value.call(globalThis, .undefined, &.{}) catch |err| globalThis.takeException(err);
|
||||
vm.unhandled_pending_rejection_to_capture = prev_unhandled_pending_rejection_to_capture;
|
||||
|
||||
vm.global.handleRejectedPromises();
|
||||
|
||||
if (return_value == .zero) {
|
||||
return_value = return_value_from_function;
|
||||
}
|
||||
|
||||
if (return_value.asAnyPromise()) |promise| {
|
||||
vm.waitForPromise(promise);
|
||||
scope.apply(vm);
|
||||
switch (promise.unwrap(globalThis.vm(), .mark_handled)) {
|
||||
.fulfilled => {
|
||||
break :brk null;
|
||||
},
|
||||
.rejected => |rejected| {
|
||||
// since we know for sure it rejected, we should always return the error
|
||||
break :brk rejected.toError() orelse rejected;
|
||||
},
|
||||
.pending => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
if (return_value != return_value_from_function) {
|
||||
if (return_value_from_function.asAnyPromise()) |existing| {
|
||||
existing.setHandled(globalThis.vm());
|
||||
}
|
||||
}
|
||||
|
||||
scope.apply(vm);
|
||||
|
||||
break :brk return_value.toError() orelse return_value_from_function.toError();
|
||||
};
|
||||
const result_, const return_value_from_function = try this.getValueAsToThrow(globalThis, try this.getValue(globalThis, thisValue, "toThrow", "<green>expected<r>"));
|
||||
|
||||
const did_throw = result_ != null;
|
||||
|
||||
@@ -2506,10 +2451,152 @@ pub const Expect = struct {
|
||||
expected_value.getClassName(globalThis, &expected_class);
|
||||
return this.throw(globalThis, signature, expected_fmt, .{ expected_class, result.toFmt(&formatter) });
|
||||
}
|
||||
pub fn toMatchInlineSnapshot(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) bun.JSError!JSValue {
|
||||
// in jest, a failing inline snapshot does not block the rest from running
|
||||
// not sure why - empty snapshots will autofill and with the `-u` flag none will fail
|
||||
fn getValueAsToThrow(this: *Expect, globalThis: *JSGlobalObject, value: JSValue) bun.JSError!struct { ?JSValue, JSValue } {
|
||||
const vm = globalThis.bunVM();
|
||||
|
||||
var return_value_from_function: JSValue = .zero;
|
||||
|
||||
if (!value.jsType().isFunction()) {
|
||||
if (this.flags.promise != .none) {
|
||||
return .{ value, return_value_from_function };
|
||||
}
|
||||
|
||||
return globalThis.throw("Expected value must be a function", .{});
|
||||
}
|
||||
|
||||
var return_value: JSValue = .zero;
|
||||
|
||||
// Drain existing unhandled rejections
|
||||
vm.global.handleRejectedPromises();
|
||||
|
||||
var scope = vm.unhandledRejectionScope();
|
||||
const prev_unhandled_pending_rejection_to_capture = vm.unhandled_pending_rejection_to_capture;
|
||||
vm.unhandled_pending_rejection_to_capture = &return_value;
|
||||
vm.onUnhandledRejection = &VirtualMachine.onQuietUnhandledRejectionHandlerCaptureValue;
|
||||
return_value_from_function = value.call(globalThis, .undefined, &.{}) catch |err| globalThis.takeException(err);
|
||||
vm.unhandled_pending_rejection_to_capture = prev_unhandled_pending_rejection_to_capture;
|
||||
|
||||
vm.global.handleRejectedPromises();
|
||||
|
||||
if (return_value == .zero) {
|
||||
return_value = return_value_from_function;
|
||||
}
|
||||
|
||||
if (return_value.asAnyPromise()) |promise| {
|
||||
vm.waitForPromise(promise);
|
||||
scope.apply(vm);
|
||||
switch (promise.unwrap(globalThis.vm(), .mark_handled)) {
|
||||
.fulfilled => {
|
||||
return .{ null, return_value_from_function };
|
||||
},
|
||||
.rejected => |rejected| {
|
||||
// since we know for sure it rejected, we should always return the error
|
||||
return .{ rejected.toError() orelse rejected, return_value_from_function };
|
||||
},
|
||||
.pending => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
if (return_value != return_value_from_function) {
|
||||
if (return_value_from_function.asAnyPromise()) |existing| {
|
||||
existing.setHandled(globalThis.vm());
|
||||
}
|
||||
}
|
||||
|
||||
scope.apply(vm);
|
||||
|
||||
return .{ return_value.toError() orelse return_value_from_function.toError(), return_value_from_function };
|
||||
}
|
||||
pub fn toThrowErrorMatchingSnapshot(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) bun.JSError!JSValue {
|
||||
defer this.postMatch(globalThis);
|
||||
const thisValue = callFrame.this();
|
||||
const _arguments = callFrame.arguments_old(2);
|
||||
const arguments: []const JSValue = _arguments.ptr[0.._arguments.len];
|
||||
|
||||
incrementExpectCallCounter();
|
||||
|
||||
const not = this.flags.not;
|
||||
if (not) {
|
||||
const signature = comptime getSignature("toThrowErrorMatchingSnapshot", "", true);
|
||||
return this.throw(globalThis, signature, "\n\n<b>Matcher error<r>: Snapshot matchers cannot be used with <b>not<r>\n", .{});
|
||||
}
|
||||
|
||||
if (this.testScope() == null) {
|
||||
const signature = comptime getSignature("toThrowErrorMatchingSnapshot", "", true);
|
||||
return this.throw(globalThis, signature, "\n\n<b>Matcher error<r>: Snapshot matchers cannot be used outside of a test\n", .{});
|
||||
}
|
||||
|
||||
var hint_string: ZigString = ZigString.Empty;
|
||||
switch (arguments.len) {
|
||||
0 => {},
|
||||
1 => {
|
||||
if (arguments[0].isString()) {
|
||||
arguments[0].toZigString(&hint_string, globalThis);
|
||||
} else {
|
||||
return this.throw(globalThis, "", "\n\nMatcher error: Expected first argument to be a string\n", .{});
|
||||
}
|
||||
},
|
||||
else => return this.throw(globalThis, "", "\n\nMatcher error: Expected zero or one arguments\n", .{}),
|
||||
}
|
||||
|
||||
var hint = hint_string.toSlice(default_allocator);
|
||||
defer hint.deinit();
|
||||
|
||||
const value: JSValue = try this.fnToErrStringOrUndefined(globalThis, try this.getValue(globalThis, thisValue, "toThrowErrorMatchingSnapshot", "<green>properties<r><d>, <r>hint"));
|
||||
|
||||
return this.snapshot(globalThis, value, null, hint.slice(), "toThrowErrorMatchingSnapshot");
|
||||
}
|
||||
fn fnToErrStringOrUndefined(this: *Expect, globalThis: *JSGlobalObject, value: JSValue) !JSValue {
|
||||
const err_value, _ = try this.getValueAsToThrow(globalThis, value);
|
||||
|
||||
var err_value_res = err_value orelse JSValue.undefined;
|
||||
if (err_value_res.isAnyError()) {
|
||||
const message = try err_value_res.getTruthyComptime(globalThis, "message") orelse JSValue.undefined;
|
||||
err_value_res = message;
|
||||
} else {
|
||||
err_value_res = JSValue.undefined;
|
||||
}
|
||||
return err_value_res;
|
||||
}
|
||||
pub fn toThrowErrorMatchingInlineSnapshot(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) bun.JSError!JSValue {
|
||||
defer this.postMatch(globalThis);
|
||||
const thisValue = callFrame.this();
|
||||
const _arguments = callFrame.arguments_old(2);
|
||||
const arguments: []const JSValue = _arguments.ptr[0.._arguments.len];
|
||||
|
||||
incrementExpectCallCounter();
|
||||
|
||||
const not = this.flags.not;
|
||||
if (not) {
|
||||
const signature = comptime getSignature("toThrowErrorMatchingInlineSnapshot", "", true);
|
||||
return this.throw(globalThis, signature, "\n\n<b>Matcher error<r>: Snapshot matchers cannot be used with <b>not<r>\n", .{});
|
||||
}
|
||||
|
||||
var has_expected = false;
|
||||
var expected_string: ZigString = ZigString.Empty;
|
||||
switch (arguments.len) {
|
||||
0 => {},
|
||||
1 => {
|
||||
if (arguments[0].isString()) {
|
||||
has_expected = true;
|
||||
arguments[0].toZigString(&expected_string, globalThis);
|
||||
} else {
|
||||
return this.throw(globalThis, "", "\n\nMatcher error: Expected first argument to be a string\n", .{});
|
||||
}
|
||||
},
|
||||
else => return this.throw(globalThis, "", "\n\nMatcher error: Expected zero or one arguments\n", .{}),
|
||||
}
|
||||
|
||||
var expected = expected_string.toSlice(default_allocator);
|
||||
defer expected.deinit();
|
||||
|
||||
const expected_slice: ?[]const u8 = if (has_expected) expected.slice() else null;
|
||||
|
||||
const value: JSValue = try this.fnToErrStringOrUndefined(globalThis, try this.getValue(globalThis, thisValue, "toThrowErrorMatchingInlineSnapshot", "<green>properties<r><d>, <r>hint"));
|
||||
|
||||
return this.inlineSnapshot(globalThis, callFrame, value, null, expected_slice, "toThrowErrorMatchingInlineSnapshot");
|
||||
}
|
||||
pub fn toMatchInlineSnapshot(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame) bun.JSError!JSValue {
|
||||
defer this.postMatch(globalThis);
|
||||
const thisValue = callFrame.this();
|
||||
const _arguments = callFrame.arguments_old(2);
|
||||
@@ -2556,48 +2643,43 @@ pub const Expect = struct {
|
||||
var expected = expected_string.toSlice(default_allocator);
|
||||
defer expected.deinit();
|
||||
|
||||
const value: JSValue = try this.getValue(globalThis, thisValue, "toMatchInlineSnapshot", "<green>properties<r><d>, <r>hint");
|
||||
const expected_slice: ?[]const u8 = if (has_expected) expected.slice() else null;
|
||||
|
||||
if (!value.isObject() and property_matchers != null) {
|
||||
const signature = comptime getSignature("toMatchInlineSnapshot", "<green>properties<r><d>, <r>hint", false);
|
||||
return this.throw(globalThis, signature, "\n\n<b>Matcher error: <red>received<r> values must be an object when the matcher has <green>properties<r>\n", .{});
|
||||
}
|
||||
|
||||
if (property_matchers) |_prop_matchers| {
|
||||
const prop_matchers = _prop_matchers;
|
||||
|
||||
if (!value.jestDeepMatch(prop_matchers, globalThis, true)) {
|
||||
// TODO: print diff with properties from propertyMatchers
|
||||
const signature = comptime getSignature("toMatchInlineSnapshot", "<green>propertyMatchers<r>", false);
|
||||
const fmt = signature ++ "\n\nExpected <green>propertyMatchers<r> to match properties from received object" ++
|
||||
"\n\nReceived: {any}\n";
|
||||
|
||||
var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis };
|
||||
return globalThis.throwPretty(fmt, .{value.toFmt(&formatter)});
|
||||
}
|
||||
}
|
||||
|
||||
const result: ?[]const u8 = if (has_expected) expected.byteSlice() else null;
|
||||
const value = try this.getValue(globalThis, thisValue, "toMatchInlineSnapshot", "<green>properties<r><d>, <r>hint");
|
||||
return this.inlineSnapshot(globalThis, callFrame, value, property_matchers, expected_slice, "toMatchInlineSnapshot");
|
||||
}
|
||||
fn inlineSnapshot(
|
||||
this: *Expect,
|
||||
globalThis: *JSGlobalObject,
|
||||
callFrame: *CallFrame,
|
||||
value: JSValue,
|
||||
property_matchers: ?JSValue,
|
||||
result: ?[]const u8,
|
||||
comptime fn_name: []const u8,
|
||||
) bun.JSError!JSValue {
|
||||
// jest counts inline snapshots towards the snapshot counter for some reason
|
||||
_ = Jest.runner.?.snapshots.addCount(this, "") catch |e| switch (e) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
error.NoTest => {},
|
||||
};
|
||||
|
||||
const update = Jest.runner.?.snapshots.update_snapshots;
|
||||
var needs_write = false;
|
||||
|
||||
var pretty_value: MutableString = MutableString.init(default_allocator, 0) catch unreachable;
|
||||
value.jestSnapshotPrettyFormat(&pretty_value, globalThis) catch {
|
||||
var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis };
|
||||
return globalThis.throw("Failed to pretty format value: {s}", .{value.toFmt(&formatter)});
|
||||
};
|
||||
var pretty_value: MutableString = try MutableString.init(default_allocator, 0);
|
||||
defer pretty_value.deinit();
|
||||
try this.matchAndFmtSnapshot(globalThis, value, property_matchers, &pretty_value, fn_name);
|
||||
|
||||
if (result) |saved_value| {
|
||||
if (strings.eqlLong(pretty_value.slice(), saved_value, true)) {
|
||||
Jest.runner.?.snapshots.passed += 1;
|
||||
return .undefined;
|
||||
} else if (update) {
|
||||
Jest.runner.?.snapshots.passed += 1;
|
||||
needs_write = true;
|
||||
} else {
|
||||
Jest.runner.?.snapshots.failed += 1;
|
||||
const signature = comptime getSignature("toMatchInlineSnapshot", "<green>expected<r>", false);
|
||||
const signature = comptime getSignature(fn_name, "<green>expected<r>", false);
|
||||
const fmt = signature ++ "\n\n{any}\n";
|
||||
const diff_format = DiffFormatter{
|
||||
.received_string = pretty_value.slice(),
|
||||
@@ -2613,24 +2695,40 @@ pub const Expect = struct {
|
||||
|
||||
if (needs_write) {
|
||||
if (this.testScope() == null) {
|
||||
const signature = comptime getSignature("toMatchSnapshot", "", true);
|
||||
const signature = comptime getSignature(fn_name, "", true);
|
||||
return this.throw(globalThis, signature, "\n\n<b>Matcher error<r>: Snapshot matchers cannot be used outside of a test\n", .{});
|
||||
}
|
||||
|
||||
// 1. find the src loc of the snapshot
|
||||
const srcloc = callFrame.getCallerSrcLoc(globalThis);
|
||||
defer srcloc.str.deref();
|
||||
const describe = this.testScope().?.describe;
|
||||
const fget = Jest.runner.?.files.get(describe.file_id);
|
||||
|
||||
if (srcloc.source_file_id != this.testScope().?.describe.file_id) {
|
||||
const signature = comptime getSignature("toMatchSnapshot", "", true);
|
||||
return this.throw(globalThis, signature, "\n\n<b>Matcher error<r>: Inline snapshot matchers must be called from the same file as the test\n", .{});
|
||||
if (!srcloc.str.eqlUTF8(fget.source.path.text)) {
|
||||
const signature = comptime getSignature(fn_name, "", true);
|
||||
return this.throw(globalThis, signature,
|
||||
\\
|
||||
\\
|
||||
\\<b>Matcher error<r>: Inline snapshot matchers must be called from the test file:
|
||||
\\ Expected to be called from file: <green>"{}"<r>
|
||||
\\ {s} called from file: <red>"{}"<r>
|
||||
\\
|
||||
, .{
|
||||
std.zig.fmtEscapes(fget.source.path.text),
|
||||
fn_name,
|
||||
std.zig.fmtEscapes(srcloc.str.toUTF8(Jest.runner.?.snapshots.allocator).slice()),
|
||||
});
|
||||
}
|
||||
|
||||
// 2. save to write later
|
||||
try Jest.runner.?.snapshots.addInlineSnapshotToWrite(srcloc.source_file_id, .{
|
||||
try Jest.runner.?.snapshots.addInlineSnapshotToWrite(describe.file_id, .{
|
||||
.line = srcloc.line,
|
||||
.col = srcloc.column,
|
||||
.value = pretty_value.toOwnedSlice(),
|
||||
.has_matchers = property_matchers != null,
|
||||
.is_added = result == null,
|
||||
.kind = fn_name,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2689,17 +2787,20 @@ pub const Expect = struct {
|
||||
|
||||
const value: JSValue = try this.getValue(globalThis, thisValue, "toMatchSnapshot", "<green>properties<r><d>, <r>hint");
|
||||
|
||||
if (!value.isObject() and property_matchers != null) {
|
||||
const signature = comptime getSignature("toMatchSnapshot", "<green>properties<r><d>, <r>hint", false);
|
||||
return this.throw(globalThis, signature, "\n\n<b>Matcher error: <red>received<r> values must be an object when the matcher has <green>properties<r>\n", .{});
|
||||
}
|
||||
|
||||
return this.snapshot(globalThis, value, property_matchers, hint.slice(), "toMatchSnapshot");
|
||||
}
|
||||
fn matchAndFmtSnapshot(this: *Expect, globalThis: *JSGlobalObject, value: JSValue, property_matchers: ?JSValue, pretty_value: *MutableString, comptime fn_name: []const u8) bun.JSError!void {
|
||||
if (property_matchers) |_prop_matchers| {
|
||||
if (!value.isObject()) {
|
||||
const signature = comptime getSignature(fn_name, "<green>properties<r><d>, <r>hint", false);
|
||||
return this.throw(globalThis, signature, "\n\n<b>Matcher error: <red>received<r> values must be an object when the matcher has <green>properties<r>\n", .{});
|
||||
}
|
||||
|
||||
const prop_matchers = _prop_matchers;
|
||||
|
||||
if (!value.jestDeepMatch(prop_matchers, globalThis, true)) {
|
||||
// TODO: print diff with properties from propertyMatchers
|
||||
const signature = comptime getSignature("toMatchSnapshot", "<green>propertyMatchers<r>", false);
|
||||
const signature = comptime getSignature(fn_name, "<green>propertyMatchers<r>", false);
|
||||
const fmt = signature ++ "\n\nExpected <green>propertyMatchers<r> to match properties from received object" ++
|
||||
"\n\nReceived: {any}\n";
|
||||
|
||||
@@ -2708,14 +2809,17 @@ pub const Expect = struct {
|
||||
}
|
||||
}
|
||||
|
||||
var pretty_value: MutableString = MutableString.init(default_allocator, 0) catch unreachable;
|
||||
value.jestSnapshotPrettyFormat(&pretty_value, globalThis) catch {
|
||||
value.jestSnapshotPrettyFormat(pretty_value, globalThis) catch {
|
||||
var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis };
|
||||
return globalThis.throw("Failed to pretty format value: {s}", .{value.toFmt(&formatter)});
|
||||
};
|
||||
}
|
||||
fn snapshot(this: *Expect, globalThis: *JSGlobalObject, value: JSValue, property_matchers: ?JSValue, hint: []const u8, comptime fn_name: []const u8) bun.JSError!JSValue {
|
||||
var pretty_value: MutableString = try MutableString.init(default_allocator, 0);
|
||||
defer pretty_value.deinit();
|
||||
try this.matchAndFmtSnapshot(globalThis, value, property_matchers, &pretty_value, fn_name);
|
||||
|
||||
const existing_value = Jest.runner.?.snapshots.getOrPut(this, pretty_value.slice(), hint.slice()) catch |err| {
|
||||
const existing_value = Jest.runner.?.snapshots.getOrPut(this, pretty_value.slice(), hint) catch |err| {
|
||||
var formatter = JSC.ConsoleObject.Formatter{ .globalThis = globalThis };
|
||||
const test_file_path = Jest.runner.?.files.get(this.testScope().?.describe.file_id).source.path.text;
|
||||
return switch (err) {
|
||||
@@ -2734,7 +2838,7 @@ pub const Expect = struct {
|
||||
}
|
||||
|
||||
Jest.runner.?.snapshots.failed += 1;
|
||||
const signature = comptime getSignature("toMatchSnapshot", "<green>expected<r>", false);
|
||||
const signature = comptime getSignature(fn_name, "<green>expected<r>", false);
|
||||
const fmt = signature ++ "\n\n{any}\n";
|
||||
const diff_format = DiffFormatter{
|
||||
.received_string = pretty_value.slice(),
|
||||
@@ -4335,8 +4439,6 @@ pub const Expect = struct {
|
||||
pub const toHaveReturnedWith = notImplementedJSCFn;
|
||||
pub const toHaveLastReturnedWith = notImplementedJSCFn;
|
||||
pub const toHaveNthReturnedWith = notImplementedJSCFn;
|
||||
pub const toThrowErrorMatchingSnapshot = notImplementedJSCFn;
|
||||
pub const toThrowErrorMatchingInlineSnapshot = notImplementedJSCFn;
|
||||
|
||||
pub fn getStaticNot(globalThis: *JSGlobalObject, _: JSValue, _: JSValue) JSValue {
|
||||
return ExpectStatic.create(globalThis, .{ .not = true });
|
||||
|
||||
@@ -37,8 +37,10 @@ pub const Snapshots = struct {
|
||||
pub const InlineSnapshotToWrite = struct {
|
||||
line: c_ulong,
|
||||
col: c_ulong,
|
||||
value: []const u8,
|
||||
value: []const u8, // owned by Snapshots.allocator
|
||||
has_matchers: bool,
|
||||
is_added: bool,
|
||||
kind: []const u8, // static lifetime
|
||||
|
||||
fn lessThanFn(_: void, a: InlineSnapshotToWrite, b: InlineSnapshotToWrite) bool {
|
||||
if (a.line < b.line) return true;
|
||||
@@ -53,6 +55,18 @@ pub const Snapshots = struct {
|
||||
file: std.fs.File,
|
||||
};
|
||||
|
||||
pub fn addCount(this: *Snapshots, expect: *Expect, hint: []const u8) !struct { []const u8, usize } {
|
||||
this.total += 1;
|
||||
const snapshot_name = try expect.getSnapshotName(this.allocator, hint);
|
||||
const count_entry = try this.counts.getOrPut(snapshot_name);
|
||||
if (count_entry.found_existing) {
|
||||
this.allocator.free(snapshot_name);
|
||||
count_entry.value_ptr.* += 1;
|
||||
return .{ count_entry.key_ptr.*, count_entry.value_ptr.* };
|
||||
}
|
||||
count_entry.value_ptr.* = 1;
|
||||
return .{ count_entry.key_ptr.*, count_entry.value_ptr.* };
|
||||
}
|
||||
pub fn getOrPut(this: *Snapshots, expect: *Expect, target_value: []const u8, hint: string) !?string {
|
||||
switch (try this.getSnapshotFile(expect.testScope().?.describe.file_id)) {
|
||||
.result => {},
|
||||
@@ -65,21 +79,7 @@ pub const Snapshots = struct {
|
||||
},
|
||||
}
|
||||
|
||||
const snapshot_name = try expect.getSnapshotName(this.allocator, hint);
|
||||
this.total += 1;
|
||||
|
||||
const count_entry = try this.counts.getOrPut(snapshot_name);
|
||||
const counter = brk: {
|
||||
if (count_entry.found_existing) {
|
||||
this.allocator.free(snapshot_name);
|
||||
count_entry.value_ptr.* += 1;
|
||||
break :brk count_entry.value_ptr.*;
|
||||
}
|
||||
count_entry.value_ptr.* = 1;
|
||||
break :brk count_entry.value_ptr.*;
|
||||
};
|
||||
|
||||
const name = count_entry.key_ptr.*;
|
||||
const name, const counter = try this.addCount(expect, hint);
|
||||
|
||||
var counter_string_buf = [_]u8{0} ** 32;
|
||||
const counter_string = try std.fmt.bufPrint(&counter_string_buf, "{d}", .{counter});
|
||||
@@ -276,7 +276,7 @@ pub const Snapshots = struct {
|
||||
inline_snapshot_dbg("Finding byte for {}/{}", .{ ils.line, ils.col });
|
||||
const byte_offset_add = logger.Source.lineColToByteOffset(file_text[last_byte..], last_line, last_col, ils.line, ils.col) orelse {
|
||||
inline_snapshot_dbg("-> Could not find byte", .{});
|
||||
try log.addErrorFmt(&source, .{ .start = @intCast(uncommitted_segment_end) }, arena, "Failed to update inline snapshot: Could not find byte for line/column: {d}/{d}", .{ ils.line, ils.col });
|
||||
try log.addErrorFmt(&source, .{ .start = @intCast(uncommitted_segment_end) }, arena, "Failed to update inline snapshot: Ln {d}, Col {d} not found", .{ ils.line, ils.col });
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -296,7 +296,7 @@ pub const Snapshots = struct {
|
||||
},
|
||||
else => {},
|
||||
};
|
||||
const fn_name = "toMatchInlineSnapshot";
|
||||
const fn_name = ils.kind;
|
||||
if (!bun.strings.startsWith(file_text[next_start..], fn_name)) {
|
||||
try log.addErrorFmt(&source, .{ .start = @intCast(next_start) }, arena, "Failed to update inline snapshot: Could not find 'toMatchInlineSnapshot' here", .{});
|
||||
continue;
|
||||
@@ -400,6 +400,8 @@ pub const Snapshots = struct {
|
||||
try result_text.appendSlice("`");
|
||||
try bun.js_printer.writePreQuotedString(ils.value, @TypeOf(result_text_writer), result_text_writer, '`', false, false, .utf8);
|
||||
try result_text.appendSlice("`");
|
||||
|
||||
if (ils.is_added) Jest.runner.?.snapshots.added += 1;
|
||||
}
|
||||
|
||||
// commit the last segment
|
||||
|
||||
@@ -1603,7 +1603,12 @@ pub fn onClose(
|
||||
tunnel.detachAndDeref();
|
||||
}
|
||||
const in_progress = client.state.stage != .done and client.state.stage != .fail and client.state.flags.is_redirect_pending == false;
|
||||
|
||||
if (client.state.flags.is_redirect_pending) {
|
||||
// if the connection is closed and we are pending redirect just do the redirect
|
||||
// in this case we will re-connect or go to a different socket if needed
|
||||
client.doRedirect(is_ssl, if (is_ssl) &http_thread.https_context else &http_thread.http_context, socket);
|
||||
return;
|
||||
}
|
||||
if (in_progress) {
|
||||
// if the peer closed after a full chunk, treat this
|
||||
// as if the transfer had complete, browsers appear to ignore
|
||||
@@ -2888,6 +2893,7 @@ pub fn doRedirect(
|
||||
ctx: *NewHTTPContext(is_ssl),
|
||||
socket: NewHTTPContext(is_ssl).HTTPSocket,
|
||||
) void {
|
||||
log("doRedirect", .{});
|
||||
if (this.state.original_request_body == .stream) {
|
||||
// we cannot follow redirect from a stream right now
|
||||
// NOTE: we can use .tee(), reset the readable stream and cancel/wait pending write requests before redirecting. node.js just errors here so we just closeAndFail too.
|
||||
@@ -2932,6 +2938,7 @@ pub fn doRedirect(
|
||||
return;
|
||||
}
|
||||
this.state.reset(this.allocator);
|
||||
log("doRedirect state reset", .{});
|
||||
// also reset proxy to redirect
|
||||
this.flags.proxy_tunneling = false;
|
||||
if (this.proxy_tunnel) |tunnel| {
|
||||
|
||||
@@ -1246,53 +1246,10 @@ ServerResponse.prototype._implicitHeader = function () {
|
||||
this.writeHead(this.statusCode);
|
||||
};
|
||||
|
||||
function flushFirstWrite(self) {
|
||||
// headersSent = already flushed the first write
|
||||
if (self.headersSent) return;
|
||||
self.headersSent = true;
|
||||
let firstWrite = self[firstWriteSymbol];
|
||||
// at this point, the user did not call end and we have not flushed the first write
|
||||
// we need to flush it now and behave like chunked encoding
|
||||
|
||||
self._reply(
|
||||
new Response(
|
||||
new ReadableStream({
|
||||
type: "direct",
|
||||
pull: async controller => {
|
||||
self[controllerSymbol] = controller;
|
||||
if (firstWrite) {
|
||||
controller.write(firstWrite);
|
||||
await controller.flush(); // flush the first write
|
||||
}
|
||||
firstWrite = undefined;
|
||||
if (!self[finishedSymbol]) {
|
||||
const { promise, resolve } = $newPromiseCapability(GlobalPromise);
|
||||
self[deferredSymbol] = resolve;
|
||||
return await promise;
|
||||
}
|
||||
},
|
||||
}),
|
||||
{
|
||||
headers: self[headersSymbol],
|
||||
status: self.statusCode,
|
||||
statusText: self.statusMessage ?? STATUS_CODES[self.statusCode],
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
ServerResponse.prototype._write = function (chunk, encoding, callback) {
|
||||
if (this[firstWriteSymbol] === undefined && !this.headersSent) {
|
||||
this[firstWriteSymbol] = chunk;
|
||||
callback();
|
||||
const headers = this[headersSymbol];
|
||||
const hasContentLength = headers && headers.has("Content-Length");
|
||||
if (hasContentLength) {
|
||||
// wait for .end()
|
||||
return;
|
||||
}
|
||||
// We still wanna to wait for more writes if the user call 2 consecutives writes
|
||||
// but if the user delay it too much we need to flush
|
||||
setTimeout(flushFirstWrite, 1, this);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1306,17 +1263,6 @@ ServerResponse.prototype._writev = function (chunks, callback) {
|
||||
if (chunks.length === 1 && !this.headersSent && this[firstWriteSymbol] === undefined) {
|
||||
this[firstWriteSymbol] = chunks[0].chunk;
|
||||
callback();
|
||||
|
||||
const headers = this[headersSymbol];
|
||||
const hasContentLength = headers && headers.has("Content-Length");
|
||||
if (hasContentLength) {
|
||||
// wait for .end()
|
||||
return;
|
||||
}
|
||||
|
||||
// We still wanna to wait for more writes if the user call 2 consecutives writes
|
||||
// but if the user delay it too much we need to flush
|
||||
setTimeout(flushFirstWrite, 1, this);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1324,6 +1270,7 @@ ServerResponse.prototype._writev = function (chunks, callback) {
|
||||
for (const chunk of chunks) {
|
||||
controller.write(chunk.chunk);
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -99,6 +99,15 @@ function finishSocket(hasError) {
|
||||
detachSocket(this);
|
||||
this.emit("close", hasError);
|
||||
}
|
||||
|
||||
function destroyNT(self, err) {
|
||||
self.destroy(err);
|
||||
}
|
||||
function destroyWhenAborted(err) {
|
||||
if (!this.destroyed) {
|
||||
this.destroy(err.target.reason);
|
||||
}
|
||||
}
|
||||
// Provide a better error message when we call end() as a result
|
||||
// of the other side sending a FIN. The standard 'write after end'
|
||||
// is overly vague, and makes it seem like the user's code is to blame.
|
||||
@@ -479,9 +488,12 @@ const Socket = (function (InternalSocket) {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (signal) {
|
||||
signal.addEventListener("abort", () => this.destroy());
|
||||
if (signal.aborted) {
|
||||
process.nextTick(destroyNT, this, signal.reason);
|
||||
} else {
|
||||
signal.addEventListener("abort", destroyWhenAborted.bind(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5371,14 +5371,18 @@ function createNativeStreamReadable(Readable) {
|
||||
}
|
||||
|
||||
if (isClosed) {
|
||||
nativeReadable.push(null);
|
||||
ProcessNextTick(() => {
|
||||
nativeReadable.push(null);
|
||||
});
|
||||
}
|
||||
|
||||
return remainder.byteLength > 0 ? remainder : undefined;
|
||||
}
|
||||
|
||||
if (isClosed) {
|
||||
nativeReadable.push(null);
|
||||
ProcessNextTick(() => {
|
||||
nativeReadable.push(null);
|
||||
});
|
||||
}
|
||||
|
||||
return view;
|
||||
@@ -5390,7 +5394,9 @@ function createNativeStreamReadable(Readable) {
|
||||
}
|
||||
|
||||
if (isClosed) {
|
||||
nativeReadable.push(null);
|
||||
ProcessNextTick(() => {
|
||||
nativeReadable.push(null);
|
||||
});
|
||||
}
|
||||
|
||||
return view;
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
const Api = @import("./api/schema.zig").Api;
|
||||
const Options = @import("./options.zig");
|
||||
var options: Options.BundleOptions = undefined;
|
||||
|
||||
export fn init() void {
|
||||
if (!alloc.needs_setup) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export fn setOptions(options_ptr: [*c]u8, options_len: c_int) void {}
|
||||
@@ -605,7 +605,7 @@ it("should run with bun instead of npm even with leading spaces", async () => {
|
||||
env: bunEnv,
|
||||
});
|
||||
|
||||
expect(stderr.toString()).toBe("$ bun run other_script \n$ echo hi \n");
|
||||
expect(stderr.toString()).toMatch(/\$ bun(-debug)? run other_script \n\$ echo hi \n/);
|
||||
expect(stdout.toString()).toEndWith("hi\n");
|
||||
expect(exitCode).toBe(0);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Bun Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`expect() toMatchSnapshot to return undefined 1`] = `"abc"`;
|
||||
|
||||
exports[`expect() toThrowErrorMatchingSnapshot to return undefined 1`] = `undefined`;
|
||||
|
||||
exports[`expect() toThrowErrorMatchingSnapshot to return undefined: undefined 1`] = `undefined`;
|
||||
|
||||
@@ -4744,7 +4744,7 @@ describe("expect()", () => {
|
||||
expect(expect("abc").toMatch("a")).toBeUndefined();
|
||||
});
|
||||
test.todo("toMatchInlineSnapshot to return undefined", () => {
|
||||
expect(expect("abc").toMatchInlineSnapshot()).toBeUndefined();
|
||||
expect(expect("abc").toMatchInlineSnapshot('"abc"')).toBeUndefined();
|
||||
});
|
||||
test("toMatchObject to return undefined", () => {
|
||||
expect(expect({}).toMatchObject({})).toBeUndefined();
|
||||
@@ -4768,11 +4768,19 @@ describe("expect()", () => {
|
||||
}).toThrow(),
|
||||
).toBeUndefined();
|
||||
});
|
||||
test.todo("toThrowErrorMatchingInlineSnapshot to return undefined", () => {
|
||||
expect(expect(() => {}).toThrowErrorMatchingInlineSnapshot()).toBeUndefined();
|
||||
test("toThrowErrorMatchingInlineSnapshot to return undefined", () => {
|
||||
expect(
|
||||
expect(() => {
|
||||
throw 0;
|
||||
}).toThrowErrorMatchingInlineSnapshot("undefined"),
|
||||
).toBeUndefined();
|
||||
});
|
||||
test.todo("toThrowErrorMatchingSnapshot to return undefined", () => {
|
||||
expect(expect(() => {}).toThrowErrorMatchingSnapshot()).toBeUndefined();
|
||||
test("toThrowErrorMatchingSnapshot to return undefined", () => {
|
||||
expect(
|
||||
expect(() => {
|
||||
throw 0;
|
||||
}).toThrowErrorMatchingSnapshot("undefined"),
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
test(' " " to contain ""', () => {
|
||||
|
||||
@@ -587,3 +587,23 @@ exports[`snapshots unicode surrogate halves 1`] = `
|
||||
exports[\`abc 1\`] = \`"😊abc\\\`\\\${def} <20>, <20> "\`;
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`error inline snapshots 1`] = `"hello"`;
|
||||
|
||||
exports[`error inline snapshots 2`] = `undefined`;
|
||||
|
||||
exports[`error inline snapshots 3`] = `undefined`;
|
||||
|
||||
exports[`error inline snapshots 4`] = `undefined`;
|
||||
|
||||
exports[`error inline snapshots: hint 1`] = `undefined`;
|
||||
|
||||
exports[`snapshot numbering 1`] = `"item one"`;
|
||||
|
||||
exports[`snapshot numbering 2`] = `"snap"`;
|
||||
|
||||
exports[`snapshot numbering 4`] = `"snap"`;
|
||||
|
||||
exports[`snapshot numbering 6`] = `"hello"`;
|
||||
|
||||
exports[`snapshot numbering: hinted 1`] = `"hello"`;
|
||||
|
||||
@@ -483,13 +483,14 @@ class InlineSnapshotTester {
|
||||
|
||||
describe("inline snapshots", () => {
|
||||
const bad = '"bad"';
|
||||
const helper_js = /*js*/ `
|
||||
import {expect} from "bun:test";
|
||||
export function wrongFile(value) {
|
||||
expect(value).toMatchInlineSnapshot();
|
||||
}
|
||||
`;
|
||||
const tester = new InlineSnapshotTester({
|
||||
"helper.js": /*js*/ `
|
||||
import {expect} from "bun:test";
|
||||
export function wrongFile(value) {
|
||||
expect(value).toMatchInlineSnapshot();
|
||||
}
|
||||
`,
|
||||
"helper.js": helper_js,
|
||||
});
|
||||
test("changing inline snapshot", () => {
|
||||
tester.test(
|
||||
@@ -718,7 +719,7 @@ describe("inline snapshots", () => {
|
||||
tester.testError(
|
||||
{
|
||||
update: true,
|
||||
msg: "Matcher error: Inline snapshot matchers must be called from the same file as the test",
|
||||
msg: "Inline snapshot matchers must be called from the test file",
|
||||
},
|
||||
/*js*/ `
|
||||
import {wrongFile} from "./helper";
|
||||
@@ -727,5 +728,91 @@ describe("inline snapshots", () => {
|
||||
});
|
||||
`,
|
||||
);
|
||||
expect(readFileSync(tester.tmpdir + "/helper.js", "utf-8")).toBe(helper_js);
|
||||
});
|
||||
it("is right file", () => {
|
||||
tester.test(
|
||||
v => /*js*/ `
|
||||
import {wrongFile} from "./helper";
|
||||
test("cases", () => {
|
||||
expect("rightfile").toMatchInlineSnapshot(${v("", '"9"', '`"rightfile"`')});
|
||||
expect(wrongFile).toMatchInlineSnapshot(${v("", '"9"', "`[Function: wrongFile]`")});
|
||||
});
|
||||
`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("error snapshots", () => {
|
||||
expect(() => {
|
||||
throw new Error("hello");
|
||||
}).toThrowErrorMatchingInlineSnapshot(`"hello"`);
|
||||
expect(() => {
|
||||
throw 0;
|
||||
}).toThrowErrorMatchingInlineSnapshot(`undefined`);
|
||||
expect(() => {
|
||||
throw { a: "b" };
|
||||
}).toThrowErrorMatchingInlineSnapshot(`undefined`);
|
||||
expect(() => {
|
||||
throw undefined; // this one doesn't work in jest because it doesn't think the function threw
|
||||
}).toThrowErrorMatchingInlineSnapshot(`undefined`);
|
||||
});
|
||||
test("error inline snapshots", () => {
|
||||
expect(() => {
|
||||
throw new Error("hello");
|
||||
}).toThrowErrorMatchingSnapshot();
|
||||
expect(() => {
|
||||
throw 0;
|
||||
}).toThrowErrorMatchingSnapshot();
|
||||
expect(() => {
|
||||
throw { a: "b" };
|
||||
}).toThrowErrorMatchingSnapshot();
|
||||
expect(() => {
|
||||
throw undefined;
|
||||
}).toThrowErrorMatchingSnapshot();
|
||||
expect(() => {
|
||||
throw "abcdef";
|
||||
}).toThrowErrorMatchingSnapshot("hint");
|
||||
expect(() => {
|
||||
throw new Error("😊");
|
||||
}).toThrowErrorMatchingInlineSnapshot(`"😊"`);
|
||||
});
|
||||
|
||||
test("snapshot numbering", () => {
|
||||
function fails() {
|
||||
throw new Error("snap");
|
||||
}
|
||||
expect("item one").toMatchSnapshot();
|
||||
expect(fails).toThrowErrorMatchingSnapshot();
|
||||
expect("1").toMatchInlineSnapshot(`"1"`);
|
||||
expect(fails).toThrowErrorMatchingSnapshot();
|
||||
expect(fails).toThrowErrorMatchingInlineSnapshot(`"snap"`);
|
||||
expect("hello").toMatchSnapshot();
|
||||
expect("hello").toMatchSnapshot("hinted");
|
||||
});
|
||||
|
||||
test("write snapshot from filter", async () => {
|
||||
const sver = (m: string, a: boolean) => /*js*/ `
|
||||
test("mysnap", () => {
|
||||
expect("${m}").toMatchInlineSnapshot(${a ? '`"' + m + '"`' : ""});
|
||||
expect(() => {throw new Error("${m}!")}).toThrowErrorMatchingInlineSnapshot(${a ? '`"' + m + '!"`' : ""});
|
||||
})
|
||||
`;
|
||||
const dir = tempDirWithFiles("writesnapshotfromfilter", {
|
||||
"mytests": {
|
||||
"snap.test.ts": sver("a", false),
|
||||
"snap2.test.ts": sver("b", false),
|
||||
"more": {
|
||||
"testing.test.ts": sver("TEST", false),
|
||||
},
|
||||
},
|
||||
});
|
||||
await $`cd ${dir} && ${bunExe()} test mytests`;
|
||||
expect(await Bun.file(dir + "/mytests/snap.test.ts").text()).toBe(sver("a", true));
|
||||
expect(await Bun.file(dir + "/mytests/snap2.test.ts").text()).toBe(sver("b", true));
|
||||
expect(await Bun.file(dir + "/mytests/more/testing.test.ts").text()).toBe(sver("TEST", true));
|
||||
await $`cd ${dir} && ${bunExe()} test mytests`;
|
||||
expect(await Bun.file(dir + "/mytests/snap.test.ts").text()).toBe(sver("a", true));
|
||||
expect(await Bun.file(dir + "/mytests/snap2.test.ts").text()).toBe(sver("b", true));
|
||||
expect(await Bun.file(dir + "/mytests/more/testing.test.ts").text()).toBe(sver("TEST", true));
|
||||
});
|
||||
|
||||
@@ -2,8 +2,16 @@ import { udpSocket } from "bun";
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { disableAggressiveGCScope, randomPort } from "harness";
|
||||
import { dataCases, dataTypes } from "./testdata";
|
||||
import { heapStats } from "bun:jsc";
|
||||
|
||||
describe("udpSocket()", () => {
|
||||
test("connect with invalid hostname rejects", async () => {
|
||||
expect(async () =>
|
||||
udpSocket({
|
||||
connect: { hostname: "example!!!!!.com", port: 443 },
|
||||
}),
|
||||
).toThrow();
|
||||
});
|
||||
test("can create a socket", async () => {
|
||||
const socket = await udpSocket({});
|
||||
expect(socket).toBeInstanceOf(Object);
|
||||
|
||||
@@ -2347,22 +2347,6 @@ it("should emit close when connection is aborted", async () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("must set headersSent to true after headers are sent #3458", async () => {
|
||||
const server = createServer().listen(0);
|
||||
try {
|
||||
await once(server, "listening");
|
||||
fetch(`http://localhost:${server.address().port}`).then(res => res.text());
|
||||
const [req, res] = await once(server, "request");
|
||||
expect(res.headersSent).toBe(false);
|
||||
const { promise, resolve } = Promise.withResolvers();
|
||||
res.end("OK", resolve);
|
||||
await promise;
|
||||
expect(res.headersSent).toBe(true);
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("should emit timeout event", async () => {
|
||||
const server = http.createServer().listen(0);
|
||||
try {
|
||||
@@ -2441,54 +2425,6 @@ it("must set headersSent to true after headers are sent when using chunk encoded
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
it("response body streaming is immediate (#13696)", async () => {
|
||||
const totalChunks = 10;
|
||||
const spacing = 50;
|
||||
const acceptableDelay = 20;
|
||||
let totalSize = 0;
|
||||
let receivedSize = 0;
|
||||
let server: Server;
|
||||
try {
|
||||
server = createServer(async (req, res) => {
|
||||
res.writeHead(200, { "Content-Type": "text/plain" });
|
||||
for (let i = 0; i < totalChunks; i++) {
|
||||
const payload = `${new Date().getTime().toString()}\n`;
|
||||
totalSize += payload.length;
|
||||
res.write(payload);
|
||||
|
||||
if (i + 1 < totalChunks) await Bun.sleep(spacing);
|
||||
}
|
||||
res.end();
|
||||
});
|
||||
const url = await listen(server);
|
||||
const res = await fetch(url);
|
||||
const reader = res.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
let receivedChunks = 0;
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
receivedChunks++;
|
||||
receivedSize += value.byteLength;
|
||||
|
||||
// Verify that chunks are not held up longer than necessary
|
||||
// at the receiver. This is likely to be in the single digits.
|
||||
//
|
||||
// #13696: Bun would delay the initial chunks and then send multiple
|
||||
// chunks before real-time streaming started working.
|
||||
expect(new Date().getTime() - Number.parseInt(decoder.decode(value).trimEnd(), 10)).toBeLessThan(acceptableDelay);
|
||||
}
|
||||
// Verify that the correct number of chunks were sent (in case server
|
||||
// decides to send no chunks at all).
|
||||
expect(receivedChunks).toEqual(totalChunks);
|
||||
// Also verify the total size in case some data was lost.
|
||||
expect(receivedSize).toEqual(totalSize);
|
||||
} finally {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("should work when sending https.request with agent:false", async () => {
|
||||
const { promise, resolve, reject } = Promise.withResolvers();
|
||||
@@ -2548,12 +2484,13 @@ it("client should use chunked encoded if more than one write is called", async (
|
||||
req.on("error", reject);
|
||||
|
||||
// Write chunks to the request body
|
||||
req.write("Hello");
|
||||
req.write(" ");
|
||||
await sleep(100);
|
||||
req.write("World");
|
||||
req.write(" ");
|
||||
await sleep(100);
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
req.write("chunk");
|
||||
await sleep(50);
|
||||
req.write(" ");
|
||||
await sleep(50);
|
||||
}
|
||||
req.write("BUN!");
|
||||
// End the request and signal no more data will be sent
|
||||
req.end();
|
||||
@@ -2561,7 +2498,7 @@ it("client should use chunked encoded if more than one write is called", async (
|
||||
const chunks = await promise;
|
||||
expect(chunks.length).toBeGreaterThan(1);
|
||||
expect(chunks[chunks.length - 1]?.toString()).toEndWith("BUN!");
|
||||
expect(Buffer.concat(chunks).toString()).toBe("Hello World BUN!");
|
||||
expect(Buffer.concat(chunks).toString()).toBe("chunk ".repeat(4) + "BUN!");
|
||||
});
|
||||
|
||||
it("client should use content-length if only one write is called", async () => {
|
||||
|
||||
@@ -563,6 +563,39 @@ it("should not hang after destroy", async () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("should trigger error when aborted even if connection failed #13126", async () => {
|
||||
const signal = AbortSignal.timeout(100);
|
||||
const socket = createConnection({
|
||||
host: "example.com",
|
||||
port: 999,
|
||||
signal: signal,
|
||||
});
|
||||
const { promise, resolve, reject } = Promise.withResolvers();
|
||||
|
||||
socket.on("connect", reject);
|
||||
socket.on("error", resolve);
|
||||
|
||||
const err = (await promise) as Error;
|
||||
expect(err.name).toBe("TimeoutError");
|
||||
});
|
||||
|
||||
it("should trigger error when aborted even if connection failed, and the signal is already aborted #13126", async () => {
|
||||
const signal = AbortSignal.timeout(1);
|
||||
await Bun.sleep(10);
|
||||
const socket = createConnection({
|
||||
host: "example.com",
|
||||
port: 999,
|
||||
signal: signal,
|
||||
});
|
||||
const { promise, resolve, reject } = Promise.withResolvers();
|
||||
|
||||
socket.on("connect", reject);
|
||||
socket.on("error", resolve);
|
||||
|
||||
const err = (await promise) as Error;
|
||||
expect(err.name).toBe("TimeoutError");
|
||||
});
|
||||
|
||||
it.if(isWindows)(
|
||||
"should work with named pipes",
|
||||
async () => {
|
||||
|
||||
@@ -544,3 +544,26 @@ it("should emit prefinish on current tick", done => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
for (const size of [0x10, 0xffff, 0x10000, 0x1f000, 0x20000, 0x20010, 0x7ffff, 0x80000, 0xa0000, 0xa0010]) {
|
||||
it(`should emit 'readable' with null data and 'close' exactly once each, 0x${size.toString(16)} bytes`, async () => {
|
||||
const path = `${tmpdir()}/${Date.now()}.readable_and_close.txt`;
|
||||
writeFileSync(path, new Uint8Array(size));
|
||||
const stream = createReadStream(path);
|
||||
const close_resolvers = Promise.withResolvers();
|
||||
const readable_resolvers = Promise.withResolvers();
|
||||
|
||||
stream.on("close", () => {
|
||||
close_resolvers.resolve();
|
||||
});
|
||||
|
||||
stream.on("readable", () => {
|
||||
const data = stream.read();
|
||||
if (data === null) {
|
||||
readable_resolvers.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all([close_resolvers.promise, readable_resolvers.promise]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import net from "net";
|
||||
import { join } from "path";
|
||||
import { gzipSync } from "zlib";
|
||||
import { Readable } from "stream";
|
||||
import { once } from "events";
|
||||
import type { AddressInfo } from "net";
|
||||
const tmp_dir = tmpdirSync();
|
||||
|
||||
const fixture = readFileSync(join(import.meta.dir, "fetch.js.txt"), "utf8").replaceAll("\r\n", "\n");
|
||||
@@ -2259,3 +2261,59 @@ describe("fetch should allow duplex", () => {
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it("should allow to follow redirect if connection is closed, abort should work even if the socket was closed before the redirect", async () => {
|
||||
for (const type of ["normal", "delay"]) {
|
||||
await using server = net.createServer(socket => {
|
||||
let body = "";
|
||||
socket.on("data", data => {
|
||||
body += data.toString("utf8");
|
||||
|
||||
const headerEndIndex = body.indexOf("\r\n\r\n");
|
||||
if (headerEndIndex !== -1) {
|
||||
// headers received
|
||||
const headers = body.split("\r\n\r\n")[0];
|
||||
const path = headers.split("\r\n")[0].split(" ")[1];
|
||||
if (path === "/redirect") {
|
||||
socket.end(
|
||||
"HTTP/1.1 308 Permanent Redirect\r\nCache-Control: public, max-age=0, must-revalidate\r\nContent-Type: text/plain\r\nLocation: /\r\nConnection: close\r\n\r\n",
|
||||
);
|
||||
} else {
|
||||
if (type === "delay") {
|
||||
setTimeout(() => {
|
||||
if (!socket.destroyed)
|
||||
socket.end(
|
||||
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 9\r\nConnection: close\r\n\r\nHello Bun",
|
||||
);
|
||||
}, 200);
|
||||
} else {
|
||||
socket.end(
|
||||
"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 9\r\nConnection: close\r\n\r\nHello Bun",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
await once(server.listen(0), "listening");
|
||||
|
||||
try {
|
||||
const response = await fetch(`http://localhost:${(server.address() as AddressInfo).port}/redirect`, {
|
||||
signal: AbortSignal.timeout(150),
|
||||
});
|
||||
if (type === "delay") {
|
||||
console.error(response, type);
|
||||
expect.unreachable();
|
||||
} else {
|
||||
expect(response.status).toBe(200);
|
||||
expect(await response.text()).toBe("Hello Bun");
|
||||
}
|
||||
} catch (err) {
|
||||
if (type === "delay") {
|
||||
expect((err as Error).name).toBe("TimeoutError");
|
||||
} else {
|
||||
expect.unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -267,7 +267,7 @@ it("setTimeout if refreshed before run, should reschedule to run later", done =>
|
||||
let start = Date.now();
|
||||
let timer = setTimeout(() => {
|
||||
let end = Date.now();
|
||||
expect(end - start).toBeGreaterThan(149);
|
||||
expect(end - start).toBeGreaterThanOrEqual(149);
|
||||
done();
|
||||
}, 100);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user