diff options
Diffstat (limited to 'src/bundler/bundle_v2.zig')
-rw-r--r-- | src/bundler/bundle_v2.zig | 247 |
1 files changed, 184 insertions, 63 deletions
diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index 2bdac2817..b0ce41fb8 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -1,5 +1,4 @@ const Bundler = bun.Bundler; -const GenerateNodeModulesBundle = @This(); const bun = @import("root").bun; const from = bun.from; const string = bun.string; @@ -76,6 +75,8 @@ const B = js_ast.B; const Binding = js_ast.Binding; const AutoBitSet = bun.bit_set.AutoBitSet; const renamer = bun.renamer; +const StableSymbolCount = renamer.StableSymbolCount; +const MinifyRenamer = renamer.MinifyRenamer; const Scope = js_ast.Scope; const JSC = bun.JSC; const debugTreeShake = Output.scoped(.TreeShake, true); @@ -414,6 +415,11 @@ pub const BundleV2 = struct { generator.linker.resolver = &generator.bundler.resolver; generator.linker.graph.code_splitting = bundler.options.code_splitting; generator.graph.code_splitting = bundler.options.code_splitting; + + generator.linker.options.minify_syntax = bundler.options.minify_syntax; + generator.linker.options.minify_identifiers = bundler.options.minify_identifiers; + generator.linker.options.minify_whitespace = bundler.options.minify_whitespace; + var pool = try generator.graph.allocator.create(ThreadPool); if (enable_reloading) { Watcher.enableHotModuleReloading(generator); @@ -973,6 +979,7 @@ const ParseTask = struct { opts.features.trim_unused_imports = loader.isTypeScript() or (bundler.options.trim_unused_imports orelse false); opts.features.inlining = bundler.options.minify_syntax; opts.features.minify_syntax = bundler.options.minify_syntax; + opts.features.minify_identifiers = bundler.options.minify_identifiers; opts.features.should_fold_typescript_constant_expressions = opts.features.inlining or loader.isTypeScript(); opts.tree_shaking = task.tree_shaking; @@ -2064,6 +2071,7 @@ const LinkerContext = struct { tree_shaking: bool = true, minify_whitespace: bool = false, minify_syntax: bool = false, + minify_identifiers: bool = false, mode: Mode = Mode.bundle, @@ -2639,10 +2647,8 @@ const LinkerContext = struct { S.Local, S.Local{ .is_export = true, - .decls = bun.fromSlice( - []js_ast.G.Decl, + .decls = js_ast.G.Decl.List.fromSlice( this.allocator, - []const js_ast.G.Decl, &.{ .{ .binding = Binding.alloc( @@ -3624,7 +3630,7 @@ const LinkerContext = struct { allocator_, js_ast.S.Local, .{ - .decls = decls, + .decls = G.Decl.List.init(decls), }, loc, ); @@ -4290,7 +4296,7 @@ const LinkerContext = struct { for (stable_ref_list.items, clause_items.slice()) |stable_ref, *clause_item| { const ref = stable_ref.ref; - const alias = r.nextRenamedName(c.graph.symbols.get(ref).?.original_name); + const alias = if (c.options.minify_identifiers) try r.nextMinifiedName(c.allocator) else r.nextRenamedName(c.graph.symbols.get(ref).?.original_name); clause_item.* = .{ .name = .{ @@ -4411,59 +4417,135 @@ const LinkerContext = struct { chunk: *Chunk, files_in_order: []const u32, ) !renamer.Renamer { - - // TODO: minify identifiers const all_module_scopes = c.graph.ast.items(.module_scope); const all_flags: []const JSMeta.Flags = c.graph.meta.items(.flags); const all_parts: []const js_ast.Part.List = c.graph.ast.items(.parts); const all_wrapper_refs: []const Ref = c.graph.ast.items(.wrapper_ref); const all_import_records: []const ImportRecord.List = c.graph.ast.items(.import_records); - var r = try renamer.NumberRenamer.init( - allocator, - allocator, - c.graph.symbols, - brk: { - var reserved_names = try renamer.computeInitialReservedNames(allocator); + var reserved_names = try renamer.computeInitialReservedNames(allocator); + for (files_in_order) |source_index| { + renamer.computeReservedNamesForScope(&all_module_scopes[source_index], &c.graph.symbols, &reserved_names, allocator); + } + + var sorted_imports_from_other_chunks: std.ArrayList(StableRef) = brk: { + var list = std.ArrayList(StableRef).init(allocator); + var count: u32 = 0; + var imports_from_other_chunks = chunk.content.javascript.imports_from_other_chunks.values(); + for (imports_from_other_chunks) |item| { + count += item.len; + } + + list.ensureTotalCapacityPrecise(count) catch unreachable; + list.items.len = count; + var remain = list.items; + const stable_source_indices = c.graph.stable_source_indices; + for (imports_from_other_chunks) |item| { + for (item.slice()) |ref| { + remain[0] = StableRef{ + .stable_source_index = stable_source_indices[ref.ref.sourceIndex()], + .ref = ref.ref, + }; + remain = remain[1..]; + } + } + + std.sort.sort(StableRef, list.items, {}, StableRef.isLessThan); + break :brk list; + }; + defer sorted_imports_from_other_chunks.deinit(); + + if (c.options.minify_identifiers) { + const first_top_level_slots: js_ast.SlotCounts = brk: { + var slots = js_ast.SlotCounts{}; + const nested_scope_slot_counts = c.graph.ast.items(.nested_scope_slot_counts); + for (files_in_order) |i| { + slots.unionMax(nested_scope_slot_counts[i]); + } + break :brk slots; + }; + + var minify_renamer = try MinifyRenamer.init(allocator, c.graph.symbols, first_top_level_slots, reserved_names); + + var top_level_symbols = renamer.StableSymbolCount.Array.init(allocator); + defer top_level_symbols.deinit(); + + var top_level_symbols_all = renamer.StableSymbolCount.Array.init(allocator); + var stable_source_indices = c.graph.stable_source_indices; + var freq = js_ast.CharFreq{ + .freqs = [_]i32{0} ** 64, + }; + var capacity = sorted_imports_from_other_chunks.items.len; + { + const char_freqs = c.graph.ast.items(.char_freq); for (files_in_order) |source_index| { - renamer.computeReservedNamesForScope(&all_module_scopes[source_index], &c.graph.symbols, &reserved_names, allocator); + if (char_freqs[source_index]) |char_freq| { + freq.include(char_freq); + } } + } - break :brk reserved_names; - }, - ); - { - var sorted_imports_from_other_chunks: std.ArrayList(StableRef) = brk: { - var list = std.ArrayList(StableRef).init(allocator); - var count: u32 = 0; - var imports_from_other_chunks = chunk.content.javascript.imports_from_other_chunks.values(); - for (imports_from_other_chunks) |item| { - count += item.len; + const uses_exports_ref_list = c.graph.ast.items(.uses_exports_ref); + const uses_module_ref_list = c.graph.ast.items(.uses_module_ref); + const exports_ref_list = c.graph.ast.items(.exports_ref); + const module_ref_list = c.graph.ast.items(.module_ref); + const parts_list = c.graph.ast.items(.parts); + + for (files_in_order) |source_index| { + const uses_exports_ref = uses_exports_ref_list[source_index]; + const uses_module_ref = uses_module_ref_list[source_index]; + const exports_ref = exports_ref_list[source_index]; + const module_ref = module_ref_list[source_index]; + const parts = parts_list[source_index]; + + top_level_symbols.clearRetainingCapacity(); + + if (uses_exports_ref) { + try minify_renamer.accumulateSymbolUseCount(&top_level_symbols, exports_ref, 1, stable_source_indices); + } + if (uses_module_ref) { + try minify_renamer.accumulateSymbolUseCount(&top_level_symbols, module_ref, 1, stable_source_indices); } - list.ensureTotalCapacityPrecise(count) catch unreachable; - list.items.len = count; - var remain = list.items; - const stable_source_indices = c.graph.stable_source_indices; - for (imports_from_other_chunks) |item| { - for (item.slice()) |ref| { - remain[0] = StableRef{ - .stable_source_index = stable_source_indices[ref.ref.sourceIndex()], - .ref = ref.ref, - }; - remain = remain[1..]; + for (parts.slice()) |part| { + if (!part.is_live) { + continue; + } + + try minify_renamer.accumulateSymbolUseCounts(&top_level_symbols, part.symbol_uses, stable_source_indices); + + for (part.declared_symbols.refs()) |declared_ref| { + try minify_renamer.accumulateSymbolUseCount(&top_level_symbols, declared_ref, 1, stable_source_indices); } } - std.sort.sort(StableRef, list.items, {}, StableRef.isLessThan); - break :brk list; - }; - defer sorted_imports_from_other_chunks.deinit(); + std.sort.sort(renamer.StableSymbolCount, top_level_symbols.items, {}, StableSymbolCount.lessThan); + capacity += top_level_symbols.items.len; + top_level_symbols_all.appendSlice(top_level_symbols.items) catch unreachable; + } - for (sorted_imports_from_other_chunks.items) |stable_ref| { - r.addTopLevelSymbol(stable_ref.ref); + top_level_symbols.clearRetainingCapacity(); + for (sorted_imports_from_other_chunks.items) |stable| { + try minify_renamer.accumulateSymbolUseCount(&top_level_symbols, stable.ref, 1, stable_source_indices); } + top_level_symbols_all.appendSlice(top_level_symbols.items) catch unreachable; + try minify_renamer.allocateTopLevelSymbolSlots(top_level_symbols_all); + + var minifier = freq.compile(allocator); + try minify_renamer.assignNamesByFrequency(&minifier); + + return minify_renamer.toRenamer(); + } + + var r = try renamer.NumberRenamer.init( + allocator, + allocator, + c.graph.symbols, + reserved_names, + ); + for (sorted_imports_from_other_chunks.items) |stable_ref| { + r.addTopLevelSymbol(stable_ref.ref); } var sorted_ = std.ArrayList(u32).init(r.temp_allocator); @@ -4677,6 +4759,8 @@ const LinkerContext = struct { .allocator = allocator, .require_ref = runtimeRequireRef, .minify_whitespace = c.options.minify_whitespace, + .minify_identifiers = c.options.minify_identifiers, + .minify_syntax = c.options.minify_syntax, .const_values = c.graph.const_values, }; @@ -5135,10 +5219,8 @@ const LinkerContext = struct { Stmt.alloc( S.Local, .{ - .decls = bun.fromSlice( - []js_ast.G.Decl, + .decls = js_ast.G.Decl.List.fromSlice( temp_allocator, - []const js_ast.G.Decl, &.{ .{ .binding = Binding.alloc( @@ -5302,6 +5384,7 @@ const LinkerContext = struct { .require_or_import_meta_for_source_callback = js_printer.RequireOrImportMeta.Callback.init(LinkerContext, requireOrImportMetaForSource, c), .minify_whitespace = c.options.minify_whitespace, + .minify_syntax = c.options.minify_syntax, .const_values = c.graph.const_values, }; @@ -5355,6 +5438,46 @@ const LinkerContext = struct { } }; + fn mergeAdjacentLocalStmts(stmts: *std.ArrayList(Stmt), allocator: std.mem.Allocator) void { + if (stmts.items.len == 0) + return; + + var did_merge_with_previous_local = false; + var end: usize = 1; + + for (stmts.items[1..]) |stmt| { + // Try to merge with the previous variable statement + if (stmt.data == .s_local) { + var after = stmt.data.s_local; + if (stmts.items[end - 1].data == .s_local) { + var before = stmts.items[end - 1].data.s_local; + // It must be the same kind of variable statement (i.e. let/var/const) + if (before.kind == after.kind and before.is_export == after.is_export) { + if (did_merge_with_previous_local) { + // Avoid O(n^2) behavior for repeated variable declarations + // Appending to this decls list is safe because did_merge_with_previous_local is true + before.decls.append(allocator, after.decls.slice()) catch unreachable; + } else { + // Append the declarations to the previous variable statement + did_merge_with_previous_local = true; + + var clone = std.ArrayList(G.Decl).initCapacity(allocator, before.decls.len + after.decls.len) catch unreachable; + clone.appendSliceAssumeCapacity(before.decls.slice()); + clone.appendSliceAssumeCapacity(after.decls.slice()); + before.decls.update(clone); + } + continue; + } + } + } + + did_merge_with_previous_local = false; + stmts.items[end] = stmt; + end += 1; + } + stmts.items.len = end; + } + fn shouldRemoveImportExportStmt( c: *LinkerContext, stmts: *StmtList, @@ -5380,10 +5503,8 @@ const LinkerContext = struct { Stmt.alloc( S.Local, S.Local{ - .decls = try bun.fromSlice( - []G.Decl, + .decls = G.Decl.List.fromSlice( allocator, - []const G.Decl, &.{ .{ .binding = Binding.alloc( @@ -5402,7 +5523,7 @@ const LinkerContext = struct { ), }, }, - ), + ) catch unreachable, }, record.range.loc, ), @@ -5425,10 +5546,8 @@ const LinkerContext = struct { Stmt.alloc( S.Local, S.Local{ - .decls = try bun.fromSlice( - []G.Decl, + .decls = try G.Decl.List.fromSlice( allocator, - []const G.Decl, &.{ .{ .binding = Binding.alloc( @@ -5902,7 +6021,7 @@ const LinkerContext = struct { 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]; + const decl = stmt.data.s_local.decls.ptr[0]; stmt = Stmt.alloc( S.SExpr, S.SExpr{ @@ -5939,10 +6058,8 @@ const LinkerContext = struct { stmt = Stmt.alloc( S.Local, S.Local{ - .decls = try bun.fromSlice( - []js_ast.G.Decl, + .decls = try G.Decl.List.fromSlice( allocator, - []const js_ast.G.Decl, &.{ .{ .binding = Binding.alloc( @@ -6004,10 +6121,8 @@ const LinkerContext = struct { stmt = Stmt.alloc( S.Local, S.Local{ - .decls = try bun.fromSlice( - []js_ast.G.Decl, + .decls = try G.Decl.List.fromSlice( allocator, - []const js_ast.G.Decl, &.{ .{ .binding = Binding.alloc( @@ -6247,6 +6362,10 @@ const LinkerContext = struct { stmts.inside_wrapper_prefix.items.len = 0; stmts.inside_wrapper_suffix.items.len = 0; + if (c.options.minify_syntax) { + mergeAdjacentLocalStmts(&stmts.all_stmts, temp_allocator); + } + // TODO: mergeAdjacentLocalStmts var out_stmts: []js_ast.Stmt = stmts.all_stmts.items; @@ -6338,7 +6457,7 @@ const LinkerContext = struct { Stmt.alloc( S.Local, S.Local{ - .decls = decls, + .decls = G.Decl.List.init(decls), }, Logger.Loc.Empty, ), @@ -6390,7 +6509,7 @@ const LinkerContext = struct { .s_local => |local| { 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| { + for (local.decls.slice()) |*decl| { const binding = decl.binding.toExpr(&hoisty); if (decl.value) |other| { value = value.joinWithComma( @@ -6432,7 +6551,7 @@ const LinkerContext = struct { Stmt.alloc( S.Local, S.Local{ - .decls = hoisty.decls.items, + .decls = G.Decl.List.fromList(hoisty.decls), }, Logger.Loc.Empty, ), @@ -6488,7 +6607,7 @@ const LinkerContext = struct { Stmt.alloc( S.Local, S.Local{ - .decls = decls, + .decls = G.Decl.List.init(decls), }, Logger.Loc.Empty, ), @@ -6525,6 +6644,8 @@ const LinkerContext = struct { .commonjs_named_exports_ref = ast.exports_ref, .commonjs_named_exports_deoptimized = flags.wrap == .cjs, .const_values = c.graph.const_values, + .minify_whitespace = c.options.minify_whitespace, + .minify_syntax = c.options.minify_syntax, .allocator = allocator, .to_esm_ref = toESMRef, |