mirror of
https://github.com/oven-sh/bun
synced 2026-02-16 13:51:47 +00:00
Compare commits
5 Commits
claude/fix
...
claude/fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
846f6aa577 | ||
|
|
70b354aa04 | ||
|
|
9d5a800c3d | ||
|
|
77ca318336 | ||
|
|
337a9f7f2b |
@@ -1140,14 +1140,14 @@ export fn Bun__runVirtualModule(globalObject: *JSGlobalObject, specifier_ptr: *c
|
||||
fn getHardcodedModule(jsc_vm: *VirtualMachine, specifier: bun.String, hardcoded: HardcodedModule) ?ResolvedSource {
|
||||
analytics.Features.builtin_modules.insert(hardcoded);
|
||||
return switch (hardcoded) {
|
||||
.@"bun:main" => .{
|
||||
.@"bun:main" => if (jsc_vm.entry_point.generated) .{
|
||||
.allocator = null,
|
||||
.source_code = bun.String.cloneUTF8(jsc_vm.entry_point.source.contents),
|
||||
.specifier = specifier,
|
||||
.source_url = specifier,
|
||||
.tag = .esm,
|
||||
.source_code_needs_deref = true,
|
||||
},
|
||||
} else null,
|
||||
.@"bun:internal-for-testing" => {
|
||||
if (!Environment.isDebug) {
|
||||
if (!is_allowed_to_use_internal_testing_apis)
|
||||
|
||||
@@ -1616,7 +1616,7 @@ fn _resolve(
|
||||
if (strings.eqlComptime(std.fs.path.basename(specifier), Runtime.Runtime.Imports.alt_name)) {
|
||||
ret.path = Runtime.Runtime.Imports.Name;
|
||||
return;
|
||||
} else if (strings.eqlComptime(specifier, main_file_name)) {
|
||||
} else if (strings.eqlComptime(specifier, main_file_name) and jsc_vm.entry_point.generated) {
|
||||
ret.result = null;
|
||||
ret.path = jsc_vm.entry_point.source.path.text;
|
||||
return;
|
||||
|
||||
@@ -276,7 +276,7 @@ pub const JSBundler = struct {
|
||||
autoload_dotenv: bool = true,
|
||||
autoload_bunfig: bool = true,
|
||||
autoload_tsconfig: bool = false,
|
||||
autoload_package_json: bool = false,
|
||||
autoload_package_json: bool = true,
|
||||
|
||||
pub fn fromJS(globalThis: *jsc.JSGlobalObject, config: jsc.JSValue, allocator: std.mem.Allocator, compile_target: ?CompileTarget) JSError!?CompileOptions {
|
||||
var this = CompileOptions{
|
||||
|
||||
@@ -4,7 +4,7 @@ const TimerObjectInternals = @This();
|
||||
/// Identifier for this timer that is exposed to JavaScript (by `+timer`)
|
||||
id: i32 = -1,
|
||||
interval: u31 = 0,
|
||||
strong_this: jsc.Strong.Optional = .empty,
|
||||
this_value: jsc.JSRef = .empty(),
|
||||
flags: Flags = .{},
|
||||
|
||||
/// Used by:
|
||||
@@ -76,31 +76,41 @@ pub fn runImmediateTask(this: *TimerObjectInternals, vm: *VirtualMachine) bool {
|
||||
// loop alive other than setImmediates
|
||||
(!this.flags.is_keeping_event_loop_alive and !vm.isEventLoopAliveExcludingImmediates()))
|
||||
{
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
this.this_value.downgrade();
|
||||
this.deref();
|
||||
return false;
|
||||
}
|
||||
|
||||
const timer = this.strong_this.get() orelse {
|
||||
const timer = this.this_value.tryGet() orelse {
|
||||
if (Environment.isDebug) {
|
||||
@panic("TimerObjectInternals.runImmediateTask: this_object is null");
|
||||
}
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
this.deref();
|
||||
return false;
|
||||
};
|
||||
const globalThis = vm.global;
|
||||
this.strong_this.deinit();
|
||||
this.this_value.downgrade();
|
||||
this.eventLoopTimer().state = .FIRED;
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
timer.ensureStillAlive();
|
||||
|
||||
vm.eventLoop().enter();
|
||||
const callback = ImmediateObject.js.callbackGetCached(timer).?;
|
||||
const arguments = ImmediateObject.js.argumentsGetCached(timer).?;
|
||||
this.ref();
|
||||
const exception_thrown = this.run(globalThis, timer, callback, arguments, this.asyncID(), vm);
|
||||
this.deref();
|
||||
|
||||
if (this.eventLoopTimer().state == .FIRED) {
|
||||
this.deref();
|
||||
}
|
||||
const exception_thrown = brk: {
|
||||
this.ref();
|
||||
defer {
|
||||
if (this.eventLoopTimer().state == .FIRED) {
|
||||
this.deref();
|
||||
}
|
||||
this.deref();
|
||||
}
|
||||
break :brk this.run(globalThis, timer, callback, arguments, this.asyncID(), vm);
|
||||
};
|
||||
// --- after this point, the timer is no longer guaranteed to be alive ---
|
||||
|
||||
vm.eventLoop().exitMaybeDrainMicrotasks(!exception_thrown) catch return true;
|
||||
|
||||
@@ -120,7 +130,13 @@ pub fn fire(this: *TimerObjectInternals, _: *const timespec, vm: *jsc.VirtualMac
|
||||
this.eventLoopTimer().state = .FIRED;
|
||||
|
||||
const globalThis = vm.global;
|
||||
const this_object = this.strong_this.get().?;
|
||||
const this_object = this.this_value.tryGet() orelse {
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
this.flags.has_cleared_timer = true;
|
||||
this.this_value.downgrade();
|
||||
this.deref();
|
||||
return;
|
||||
};
|
||||
|
||||
const callback: JSValue, const arguments: JSValue, var idle_timeout: JSValue, var repeat: JSValue = switch (kind) {
|
||||
.setImmediate => .{
|
||||
@@ -143,7 +159,7 @@ pub fn fire(this: *TimerObjectInternals, _: *const timespec, vm: *jsc.VirtualMac
|
||||
}
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
this.flags.has_cleared_timer = true;
|
||||
this.strong_this.deinit();
|
||||
this.this_value.downgrade();
|
||||
this.deref();
|
||||
|
||||
return;
|
||||
@@ -152,7 +168,7 @@ pub fn fire(this: *TimerObjectInternals, _: *const timespec, vm: *jsc.VirtualMac
|
||||
var time_before_call: timespec = undefined;
|
||||
|
||||
if (kind != .setInterval) {
|
||||
this.strong_this.clearWithoutDeallocation();
|
||||
this.this_value.downgrade();
|
||||
} else {
|
||||
time_before_call = timespec.msFromNow(.allow_mocked_time, this.interval);
|
||||
}
|
||||
@@ -239,7 +255,7 @@ fn convertToInterval(this: *TimerObjectInternals, global: *JSGlobalObject, timer
|
||||
|
||||
// https://github.com/nodejs/node/blob/a7cbb904745591c9a9d047a364c2c188e5470047/lib/internal/timers.js#L613
|
||||
TimeoutObject.js.idleTimeoutSetCached(timer, global, repeat);
|
||||
this.strong_this.set(global, timer);
|
||||
this.this_value.setStrong(timer, global);
|
||||
this.flags.kind = .setInterval;
|
||||
this.interval = new_interval;
|
||||
this.reschedule(timer, vm, global);
|
||||
@@ -297,7 +313,7 @@ pub fn init(
|
||||
this.reschedule(timer, vm, global);
|
||||
}
|
||||
|
||||
this.strong_this.set(global, timer);
|
||||
this.this_value.setStrong(timer, global);
|
||||
}
|
||||
|
||||
pub fn doRef(this: *TimerObjectInternals, _: *jsc.JSGlobalObject, this_value: JSValue) JSValue {
|
||||
@@ -327,7 +343,7 @@ pub fn doRefresh(this: *TimerObjectInternals, globalObject: *jsc.JSGlobalObject,
|
||||
return this_value;
|
||||
}
|
||||
|
||||
this.strong_this.set(globalObject, this_value);
|
||||
this.this_value.setStrong(this_value, globalObject);
|
||||
this.reschedule(this_value, VirtualMachine.get(), globalObject);
|
||||
|
||||
return this_value;
|
||||
@@ -350,12 +366,18 @@ pub fn cancel(this: *TimerObjectInternals, vm: *VirtualMachine) void {
|
||||
this.setEnableKeepingEventLoopAlive(vm, false);
|
||||
this.flags.has_cleared_timer = true;
|
||||
|
||||
if (this.flags.kind == .setImmediate) return;
|
||||
if (this.flags.kind == .setImmediate) {
|
||||
// Release the strong reference so the GC can collect the JS object.
|
||||
// The immediate task is still in the event loop queue and will be skipped
|
||||
// by runImmediateTask when it sees has_cleared_timer == true.
|
||||
this.this_value.downgrade();
|
||||
return;
|
||||
}
|
||||
|
||||
const was_active = this.eventLoopTimer().state == .ACTIVE;
|
||||
|
||||
this.eventLoopTimer().state = .CANCELLED;
|
||||
this.strong_this.deinit();
|
||||
this.this_value.downgrade();
|
||||
|
||||
if (was_active) {
|
||||
vm.timer.remove(this.eventLoopTimer());
|
||||
@@ -442,12 +464,12 @@ pub fn getDestroyed(this: *TimerObjectInternals) bool {
|
||||
}
|
||||
|
||||
pub fn finalize(this: *TimerObjectInternals) void {
|
||||
this.strong_this.deinit();
|
||||
this.this_value.finalize();
|
||||
this.deref();
|
||||
}
|
||||
|
||||
pub fn deinit(this: *TimerObjectInternals) void {
|
||||
this.strong_this.deinit();
|
||||
this.this_value.deinit();
|
||||
const vm = VirtualMachine.get();
|
||||
const kind = this.flags.kind;
|
||||
|
||||
|
||||
@@ -68,6 +68,16 @@ pub const Chunk = struct {
|
||||
}
|
||||
|
||||
pub fn getCSSChunkForHTML(this: *const Chunk, chunks: []Chunk) ?*Chunk {
|
||||
// Look up the CSS chunk via the JS chunk's css_chunks indices.
|
||||
// This correctly handles deduplicated CSS chunks that are shared
|
||||
// across multiple HTML entry points (see issue #23668).
|
||||
if (this.getJSChunkForHTML(chunks)) |js_chunk| {
|
||||
const css_chunk_indices = js_chunk.content.javascript.css_chunks;
|
||||
if (css_chunk_indices.len > 0) {
|
||||
return &chunks[css_chunk_indices[0]];
|
||||
}
|
||||
}
|
||||
// Fallback: match by entry_point_id for cases without a JS chunk.
|
||||
const entry_point_id = this.entry_point.entry_point_id;
|
||||
for (chunks) |*other| {
|
||||
if (other.content == .css) {
|
||||
|
||||
@@ -150,6 +150,7 @@ pub const ClientEntryPoint = struct {
|
||||
|
||||
pub const ServerEntryPoint = struct {
|
||||
source: logger.Source = undefined,
|
||||
generated: bool = false,
|
||||
|
||||
pub fn generate(
|
||||
entry: *ServerEntryPoint,
|
||||
@@ -230,6 +231,7 @@ pub const ServerEntryPoint = struct {
|
||||
entry.source = logger.Source.initPathString(name, code);
|
||||
entry.source.path.text = name;
|
||||
entry.source.path.namespace = "server-entry";
|
||||
entry.generated = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ pub noinline fn computeChunks(
|
||||
|
||||
const entry_source_indices = this.graph.entry_points.items(.source_index);
|
||||
const css_asts = this.graph.ast.items(.css);
|
||||
const css_chunking = this.options.css_chunking;
|
||||
var html_chunks = bun.StringArrayHashMap(Chunk).init(temp_allocator);
|
||||
const loaders = this.parse_graph.input_files.items(.loader);
|
||||
const ast_targets = this.graph.ast.items(.target);
|
||||
@@ -148,10 +147,11 @@ pub noinline fn computeChunks(
|
||||
if (css_source_indices.len > 0) {
|
||||
const order = this.findImportedFilesInCSSOrder(temp_allocator, css_source_indices.slice());
|
||||
|
||||
const use_content_based_key = css_chunking or has_server_html_imports;
|
||||
const hash_to_use = if (!use_content_based_key)
|
||||
bun.hash(try temp_allocator.dupe(u8, entry_bits.bytes(this.graph.entry_points.len)))
|
||||
else brk: {
|
||||
// Always use content-based hashing for CSS chunk deduplication.
|
||||
// This ensures that when multiple JS entry points import the
|
||||
// same CSS files, they share a single CSS output chunk rather
|
||||
// than producing duplicates that collide on hash-based naming.
|
||||
const hash_to_use = brk: {
|
||||
var hasher = std.hash.Wyhash.init(5);
|
||||
bun.writeAnyToHasher(&hasher, order.len);
|
||||
for (order.slice()) |x| x.hash(&hasher);
|
||||
@@ -322,7 +322,10 @@ pub noinline fn computeChunks(
|
||||
const remapped_css_indexes = try temp_allocator.alloc(u32, css_chunks.count());
|
||||
|
||||
const css_chunk_values = css_chunks.values();
|
||||
for (sorted_css_keys, js_chunks.count()..) |key, sorted_index| {
|
||||
// Use sorted_chunks.len as the starting index because HTML chunks
|
||||
// may be interleaved with JS chunks, so js_chunks.count() would be
|
||||
// incorrect when HTML entry points are present.
|
||||
for (sorted_css_keys, sorted_chunks.len..) |key, sorted_index| {
|
||||
const index = css_chunks.getIndex(key) orelse unreachable;
|
||||
sorted_chunks.appendAssumeCapacity(css_chunk_values[index]);
|
||||
remapped_css_indexes[index] = @intCast(sorted_index);
|
||||
|
||||
@@ -477,7 +477,7 @@ pub const Command = struct {
|
||||
compile_autoload_dotenv: bool = true,
|
||||
compile_autoload_bunfig: bool = true,
|
||||
compile_autoload_tsconfig: bool = false,
|
||||
compile_autoload_package_json: bool = false,
|
||||
compile_autoload_package_json: bool = true,
|
||||
compile_executable_path: ?[]const u8 = null,
|
||||
windows: options.WindowsOptions = .{},
|
||||
};
|
||||
|
||||
@@ -766,19 +766,13 @@ pub extern fn napi_type_tag_object(env: napi_env, _: napi_value, _: [*c]const na
|
||||
pub extern fn napi_check_object_type_tag(env: napi_env, _: napi_value, _: [*c]const napi_type_tag, _: *bool) napi_status;
|
||||
|
||||
// do nothing for both of these
|
||||
pub export fn napi_open_callback_scope(env_: napi_env, _: napi_value, _: *anyopaque, _: *anyopaque) napi_status {
|
||||
pub export fn napi_open_callback_scope(_: napi_env, _: napi_value, _: *anyopaque, _: *anyopaque) napi_status {
|
||||
log("napi_open_callback_scope", .{});
|
||||
const env = env_ orelse {
|
||||
return envIsNull();
|
||||
};
|
||||
return env.ok();
|
||||
return @intFromEnum(NapiStatus.ok);
|
||||
}
|
||||
pub export fn napi_close_callback_scope(env_: napi_env, _: *anyopaque) napi_status {
|
||||
pub export fn napi_close_callback_scope(_: napi_env, _: *anyopaque) napi_status {
|
||||
log("napi_close_callback_scope", .{});
|
||||
const env = env_ orelse {
|
||||
return envIsNull();
|
||||
};
|
||||
return env.ok();
|
||||
return @intFromEnum(NapiStatus.ok);
|
||||
}
|
||||
pub extern fn napi_throw(env: napi_env, @"error": napi_value) napi_status;
|
||||
pub extern fn napi_throw_error(env: napi_env, code: [*c]const u8, msg: [*c]const u8) napi_status;
|
||||
|
||||
@@ -12,24 +12,32 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
|
||||
// Shared getter/setter functions: .bind(obj, key) avoids creating a closure
|
||||
// and JSLexicalEnvironment per property. BoundFunction is much cheaper.
|
||||
// Must be regular functions (not arrows) so .bind() can set `this`.
|
||||
function __accessProp(key) {
|
||||
return this[key];
|
||||
}
|
||||
|
||||
// This is used to implement "export * from" statements. It copies properties
|
||||
// from the imported module to the current module's ESM export object. If the
|
||||
// current module is an entry point and the target format is CommonJS, we
|
||||
// also copy the properties to "module.exports" in addition to our module's
|
||||
// internal ESM export object.
|
||||
export var __reExport = (target, mod, secondTarget) => {
|
||||
for (let key of __getOwnPropNames(mod))
|
||||
var keys = __getOwnPropNames(mod);
|
||||
for (let key of keys)
|
||||
if (!__hasOwnProp.call(target, key) && key !== "default")
|
||||
__defProp(target, key, {
|
||||
get: () => mod[key],
|
||||
get: __accessProp.bind(mod, key),
|
||||
enumerable: true,
|
||||
});
|
||||
|
||||
if (secondTarget) {
|
||||
for (let key of __getOwnPropNames(mod))
|
||||
for (let key of keys)
|
||||
if (!__hasOwnProp.call(secondTarget, key) && key !== "default")
|
||||
__defProp(secondTarget, key, {
|
||||
get: () => mod[key],
|
||||
get: __accessProp.bind(mod, key),
|
||||
enumerable: true,
|
||||
});
|
||||
|
||||
@@ -37,11 +45,22 @@ export var __reExport = (target, mod, secondTarget) => {
|
||||
}
|
||||
};
|
||||
|
||||
/*__PURE__*/
|
||||
var __toESMCache_node;
|
||||
/*__PURE__*/
|
||||
var __toESMCache_esm;
|
||||
|
||||
// Converts the module from CommonJS to ESM. When in node mode (i.e. in an
|
||||
// ".mjs" file, package.json has "type: module", or the "__esModule" export
|
||||
// in the CommonJS file is falsy or missing), the "default" property is
|
||||
// overridden to point to the original CommonJS exports object instead.
|
||||
export var __toESM = (mod, isNodeMode, target) => {
|
||||
var canCache = mod != null && typeof mod === "object";
|
||||
if (canCache) {
|
||||
var cache = isNodeMode ? (__toESMCache_node ??= new WeakMap()) : (__toESMCache_esm ??= new WeakMap());
|
||||
var cached = cache.get(mod);
|
||||
if (cached) return cached;
|
||||
}
|
||||
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
||||
const to =
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
||||
@@ -53,34 +72,34 @@ export var __toESM = (mod, isNodeMode, target) => {
|
||||
for (let key of __getOwnPropNames(mod))
|
||||
if (!__hasOwnProp.call(to, key))
|
||||
__defProp(to, key, {
|
||||
get: () => mod[key],
|
||||
get: __accessProp.bind(mod, key),
|
||||
enumerable: true,
|
||||
});
|
||||
|
||||
if (canCache) cache.set(mod, to);
|
||||
return to;
|
||||
};
|
||||
|
||||
// Converts the module from ESM to CommonJS. This clones the input module
|
||||
// object with the addition of a non-enumerable "__esModule" property set
|
||||
// to "true", which overwrites any existing export named "__esModule".
|
||||
var __moduleCache = /* @__PURE__ */ new WeakMap();
|
||||
export var __toCommonJS = /* @__PURE__ */ from => {
|
||||
var entry = __moduleCache.get(from),
|
||||
export var __toCommonJS = from => {
|
||||
var entry = (__moduleCache ??= new WeakMap()).get(from),
|
||||
desc;
|
||||
if (entry) return entry;
|
||||
entry = __defProp({}, "__esModule", { value: true });
|
||||
if ((from && typeof from === "object") || typeof from === "function")
|
||||
__getOwnPropNames(from).map(
|
||||
key =>
|
||||
!__hasOwnProp.call(entry, key) &&
|
||||
for (var key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(entry, key))
|
||||
__defProp(entry, key, {
|
||||
get: () => from[key],
|
||||
get: __accessProp.bind(from, key),
|
||||
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable,
|
||||
}),
|
||||
);
|
||||
});
|
||||
__moduleCache.set(from, entry);
|
||||
return entry;
|
||||
};
|
||||
/*__PURE__*/
|
||||
var __moduleCache;
|
||||
|
||||
// When you do know the module is CJS
|
||||
export var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
||||
@@ -97,6 +116,10 @@ export var __name = (target, name) => {
|
||||
|
||||
// ESM export -> CJS export
|
||||
// except, writable incase something re-exports
|
||||
var __returnValue = v => v;
|
||||
function __exportSetter(name, newValue) {
|
||||
this[name] = __returnValue.bind(null, newValue);
|
||||
}
|
||||
|
||||
export var __export = /* @__PURE__ */ (target, all) => {
|
||||
for (var name in all)
|
||||
@@ -104,15 +127,19 @@ export var __export = /* @__PURE__ */ (target, all) => {
|
||||
get: all[name],
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
set: newValue => (all[name] = () => newValue),
|
||||
set: __exportSetter.bind(all, name),
|
||||
});
|
||||
};
|
||||
|
||||
function __exportValueSetter(name, newValue) {
|
||||
this[name] = newValue;
|
||||
}
|
||||
|
||||
export var __exportValue = (target, all) => {
|
||||
for (var name in all) {
|
||||
__defProp(target, name, {
|
||||
get: () => all[name],
|
||||
set: newValue => (all[name] = newValue),
|
||||
get: __accessProp.bind(all, name),
|
||||
set: __exportValueSetter.bind(all, name),
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
@@ -2,13 +2,17 @@
|
||||
|
||||
exports[`Bun.build Bun.write(BuildArtifact) 1`] = `
|
||||
"var __defProp = Object.defineProperty;
|
||||
var __returnValue = (v) => v;
|
||||
function __exportSetter(name, newValue) {
|
||||
this[name] = __returnValue.bind(null, newValue);
|
||||
}
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, {
|
||||
get: all[name],
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
set: (newValue) => all[name] = () => newValue
|
||||
set: __exportSetter.bind(all, name)
|
||||
});
|
||||
};
|
||||
|
||||
@@ -31,13 +35,17 @@ NS.then(({ fn: fn2 }) => {
|
||||
|
||||
exports[`Bun.build outdir + reading out blobs works 1`] = `
|
||||
"var __defProp = Object.defineProperty;
|
||||
var __returnValue = (v) => v;
|
||||
function __exportSetter(name, newValue) {
|
||||
this[name] = __returnValue.bind(null, newValue);
|
||||
}
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, {
|
||||
get: all[name],
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
set: (newValue) => all[name] = () => newValue
|
||||
set: __exportSetter.bind(all, name)
|
||||
});
|
||||
};
|
||||
|
||||
@@ -58,23 +66,27 @@ NS.then(({ fn: fn2 }) => {
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Bun.build BuildArtifact properties: hash 1`] = `"d1c7nm6t"`;
|
||||
exports[`Bun.build BuildArtifact properties: hash 1`] = `"est79qzq"`;
|
||||
|
||||
exports[`Bun.build BuildArtifact properties + entry.naming: hash 1`] = `"rm7e36cf"`;
|
||||
exports[`Bun.build BuildArtifact properties + entry.naming: hash 1`] = `"7gfnt0h6"`;
|
||||
|
||||
exports[`Bun.build BuildArtifact properties sourcemap: hash index.js 1`] = `"d1c7nm6t"`;
|
||||
exports[`Bun.build BuildArtifact properties sourcemap: hash index.js 1`] = `"est79qzq"`;
|
||||
|
||||
exports[`Bun.build BuildArtifact properties sourcemap: hash index.js.map 1`] = `"00000000"`;
|
||||
|
||||
exports[`Bun.build new Response(BuildArtifact) sets content type: response text 1`] = `
|
||||
"var __defProp = Object.defineProperty;
|
||||
var __returnValue = (v) => v;
|
||||
function __exportSetter(name, newValue) {
|
||||
this[name] = __returnValue.bind(null, newValue);
|
||||
}
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, {
|
||||
get: all[name],
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
set: (newValue) => all[name] = () => newValue
|
||||
set: __exportSetter.bind(all, name)
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -564,6 +564,54 @@ describe("bundler", () => {
|
||||
},
|
||||
compile: true,
|
||||
});
|
||||
// https://github.com/oven-sh/bun/issues/27058
|
||||
itBundled("compile/DynamicRequireWithPackageJsonMain", {
|
||||
files: {
|
||||
"/entry.tsx": /* tsx */ `
|
||||
const req = (x) => require(x);
|
||||
const result = req('pkg-with-main');
|
||||
console.log(JSON.stringify(result));
|
||||
`,
|
||||
"/node_modules/pkg-with-main/package.json": JSON.stringify({ name: "pkg-with-main", main: "lib/index.js" }),
|
||||
"/node_modules/pkg-with-main/lib/index.js": "throw new Error('Must be runtime import.')",
|
||||
},
|
||||
runtimeFiles: {
|
||||
"/node_modules/pkg-with-main/package.json": JSON.stringify({ name: "pkg-with-main", main: "lib/index.js" }),
|
||||
"/node_modules/pkg-with-main/lib/index.js": `module.exports = { hello: "world" };`,
|
||||
},
|
||||
run: {
|
||||
stdout: '{"hello":"world"}',
|
||||
setCwd: true,
|
||||
},
|
||||
compile: true,
|
||||
});
|
||||
// https://github.com/oven-sh/bun/issues/27058
|
||||
itBundled("compile/DynamicRequireWithPackageJsonExports", {
|
||||
files: {
|
||||
"/entry.tsx": /* tsx */ `
|
||||
const req = (x) => require(x);
|
||||
const result = req('pkg-with-exports');
|
||||
console.log(JSON.stringify(result));
|
||||
`,
|
||||
"/node_modules/pkg-with-exports/package.json": JSON.stringify({
|
||||
name: "pkg-with-exports",
|
||||
exports: { ".": "./src/entry.js" },
|
||||
}),
|
||||
"/node_modules/pkg-with-exports/src/entry.js": "throw new Error('Must be runtime import.')",
|
||||
},
|
||||
runtimeFiles: {
|
||||
"/node_modules/pkg-with-exports/package.json": JSON.stringify({
|
||||
name: "pkg-with-exports",
|
||||
exports: { ".": "./src/entry.js" },
|
||||
}),
|
||||
"/node_modules/pkg-with-exports/src/entry.js": `module.exports = { status: "ok" };`,
|
||||
},
|
||||
run: {
|
||||
stdout: '{"status":"ok"}',
|
||||
setCwd: true,
|
||||
},
|
||||
compile: true,
|
||||
});
|
||||
// see comment in `usePackageManager` for why this is a test
|
||||
itBundled("compile/NoAutoInstall", {
|
||||
files: {
|
||||
|
||||
@@ -1113,7 +1113,7 @@ describe("bundler", () => {
|
||||
snapshotSourceMap: {
|
||||
"entry.js.map": {
|
||||
files: ["../node_modules/react/index.js", "../entry.js"],
|
||||
mappingsExactMatch: "qYACA,WAAW,IAAQ,EAAE,ICDrB,eACA,QAAQ,IAAI,CAAK",
|
||||
mappingsExactMatch: "miBACA,WAAW,IAAQ,EAAE,ICDrB,eACA,QAAQ,IAAI,CAAK",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -843,4 +843,60 @@ body {
|
||||
api.expectFile("out/" + jsFile).toContain("sourceMappingURL");
|
||||
},
|
||||
});
|
||||
|
||||
// Test that multiple HTML entrypoints sharing the same CSS file both get
|
||||
// the CSS link tag in production mode (css_chunking deduplication).
|
||||
// Regression test for https://github.com/oven-sh/bun/issues/23668
|
||||
itBundled("html/SharedCSSProductionMultipleEntries", {
|
||||
outdir: "out/",
|
||||
production: true,
|
||||
files: {
|
||||
"/entry1.html": `<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="./global.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="./main1.tsx"></script>
|
||||
</body>
|
||||
</html>`,
|
||||
"/entry2.html": `<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="./global.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="./main2.tsx"></script>
|
||||
</body>
|
||||
</html>`,
|
||||
"/global.css": `h1 { font-size: 24px; }`,
|
||||
"/main1.tsx": `console.log("entry1");`,
|
||||
"/main2.tsx": `console.log("entry2");`,
|
||||
},
|
||||
entryPoints: ["/entry1.html", "/entry2.html"],
|
||||
onAfterBundle(api) {
|
||||
const entry1Html = api.readFile("out/entry1.html");
|
||||
const entry2Html = api.readFile("out/entry2.html");
|
||||
|
||||
// Both HTML files must contain a CSS link tag
|
||||
const cssMatch1 = entry1Html.match(/href="(.*\.css)"/);
|
||||
const cssMatch2 = entry2Html.match(/href="(.*\.css)"/);
|
||||
|
||||
expect(cssMatch1).not.toBeNull();
|
||||
expect(cssMatch2).not.toBeNull();
|
||||
|
||||
// Both should reference the same deduplicated CSS chunk
|
||||
expect(cssMatch1![1]).toBe(cssMatch2![1]);
|
||||
|
||||
// The CSS file should contain the shared styles
|
||||
const cssContent = api.readFile("out/" + cssMatch1![1]);
|
||||
expect(cssContent).toContain("font-size");
|
||||
|
||||
// Both HTML files should also have their respective JS bundles
|
||||
expect(entry1Html).toMatch(/src=".*\.js"/);
|
||||
expect(entry2Html).toMatch(/src=".*\.js"/);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -57,17 +57,17 @@ describe("bundler", () => {
|
||||
"../entry.tsx",
|
||||
],
|
||||
mappings: [
|
||||
["react.development.js:524:'getContextName'", "1:5412:Y1"],
|
||||
["react.development.js:524:'getContextName'", "1:5567:Y1"],
|
||||
["react.development.js:2495:'actScopeDepth'", "23:4082:GJ++"],
|
||||
["react.development.js:696:''Component'", '1:7474:\'Component "%s"'],
|
||||
["entry.tsx:6:'\"Content-Type\"'", '100:18809:"Content-Type"'],
|
||||
["entry.tsx:11:'<html>'", "100:19063:void"],
|
||||
["entry.tsx:23:'await'", "100:19163:await"],
|
||||
["react.development.js:696:''Component'", '1:7629:\'Component "%s"'],
|
||||
["entry.tsx:6:'\"Content-Type\"'", '100:18808:"Content-Type"'],
|
||||
["entry.tsx:11:'<html>'", "100:19062:void"],
|
||||
["entry.tsx:23:'await'", "100:19161:await"],
|
||||
],
|
||||
},
|
||||
},
|
||||
expectExactFilesize: {
|
||||
"out/entry.js": 221720,
|
||||
"out/entry.js": 221895,
|
||||
},
|
||||
run: {
|
||||
stdout: "<!DOCTYPE html><html><body><h1>Hello World</h1><p>This is an example.</p></body></html>",
|
||||
|
||||
@@ -76,13 +76,17 @@ describe("bundler", () => {
|
||||
|
||||
expect(bundled).toMatchInlineSnapshot(`
|
||||
"var __defProp = Object.defineProperty;
|
||||
var __returnValue = (v) => v;
|
||||
function __exportSetter(name, newValue) {
|
||||
this[name] = __returnValue.bind(null, newValue);
|
||||
}
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, {
|
||||
get: all[name],
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
set: (newValue) => all[name] = () => newValue
|
||||
set: __exportSetter.bind(all, name)
|
||||
});
|
||||
};
|
||||
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
||||
@@ -160,7 +164,7 @@ describe("bundler", () => {
|
||||
var { AsyncEntryPoint: AsyncEntryPoint2 } = await Promise.resolve().then(() => exports_AsyncEntryPoint);
|
||||
AsyncEntryPoint2();
|
||||
|
||||
//# debugId=5E85CC0956C6307964756E2164756E21
|
||||
//# debugId=42062903F19477CF64756E2164756E21
|
||||
//# sourceMappingURL=out.js.map
|
||||
"
|
||||
`);
|
||||
@@ -337,13 +341,17 @@ describe("bundler", () => {
|
||||
|
||||
expect(bundled).toMatchInlineSnapshot(`
|
||||
"var __defProp = Object.defineProperty;
|
||||
var __returnValue = (v) => v;
|
||||
function __exportSetter(name, newValue) {
|
||||
this[name] = __returnValue.bind(null, newValue);
|
||||
}
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, {
|
||||
get: all[name],
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
set: (newValue) => all[name] = () => newValue
|
||||
set: __exportSetter.bind(all, name)
|
||||
});
|
||||
};
|
||||
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
||||
@@ -402,7 +410,7 @@ describe("bundler", () => {
|
||||
var { AsyncEntryPoint: AsyncEntryPoint2 } = await Promise.resolve().then(() => exports_AsyncEntryPoint);
|
||||
AsyncEntryPoint2();
|
||||
|
||||
//# debugId=C92CBF0103732ECC64756E2164756E21
|
||||
//# debugId=BF876FBF618133C264756E2164756E21
|
||||
//# sourceMappingURL=out.js.map
|
||||
"
|
||||
`);
|
||||
|
||||
@@ -2150,10 +2150,7 @@ c {
|
||||
toplevel-tilde.css: WARNING: CSS nesting syntax is not supported in the configured target environment (chrome10)
|
||||
`, */
|
||||
});
|
||||
// TODO: Bun's bundler doesn't support multiple entry points generating CSS outputs
|
||||
// with identical content hashes to the same output path. This test exposes that
|
||||
// limitation. Skip until the bundler can deduplicate or handle this case.
|
||||
itBundled.skip("css/MetafileCSSBundleTwoToOne", {
|
||||
itBundled("css/MetafileCSSBundleTwoToOne", {
|
||||
files: {
|
||||
"/foo/entry.js": /* js */ `
|
||||
import '../common.css'
|
||||
|
||||
@@ -103,11 +103,11 @@ console.log(favicon);
|
||||
"files": [
|
||||
{
|
||||
"input": "client.html",
|
||||
"path": "./client-s249t5qg.js",
|
||||
"path": "./client-b5m4ng86.js",
|
||||
"loader": "js",
|
||||
"isEntry": true,
|
||||
"headers": {
|
||||
"etag": "fxoJ6L-0X3o",
|
||||
"etag": "Ax71YVYyZQc",
|
||||
"content-type": "text/javascript;charset=utf-8"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -634,3 +634,42 @@ test.concurrent("bun serve files with correct Content-Type headers", async () =>
|
||||
// The process will be automatically cleaned up by 'await using'
|
||||
}
|
||||
});
|
||||
|
||||
test("importing bun:main from HTML entry preload does not crash", async () => {
|
||||
const dir = tempDirWithFiles("html-entry-bun-main", {
|
||||
"index.html": /*html*/ `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Test</title></head>
|
||||
<body><h1>Hello</h1></body>
|
||||
</html>
|
||||
`,
|
||||
"preload.mjs": /*js*/ `
|
||||
try {
|
||||
await import("bun:main");
|
||||
} catch {}
|
||||
// Signal that preload ran successfully without crashing
|
||||
console.log("PRELOAD_OK");
|
||||
`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "--preload", "./preload.mjs", "index.html", "--port=0"],
|
||||
env: bunEnv,
|
||||
cwd: dir,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
});
|
||||
|
||||
const decoder = new TextDecoder();
|
||||
let text = "";
|
||||
for await (const chunk of proc.stdout) {
|
||||
text += decoder.decode(chunk, { stream: true });
|
||||
if (text.includes("http://")) break;
|
||||
}
|
||||
|
||||
expect(text).toContain("PRELOAD_OK");
|
||||
|
||||
proc.kill();
|
||||
await proc.exited;
|
||||
});
|
||||
|
||||
@@ -92,13 +92,17 @@ test("cyclic imports with async dependencies should generate async wrappers", as
|
||||
|
||||
expect(bundled).toMatchInlineSnapshot(`
|
||||
"var __defProp = Object.defineProperty;
|
||||
var __returnValue = (v) => v;
|
||||
function __exportSetter(name, newValue) {
|
||||
this[name] = __returnValue.bind(null, newValue);
|
||||
}
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, {
|
||||
get: all[name],
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
set: (newValue) => all[name] = () => newValue
|
||||
set: __exportSetter.bind(all, name)
|
||||
});
|
||||
};
|
||||
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
||||
@@ -176,7 +180,7 @@ test("cyclic imports with async dependencies should generate async wrappers", as
|
||||
var { AsyncEntryPoint: AsyncEntryPoint2 } = await Promise.resolve().then(() => exports_AsyncEntryPoint);
|
||||
AsyncEntryPoint2();
|
||||
|
||||
//# debugId=986E7BD819E590FD64756E2164756E21
|
||||
//# debugId=2020261114B67BB564756E2164756E21
|
||||
//# sourceMappingURL=entryBuild.js.map
|
||||
"
|
||||
`);
|
||||
|
||||
Reference in New Issue
Block a user