diff options
author | 2023-04-16 01:56:14 -0700 | |
---|---|---|
committer | 2023-04-16 01:56:14 -0700 | |
commit | e05cc8db90b4d1d093777b580b82c17550740a6b (patch) | |
tree | 6c762be47d5cd172d7907780e9b01f7f9746d8b7 | |
parent | 09357f55f9115bb05c57d07ee489a11e79b1a42b (diff) | |
download | bun-e05cc8db90b4d1d093777b580b82c17550740a6b.tar.gz bun-e05cc8db90b4d1d093777b580b82c17550740a6b.tar.zst bun-e05cc8db90b4d1d093777b580b82c17550740a6b.zip |
prepare for unwrapping
-rw-r--r-- | src/bundler/bundle_v2.zig | 102 | ||||
-rw-r--r-- | src/bundler/generate_node_modules_bundle.zig | 2 | ||||
-rw-r--r-- | src/cli.zig | 2 | ||||
-rw-r--r-- | src/feature_flags.zig | 43 | ||||
-rw-r--r-- | src/js_ast.zig | 3 | ||||
-rw-r--r-- | src/js_parser.zig | 131 | ||||
-rw-r--r-- | src/js_printer.zig | 9 | ||||
-rw-r--r-- | src/options.zig | 19 | ||||
-rw-r--r-- | src/runtime.zig | 8 | ||||
-rw-r--r-- | src/string_immutable.zig | 1 |
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; } |