aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bundler/bundle_v2.zig102
-rw-r--r--src/bundler/generate_node_modules_bundle.zig2
-rw-r--r--src/cli.zig2
-rw-r--r--src/feature_flags.zig43
-rw-r--r--src/js_ast.zig3
-rw-r--r--src/js_parser.zig131
-rw-r--r--src/js_printer.zig9
-rw-r--r--src/options.zig19
-rw-r--r--src/runtime.zig8
-rw-r--r--src/string_immutable.zig1
10 files changed, 226 insertions, 94 deletions
diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig
index 0a5b238b0..a94f74cba 100644
--- a/src/bundler/bundle_v2.zig
+++ b/src/bundler/bundle_v2.zig
@@ -2673,6 +2673,7 @@ const LinkerContext = struct {
var lazy_exports: []bool = this.graph.ast.items(.has_lazy_export);
var symbols = &this.graph.symbols;
defer this.graph.symbols = symbols.*;
+ var commonjs_named_exports: []js_ast.Ast.CommonJSNamedExports = this.graph.ast.items(.commonjs_named_exports);
// Step 1: Figure out what modules must be CommonJS
for (reachable) |source_index_| {
@@ -5344,8 +5345,8 @@ const LinkerContext = struct {
loc,
),
.value = Expr.init(
- E.Require,
- E.Require{
+ E.RequireString,
+ E.RequireString{
.import_record_index = import_record_index,
},
loc,
@@ -5389,8 +5390,8 @@ const LinkerContext = struct {
loc,
),
.value = Expr.init(
- E.Require,
- E.Require{
+ E.RequireString,
+ E.RequireString{
.import_record_index = import_record_index,
},
loc,
@@ -5692,8 +5693,8 @@ const LinkerContext = struct {
}
break :brk Expr.init(
- E.Require,
- E.Require{
+ E.RequireString,
+ E.RequireString{
.import_record_index = s.import_record_index,
},
stmt.loc,
@@ -5850,7 +5851,30 @@ const LinkerContext = struct {
stmt.loc,
);
stmt.data.s_local.is_export = false;
-
+ } else if (FeatureFlags.unwrap_commonjs_to_esm and s.was_commonjs_export and wrap == .cjs) {
+ std.debug.assert(stmt.data.s_local.decls.len == 1);
+ const decl = stmt.data.s_local.decls[0];
+ stmt = Stmt.alloc(
+ S.SExpr,
+ S.SExpr{
+ .value = Expr.init(
+ E.Binary,
+ E.Binary{
+ .op = .bin_assign,
+ .left = Expr.init(
+ E.CommonJSExportIdentifier,
+ E.CommonJSExportIdentifier{
+ .ref = decl.binding.data.b_identifier.ref,
+ },
+ decl.binding.loc,
+ ),
+ .right = decl.value orelse Expr.init(E.Undefined, E.Undefined{}, Logger.Loc.Empty),
+ },
+ stmt.loc,
+ ),
+ },
+ stmt.loc,
+ );
}
},
@@ -5986,7 +6010,10 @@ const LinkerContext = struct {
// const resolved_exports: []ResolvedExports = c.graph.meta.items(.resolved_exports);
const all_flags: []const JSMeta.Flags = c.graph.meta.items(.flags);
const flags = all_flags[part_range.source_index.get()];
- const wrapper_part_index = c.graph.meta.items(.wrapper_part_index)[part_range.source_index.get()];
+ const wrapper_part_index = if (flags.wrap != .none)
+ c.graph.meta.items(.wrapper_part_index)[part_range.source_index.get()]
+ else
+ Index.invalid;
// referencing everything by array makes the code a lot more annoying :(
const ast: js_ast.Ast = c.graph.ast.get(part_range.source_index.get());
@@ -6041,7 +6068,6 @@ const LinkerContext = struct {
const index = part_range.part_index_begin + @truncate(u32, index_);
if (!part.is_live) {
// Skip the part if it's not in this chunk
-
continue;
}
@@ -6175,17 +6201,20 @@ const LinkerContext = struct {
// TODO: mergeAdjacentLocalStmts
var out_stmts: []js_ast.Stmt = stmts.all_stmts.items;
+
// Optionally wrap all statements in a closure
if (needs_wrapper) {
switch (flags.wrap) {
.cjs => {
+ var uses_exports_ref = ast.uses_exports_ref;
+
// Only include the arguments that are actually used
var args = std.ArrayList(js_ast.G.Arg).initCapacity(
temp_allocator,
- if (ast.uses_module_ref or ast.uses_exports_ref) 2 else 0,
+ if (ast.uses_module_ref or uses_exports_ref) 2 else 0,
) catch unreachable;
- if (ast.uses_module_ref or ast.uses_exports_ref) {
+ if (ast.uses_module_ref or uses_exports_ref) {
args.appendAssumeCapacity(
js_ast.G.Arg{
.binding = js_ast.Binding.alloc(
@@ -6310,30 +6339,32 @@ const LinkerContext = struct {
var stmt: Stmt = stmt_;
switch (stmt.data) {
.s_local => |local| {
- var value: Expr = Expr.init(E.Missing, E.Missing{}, Logger.Loc.Empty);
- for (local.decls) |*decl| {
- const binding = decl.binding.toExpr(&hoisty);
- if (decl.value) |other| {
- value = value.joinWithComma(
- binding.assign(
- other,
+ if (local.was_commonjs_export or ast.commonjs_named_exports.count() == 0) {
+ var value: Expr = Expr.init(E.Missing, E.Missing{}, Logger.Loc.Empty);
+ for (local.decls) |*decl| {
+ const binding = decl.binding.toExpr(&hoisty);
+ if (decl.value) |other| {
+ value = value.joinWithComma(
+ binding.assign(
+ other,
+ temp_allocator,
+ ),
temp_allocator,
- ),
- temp_allocator,
- );
+ );
+ }
}
- }
- if (value.isEmpty()) {
- continue;
+ if (value.isEmpty()) {
+ continue;
+ }
+ stmt = Stmt.alloc(
+ S.SExpr,
+ S.SExpr{
+ .value = value,
+ },
+ stmt.loc,
+ );
}
- stmt = Stmt.alloc(
- S.SExpr,
- S.SExpr{
- .value = value,
- },
- stmt.loc,
- );
},
.s_class, .s_function => {
stmts.outside_wrapper_prefix.append(stmt) catch unreachable;
@@ -6347,7 +6378,7 @@ const LinkerContext = struct {
inner_stmts.len = end;
}
- if (!c.options.minify_syntax and hoisty.decls.items.len > 0) {
+ if (hoisty.decls.items.len > 0) {
stmts.outside_wrapper_prefix.append(
Stmt.alloc(
S.Local,
@@ -6443,6 +6474,7 @@ const LinkerContext = struct {
.commonjs_named_exports = ast.commonjs_named_exports,
.commonjs_named_exports_ref = ast.exports_ref,
+ .commonjs_named_exports_deoptimized = flags.wrap == .cjs,
.const_values = c.graph.const_values,
.allocator = allocator,
@@ -6813,6 +6845,12 @@ const LinkerContext = struct {
for (_parts, 0..) |part, part_index| {
var can_be_removed_if_unused = part.can_be_removed_if_unused;
+ if (can_be_removed_if_unused and part.tag == .commonjs_named_export) {
+ if (c.graph.meta.items(.flags)[id].wrap == .cjs) {
+ can_be_removed_if_unused = false;
+ }
+ }
+
// Also include any statement-level imports
for (part.import_record_indices.slice()) |import_record_Index| {
var record: *ImportRecord = &import_records[source_index].slice()[import_record_Index];
diff --git a/src/bundler/generate_node_modules_bundle.zig b/src/bundler/generate_node_modules_bundle.zig
index 4a3323086..bd25931cd 100644
--- a/src/bundler/generate_node_modules_bundle.zig
+++ b/src/bundler/generate_node_modules_bundle.zig
@@ -535,7 +535,7 @@ pub fn generate(
defer this.bundler.resetStore();
if (this.bundler.resolver.resolve(
this.bundler.fs.top_level_dir,
- this.bundler.options.jsx.import_source,
+ this.bundler.options.jsx.importSource(),
.require,
)) |new_jsx_runtime| {
_new_jsx_runtime_resolve_result = new_jsx_runtime;
diff --git a/src/cli.zig b/src/cli.zig
index 2a368fc39..eaaececc9 100644
--- a/src/cli.zig
+++ b/src/cli.zig
@@ -918,6 +918,8 @@ pub const Command = struct {
transform_only: bool = false,
minify_syntax: bool = false,
minify_whitespace: bool = false,
+
+ always_
};
const _ctx = Command.Context{
diff --git a/src/feature_flags.zig b/src/feature_flags.zig
index a0db75f79..a01fea7dc 100644
--- a/src/feature_flags.zig
+++ b/src/feature_flags.zig
@@ -122,15 +122,42 @@ pub const react_server_components = true;
pub const help_catch_memory_issues = @import("bun").Environment.allow_assert;
-/// Disabled because we need to handle module scope for CJS better.
+/// This performs similar transforms as https://github.com/rollup/plugins/tree/master/packages/commonjs
///
-/// The current bugs are:
-/// - We need to handle name collisions in the top-level due to hoisted functions
-/// It breaks when multiple modules bundled together have functions with the
-/// same name at the top-level scope.
-/// - Cyclical requires need to be a de-optimization.
+/// Though, not exactly the same.
///
-/// Once fixed, it's a very meaningful bundle size improvement
-pub const commonjs_to_esm = false;
+/// There are two scenarios where this kicks in:
+///
+/// 1) You import a CommonJS module using ESM.
+///
+/// Semantically, CommonJS expects us to wrap everything in a closure. That
+/// bloats the code. We want to make the generated code as small as we can.
+///
+/// To avoid that, we attempt to unwrap the CommonJS module into ESM.
+///
+/// But, we can't always do that. When you have cyclical require() or directly
+/// mutate exported bindings, we can't unwrap it.
+///
+/// However, in the simple case, where you do something like
+///
+/// exports.foo = 123;
+/// exports.bar = 456;
+///
+/// We can unwrap it into
+///
+/// export const foo = 123;
+/// export const bar = 456;
+///
+/// 2) You import a CommonJS module using CommonJS.
+///
+/// This is a bit more complicated. We want to avoid the closure wrapper, but
+/// it's really difficult to track down all the places where you mutate the
+/// exports object. `require.cache` makes it even more complicated.
+/// So, we just wrap the entire module in a closure.
+///
+/// But what if we previously unwrapped it?
+///
+/// In that case, we wrap it again in the printer.
+pub const unwrap_commonjs_to_esm = true;
pub const boundary_based_chunking = true;
diff --git a/src/js_ast.zig b/src/js_ast.zig
index 05cdc23eb..3ccf1b1f6 100644
--- a/src/js_ast.zig
+++ b/src/js_ast.zig
@@ -5064,6 +5064,8 @@ pub const S = struct {
// statements where the import is never used.
was_ts_import_equals: bool = false,
+ was_commonjs_export: bool = false,
+
pub const Kind = enum(u2) {
k_var,
k_let,
@@ -5695,6 +5697,7 @@ pub const Part = struct {
bun_plugin,
bun_test,
dead_due_to_inlining,
+ commonjs_named_export,
};
pub const SymbolUseMap = std.ArrayHashMapUnmanaged(Ref, Symbol.Use, RefHashCtx, false);
diff --git a/src/js_parser.zig b/src/js_parser.zig
index 261eabf3e..953c479b3 100644
--- a/src/js_parser.zig
+++ b/src/js_parser.zig
@@ -2952,6 +2952,18 @@ pub const Parser = struct {
sliced.items[0] = stmt;
try p.appendPart(parts_list, sliced.items);
},
+
+ // Hoist functions to the top in the output
+ // This is normally done by the JS parser, but we need to do it here
+ // incase we have CommonJS exports converted to ESM exports there are assignments
+ // to the exports object that need to be hoisted.
+ .s_function => {
+ var sliced = try ListManaged(Stmt).initCapacity(p.allocator, 1);
+ sliced.items.len = 1;
+ sliced.items[0] = stmt;
+ try p.appendPart(&before, sliced.items);
+ },
+
else => {
var sliced = try ListManaged(Stmt).initCapacity(p.allocator, 1);
sliced.items.len = 1;
@@ -3011,13 +3023,13 @@ pub const Parser = struct {
var did_import_fast_refresh = false;
_ = did_import_fast_refresh;
- if (comptime FeatureFlags.commonjs_to_esm) {
+ if (comptime FeatureFlags.unwrap_commonjs_to_esm) {
if (p.commonjs_named_exports.count() > 0) {
var export_refs = p.commonjs_named_exports.values();
var export_names = p.commonjs_named_exports.keys();
if (!p.commonjs_named_exports_deoptimized) {
- // We make this safe by doing toCommonJS() at runtimes
+ // We make this safe by doing toCommonJS() at runtime
for (export_refs, export_names) |*export_ref, alias| {
if (export_ref.needs_decl) {
var this_stmts = p.allocator.alloc(Stmt, 2) catch unreachable;
@@ -3033,6 +3045,7 @@ pub const Parser = struct {
S.Local{
.kind = .k_var,
.is_export = false,
+ .was_commonjs_export = true,
.decls = decls,
},
export_ref.loc_ref.loc,
@@ -3056,7 +3069,8 @@ pub const Parser = struct {
before.append(.{
.stmts = this_stmts,
.declared_symbols = declared_symbols,
- .can_be_removed_if_unused = true,
+ .tag = .commonjs_named_export,
+ .can_be_removed_if_unused = p.stmtsCanBeRemovedIfUnused(this_stmts),
}) catch unreachable;
}
}
@@ -3094,7 +3108,7 @@ pub const Parser = struct {
const left = bin.left;
const right = bin.right;
if (bin.op == .bin_assign and
- right.data == .e_require and
+ right.data == .e_require_string and
left.data == .e_dot and
strings.eqlComptime(left.data.e_dot.name, "exports") and
left.data.e_dot.target.data == .e_identifier and
@@ -3104,7 +3118,7 @@ pub const Parser = struct {
.ast = js_ast.Ast{
.allocator = p.allocator,
.import_records = ImportRecord.List.init(p.import_records.items),
- .redirect_import_record_index = right.data.e_require.import_record_index,
+ .redirect_import_record_index = right.data.e_require_string.import_record_index,
.named_imports = p.named_imports,
.named_exports = p.named_exports,
},
@@ -3182,6 +3196,10 @@ pub const Parser = struct {
}
}
+ if (exports_kind == .esm and p.commonjs_named_exports.count() > 0) {
+ exports_kind = .esm_with_dynamic_fallback;
+ }
+
// Auto inject jest globals into the test file
if (p.options.features.inject_jest_globals) outer: {
var jest: *Jest = &p.jest;
@@ -3416,7 +3434,7 @@ pub const Parser = struct {
// if (use_automatic_identifier) {
// break :brk automatic_identifier;
// } else if (p.options.features.dynamic_require) {
- // break :brk p.newExpr(E.Require{ .import_record_index = import_record_id }, loc);
+ // break :brk p.newExpr(E.RequireString{ .import_record_index = import_record_id }, loc);
// } else {
// require_call_args_base[require_call_args_i] = automatic_identifier;
// require_call_args_i += 1;
@@ -3487,7 +3505,7 @@ pub const Parser = struct {
// // }
// p.import_records.items[import_record_id].tag = .jsx_import;
- // if (dot_call_target.data != .e_require) {
+ // if (dot_call_target.data != .e_require_string) {
// // When everything is CommonJS
// // We import JSX like this:
// // var {jsxDev} = require("react/jsx-dev")
@@ -3525,7 +3543,7 @@ pub const Parser = struct {
// if (p.options.can_import_from_bundle or p.options.enable_legacy_bundling or !p.options.features.allow_runtime) {
// break :brk classic_identifier;
// } else if (p.options.features.dynamic_require) {
- // break :brk p.newExpr(E.Require{ .import_record_index = import_record_id }, loc);
+ // break :brk p.newExpr(E.RequireString{ .import_record_index = import_record_id }, loc);
// } else {
// const require_call_args_start = require_call_args_i;
// require_call_args_base[require_call_args_i] = classic_identifier;
@@ -3570,7 +3588,7 @@ pub const Parser = struct {
// decl_i += 1;
// }
- // if (dot_call_target.data != .e_require) {
+ // if (dot_call_target.data != .e_require_string) {
// jsx_part_stmts[stmt_i] = p.s(S.Import{
// .namespace_ref = classic_namespace_ref,
// .star_name_loc = loc,
@@ -4508,6 +4526,7 @@ fn NewParser_(
commonjs_named_exports: js_ast.Ast.CommonJSNamedExports = .{},
commonjs_named_exports_deoptimized: bool = false,
commonjs_named_exports_needs_conversion: u32 = std.math.maxInt(u32),
+ had_commonjs_named_exports_this_visit: bool = false,
parse_pass_symbol_uses: ParsePassSymbolUsageType = undefined,
// duplicate_case_checker: void,
@@ -14093,6 +14112,8 @@ fn NewParser_(
p.scopes_for_current_part.clearRetainingCapacity();
p.import_records_for_current_part.clearRetainingCapacity();
+ p.had_commonjs_named_exports_this_visit = false;
+
const allocator = p.allocator;
var opts = PrependTempRefsOpts{};
var partStmts = ListManaged(Stmt).fromOwnedSlice(allocator, stmts);
@@ -14200,8 +14221,10 @@ fn NewParser_(
),
.scopes = try p.scopes_for_current_part.toOwnedSlice(p.allocator),
.can_be_removed_if_unused = p.stmtsCanBeRemovedIfUnused(_stmts),
+ .tag = if (p.had_commonjs_named_exports_this_visit) js_ast.Part.Tag.commonjs_named_export else .none,
});
p.symbol_uses = .{};
+ p.had_commonjs_named_exports_this_visit = false;
} else if (p.declared_symbols.len() > 0 or p.symbol_uses.count() > 0) {
// if the part is dead, invalidate all the usage counts
@@ -17021,7 +17044,7 @@ fn NewParser_(
}
}
- if (comptime FeatureFlags.commonjs_to_esm) {
+ if (comptime FeatureFlags.unwrap_commonjs_to_esm) {
if (!p.is_control_flow_dead and id.ref.eql(p.exports_ref) and !p.commonjs_named_exports_deoptimized) {
if (identifier_opts.is_delete_target) {
p.deoptimizeCommonJSNamedExports();
@@ -17068,34 +17091,38 @@ fn NewParser_(
}
},
.e_string => |str| {
- // minify "long-string".length to 11
- if (strings.eqlComptime(name, "length")) {
- return p.newExpr(E.Number{ .value = @intToFloat(f64, str.javascriptLength()) }, loc);
+ if (p.options.features.minify_syntax) {
+ // minify "long-string".length to 11
+ if (strings.eqlComptime(name, "length")) {
+ return p.newExpr(E.Number{ .value = @intToFloat(f64, str.javascriptLength()) }, loc);
+ }
}
},
.e_object => |obj| {
if (comptime FeatureFlags.inline_properties_in_transpiler) {
- //
- // Rewrite a property access like this:
- // { f: () => {} }.f
- // To:
- // () => {}
- //
- // To avoid thinking too much about edgecases, only do this for:
- // 1) Objects with a single property
- // 2) Not a method, not a computed property
- if (obj.properties.len == 1 and
- !identifier_opts.is_delete_target and
- identifier_opts.assign_target == .none and !identifier_opts.is_call_target)
- {
- const prop: G.Property = obj.properties.ptr[0];
- if (prop.value != null and
- prop.flags.count() == 0 and
- prop.key != null and
- prop.key.?.data == .e_string and
- prop.key.?.data.e_string.eql([]const u8, name))
+ if (p.options.features.minify_syntax) {
+ //
+ // Rewrite a property access like this:
+ // { f: () => {} }.f
+ // To:
+ // () => {}
+ //
+ // To avoid thinking too much about edgecases, only do this for:
+ // 1) Objects with a single property
+ // 2) Not a method, not a computed property
+ if (obj.properties.len == 1 and
+ !identifier_opts.is_delete_target and
+ identifier_opts.assign_target == .none and !identifier_opts.is_call_target)
{
- return prop.value.?;
+ const prop: G.Property = obj.properties.ptr[0];
+ if (prop.value != null and
+ prop.flags.count() == 0 and
+ prop.key != null and
+ prop.key.?.data == .e_string and
+ prop.key.?.data.e_string.eql([]const u8, name))
+ {
+ return prop.value.?;
+ }
}
}
}
@@ -17557,7 +17584,7 @@ fn NewParser_(
.s_expr => |data| {
p.stmt_expr_value = data.value.data;
const is_top_level = p.current_scope == p.module_scope;
- if (comptime FeatureFlags.commonjs_to_esm) {
+ if (comptime FeatureFlags.unwrap_commonjs_to_esm) {
p.commonjs_named_exports_needs_conversion = if (is_top_level)
std.math.maxInt(u32)
else
@@ -17569,7 +17596,7 @@ fn NewParser_(
// simplify unused
data.value = SideEffects.simpifyUnusedExpr(p, data.value) orelse data.value.toEmpty();
- if (comptime FeatureFlags.commonjs_to_esm) {
+ if (comptime FeatureFlags.unwrap_commonjs_to_esm) {
if (is_top_level) {
if (data.value.data == .e_binary) {
const to_convert = p.commonjs_named_exports_needs_conversion;
@@ -17589,9 +17616,9 @@ fn NewParser_(
.value = bin.right,
};
p.recordDeclaredSymbol(ref) catch unreachable;
- p.ignoreUsage(ref);
p.esm_export_keyword.loc = stmt.loc;
p.esm_export_keyword.len = 5;
+ p.had_commonjs_named_exports_this_visit = true;
var clause_items = p.allocator.alloc(js_ast.ClauseItem, 1) catch unreachable;
clause_items[0] = js_ast.ClauseItem{
// We want the generated name to not conflict
@@ -17608,6 +17635,7 @@ fn NewParser_(
S.Local{
.kind = .k_var,
.is_export = false,
+ .was_commonjs_export = true,
.decls = decls,
},
stmt.loc,
@@ -19480,11 +19508,16 @@ fn NewParser_(
break :list_getter &after;
},
.s_function => |data| {
- // Manually hoist block-level function declarations to preserve semantics.
- // This is only done for function declarations that are not generators
- // or async functions, since this is a backwards-compatibility hack from
- // Annex B of the JavaScript standard.
- if (!p.current_scope.kindStopsHoisting() and p.symbols.items[data.func.name.?.ref.?.innerIndex()].kind == .hoisted_function) {
+ if (
+ // Hoist module-level functions when
+ ((FeatureFlags.unwrap_commonjs_to_esm and p.current_scope == p.module_scope and !data.func.flags.contains(.is_export)) or
+
+ // Manually hoist block-level function declarations to preserve semantics.
+ // This is only done for function declarations that are not generators
+ // or async functions, since this is a backwards-compatibility hack from
+ // Annex B of the JavaScript standard.
+ !p.current_scope.kindStopsHoisting()) and p.symbols.items[data.func.name.?.ref.?.innerIndex()].kind == .hoisted_function)
+ {
break :list_getter &before;
}
},
@@ -19627,22 +19660,22 @@ fn NewParser_(
try stmts.resize(total_size);
}
- var i: usize = 0;
+ var remain = stmts.items;
for (before.items) |item| {
- stmts.items[i] = item;
- i += 1;
+ remain[0] = item;
+ remain = remain[1..];
}
const visited_slice = visited.items[0..visited_count];
for (visited_slice) |item| {
- stmts.items[i] = item;
- i += 1;
+ remain[0] = item;
+ remain = remain[1..];
}
for (after.items) |item| {
- stmts.items[i] = item;
- i += 1;
+ remain[0] = item;
+ remain = remain[1..];
}
}
@@ -19667,7 +19700,7 @@ fn NewParser_(
switch (stmt.data) {
.s_empty, .s_comment, .s_directive, .s_debugger, .s_type_script => continue,
.s_local => |local| {
- if (!local.is_export and local.kind == .k_const) {
+ if (!local.is_export and local.kind == .k_const and !local.was_commonjs_export) {
var decls: []Decl = local.decls;
var end: usize = 0;
for (decls) |decl| {
diff --git a/src/js_printer.zig b/src/js_printer.zig
index 7bd7fcc70..de0a978dc 100644
--- a/src/js_printer.zig
+++ b/src/js_printer.zig
@@ -490,6 +490,7 @@ pub const Options = struct {
css_import_behavior: Api.CssInJsBehavior = Api.CssInJsBehavior.facade,
commonjs_named_exports: js_ast.Ast.CommonJSNamedExports = .{},
+ commonjs_named_exports_deoptimized: bool = false,
commonjs_named_exports_ref: Ref = Ref.None,
minify_whitespace: bool = false,
@@ -1942,7 +1943,7 @@ fn NewPrinter(
for (p.options.commonjs_named_exports.keys(), p.options.commonjs_named_exports.values()) |key, value| {
if (value.loc_ref.ref.?.eql(id.ref)) {
- if (value.needs_decl) {
+ if (p.options.commonjs_named_exports_deoptimized or value.needs_decl) {
p.printSymbol(p.options.commonjs_named_exports_ref);
p.print(".");
p.print(key);
@@ -2074,7 +2075,7 @@ fn NewPrinter(
p.print(")");
}
},
- .e_require => |e| {
+ .e_require_string => |e| {
if (rewrite_esm_to_cjs and p.importRecord(e.import_record_index).is_legacy_bundled) {
p.printIndent();
p.printBundledRequire(e);
@@ -2086,7 +2087,7 @@ fn NewPrinter(
p.printRequireOrImportExpr(e.import_record_index, &([_]G.Comment{}), level, flags);
}
},
- .e_require_or_require_resolve => |e| {
+ .e_require_resolve_string => |e| {
if (p.options.rewrite_require_resolve) {
// require.resolve("../src.js") => "../src.js"
p.printSpaceBeforeIdentifier();
@@ -4869,7 +4870,7 @@ fn NewPrinter(
std.fmt.formatInt(module_id, 16, .lower, .{}, p) catch unreachable;
}
- pub fn printBundledRequire(p: *Printer, require: E.Require) void {
+ pub fn printBundledRequire(p: *Printer, require: E.RequireString) void {
if (p.importRecord(require.import_record_index).is_internal) {
return;
}
diff --git a/src/options.zig b/src/options.zig
index a189c4d7a..41fd49a25 100644
--- a/src/options.zig
+++ b/src/options.zig
@@ -1407,11 +1407,30 @@ pub const BundleOptions = struct {
minify_whitespace: bool = false,
minify_syntax: bool = false,
+ // This is a list of packages which even when require() is used, we will
+ // instead convert to ESM import statements.
+ //
+ // This is not normally a safe transformation.
+ //
+ // So we have a list of packages which we know are safe to do this with.
+ unwrap_commonjs_packages: []const string = default_unwrap_commonjs_packages,
+
pub fn setProduction(this: *BundleOptions, value: bool) void {
this.production = value;
this.jsx.development = !value;
}
+ pub const default_unwrap_commonjs_packages = [_]string{
+ "__bun-test-unwrap-commonjs__",
+ "react",
+ "react-client",
+ "react-dom",
+ "react-is",
+ "react-refresh",
+ "react-server",
+ "scheduler",
+ };
+
pub inline fn cssImportBehavior(this: *const BundleOptions) Api.CssInJsBehavior {
switch (this.platform) {
.neutral, .browser => {
diff --git a/src/runtime.zig b/src/runtime.zig
index 8a0c4611b..7af9015ed 100644
--- a/src/runtime.zig
+++ b/src/runtime.zig
@@ -326,6 +326,14 @@ pub const Runtime = struct {
dont_bundle_twice: bool = false,
+ /// This is a list of packages which even when require() is used, we will
+ /// instead convert to ESM import statements.
+ ///
+ /// This is not normally a safe transformation.
+ ///
+ /// So we have a list of packages which we know are safe to do this with.
+ unwrap_commonjs_packages: []const string = .{},
+
pub const ReplaceableExport = union(enum) {
delete: void,
replace: JSAst.Expr,
diff --git a/src/string_immutable.zig b/src/string_immutable.zig
index f1a17ad16..bc0b59ea0 100644
--- a/src/string_immutable.zig
+++ b/src/string_immutable.zig
@@ -4349,6 +4349,7 @@ pub fn concatWithLength(
@memcpy(remain.ptr, arg.ptr, arg.len);
remain = remain[arg.len..];
}
+ std.debug.assert(remain.len == 0); // all bytes should be used
return out;
}