mirror of
https://github.com/oven-sh/bun
synced 2026-02-08 09:58:55 +00:00
Compare commits
6 Commits
ciro/fix-a
...
pfg/print-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1932d440df | ||
|
|
72a6278b3f | ||
|
|
bd232189b4 | ||
|
|
87df7527bb | ||
|
|
a1f756fea9 | ||
|
|
03dfd7d96b |
@@ -693,6 +693,7 @@ pub const FormatOptions = struct {
|
||||
single_line: bool = false,
|
||||
default_indent: u16 = 0,
|
||||
error_display_level: ErrorDisplayLevel = .full,
|
||||
multiline_strings: bool = false,
|
||||
pub const ErrorDisplayLevel = enum {
|
||||
normal,
|
||||
warn,
|
||||
@@ -824,6 +825,7 @@ pub fn format2(
|
||||
.stack_check = bun.StackCheck.init(),
|
||||
.can_throw_stack_overflow = true,
|
||||
.error_display_level = options.error_display_level,
|
||||
.multiline_strings = options.multiline_strings,
|
||||
};
|
||||
defer fmt.deinit();
|
||||
const tag = try ConsoleObject.Formatter.Tag.get(vals[0], global);
|
||||
@@ -995,6 +997,7 @@ pub const Formatter = struct {
|
||||
/// If ArrayBuffer-like objects contain ascii text, the buffer is printed as a string.
|
||||
/// Set true in the error printer so that ShellError prints a more readable message.
|
||||
format_buffer_as_text: bool = false,
|
||||
multiline_strings: bool = false,
|
||||
|
||||
pub fn deinit(this: *Formatter) void {
|
||||
if (bun.take(&this.map_node)) |node| {
|
||||
@@ -2111,12 +2114,36 @@ pub const Formatter = struct {
|
||||
defer if (comptime enable_ansi_colors)
|
||||
writer.writeAll(Output.prettyFmt("<r>", true));
|
||||
|
||||
if (str.isUTF16()) {
|
||||
try this.printAs(.JSON, Writer, writer_, value, .StringObject, enable_ansi_colors);
|
||||
return;
|
||||
}
|
||||
switch (str.isUTF16()) {
|
||||
inline else => |isUTF16| {
|
||||
const encoding: bun.strings.Encoding = comptime if (isUTF16) .utf16 else .latin1;
|
||||
const slice = if (isUTF16) str.utf16() else str.latin1();
|
||||
|
||||
JSPrinter.writeJSONString(str.latin1(), Writer, writer_, .latin1) catch unreachable;
|
||||
if (this.multiline_strings and std.mem.indexOfScalar(encoding.Unit(), slice, '\n') != null) {
|
||||
writer.writeAll("\"");
|
||||
var lines = std.mem.splitScalar(encoding.Unit(), slice, '\n');
|
||||
|
||||
if (lines.next()) |line| {
|
||||
JSPrinter.writePreQuotedString(encoding, line, Writer, writer_, '"', false, true) catch {};
|
||||
}
|
||||
|
||||
while (lines.next()) |line| {
|
||||
writer.writeAll("\n");
|
||||
this.writeIndent(Writer, writer_) catch {};
|
||||
JSPrinter.writePreQuotedString(encoding, line, Writer, writer_, '"', false, true) catch {};
|
||||
}
|
||||
writer.writeAll("\"");
|
||||
} else {
|
||||
if (isUTF16) {
|
||||
try this.printAs(.JSON, Writer, writer_, value, .StringObject, enable_ansi_colors);
|
||||
return;
|
||||
}
|
||||
writer.writeAll("\"");
|
||||
JSPrinter.writePreQuotedString(encoding, @ptrCast(slice), Writer, writer_, '"', false, true) catch {};
|
||||
writer.writeAll("\"");
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
/// Version 13: Hoist `import.meta.require` definition, see #15738
|
||||
/// Version 14: Updated global defines table list.
|
||||
/// Version 15: Updated global defines table list.
|
||||
const expected_version = 15;
|
||||
/// Version 16: Emojis in strings are preserved in unicode output (node/web)
|
||||
const expected_version = 16;
|
||||
|
||||
const debug = Output.scoped(.cache, false);
|
||||
const MINIMUM_CACHE_SIZE = 50 * 1024;
|
||||
|
||||
@@ -34,6 +34,9 @@ main: []const u8 = "",
|
||||
main_is_html_entrypoint: bool = false,
|
||||
main_resolved_path: bun.String = bun.String.empty,
|
||||
main_hash: u32 = 0,
|
||||
/// Set if code overrides Bun.main to a custom value, and then reset when the VM loads a new file
|
||||
/// (e.g. when bun:test starts testing a new file)
|
||||
overridden_main: jsc.Strong.Optional = .empty,
|
||||
entry_point: ServerEntryPoint = undefined,
|
||||
origin: URL = URL{},
|
||||
node_fs: ?*bun.api.node.fs.NodeFS = null,
|
||||
@@ -1906,6 +1909,7 @@ pub fn deinit(this: *VirtualMachine) void {
|
||||
if (this.rare_data) |rare_data| {
|
||||
rare_data.deinit();
|
||||
}
|
||||
this.overridden_main.deinit();
|
||||
this.has_terminated = true;
|
||||
}
|
||||
|
||||
@@ -2084,7 +2088,10 @@ extern fn Bun__loadHTMLEntryPoint(global: *JSGlobalObject) *JSInternalPromise;
|
||||
pub fn reloadEntryPoint(this: *VirtualMachine, entry_path: []const u8) !*JSInternalPromise {
|
||||
this.has_loaded = false;
|
||||
this.main = entry_path;
|
||||
this.main_resolved_path.deref();
|
||||
this.main_resolved_path = .empty;
|
||||
this.main_hash = Watcher.getHash(entry_path);
|
||||
this.overridden_main.deinit();
|
||||
|
||||
try this.ensureDebugger(true);
|
||||
|
||||
@@ -2152,7 +2159,10 @@ export fn Bun__VirtualMachine__setOverrideModuleRunMainPromise(vm: *VirtualMachi
|
||||
pub fn reloadEntryPointForTestRunner(this: *VirtualMachine, entry_path: []const u8) !*JSInternalPromise {
|
||||
this.has_loaded = false;
|
||||
this.main = entry_path;
|
||||
this.main_resolved_path.deref();
|
||||
this.main_resolved_path = .empty;
|
||||
this.main_hash = Watcher.getHash(entry_path);
|
||||
this.overridden_main.deinit();
|
||||
|
||||
this.eventLoop().ensureWaker();
|
||||
|
||||
|
||||
@@ -47,43 +47,50 @@ pub const BunObject = struct {
|
||||
|
||||
// --- Callbacks ---
|
||||
|
||||
// --- Lazy property callbacks ---
|
||||
pub const CryptoHasher = toJSLazyPropertyCallback(Crypto.CryptoHasher.getter);
|
||||
pub const CSRF = toJSLazyPropertyCallback(Bun.getCSRFObject);
|
||||
pub const FFI = toJSLazyPropertyCallback(Bun.FFIObject.getter);
|
||||
pub const FileSystemRouter = toJSLazyPropertyCallback(Bun.getFileSystemRouter);
|
||||
pub const Glob = toJSLazyPropertyCallback(Bun.getGlobConstructor);
|
||||
pub const MD4 = toJSLazyPropertyCallback(Crypto.MD4.getter);
|
||||
pub const MD5 = toJSLazyPropertyCallback(Crypto.MD5.getter);
|
||||
pub const SHA1 = toJSLazyPropertyCallback(Crypto.SHA1.getter);
|
||||
pub const SHA224 = toJSLazyPropertyCallback(Crypto.SHA224.getter);
|
||||
pub const SHA256 = toJSLazyPropertyCallback(Crypto.SHA256.getter);
|
||||
pub const SHA384 = toJSLazyPropertyCallback(Crypto.SHA384.getter);
|
||||
pub const SHA512 = toJSLazyPropertyCallback(Crypto.SHA512.getter);
|
||||
pub const SHA512_256 = toJSLazyPropertyCallback(Crypto.SHA512_256.getter);
|
||||
pub const TOML = toJSLazyPropertyCallback(Bun.getTOMLObject);
|
||||
pub const Transpiler = toJSLazyPropertyCallback(Bun.getTranspilerConstructor);
|
||||
pub const argv = toJSLazyPropertyCallback(Bun.getArgv);
|
||||
pub const cwd = toJSLazyPropertyCallback(Bun.getCWD);
|
||||
pub const embeddedFiles = toJSLazyPropertyCallback(Bun.getEmbeddedFiles);
|
||||
pub const enableANSIColors = toJSLazyPropertyCallback(Bun.enableANSIColors);
|
||||
pub const hash = toJSLazyPropertyCallback(Bun.getHashObject);
|
||||
pub const inspect = toJSLazyPropertyCallback(Bun.getInspect);
|
||||
pub const origin = toJSLazyPropertyCallback(Bun.getOrigin);
|
||||
pub const semver = toJSLazyPropertyCallback(Bun.getSemver);
|
||||
pub const stderr = toJSLazyPropertyCallback(Bun.getStderr);
|
||||
pub const stdin = toJSLazyPropertyCallback(Bun.getStdin);
|
||||
pub const stdout = toJSLazyPropertyCallback(Bun.getStdout);
|
||||
pub const unsafe = toJSLazyPropertyCallback(Bun.getUnsafe);
|
||||
pub const S3Client = toJSLazyPropertyCallback(Bun.getS3ClientConstructor);
|
||||
pub const s3 = toJSLazyPropertyCallback(Bun.getS3DefaultClient);
|
||||
pub const ValkeyClient = toJSLazyPropertyCallback(Bun.getValkeyClientConstructor);
|
||||
pub const valkey = toJSLazyPropertyCallback(Bun.getValkeyDefaultClient);
|
||||
// --- Lazy property callbacks ---
|
||||
|
||||
// --- Getters ---
|
||||
pub const CryptoHasher = toJSGetter(Crypto.CryptoHasher.getter);
|
||||
pub const CSRF = toJSGetter(Bun.getCSRFObject);
|
||||
pub const FFI = toJSGetter(Bun.FFIObject.getter);
|
||||
pub const FileSystemRouter = toJSGetter(Bun.getFileSystemRouter);
|
||||
pub const Glob = toJSGetter(Bun.getGlobConstructor);
|
||||
pub const MD4 = toJSGetter(Crypto.MD4.getter);
|
||||
pub const MD5 = toJSGetter(Crypto.MD5.getter);
|
||||
pub const SHA1 = toJSGetter(Crypto.SHA1.getter);
|
||||
pub const SHA224 = toJSGetter(Crypto.SHA224.getter);
|
||||
pub const SHA256 = toJSGetter(Crypto.SHA256.getter);
|
||||
pub const SHA384 = toJSGetter(Crypto.SHA384.getter);
|
||||
pub const SHA512 = toJSGetter(Crypto.SHA512.getter);
|
||||
pub const SHA512_256 = toJSGetter(Crypto.SHA512_256.getter);
|
||||
pub const TOML = toJSGetter(Bun.getTOMLObject);
|
||||
pub const Transpiler = toJSGetter(Bun.getTranspilerConstructor);
|
||||
pub const argv = toJSGetter(Bun.getArgv);
|
||||
pub const cwd = toJSGetter(Bun.getCWD);
|
||||
pub const embeddedFiles = toJSGetter(Bun.getEmbeddedFiles);
|
||||
pub const enableANSIColors = toJSGetter(Bun.enableANSIColors);
|
||||
pub const hash = toJSGetter(Bun.getHashObject);
|
||||
pub const inspect = toJSGetter(Bun.getInspect);
|
||||
pub const main = toJSGetter(Bun.getMain);
|
||||
pub const origin = toJSGetter(Bun.getOrigin);
|
||||
pub const semver = toJSGetter(Bun.getSemver);
|
||||
pub const stderr = toJSGetter(Bun.getStderr);
|
||||
pub const stdin = toJSGetter(Bun.getStdin);
|
||||
pub const stdout = toJSGetter(Bun.getStdout);
|
||||
pub const unsafe = toJSGetter(Bun.getUnsafe);
|
||||
pub const S3Client = toJSGetter(Bun.getS3ClientConstructor);
|
||||
pub const s3 = toJSGetter(Bun.getS3DefaultClient);
|
||||
pub const ValkeyClient = toJSGetter(Bun.getValkeyClientConstructor);
|
||||
pub const valkey = toJSGetter(Bun.getValkeyDefaultClient);
|
||||
pub const main = Bun.getMain;
|
||||
// --- Getters ---
|
||||
|
||||
fn getterName(comptime baseName: anytype) [:0]const u8 {
|
||||
return "BunObject_getter_" ++ baseName;
|
||||
// --- Setters ---
|
||||
pub const setMain = Bun.setMain;
|
||||
// --- Setters ---
|
||||
|
||||
fn lazyPropertyCallbackName(comptime baseName: anytype) [:0]const u8 {
|
||||
return "BunObject_lazyPropCb_" ++ baseName;
|
||||
}
|
||||
|
||||
fn callbackName(comptime baseName: anytype) [:0]const u8 {
|
||||
@@ -94,10 +101,10 @@ pub const BunObject = struct {
|
||||
|
||||
const LazyPropertyCallback = fn (*jsc.JSGlobalObject, *jsc.JSObject) callconv(jsc.conv) JSValue;
|
||||
|
||||
fn toJSGetter(comptime getter: anytype) LazyPropertyCallback {
|
||||
fn toJSLazyPropertyCallback(comptime wrapped: anytype) LazyPropertyCallback {
|
||||
return struct {
|
||||
pub fn callback(this: *jsc.JSGlobalObject, object: *jsc.JSObject) callconv(jsc.conv) JSValue {
|
||||
return bun.jsc.toJSHostCall(this, @src(), getter, .{ this, object });
|
||||
return bun.jsc.toJSHostCall(this, @src(), wrapped, .{ this, object });
|
||||
}
|
||||
}.callback;
|
||||
}
|
||||
@@ -107,43 +114,42 @@ pub const BunObject = struct {
|
||||
@compileError("Must be comptime");
|
||||
}
|
||||
|
||||
// --- Getters ---
|
||||
@export(&BunObject.CryptoHasher, .{ .name = getterName("CryptoHasher") });
|
||||
@export(&BunObject.CSRF, .{ .name = getterName("CSRF") });
|
||||
@export(&BunObject.FFI, .{ .name = getterName("FFI") });
|
||||
@export(&BunObject.FileSystemRouter, .{ .name = getterName("FileSystemRouter") });
|
||||
@export(&BunObject.MD4, .{ .name = getterName("MD4") });
|
||||
@export(&BunObject.MD5, .{ .name = getterName("MD5") });
|
||||
@export(&BunObject.SHA1, .{ .name = getterName("SHA1") });
|
||||
@export(&BunObject.SHA224, .{ .name = getterName("SHA224") });
|
||||
@export(&BunObject.SHA256, .{ .name = getterName("SHA256") });
|
||||
@export(&BunObject.SHA384, .{ .name = getterName("SHA384") });
|
||||
@export(&BunObject.SHA512, .{ .name = getterName("SHA512") });
|
||||
@export(&BunObject.SHA512_256, .{ .name = getterName("SHA512_256") });
|
||||
// --- Lazy property callbacks ---
|
||||
@export(&BunObject.CryptoHasher, .{ .name = lazyPropertyCallbackName("CryptoHasher") });
|
||||
@export(&BunObject.CSRF, .{ .name = lazyPropertyCallbackName("CSRF") });
|
||||
@export(&BunObject.FFI, .{ .name = lazyPropertyCallbackName("FFI") });
|
||||
@export(&BunObject.FileSystemRouter, .{ .name = lazyPropertyCallbackName("FileSystemRouter") });
|
||||
@export(&BunObject.MD4, .{ .name = lazyPropertyCallbackName("MD4") });
|
||||
@export(&BunObject.MD5, .{ .name = lazyPropertyCallbackName("MD5") });
|
||||
@export(&BunObject.SHA1, .{ .name = lazyPropertyCallbackName("SHA1") });
|
||||
@export(&BunObject.SHA224, .{ .name = lazyPropertyCallbackName("SHA224") });
|
||||
@export(&BunObject.SHA256, .{ .name = lazyPropertyCallbackName("SHA256") });
|
||||
@export(&BunObject.SHA384, .{ .name = lazyPropertyCallbackName("SHA384") });
|
||||
@export(&BunObject.SHA512, .{ .name = lazyPropertyCallbackName("SHA512") });
|
||||
@export(&BunObject.SHA512_256, .{ .name = lazyPropertyCallbackName("SHA512_256") });
|
||||
|
||||
@export(&BunObject.TOML, .{ .name = getterName("TOML") });
|
||||
@export(&BunObject.Glob, .{ .name = getterName("Glob") });
|
||||
@export(&BunObject.Transpiler, .{ .name = getterName("Transpiler") });
|
||||
@export(&BunObject.argv, .{ .name = getterName("argv") });
|
||||
@export(&BunObject.cwd, .{ .name = getterName("cwd") });
|
||||
@export(&BunObject.enableANSIColors, .{ .name = getterName("enableANSIColors") });
|
||||
@export(&BunObject.hash, .{ .name = getterName("hash") });
|
||||
@export(&BunObject.inspect, .{ .name = getterName("inspect") });
|
||||
@export(&BunObject.main, .{ .name = getterName("main") });
|
||||
@export(&BunObject.origin, .{ .name = getterName("origin") });
|
||||
@export(&BunObject.stderr, .{ .name = getterName("stderr") });
|
||||
@export(&BunObject.stdin, .{ .name = getterName("stdin") });
|
||||
@export(&BunObject.stdout, .{ .name = getterName("stdout") });
|
||||
@export(&BunObject.unsafe, .{ .name = getterName("unsafe") });
|
||||
@export(&BunObject.semver, .{ .name = getterName("semver") });
|
||||
@export(&BunObject.embeddedFiles, .{ .name = getterName("embeddedFiles") });
|
||||
@export(&BunObject.S3Client, .{ .name = getterName("S3Client") });
|
||||
@export(&BunObject.s3, .{ .name = getterName("s3") });
|
||||
@export(&BunObject.ValkeyClient, .{ .name = getterName("ValkeyClient") });
|
||||
@export(&BunObject.valkey, .{ .name = getterName("valkey") });
|
||||
// --- Getters --
|
||||
@export(&BunObject.TOML, .{ .name = lazyPropertyCallbackName("TOML") });
|
||||
@export(&BunObject.Glob, .{ .name = lazyPropertyCallbackName("Glob") });
|
||||
@export(&BunObject.Transpiler, .{ .name = lazyPropertyCallbackName("Transpiler") });
|
||||
@export(&BunObject.argv, .{ .name = lazyPropertyCallbackName("argv") });
|
||||
@export(&BunObject.cwd, .{ .name = lazyPropertyCallbackName("cwd") });
|
||||
@export(&BunObject.enableANSIColors, .{ .name = lazyPropertyCallbackName("enableANSIColors") });
|
||||
@export(&BunObject.hash, .{ .name = lazyPropertyCallbackName("hash") });
|
||||
@export(&BunObject.inspect, .{ .name = lazyPropertyCallbackName("inspect") });
|
||||
@export(&BunObject.origin, .{ .name = lazyPropertyCallbackName("origin") });
|
||||
@export(&BunObject.stderr, .{ .name = lazyPropertyCallbackName("stderr") });
|
||||
@export(&BunObject.stdin, .{ .name = lazyPropertyCallbackName("stdin") });
|
||||
@export(&BunObject.stdout, .{ .name = lazyPropertyCallbackName("stdout") });
|
||||
@export(&BunObject.unsafe, .{ .name = lazyPropertyCallbackName("unsafe") });
|
||||
@export(&BunObject.semver, .{ .name = lazyPropertyCallbackName("semver") });
|
||||
@export(&BunObject.embeddedFiles, .{ .name = lazyPropertyCallbackName("embeddedFiles") });
|
||||
@export(&BunObject.S3Client, .{ .name = lazyPropertyCallbackName("S3Client") });
|
||||
@export(&BunObject.s3, .{ .name = lazyPropertyCallbackName("s3") });
|
||||
@export(&BunObject.ValkeyClient, .{ .name = lazyPropertyCallbackName("ValkeyClient") });
|
||||
@export(&BunObject.valkey, .{ .name = lazyPropertyCallbackName("valkey") });
|
||||
// --- Lazy property callbacks ---
|
||||
|
||||
// -- Callbacks --
|
||||
// --- Callbacks ---
|
||||
@export(&BunObject.allocUnsafe, .{ .name = callbackName("allocUnsafe") });
|
||||
@export(&BunObject.build, .{ .name = callbackName("build") });
|
||||
@export(&BunObject.color, .{ .name = callbackName("color") });
|
||||
@@ -178,7 +184,15 @@ pub const BunObject = struct {
|
||||
@export(&BunObject.zstdDecompressSync, .{ .name = callbackName("zstdDecompressSync") });
|
||||
@export(&BunObject.zstdCompress, .{ .name = callbackName("zstdCompress") });
|
||||
@export(&BunObject.zstdDecompress, .{ .name = callbackName("zstdDecompress") });
|
||||
// -- Callbacks --
|
||||
// --- Callbacks ---
|
||||
|
||||
// --- Getters ---
|
||||
@export(&BunObject.main, .{ .name = "BunObject_getter_main" });
|
||||
// --- Getters ---
|
||||
|
||||
// --- Setters ---
|
||||
@export(&BunObject.setMain, .{ .name = "BunObject_setter_main" });
|
||||
// --- Setters ---
|
||||
}
|
||||
};
|
||||
|
||||
@@ -576,8 +590,10 @@ pub fn enableANSIColors(globalThis: *jsc.JSGlobalObject, _: *jsc.JSObject) jsc.J
|
||||
return JSValue.jsBoolean(Output.enable_ansi_colors);
|
||||
}
|
||||
|
||||
pub fn getMain(globalThis: *jsc.JSGlobalObject, _: *jsc.JSObject) jsc.JSValue {
|
||||
fn getMain(globalThis: *jsc.JSGlobalObject) callconv(jsc.conv) jsc.JSValue {
|
||||
const vm = globalThis.bunVM();
|
||||
// If JS has set it to a custom value, use that one
|
||||
if (vm.overridden_main.get()) |overridden_main| return overridden_main;
|
||||
|
||||
// Attempt to use the resolved filesystem path
|
||||
// This makes `eval('require.main === module')` work when the main module is a symlink.
|
||||
@@ -626,6 +642,11 @@ pub fn getMain(globalThis: *jsc.JSGlobalObject, _: *jsc.JSObject) jsc.JSValue {
|
||||
return ZigString.init(vm.main).toJS(globalThis);
|
||||
}
|
||||
|
||||
fn setMain(global_this: *jsc.JSGlobalObject, new_value: JSValue) callconv(jsc.conv) bool {
|
||||
global_this.bunVM().overridden_main.set(global_this, new_value);
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn getArgv(globalThis: *jsc.JSGlobalObject, _: *jsc.JSObject) jsc.JSValue {
|
||||
return node.process.getArgv(globalThis);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
macro(enableANSIColors) \
|
||||
macro(hash) \
|
||||
macro(inspect) \
|
||||
macro(main) \
|
||||
macro(origin) \
|
||||
macro(s3) \
|
||||
macro(semver) \
|
||||
@@ -83,12 +82,14 @@
|
||||
FOR_EACH_CALLBACK(DECLARE_ZIG_BUN_OBJECT_CALLBACK);
|
||||
#undef DECLARE_ZIG_BUN_OBJECT_CALLBACK
|
||||
|
||||
#define DECLARE_ZIG_BUN_OBJECT_GETTER(name) extern "C" JSC::EncodedJSValue SYSV_ABI BunObject_getter_##name(JSC::JSGlobalObject*, JSC::JSObject*);
|
||||
// declaration for the exported function in BunObject.zig
|
||||
#define DECLARE_ZIG_BUN_OBJECT_GETTER(name) extern "C" JSC::EncodedJSValue SYSV_ABI BunObject_lazyPropCb_##name(JSC::JSGlobalObject*, JSC::JSObject*);
|
||||
FOR_EACH_GETTER(DECLARE_ZIG_BUN_OBJECT_GETTER);
|
||||
#undef DECLARE_ZIG_BUN_OBJECT_GETTER
|
||||
|
||||
#define DEFINE_ZIG_BUN_OBJECT_GETTER_WRAPPER(name) static JSC::JSValue BunObject_getter_wrap_##name(JSC::VM &vm, JSC::JSObject *object) { \
|
||||
return JSC::JSValue::decode(BunObject_getter_##name(object->globalObject(), object)); \
|
||||
// definition of the C++ wrapper to call the Zig function
|
||||
#define DEFINE_ZIG_BUN_OBJECT_GETTER_WRAPPER(name) static JSC::JSValue BunObject_lazyPropCb_wrap_##name(JSC::VM &vm, JSC::JSObject *object) { \
|
||||
return JSC::JSValue::decode(BunObject_lazyPropCb_##name(object->globalObject(), object)); \
|
||||
} \
|
||||
|
||||
FOR_EACH_GETTER(DEFINE_ZIG_BUN_OBJECT_GETTER_WRAPPER);
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Bun {
|
||||
|
||||
extern "C" bool has_bun_garbage_collector_flag_enabled;
|
||||
|
||||
static JSValue BunObject_getter_wrap_ArrayBufferSink(VM& vm, JSObject* bunObject)
|
||||
static JSValue BunObject_lazyPropCb_wrap_ArrayBufferSink(VM& vm, JSObject* bunObject)
|
||||
{
|
||||
return jsCast<Zig::GlobalObject*>(bunObject->globalObject())->ArrayBufferSink();
|
||||
}
|
||||
@@ -694,61 +694,61 @@ JSC_DEFINE_HOST_FUNCTION(functionFileURLToPath, (JSC::JSGlobalObject * globalObj
|
||||
/* Source for BunObject.lut.h
|
||||
@begin bunObjectTable
|
||||
$ constructBunShell DontDelete|PropertyCallback
|
||||
ArrayBufferSink BunObject_getter_wrap_ArrayBufferSink DontDelete|PropertyCallback
|
||||
Cookie constructCookieObject DontDelete|ReadOnly|PropertyCallback
|
||||
CookieMap constructCookieMapObject DontDelete|ReadOnly|PropertyCallback
|
||||
CryptoHasher BunObject_getter_wrap_CryptoHasher DontDelete|PropertyCallback
|
||||
FFI BunObject_getter_wrap_FFI DontDelete|PropertyCallback
|
||||
FileSystemRouter BunObject_getter_wrap_FileSystemRouter DontDelete|PropertyCallback
|
||||
Glob BunObject_getter_wrap_Glob DontDelete|PropertyCallback
|
||||
MD4 BunObject_getter_wrap_MD4 DontDelete|PropertyCallback
|
||||
MD5 BunObject_getter_wrap_MD5 DontDelete|PropertyCallback
|
||||
SHA1 BunObject_getter_wrap_SHA1 DontDelete|PropertyCallback
|
||||
SHA224 BunObject_getter_wrap_SHA224 DontDelete|PropertyCallback
|
||||
SHA256 BunObject_getter_wrap_SHA256 DontDelete|PropertyCallback
|
||||
SHA384 BunObject_getter_wrap_SHA384 DontDelete|PropertyCallback
|
||||
SHA512 BunObject_getter_wrap_SHA512 DontDelete|PropertyCallback
|
||||
SHA512_256 BunObject_getter_wrap_SHA512_256 DontDelete|PropertyCallback
|
||||
TOML BunObject_getter_wrap_TOML DontDelete|PropertyCallback
|
||||
Transpiler BunObject_getter_wrap_Transpiler DontDelete|PropertyCallback
|
||||
embeddedFiles BunObject_getter_wrap_embeddedFiles DontDelete|PropertyCallback
|
||||
S3Client BunObject_getter_wrap_S3Client DontDelete|PropertyCallback
|
||||
s3 BunObject_getter_wrap_s3 DontDelete|PropertyCallback
|
||||
CSRF BunObject_getter_wrap_CSRF DontDelete|PropertyCallback
|
||||
ArrayBufferSink BunObject_lazyPropCb_wrap_ArrayBufferSink DontDelete|PropertyCallback
|
||||
Cookie constructCookieObject DontDelete|ReadOnly|PropertyCallback
|
||||
CookieMap constructCookieMapObject DontDelete|ReadOnly|PropertyCallback
|
||||
CryptoHasher BunObject_lazyPropCb_wrap_CryptoHasher DontDelete|PropertyCallback
|
||||
FFI BunObject_lazyPropCb_wrap_FFI DontDelete|PropertyCallback
|
||||
FileSystemRouter BunObject_lazyPropCb_wrap_FileSystemRouter DontDelete|PropertyCallback
|
||||
Glob BunObject_lazyPropCb_wrap_Glob DontDelete|PropertyCallback
|
||||
MD4 BunObject_lazyPropCb_wrap_MD4 DontDelete|PropertyCallback
|
||||
MD5 BunObject_lazyPropCb_wrap_MD5 DontDelete|PropertyCallback
|
||||
SHA1 BunObject_lazyPropCb_wrap_SHA1 DontDelete|PropertyCallback
|
||||
SHA224 BunObject_lazyPropCb_wrap_SHA224 DontDelete|PropertyCallback
|
||||
SHA256 BunObject_lazyPropCb_wrap_SHA256 DontDelete|PropertyCallback
|
||||
SHA384 BunObject_lazyPropCb_wrap_SHA384 DontDelete|PropertyCallback
|
||||
SHA512 BunObject_lazyPropCb_wrap_SHA512 DontDelete|PropertyCallback
|
||||
SHA512_256 BunObject_lazyPropCb_wrap_SHA512_256 DontDelete|PropertyCallback
|
||||
TOML BunObject_lazyPropCb_wrap_TOML DontDelete|PropertyCallback
|
||||
Transpiler BunObject_lazyPropCb_wrap_Transpiler DontDelete|PropertyCallback
|
||||
embeddedFiles BunObject_lazyPropCb_wrap_embeddedFiles DontDelete|PropertyCallback
|
||||
S3Client BunObject_lazyPropCb_wrap_S3Client DontDelete|PropertyCallback
|
||||
s3 BunObject_lazyPropCb_wrap_s3 DontDelete|PropertyCallback
|
||||
CSRF BunObject_lazyPropCb_wrap_CSRF DontDelete|PropertyCallback
|
||||
allocUnsafe BunObject_callback_allocUnsafe DontDelete|Function 1
|
||||
argv BunObject_getter_wrap_argv DontDelete|PropertyCallback
|
||||
argv BunObject_lazyPropCb_wrap_argv DontDelete|PropertyCallback
|
||||
build BunObject_callback_build DontDelete|Function 1
|
||||
concatArrayBuffers functionConcatTypedArrays DontDelete|Function 3
|
||||
connect BunObject_callback_connect DontDelete|Function 1
|
||||
cwd BunObject_getter_wrap_cwd DontEnum|DontDelete|PropertyCallback
|
||||
cwd BunObject_lazyPropCb_wrap_cwd DontEnum|DontDelete|PropertyCallback
|
||||
color BunObject_callback_color DontDelete|Function 2
|
||||
deepEquals functionBunDeepEquals DontDelete|Function 2
|
||||
deepMatch functionBunDeepMatch DontDelete|Function 2
|
||||
deflateSync BunObject_callback_deflateSync DontDelete|Function 1
|
||||
deflateSync BunObject_callback_deflateSync DontDelete|Function 1
|
||||
dns constructDNSObject ReadOnly|DontDelete|PropertyCallback
|
||||
enableANSIColors BunObject_getter_wrap_enableANSIColors DontDelete|PropertyCallback
|
||||
enableANSIColors BunObject_lazyPropCb_wrap_enableANSIColors DontDelete|PropertyCallback
|
||||
env constructEnvObject ReadOnly|DontDelete|PropertyCallback
|
||||
escapeHTML functionBunEscapeHTML DontDelete|Function 2
|
||||
fetch constructBunFetchObject ReadOnly|DontDelete|PropertyCallback
|
||||
file BunObject_callback_file DontDelete|Function 1
|
||||
fileURLToPath functionFileURLToPath DontDelete|Function 1
|
||||
fetch constructBunFetchObject ReadOnly|DontDelete|PropertyCallback
|
||||
file BunObject_callback_file DontDelete|Function 1
|
||||
fileURLToPath functionFileURLToPath DontDelete|Function 1
|
||||
gc Generated::BunObject::jsGc DontDelete|Function 1
|
||||
generateHeapSnapshot functionGenerateHeapSnapshot DontDelete|Function 1
|
||||
gunzipSync BunObject_callback_gunzipSync DontDelete|Function 1
|
||||
gzipSync BunObject_callback_gzipSync DontDelete|Function 1
|
||||
hash BunObject_getter_wrap_hash DontDelete|PropertyCallback
|
||||
hash BunObject_lazyPropCb_wrap_hash DontDelete|PropertyCallback
|
||||
indexOfLine BunObject_callback_indexOfLine DontDelete|Function 1
|
||||
inflateSync BunObject_callback_inflateSync DontDelete|Function 1
|
||||
inspect BunObject_getter_wrap_inspect DontDelete|PropertyCallback
|
||||
inspect BunObject_lazyPropCb_wrap_inspect DontDelete|PropertyCallback
|
||||
isMainThread constructIsMainThread ReadOnly|DontDelete|PropertyCallback
|
||||
jest BunObject_callback_jest DontEnum|DontDelete|Function 1
|
||||
listen BunObject_callback_listen DontDelete|Function 1
|
||||
udpSocket BunObject_callback_udpSocket DontDelete|Function 1
|
||||
main BunObject_getter_wrap_main DontDelete|PropertyCallback
|
||||
udpSocket BunObject_callback_udpSocket DontDelete|Function 1
|
||||
main bunObjectMain DontDelete|CustomAccessor
|
||||
mmap BunObject_callback_mmap DontDelete|Function 1
|
||||
nanoseconds functionBunNanoseconds DontDelete|Function 0
|
||||
openInEditor BunObject_callback_openInEditor DontDelete|Function 1
|
||||
origin BunObject_getter_wrap_origin DontEnum|ReadOnly|DontDelete|PropertyCallback
|
||||
origin BunObject_lazyPropCb_wrap_origin DontEnum|ReadOnly|DontDelete|PropertyCallback
|
||||
version_with_sha constructBunVersionWithSha DontEnum|ReadOnly|DontDelete|PropertyCallback
|
||||
password constructPasswordObject DontDelete|PropertyCallback
|
||||
pathToFileURL functionPathToFileURL DontDelete|Function 1
|
||||
@@ -767,7 +767,7 @@ JSC_DEFINE_HOST_FUNCTION(functionFileURLToPath, (JSC::JSGlobalObject * globalObj
|
||||
resolve BunObject_callback_resolve DontDelete|Function 1
|
||||
resolveSync BunObject_callback_resolveSync DontDelete|Function 1
|
||||
revision constructBunRevision ReadOnly|DontDelete|PropertyCallback
|
||||
semver BunObject_getter_wrap_semver ReadOnly|DontDelete|PropertyCallback
|
||||
semver BunObject_lazyPropCb_wrap_semver ReadOnly|DontDelete|PropertyCallback
|
||||
sql defaultBunSQLObject DontDelete|PropertyCallback
|
||||
postgres defaultBunSQLObject DontDelete|PropertyCallback
|
||||
SQL constructBunSQLObject DontDelete|PropertyCallback
|
||||
@@ -778,15 +778,15 @@ JSC_DEFINE_HOST_FUNCTION(functionFileURLToPath, (JSC::JSGlobalObject * globalObj
|
||||
sleepSync BunObject_callback_sleepSync DontDelete|Function 1
|
||||
spawn BunObject_callback_spawn DontDelete|Function 1
|
||||
spawnSync BunObject_callback_spawnSync DontDelete|Function 1
|
||||
stderr BunObject_getter_wrap_stderr DontDelete|PropertyCallback
|
||||
stdin BunObject_getter_wrap_stdin DontDelete|PropertyCallback
|
||||
stdout BunObject_getter_wrap_stdout DontDelete|PropertyCallback
|
||||
stderr BunObject_lazyPropCb_wrap_stderr DontDelete|PropertyCallback
|
||||
stdin BunObject_lazyPropCb_wrap_stdin DontDelete|PropertyCallback
|
||||
stdout BunObject_lazyPropCb_wrap_stdout DontDelete|PropertyCallback
|
||||
stringWidth Generated::BunObject::jsStringWidth DontDelete|Function 2
|
||||
unsafe BunObject_getter_wrap_unsafe DontDelete|PropertyCallback
|
||||
unsafe BunObject_lazyPropCb_wrap_unsafe DontDelete|PropertyCallback
|
||||
version constructBunVersion ReadOnly|DontDelete|PropertyCallback
|
||||
which BunObject_callback_which DontDelete|Function 1
|
||||
RedisClient BunObject_getter_wrap_ValkeyClient DontDelete|PropertyCallback
|
||||
redis BunObject_getter_wrap_valkey DontDelete|PropertyCallback
|
||||
RedisClient BunObject_lazyPropCb_wrap_ValkeyClient DontDelete|PropertyCallback
|
||||
redis BunObject_lazyPropCb_wrap_valkey DontDelete|PropertyCallback
|
||||
write BunObject_callback_write DontDelete|Function 1
|
||||
zstdCompressSync BunObject_callback_zstdCompressSync DontDelete|Function 1
|
||||
zstdDecompressSync BunObject_callback_zstdDecompressSync DontDelete|Function 1
|
||||
@@ -835,6 +835,23 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" JSC::EncodedJSValue SYSV_ABI BunObject_getter_main(JSC::JSGlobalObject*);
|
||||
extern "C" bool SYSV_ABI BunObject_setter_main(JSC::JSGlobalObject*, JSC::EncodedJSValue);
|
||||
|
||||
static JSC_DEFINE_CUSTOM_GETTER(bunObjectMain, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisEncoded, PropertyName propertyName))
|
||||
{
|
||||
(void)thisEncoded;
|
||||
(void)propertyName;
|
||||
return BunObject_getter_main(globalObject);
|
||||
}
|
||||
|
||||
static JSC_DEFINE_CUSTOM_SETTER(setBunObjectMain, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisEncoded, JSC::EncodedJSValue encodedValue, PropertyName propertyName))
|
||||
{
|
||||
(void)thisEncoded;
|
||||
(void)propertyName;
|
||||
return BunObject_setter_main(globalObject, encodedValue);
|
||||
}
|
||||
|
||||
#define bunObjectReadableStreamToArrayCodeGenerator WebCore::readableStreamReadableStreamToArrayCodeGenerator
|
||||
#define bunObjectReadableStreamToArrayBufferCodeGenerator WebCore::readableStreamReadableStreamToArrayBufferCodeGenerator
|
||||
#define bunObjectReadableStreamToBytesCodeGenerator WebCore::readableStreamReadableStreamToBytesCodeGenerator
|
||||
@@ -900,7 +917,7 @@ static void exportBunObject(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC:
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace Bun
|
||||
|
||||
namespace Zig {
|
||||
void generateNativeModule_BunObject(JSC::JSGlobalObject* lexicalGlobalObject,
|
||||
@@ -924,4 +941,4 @@ void generateNativeModule_BunObject(JSC::JSGlobalObject* lexicalGlobalObject,
|
||||
Bun::exportBunObject(vm, globalObject, object, exportNames, exportValues);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace Zig
|
||||
|
||||
@@ -535,6 +535,15 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, (JSC::JSGlobalObject * globalOb
|
||||
#endif
|
||||
|
||||
if (callCountAtStart != globalObject->napiModuleRegisterCallCount) {
|
||||
// Module self-registered via static constructor
|
||||
if (globalObject->m_pendingNapiModule) {
|
||||
// Execute the stored registration function now that dlopen has completed
|
||||
Napi::executePendingNapiModule(globalObject);
|
||||
|
||||
// Clear the pending module
|
||||
globalObject->m_pendingNapiModule = {};
|
||||
}
|
||||
|
||||
JSValue resultValue = globalObject->m_pendingNapiModuleAndExports[0].get();
|
||||
globalObject->napiModuleRegisterCallCount = 0;
|
||||
globalObject->m_pendingNapiModuleAndExports[0].clear();
|
||||
|
||||
@@ -643,6 +643,9 @@ public:
|
||||
// We will add it to the resulting napi value.
|
||||
void* m_pendingNapiModuleDlopenHandle = nullptr;
|
||||
|
||||
// Store the napi module struct to defer calling nm_register_func until after dlopen completes
|
||||
std::optional<napi_module> m_pendingNapiModule = {};
|
||||
|
||||
JSObject* nodeErrorCache() const { return m_nodeErrorCache.getInitializedOnMainThread(this); }
|
||||
|
||||
Structure* memoryFootprintStructure()
|
||||
|
||||
@@ -680,19 +680,20 @@ extern "C" napi_status napi_get_named_property(napi_env env, napi_value object,
|
||||
}
|
||||
|
||||
extern "C" size_t Bun__napi_module_register_count;
|
||||
extern "C" void napi_module_register(napi_module* mod)
|
||||
void Napi::executePendingNapiModule(Zig::GlobalObject* globalObject)
|
||||
{
|
||||
Zig::GlobalObject* globalObject = defaultGlobalObject();
|
||||
napi_env env = globalObject->makeNapiEnv(*mod);
|
||||
JSC::VM& vm = JSC::getVM(globalObject);
|
||||
auto keyStr = WTF::String::fromUTF8(mod->nm_modname);
|
||||
globalObject->napiModuleRegisterCallCount++;
|
||||
Bun__napi_module_register_count++;
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
|
||||
ASSERT(globalObject->m_pendingNapiModule);
|
||||
|
||||
auto& mod = *globalObject->m_pendingNapiModule;
|
||||
napi_env env = globalObject->makeNapiEnv(mod);
|
||||
auto keyStr = WTF::String::fromUTF8(mod.nm_modname);
|
||||
JSValue pendingNapiModule = globalObject->m_pendingNapiModuleAndExports[0].get();
|
||||
JSObject* object = (pendingNapiModule && pendingNapiModule.isObject()) ? pendingNapiModule.getObject()
|
||||
: nullptr;
|
||||
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
JSC::Strong<JSC::JSObject> strongExportsObject;
|
||||
|
||||
if (!object) {
|
||||
@@ -715,8 +716,8 @@ extern "C" void napi_module_register(napi_module* mod)
|
||||
Bun::NapiHandleScope handleScope(globalObject);
|
||||
JSValue resultValue;
|
||||
|
||||
if (mod->nm_register_func) {
|
||||
resultValue = toJS(mod->nm_register_func(env, toNapi(object, globalObject)));
|
||||
if (mod.nm_register_func) {
|
||||
resultValue = toJS(mod.nm_register_func(env, toNapi(object, globalObject)));
|
||||
} else {
|
||||
JSValue errorInstance = createError(globalObject, makeString("Module has no declared entry point."_s));
|
||||
globalObject->m_pendingNapiModuleAndExports[0].set(vm, globalObject, errorInstance);
|
||||
@@ -757,6 +758,25 @@ extern "C" void napi_module_register(napi_module* mod)
|
||||
globalObject->m_pendingNapiModuleAndExports[1].set(vm, globalObject, object);
|
||||
}
|
||||
|
||||
extern "C" void napi_module_register(napi_module* mod)
|
||||
{
|
||||
Zig::GlobalObject* globalObject = defaultGlobalObject();
|
||||
JSC::VM& vm = JSC::getVM(globalObject);
|
||||
// Increment this one even if the module is invalid so that functionDlopen
|
||||
// knows that napi_module_register was attempted
|
||||
globalObject->napiModuleRegisterCallCount++;
|
||||
|
||||
// Store the entire module struct to be processed after dlopen completes
|
||||
if (mod && mod->nm_register_func) {
|
||||
globalObject->m_pendingNapiModule = *mod;
|
||||
// Increment the counter to signal that a module registered itself
|
||||
Bun__napi_module_register_count++;
|
||||
} else {
|
||||
JSValue errorInstance = createError(globalObject, makeString("Module has no declared entry point."_s));
|
||||
globalObject->m_pendingNapiModuleAndExports[0].set(vm, globalObject, errorInstance);
|
||||
}
|
||||
}
|
||||
|
||||
static void wrap_cleanup(napi_env env, void* data, void* hint)
|
||||
{
|
||||
auto* ref = reinterpret_cast<NapiRef*>(data);
|
||||
|
||||
@@ -316,6 +316,7 @@ class JSSourceCode;
|
||||
}
|
||||
|
||||
namespace Napi {
|
||||
|
||||
JSC::SourceCode generateSourceCode(WTF::String keyString, JSC::VM& vm, JSC::JSObject* object, JSC::JSGlobalObject* globalObject);
|
||||
|
||||
class NapiRefWeakHandleOwner final : public JSC::WeakHandleOwner {
|
||||
@@ -341,6 +342,11 @@ public:
|
||||
return jscWeakValueHandleOwner;
|
||||
}
|
||||
};
|
||||
|
||||
// If a module registered itself by calling napi_module_register in a static constructor, run this
|
||||
// to run the module's entrypoint.
|
||||
void executePendingNapiModule(Zig::GlobalObject* globalObject);
|
||||
|
||||
}
|
||||
|
||||
namespace Zig {
|
||||
|
||||
@@ -422,7 +422,7 @@ pub const Snapshots = struct {
|
||||
if (needs_pre_comma) try result_text.appendSlice(", ");
|
||||
const result_text_writer = result_text.writer();
|
||||
try result_text.appendSlice("`");
|
||||
try bun.js_printer.writePreQuotedString(re_indented, @TypeOf(result_text_writer), result_text_writer, '`', false, false, .utf8);
|
||||
try bun.js_printer.writePreQuotedString(.utf8, re_indented, @TypeOf(result_text_writer), result_text_writer, '`', false, false);
|
||||
try result_text.appendSlice("`");
|
||||
|
||||
if (ils.is_added) Jest.runner.?.snapshots.added += 1;
|
||||
|
||||
@@ -101,7 +101,7 @@ pub fn writeEscapedJSON(index: u32, graph: *const Graph, linker_graph: *const Li
|
||||
var bytes = std.ArrayList(u8).init(allocator);
|
||||
defer bytes.deinit();
|
||||
try write(index, graph, linker_graph, chunks, bytes.writer());
|
||||
try bun.js_printer.writePreQuotedString(bytes.items, @TypeOf(writer), writer, '"', false, true, .utf8);
|
||||
try bun.js_printer.writePreQuotedString(.utf8, bytes.items, @TypeOf(writer), writer, '"', false, true);
|
||||
}
|
||||
|
||||
fn escapedJSONFormatter(this: HTMLImportManifest, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) bun.OOM!void {
|
||||
|
||||
@@ -231,7 +231,7 @@ const JSONFormatterUTF8 = struct {
|
||||
if (self.opts.quote) {
|
||||
try bun.js_printer.writeJSONString(self.input, @TypeOf(writer), writer, .utf8);
|
||||
} else {
|
||||
try bun.js_printer.writePreQuotedString(self.input, @TypeOf(writer), writer, '"', false, true, .utf8);
|
||||
try bun.js_printer.writePreQuotedString(.utf8, self.input, @TypeOf(writer), writer, '"', false, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -36,34 +36,7 @@ delete assert.AssertionError;
|
||||
delete assert.CallTracker;
|
||||
delete assert.strict;
|
||||
|
||||
/**
|
||||
* @link https://nodejs.org/api/test.html#class-suitecontext
|
||||
*/
|
||||
class SuiteContext {
|
||||
#name: string | undefined;
|
||||
#filePath: string | undefined;
|
||||
#abortController?: AbortController;
|
||||
|
||||
constructor(name: string | undefined, filePath: string | undefined) {
|
||||
this.#name = name;
|
||||
this.#filePath = filePath || Bun.main;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this.#name!;
|
||||
}
|
||||
|
||||
get filePath(): string {
|
||||
return this.#filePath!;
|
||||
}
|
||||
|
||||
get signal(): AbortSignal {
|
||||
if (this.#abortController === undefined) {
|
||||
this.#abortController = new AbortController();
|
||||
}
|
||||
return this.#abortController.signal;
|
||||
}
|
||||
}
|
||||
let checkNotInsideTest: (ctx: TestContext | undefined, fn: string) => void;
|
||||
|
||||
/**
|
||||
* @link https://nodejs.org/api/test.html#class-testcontext
|
||||
@@ -143,25 +116,25 @@ class TestContext {
|
||||
|
||||
before(arg0: unknown, arg1: unknown) {
|
||||
const { fn } = createHook(arg0, arg1);
|
||||
const { beforeAll } = bunTest(this);
|
||||
const { beforeAll } = bunTest();
|
||||
beforeAll(fn);
|
||||
}
|
||||
|
||||
after(arg0: unknown, arg1: unknown) {
|
||||
const { fn } = createHook(arg0, arg1);
|
||||
const { afterAll } = bunTest(this);
|
||||
const { afterAll } = bunTest();
|
||||
afterAll(fn);
|
||||
}
|
||||
|
||||
beforeEach(arg0: unknown, arg1: unknown) {
|
||||
const { fn } = createHook(arg0, arg1);
|
||||
const { beforeEach } = bunTest(this);
|
||||
const { beforeEach } = bunTest();
|
||||
beforeEach(fn);
|
||||
}
|
||||
|
||||
afterEach(arg0: unknown, arg1: unknown) {
|
||||
const { fn } = createHook(arg0, arg1);
|
||||
const { afterEach } = bunTest(this);
|
||||
const { afterEach } = bunTest();
|
||||
afterEach(fn);
|
||||
}
|
||||
|
||||
@@ -172,11 +145,9 @@ class TestContext {
|
||||
test(arg0: unknown, arg1: unknown, arg2: unknown) {
|
||||
const { name, fn, options } = createTest(arg0, arg1, arg2);
|
||||
|
||||
if (this.#insideTest) {
|
||||
throwNotImplemented("test() inside another test()", 5090, "Use `bun:test` in the interim.");
|
||||
}
|
||||
this.#checkNotInsideTest("test");
|
||||
|
||||
const { test } = bunTest(this);
|
||||
const { test } = bunTest();
|
||||
if (options.only) {
|
||||
test.only(name, fn);
|
||||
} else if (options.todo) {
|
||||
@@ -191,90 +162,101 @@ class TestContext {
|
||||
describe(arg0: unknown, arg1: unknown, arg2: unknown) {
|
||||
const { name, fn } = createDescribe(arg0, arg1, arg2);
|
||||
|
||||
if (this.#insideTest) {
|
||||
throwNotImplemented("describe() inside another test()", 5090, "Use `bun:test` in the interim.");
|
||||
}
|
||||
this.#checkNotInsideTest("describe");
|
||||
|
||||
const { describe } = bunTest(this);
|
||||
const { describe } = bunTest();
|
||||
describe(name, fn);
|
||||
}
|
||||
|
||||
#checkNotInsideTest(fn: string) {
|
||||
if (this.#insideTest) {
|
||||
throwNotImplemented(`${fn}() inside another test()`, 5090, "Use `bun:test` in the interim.");
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
// expose this function to the rest of this file without exposing it to user JS
|
||||
checkNotInsideTest = (ctx: TestContext | undefined, fn: string) => {
|
||||
if (ctx) ctx.#checkNotInsideTest(fn);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function bunTest(ctx: SuiteContext | TestContext) {
|
||||
return jest(ctx.filePath);
|
||||
function bunTest() {
|
||||
return jest(Bun.main);
|
||||
}
|
||||
|
||||
let ctx = new TestContext(false, undefined, Bun.main, undefined);
|
||||
let ctx: TestContext | undefined = undefined;
|
||||
|
||||
function describe(arg0: unknown, arg1: unknown, arg2: unknown) {
|
||||
const { name, fn } = createDescribe(arg0, arg1, arg2);
|
||||
const { describe } = bunTest(ctx);
|
||||
const { describe } = bunTest();
|
||||
describe(name, fn);
|
||||
}
|
||||
|
||||
describe.skip = function (arg0: unknown, arg1: unknown, arg2: unknown) {
|
||||
const { name, fn } = createDescribe(arg0, arg1, arg2);
|
||||
const { describe } = bunTest(ctx);
|
||||
const { describe } = bunTest();
|
||||
describe.skip(name, fn);
|
||||
};
|
||||
|
||||
describe.todo = function (arg0: unknown, arg1: unknown, arg2: unknown) {
|
||||
const { name, fn } = createDescribe(arg0, arg1, arg2);
|
||||
const { describe } = bunTest(ctx);
|
||||
const { describe } = bunTest();
|
||||
describe.todo(name, fn);
|
||||
};
|
||||
|
||||
describe.only = function (arg0: unknown, arg1: unknown, arg2: unknown) {
|
||||
const { name, fn } = createDescribe(arg0, arg1, arg2);
|
||||
const { describe } = bunTest(ctx);
|
||||
const { describe } = bunTest();
|
||||
describe.only(name, fn);
|
||||
};
|
||||
|
||||
function test(arg0: unknown, arg1: unknown, arg2: unknown) {
|
||||
const { name, fn, options } = createTest(arg0, arg1, arg2);
|
||||
const { test } = bunTest(ctx);
|
||||
const { test } = bunTest();
|
||||
test(name, fn, options);
|
||||
}
|
||||
|
||||
test.skip = function (arg0: unknown, arg1: unknown, arg2: unknown) {
|
||||
const { name, fn, options } = createTest(arg0, arg1, arg2);
|
||||
const { test } = bunTest(ctx);
|
||||
const { test } = bunTest();
|
||||
test.skip(name, fn, options);
|
||||
};
|
||||
|
||||
test.todo = function (arg0: unknown, arg1: unknown, arg2: unknown) {
|
||||
const { name, fn, options } = createTest(arg0, arg1, arg2);
|
||||
const { test } = bunTest(ctx);
|
||||
const { test } = bunTest();
|
||||
test.todo(name, fn, options);
|
||||
};
|
||||
|
||||
test.only = function (arg0: unknown, arg1: unknown, arg2: unknown) {
|
||||
const { name, fn, options } = createTest(arg0, arg1, arg2);
|
||||
const { test } = bunTest(ctx);
|
||||
const { test } = bunTest();
|
||||
test.only(name, fn, options);
|
||||
};
|
||||
|
||||
function before(arg0: unknown, arg1: unknown) {
|
||||
const { fn } = createHook(arg0, arg1);
|
||||
const { beforeAll } = bunTest(ctx);
|
||||
const { beforeAll } = bunTest();
|
||||
beforeAll(fn);
|
||||
}
|
||||
|
||||
function after(arg0: unknown, arg1: unknown) {
|
||||
const { fn } = createHook(arg0, arg1);
|
||||
const { afterAll } = bunTest(ctx);
|
||||
const { afterAll } = bunTest();
|
||||
afterAll(fn);
|
||||
}
|
||||
|
||||
function beforeEach(arg0: unknown, arg1: unknown) {
|
||||
const { fn } = createHook(arg0, arg1);
|
||||
const { beforeEach } = bunTest(ctx);
|
||||
const { beforeEach } = bunTest();
|
||||
beforeEach(fn);
|
||||
}
|
||||
|
||||
function afterEach(arg0: unknown, arg1: unknown) {
|
||||
const { fn } = createHook(arg0, arg1);
|
||||
const { afterEach } = bunTest(ctx);
|
||||
const { afterEach } = bunTest();
|
||||
afterEach(fn);
|
||||
}
|
||||
|
||||
@@ -319,8 +301,9 @@ function parseTestOptions(arg0: unknown, arg1: unknown, arg2: unknown) {
|
||||
function createTest(arg0: unknown, arg1: unknown, arg2: unknown) {
|
||||
const { name, options, fn } = parseTestOptions(arg0, arg1, arg2);
|
||||
|
||||
checkNotInsideTest(ctx, "test");
|
||||
const originalContext = ctx;
|
||||
const context = new TestContext(true, name, ctx.filePath, originalContext);
|
||||
const context = new TestContext(true, name, Bun.main, originalContext);
|
||||
|
||||
const runTest = (done: (error?: unknown) => void) => {
|
||||
ctx = context;
|
||||
@@ -352,8 +335,9 @@ function createTest(arg0: unknown, arg1: unknown, arg2: unknown) {
|
||||
function createDescribe(arg0: unknown, arg1: unknown, arg2: unknown) {
|
||||
const { name, fn, options } = parseTestOptions(arg0, arg1, arg2);
|
||||
|
||||
checkNotInsideTest(ctx, "describe");
|
||||
const originalContext = ctx;
|
||||
const context = new TestContext(false, name, ctx.filePath, originalContext);
|
||||
const context = new TestContext(false, name, Bun.main, originalContext);
|
||||
|
||||
const runDescribe = () => {
|
||||
ctx = context;
|
||||
|
||||
@@ -151,19 +151,19 @@ pub fn quoteForJSON(text: []const u8, output_: MutableString, comptime ascii_onl
|
||||
return bytes;
|
||||
}
|
||||
|
||||
pub fn writePreQuotedString(text_in: []const u8, comptime Writer: type, writer: Writer, comptime quote_char: u8, comptime ascii_only: bool, comptime json: bool, comptime encoding: strings.Encoding) !void {
|
||||
const text = if (comptime encoding == .utf16) @as([]const u16, @alignCast(std.mem.bytesAsSlice(u16, text_in))) else text_in;
|
||||
pub fn writePreQuotedString(comptime encoding: strings.Encoding, text: []const encoding.Unit(), comptime Writer: type, writer: Writer, comptime quote_char: u8, comptime ascii_only: bool, comptime json: bool) !void {
|
||||
if (comptime json and quote_char != '"') @compileError("for json, quote_char must be '\"'");
|
||||
var i: usize = 0;
|
||||
const n: usize = text.len;
|
||||
while (i < n) {
|
||||
const width = switch (comptime encoding) {
|
||||
const width: u8 = switch (comptime encoding) {
|
||||
.latin1, .ascii => 1,
|
||||
.utf8 => strings.wtf8ByteSequenceLengthWithInvalid(text[i]),
|
||||
.utf16 => 1,
|
||||
.utf16 => if (text[i] >= strings.HIGH_SURROGATE_START and text[i] <= strings.HIGH_SURROGATE_END and text.len > i + 1 and //
|
||||
text[i + 1] >= strings.LOW_SURROGATE_START and text[i + 1] <= strings.LOW_SURROGATE_END) 2 else 1,
|
||||
};
|
||||
const clamped_width = @min(@as(usize, width), n -| i);
|
||||
const c = switch (encoding) {
|
||||
const c: i32 = switch (encoding) {
|
||||
.utf8 => strings.decodeWTF8RuneT(
|
||||
&switch (clamped_width) {
|
||||
// 0 is not returned by `wtf8ByteSequenceLengthWithInvalid`
|
||||
@@ -177,20 +177,9 @@ pub fn writePreQuotedString(text_in: []const u8, comptime Writer: type, writer:
|
||||
i32,
|
||||
0,
|
||||
),
|
||||
.ascii => brk: {
|
||||
std.debug.assert(text[i] <= 0x7F);
|
||||
break :brk text[i];
|
||||
},
|
||||
.latin1 => brk: {
|
||||
if (text[i] <= 0x7F) break :brk text[i];
|
||||
break :brk strings.latin1ToCodepointAssumeNotASCII(text[i], i32);
|
||||
},
|
||||
.utf16 => brk: {
|
||||
// TODO: if this is a part of a surrogate pair, we could parse the whole codepoint in order
|
||||
// to emit it as a single \u{result} rather than two paired \uLOW\uHIGH.
|
||||
// eg: "\u{10334}" will convert to "\uD800\uDF34" without this.
|
||||
break :brk @as(i32, text[i]);
|
||||
},
|
||||
.ascii, .latin1 => text[i],
|
||||
.utf16 => if (text[i] >= strings.HIGH_SURROGATE_START and text[i] <= strings.HIGH_SURROGATE_END and text.len > i + 1 and //
|
||||
text[i + 1] >= strings.LOW_SURROGATE_START and text[i + 1] <= strings.LOW_SURROGATE_END) @bitCast(strings.utf16DecodeSurrogatePair(text[i], text[i + 1])) else text[i],
|
||||
};
|
||||
if (canPrintWithoutEscape(i32, c, ascii_only)) {
|
||||
const remain = text[i + clamped_width ..];
|
||||
@@ -301,7 +290,7 @@ pub fn writePreQuotedString(text_in: []const u8, comptime Writer: type, writer:
|
||||
},
|
||||
|
||||
else => {
|
||||
i += @as(usize, width);
|
||||
i += @as(usize, clamped_width);
|
||||
|
||||
if (c <= 0xFF and !json) {
|
||||
const k = @as(usize, @intCast(c));
|
||||
@@ -352,13 +341,13 @@ pub fn quoteForJSONBuffer(text: []const u8, bytes: *MutableString, comptime asci
|
||||
|
||||
try bytes.growIfNeeded(estimateLengthForUTF8(text, ascii_only, '"'));
|
||||
try bytes.appendChar('"');
|
||||
try writePreQuotedString(text, @TypeOf(writer), writer, '"', ascii_only, true, .utf8);
|
||||
try writePreQuotedString(.utf8, text, @TypeOf(writer), writer, '"', ascii_only, true);
|
||||
bytes.appendChar('"') catch unreachable;
|
||||
}
|
||||
|
||||
pub fn writeJSONString(input: []const u8, comptime Writer: type, writer: Writer, comptime encoding: strings.Encoding) !void {
|
||||
try writer.writeAll("\"");
|
||||
try writePreQuotedString(input, Writer, writer, '"', false, true, encoding);
|
||||
try writePreQuotedString(encoding, input, Writer, writer, '"', false, true);
|
||||
try writer.writeAll("\"");
|
||||
}
|
||||
|
||||
@@ -1553,20 +1542,18 @@ fn NewPrinter(
|
||||
pub fn printStringCharactersUTF8(e: *Printer, text: []const u8, quote: u8) void {
|
||||
const writer = e.writer.stdWriter();
|
||||
(switch (quote) {
|
||||
'\'' => writePreQuotedString(text, @TypeOf(writer), writer, '\'', ascii_only, false, .utf8),
|
||||
'"' => writePreQuotedString(text, @TypeOf(writer), writer, '"', ascii_only, false, .utf8),
|
||||
'`' => writePreQuotedString(text, @TypeOf(writer), writer, '`', ascii_only, false, .utf8),
|
||||
'\'' => writePreQuotedString(.utf8, text, @TypeOf(writer), writer, '\'', ascii_only, false),
|
||||
'"' => writePreQuotedString(.utf8, text, @TypeOf(writer), writer, '"', ascii_only, false),
|
||||
'`' => writePreQuotedString(.utf8, text, @TypeOf(writer), writer, '`', ascii_only, false),
|
||||
else => unreachable,
|
||||
}) catch |err| switch (err) {};
|
||||
}
|
||||
pub fn printStringCharactersUTF16(e: *Printer, text: []const u16, quote: u8) void {
|
||||
const slice = std.mem.sliceAsBytes(text);
|
||||
|
||||
const writer = e.writer.stdWriter();
|
||||
(switch (quote) {
|
||||
'\'' => writePreQuotedString(slice, @TypeOf(writer), writer, '\'', ascii_only, false, .utf16),
|
||||
'"' => writePreQuotedString(slice, @TypeOf(writer), writer, '"', ascii_only, false, .utf16),
|
||||
'`' => writePreQuotedString(slice, @TypeOf(writer), writer, '`', ascii_only, false, .utf16),
|
||||
'\'' => writePreQuotedString(.utf16, text, @TypeOf(writer), writer, '\'', ascii_only, false),
|
||||
'"' => writePreQuotedString(.utf16, text, @TypeOf(writer), writer, '"', ascii_only, false),
|
||||
'`' => writePreQuotedString(.utf16, text, @TypeOf(writer), writer, '`', ascii_only, false),
|
||||
else => unreachable,
|
||||
}) catch |err| switch (err) {};
|
||||
}
|
||||
|
||||
@@ -8,6 +8,15 @@ pub const Encoding = enum {
|
||||
utf8,
|
||||
latin1,
|
||||
utf16,
|
||||
|
||||
pub fn Unit(comptime self: @This()) type {
|
||||
return switch (self) {
|
||||
.ascii => u8,
|
||||
.utf8 => u8,
|
||||
.latin1 => u8,
|
||||
.utf16 => u16,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Returned by classification functions that do not discriminate between utf8 and ascii.
|
||||
@@ -2117,7 +2126,7 @@ fn QuoteEscapeFormat(comptime flags: QuoteEscapeFormatFlags) type {
|
||||
data: []const u8,
|
||||
|
||||
pub fn format(self: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
||||
try bun.js_printer.writePreQuotedString(self.data, @TypeOf(writer), writer, flags.quote_char, false, flags.json, flags.str_encoding);
|
||||
try bun.js_printer.writePreQuotedString(flags.str_encoding, self.data, @TypeOf(writer), writer, flags.quote_char, false, flags.json);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -2262,7 +2271,6 @@ pub const eqlUtf16 = unicode.eqlUtf16;
|
||||
pub const isAllASCII = unicode.isAllASCII;
|
||||
pub const isValidUTF8 = unicode.isValidUTF8;
|
||||
pub const isValidUTF8WithoutSIMD = unicode.isValidUTF8WithoutSIMD;
|
||||
pub const latin1ToCodepointAssumeNotASCII = unicode.latin1ToCodepointAssumeNotASCII;
|
||||
pub const latin1ToCodepointBytesAssumeNotASCII = unicode.latin1ToCodepointBytesAssumeNotASCII;
|
||||
pub const latin1ToCodepointBytesAssumeNotASCII16 = unicode.latin1ToCodepointBytesAssumeNotASCII16;
|
||||
pub const literal = unicode.literal;
|
||||
@@ -2289,6 +2297,11 @@ pub const u16Trail = unicode.u16Trail;
|
||||
pub const utf16Codepoint = unicode.utf16Codepoint;
|
||||
pub const utf16CodepointWithFFFD = unicode.utf16CodepointWithFFFD;
|
||||
pub const utf16EqlString = unicode.utf16EqlString;
|
||||
pub const utf16DecodeSurrogatePair = unicode.utf16DecodeSurrogatePair;
|
||||
pub const HIGH_SURROGATE_START = unicode.HIGH_SURROGATE_START;
|
||||
pub const HIGH_SURROGATE_END = unicode.HIGH_SURROGATE_END;
|
||||
pub const LOW_SURROGATE_START = unicode.LOW_SURROGATE_START;
|
||||
pub const LOW_SURROGATE_END = unicode.LOW_SURROGATE_END;
|
||||
pub const utf8ByteSequenceLength = unicode.utf8ByteSequenceLength;
|
||||
pub const utf8ByteSequenceLengthUnsafe = unicode.utf8ByteSequenceLengthUnsafe;
|
||||
pub const w = unicode.w;
|
||||
|
||||
@@ -1374,6 +1374,17 @@ pub fn utf16CodepointWithFFFD(comptime Type: type, input: Type) UTF16Replacement
|
||||
return utf16CodepointWithFFFDAndFirstInputChar(Type, input[0], input);
|
||||
}
|
||||
|
||||
pub const HIGH_SURROGATE_START = 0xD800;
|
||||
pub const HIGH_SURROGATE_END = 0xDBFF;
|
||||
pub const LOW_SURROGATE_START = 0xDC00;
|
||||
pub const LOW_SURROGATE_END = 0xDFFF;
|
||||
|
||||
pub fn utf16DecodeSurrogatePair(a: u32, b: u32) u32 {
|
||||
bun.assert(a >= HIGH_SURROGATE_START and a <= HIGH_SURROGATE_END);
|
||||
bun.assert(b >= LOW_SURROGATE_START and b <= LOW_SURROGATE_END);
|
||||
return 0x10000 + (((a & 0x03ff) << 10) | (b & 0x03ff));
|
||||
}
|
||||
|
||||
fn utf16CodepointWithFFFDAndFirstInputChar(comptime Type: type, char: std.meta.Elem(Type), input: Type) UTF16Replacement {
|
||||
const c0 = @as(u21, char);
|
||||
|
||||
@@ -1401,7 +1412,7 @@ fn utf16CodepointWithFFFDAndFirstInputChar(comptime Type: type, char: std.meta.E
|
||||
};
|
||||
// return error.ExpectedSecondSurrogateHalf;
|
||||
|
||||
return .{ .len = 2, .code_point = 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff)) };
|
||||
return .{ .len = 2, .code_point = utf16DecodeSurrogatePair(c0, c1) };
|
||||
} else if (c0 & ~@as(u21, 0x03ff) == 0xdc00) {
|
||||
// return error.UnexpectedSecondSurrogateHalf;
|
||||
return .{ .fail = true, .len = 1, .code_point = unicode_replacement };
|
||||
@@ -1629,13 +1640,6 @@ pub fn convertUTF16toUTF8InBuffer(
|
||||
return buf[0..result];
|
||||
}
|
||||
|
||||
pub fn latin1ToCodepointAssumeNotASCII(char: u8, comptime CodePointType: type) CodePointType {
|
||||
return @as(
|
||||
CodePointType,
|
||||
@intCast(latin1ToCodepointBytesAssumeNotASCII16(char)),
|
||||
);
|
||||
}
|
||||
|
||||
const latin1_to_utf16_conversion_table = [256]u16{
|
||||
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, // 00-07
|
||||
0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, // 08-0F
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe } from "bun:test";
|
||||
import { describe, expect } from "bun:test";
|
||||
import { dedent, itBundled } from "./expectBundled";
|
||||
|
||||
interface TemplateStringTest {
|
||||
@@ -89,6 +89,20 @@ const templateStringTests: Record<string, TemplateStringTest> = {
|
||||
};
|
||||
|
||||
describe("bundler", () => {
|
||||
// Test for emoji output in bun build
|
||||
itBundled("string/EmojiDirectOutput", {
|
||||
files: {
|
||||
"a.js": `console.log("😀");`,
|
||||
},
|
||||
outfile: "out.js",
|
||||
onAfterBundle(api) {
|
||||
const content = api.readFile("out.js");
|
||||
expect(content).toContain("😀");
|
||||
expect(content).not.toContain("\\ud83d");
|
||||
expect(content).not.toContain("\\ude00");
|
||||
},
|
||||
});
|
||||
|
||||
for (const key in templateStringTests) {
|
||||
const test = templateStringTests[key];
|
||||
if ([test.capture, test.captureRaw, test.print].filter(x => x !== undefined).length !== 1) {
|
||||
|
||||
6
test/js/bun/test/printing/consolelog.fixture.ts
Normal file
6
test/js/bun/test/printing/consolelog.fixture.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
console.log("--- begin ---");
|
||||
console.log({
|
||||
a: "a",
|
||||
multiline: 'pub fn main() !void {\n std.log.info("Hello, {s}", .{name});\n}',
|
||||
});
|
||||
console.log("--- end ---");
|
||||
36
test/js/bun/test/printing/consolelogexample.test.ts
Normal file
36
test/js/bun/test/printing/consolelogexample.test.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { expect, test } from "bun:test";
|
||||
import { bunEnv, bunExe } from "harness";
|
||||
|
||||
test("Bun.inspect", async () => {
|
||||
expect(Bun.inspect("abc\ndef\nghi")).toMatchInlineSnapshot(`""abc\\ndef\\nghi""`);
|
||||
expect(Bun.inspect({ a: "abc\ndef\nghi" })).toMatchInlineSnapshot(`
|
||||
"{
|
||||
a: "abc\\ndef\\nghi",
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
test("console.log output", async () => {
|
||||
const result = Bun.spawn({
|
||||
cmd: [bunExe(), import.meta.dir + "/consolelog.fixture.ts"],
|
||||
stdio: ["inherit", "pipe", "pipe"],
|
||||
env: {
|
||||
...bunEnv,
|
||||
FORCE_COLOR: "0",
|
||||
},
|
||||
});
|
||||
await result.exited;
|
||||
const stdout = await result.stdout.text();
|
||||
const stderr = await result.stderr.text();
|
||||
expect(stderr).toBe("");
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(stdout).toMatchInlineSnapshot(`
|
||||
"--- begin ---
|
||||
{
|
||||
a: "a",
|
||||
multiline: "pub fn main() !void {\\n std.log.info(\\"Hello, {s}\\", .{name});\\n}",
|
||||
}
|
||||
--- end ---
|
||||
"
|
||||
`);
|
||||
});
|
||||
37
test/js/bun/test/printing/stringsnapshot.test.ts
Normal file
37
test/js/bun/test/printing/stringsnapshot.test.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
test("string inline snapshots", () => {
|
||||
expect("inline").toMatchInlineSnapshot(`"inline"`);
|
||||
expect("multi\nline").toMatchInlineSnapshot(`
|
||||
"multi
|
||||
line"
|
||||
`);
|
||||
expect({ key: "inline" }).toMatchInlineSnapshot(`
|
||||
{
|
||||
"key": "inline",
|
||||
}
|
||||
`);
|
||||
expect({ key: "multi\nline", value: "inline" }).toMatchInlineSnapshot(`
|
||||
{
|
||||
"key":
|
||||
"multi
|
||||
line"
|
||||
,
|
||||
"value": "inline",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test("bun inspect strings", () => {
|
||||
expect(Bun.inspect("inline")).toMatchInlineSnapshot(`""inline""`);
|
||||
expect(Bun.inspect("multi\nline")).toMatchInlineSnapshot(`""multi\\nline""`);
|
||||
expect(Bun.inspect({ key: "inline" })).toMatchInlineSnapshot(`
|
||||
"{
|
||||
key: "inline",
|
||||
}"
|
||||
`);
|
||||
expect(Bun.inspect({ key: "multi\nline", value: "inline" })).toMatchInlineSnapshot(`
|
||||
"{
|
||||
key: "multi\\nline",
|
||||
value: "inline",
|
||||
}"
|
||||
`);
|
||||
});
|
||||
3
test/js/bun/util/bun-main-test-fixture-1.ts
generated
Normal file
3
test/js/bun/util/bun-main-test-fixture-1.ts
generated
Normal file
@@ -0,0 +1,3 @@
|
||||
// this runs with bun:test, but it's not named .test.ts because it is meant to be run in CI by bun-main.test.ts, not on its own
|
||||
// this override should not persist once we start running bun-main-test-fixture-2.ts
|
||||
(Bun as any).main = "foo";
|
||||
8
test/js/bun/util/bun-main-test-fixture-2.ts
generated
Normal file
8
test/js/bun/util/bun-main-test-fixture-2.ts
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
// this runs with bun:test, but it's not named .test.ts because it is meant to be run in CI by bun-main.test.ts, not on its own
|
||||
|
||||
import { test, expect } from "bun:test";
|
||||
|
||||
test("Bun.main override from previous test is not visible", () => {
|
||||
// bun-main-test-fixture-1.ts overrode this value
|
||||
expect(Bun.main).toEndWith("bun-main-test-fixture-2.ts");
|
||||
});
|
||||
22
test/js/bun/util/bun-main.test.ts
Normal file
22
test/js/bun/util/bun-main.test.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { join } from "node:path";
|
||||
import "../../../harness"; // for expect().toRun()
|
||||
|
||||
describe("Bun.main", () => {
|
||||
test("can be overridden", () => {
|
||||
expect(Bun.main).toBeString();
|
||||
const override = { foo: "bar" };
|
||||
// types say Bun.main is a readonly string, but we want to write it
|
||||
// and check it can be set to a non-string
|
||||
(Bun as any).main = override;
|
||||
expect(Bun.main as any).toBe(override);
|
||||
});
|
||||
|
||||
test("override is reset when switching to a new test file", () => {
|
||||
expect([
|
||||
"test",
|
||||
join(import.meta.dir, "bun-main-test-fixture-1.ts"),
|
||||
join(import.meta.dir, "bun-main-test-fixture-2.ts"),
|
||||
]).toRun();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
// Flags: --expose-gc
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const { onGC } = require('../common/gc');
|
||||
const { createServer } = require('http');
|
||||
const { connect } = require('net');
|
||||
|
||||
// Make sure that for HTTP keepalive requests, the req object can be
|
||||
// garbage collected once the request is finished.
|
||||
// Refs: https://github.com/nodejs/node/issues/9668
|
||||
|
||||
let client;
|
||||
const server = createServer(common.mustCall((req, res) => {
|
||||
onGC(req, { ongc: common.mustCall(() => { server.close(); }) });
|
||||
req.resume();
|
||||
req.on('end', common.mustCall(() => {
|
||||
setImmediate(async () => {
|
||||
client.end();
|
||||
await globalThis.gc({ type: 'major', execution: 'async' });
|
||||
await globalThis.gc({ type: 'major', execution: 'async' });
|
||||
});
|
||||
}));
|
||||
res.end('hello world');
|
||||
}));
|
||||
|
||||
server.listen(0, common.mustCall(() => {
|
||||
client = connect(server.address().port);
|
||||
|
||||
const req = [
|
||||
'POST / HTTP/1.1',
|
||||
`Host: localhost:${server.address().port}`,
|
||||
'Connection: keep-alive',
|
||||
'Content-Length: 11',
|
||||
'',
|
||||
'hello world',
|
||||
'',
|
||||
].join('\r\n');
|
||||
|
||||
client.write(req);
|
||||
client.unref();
|
||||
}));
|
||||
26
test/js/node/test_runner/fixtures/05-test-in-test.js
Normal file
26
test/js/node/test_runner/fixtures/05-test-in-test.js
Normal file
@@ -0,0 +1,26 @@
|
||||
const { describe, test } = require("node:test");
|
||||
const assert = require("node:assert");
|
||||
|
||||
// for passing to assert.throws
|
||||
function expectedError(fn) {
|
||||
return {
|
||||
name: "NotImplementedError",
|
||||
message: `${fn}() inside another test() is not yet implemented in Bun. Track the status & thumbs up the issue: https://github.com/oven-sh/bun/issues/5090. Use \`bun:test\` in the interim.`,
|
||||
};
|
||||
}
|
||||
|
||||
test("test() inside test() (global context) throws", () => {
|
||||
assert.throws(() => test("should throw and not run the test callback", assert.fail), expectedError("test"));
|
||||
});
|
||||
|
||||
test("test() inside test() (passed context) throws", t => {
|
||||
assert.throws(() => t.test("should throw and not run the test callback", assert.fail), expectedError("test"));
|
||||
});
|
||||
|
||||
test("describe() inside test() (global context) throws", () => {
|
||||
assert.throws(() => describe("should throw and not run the test callback", assert.fail), expectedError("describe"));
|
||||
});
|
||||
|
||||
test("describe() inside test() (passed context) throws", t => {
|
||||
assert.throws(() => t.describe("should throw and not run the test callback", assert.fail), expectedError("describe"));
|
||||
});
|
||||
@@ -5,7 +5,7 @@ import { join } from "node:path";
|
||||
|
||||
describe("node:test", () => {
|
||||
test("should run basic tests", async () => {
|
||||
const { exitCode, stderr } = await runTest("01-harness.js");
|
||||
const { exitCode, stderr } = await runTests(["01-harness.js"]);
|
||||
expect({ exitCode, stderr }).toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: expect.stringContaining("0 fail"),
|
||||
@@ -13,7 +13,7 @@ describe("node:test", () => {
|
||||
});
|
||||
|
||||
test("should run hooks in the right order", async () => {
|
||||
const { exitCode, stderr } = await runTest("02-hooks.js");
|
||||
const { exitCode, stderr } = await runTests(["02-hooks.js"]);
|
||||
expect({ exitCode, stderr }).toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: expect.stringContaining("0 fail"),
|
||||
@@ -21,7 +21,7 @@ describe("node:test", () => {
|
||||
});
|
||||
|
||||
test("should run tests with different variations", async () => {
|
||||
const { exitCode, stderr } = await runTest("03-test-variations.js");
|
||||
const { exitCode, stderr } = await runTests(["03-test-variations.js"]);
|
||||
expect({ exitCode, stderr }).toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: expect.stringContaining("0 fail"),
|
||||
@@ -29,7 +29,24 @@ describe("node:test", () => {
|
||||
});
|
||||
|
||||
test("should run async tests", async () => {
|
||||
const { exitCode, stderr } = await runTest("04-async-tests.js");
|
||||
const { exitCode, stderr } = await runTests(["04-async-tests.js"]);
|
||||
expect({ exitCode, stderr }).toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: expect.stringContaining("0 fail"),
|
||||
});
|
||||
});
|
||||
|
||||
test("should run all tests from multiple files", async () => {
|
||||
const { exitCode, stderr } = await runTests(["01-harness.js", "02-hooks.js"]);
|
||||
expect({ exitCode, stderr }).toMatchObject({
|
||||
exitCode: 0,
|
||||
// 32 from 01-harness + 3 from 02-hooks
|
||||
stderr: expect.stringContaining("35 pass"),
|
||||
});
|
||||
});
|
||||
|
||||
test("should throw NotImplementedError if you call test() or describe() inside another test()", async () => {
|
||||
const { exitCode, stderr } = await runTests(["05-test-in-test.js"]);
|
||||
expect({ exitCode, stderr }).toMatchObject({
|
||||
exitCode: 0,
|
||||
stderr: expect.stringContaining("0 fail"),
|
||||
@@ -37,14 +54,14 @@ describe("node:test", () => {
|
||||
});
|
||||
});
|
||||
|
||||
async function runTest(filename: string) {
|
||||
const testPath = join(import.meta.dirname, "fixtures", filename);
|
||||
async function runTests(filenames: string[]) {
|
||||
const testPaths = filenames.map(filename => join(import.meta.dirname, "fixtures", filename));
|
||||
const {
|
||||
exited,
|
||||
stdout: stdoutStream,
|
||||
stderr: stderrStream,
|
||||
} = spawn({
|
||||
cmd: [bunExe(), "test", testPath],
|
||||
cmd: [bunExe(), "test", ...testPaths],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
@@ -111,6 +111,15 @@
|
||||
"NODE_API_EXPERIMENTAL_NOGC_ENV_OPT_OUT=1",
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
"target_name": "constructor_order_addon",
|
||||
"sources": ["constructor_order_addon.cpp"],
|
||||
"include_dirs": ["<!@(node -p \"require('node-addon-api').include\")"],
|
||||
"libraries": [],
|
||||
"dependencies": ["<!(node -p \"require('node-addon-api').gyp\")"],
|
||||
"defines": [
|
||||
"NAPI_DISABLE_CPP_EXCEPTIONS",
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
44
test/napi/napi-app/constructor_order_addon.cpp
Normal file
44
test/napi/napi-app/constructor_order_addon.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <js_native_api.h>
|
||||
#include <node_api.h>
|
||||
#include <stdio.h>
|
||||
|
||||
napi_value register_cb(napi_env env, napi_value exports);
|
||||
|
||||
static napi_module mod = {
|
||||
1, // nm_version
|
||||
0, // nm_flags
|
||||
"constructor_order_addon.c", // nm_filename
|
||||
register_cb, // nm_register_func
|
||||
"constructor_order_addon", // nm_modname
|
||||
NULL, // nm_priv
|
||||
{NULL} // reserved
|
||||
};
|
||||
|
||||
class call_register {
|
||||
public:
|
||||
call_register() {
|
||||
// should be called first during dlopen
|
||||
printf("call_register\n");
|
||||
napi_module_register(&mod);
|
||||
}
|
||||
};
|
||||
|
||||
class init_static {
|
||||
public:
|
||||
init_static() {
|
||||
// should be called second during dlopen
|
||||
printf("init_static\n");
|
||||
}
|
||||
};
|
||||
|
||||
// declare these so their constructors run
|
||||
static call_register constructor1;
|
||||
static init_static constructor2;
|
||||
|
||||
napi_value register_cb(napi_env env, napi_value exports) {
|
||||
// should be called third, after dlopen returns and bun runs the callback
|
||||
// passed to napi_module_register
|
||||
(void)env;
|
||||
printf("register_cb\n");
|
||||
return exports;
|
||||
}
|
||||
@@ -694,4 +694,8 @@ nativeTests.test_get_value_string = () => {
|
||||
}
|
||||
};
|
||||
|
||||
nativeTests.test_constructor_order = () => {
|
||||
require("./build/Debug/constructor_order_addon.node");
|
||||
};
|
||||
|
||||
module.exports = nativeTests;
|
||||
|
||||
@@ -512,6 +512,10 @@ describe("napi", () => {
|
||||
it("works when the module register function throws", async () => {
|
||||
expect(() => require("./napi-app/build/Debug/throw_addon.node")).toThrow(new Error("oops!"));
|
||||
});
|
||||
|
||||
it("runs the napi_module_register callback after dlopen finishes", () => {
|
||||
checkSameOutput("test_constructor_order", []);
|
||||
});
|
||||
});
|
||||
|
||||
async function checkSameOutput(test: string, args: any[] | string, envArgs: Record<string, string> = {}) {
|
||||
|
||||
@@ -311,32 +311,17 @@ let knownGlobals = [
|
||||
setInterval,
|
||||
setTimeout,
|
||||
queueMicrotask,
|
||||
addEventListener,
|
||||
alert,
|
||||
confirm,
|
||||
dispatchEvent,
|
||||
postMessage,
|
||||
prompt,
|
||||
removeEventListener,
|
||||
reportError,
|
||||
Bun,
|
||||
File,
|
||||
process,
|
||||
Blob,
|
||||
Buffer,
|
||||
BuildError,
|
||||
BuildMessage,
|
||||
HTMLRewriter,
|
||||
Request,
|
||||
ResolveError,
|
||||
ResolveMessage,
|
||||
Response,
|
||||
TextDecoder,
|
||||
AbortSignal,
|
||||
BroadcastChannel,
|
||||
CloseEvent,
|
||||
DOMException,
|
||||
ErrorEvent,
|
||||
Event,
|
||||
EventTarget,
|
||||
FormData,
|
||||
@@ -351,11 +336,31 @@ let knownGlobals = [
|
||||
URL,
|
||||
URLSearchParams,
|
||||
WebSocket,
|
||||
Worker,
|
||||
onmessage,
|
||||
onerror,
|
||||
];
|
||||
|
||||
if (typeof Bun === "object") {
|
||||
knownGlobals.push(
|
||||
addEventListener,
|
||||
alert,
|
||||
confirm,
|
||||
dispatchEvent,
|
||||
postMessage,
|
||||
prompt,
|
||||
removeEventListener,
|
||||
Bun,
|
||||
reportError,
|
||||
BuildError,
|
||||
BuildMessage,
|
||||
HTMLRewriter,
|
||||
ResolveError,
|
||||
ResolveMessage,
|
||||
ErrorEvent,
|
||||
Worker,
|
||||
onmessage,
|
||||
onerror,
|
||||
);
|
||||
}
|
||||
|
||||
const globalKeys = [
|
||||
"gc",
|
||||
"navigator",
|
||||
|
||||
Reference in New Issue
Block a user