aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/bun-types/bun.d.ts7
-rw-r--r--src/bun.js/api/transpiler.zig8
-rw-r--r--src/bundler.zig14
-rw-r--r--src/js_printer.zig327
-rw-r--r--src/options.zig1
5 files changed, 261 insertions, 96 deletions
diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts
index f716d3bd0..c8e309400 100644
--- a/packages/bun-types/bun.d.ts
+++ b/packages/bun-types/bun.d.ts
@@ -658,6 +658,13 @@ declare module "bun" {
jsxOptimizationInline?: boolean;
/**
+ * **Experimental**
+ *
+ * Minify whitespace and comments from the output.
+ */
+ minifyWhitespace?: boolean;
+
+ /**
* This does two things (and possibly more in the future):
* 1. `const` declarations to primitive types (excluding Object/Array) at the top of a scope before any `let` or `var` declarations will be inlined into their usages.
* 2. `let` and `const` declarations only used once are inlined into their usages.
diff --git a/src/bun.js/api/transpiler.zig b/src/bun.js/api/transpiler.zig
index 341a0d9db..24104c2b7 100644
--- a/src/bun.js/api/transpiler.zig
+++ b/src/bun.js/api/transpiler.zig
@@ -104,6 +104,9 @@ const TranspilerOptions = struct {
tree_shaking: bool = false,
trim_unused_imports: ?bool = null,
inlining: bool = false,
+
+ minify_whitespace: bool = false,
+ minify_identifiers: bool = false,
};
// Mimalloc gets unstable if we try to move this to a different thread
@@ -560,6 +563,10 @@ fn transformOptionsFromJSC(ctx: JSC.C.JSContextRef, temp_allocator: std.mem.Allo
transpiler.runtime.inlining = flag.toBoolean();
}
+ if (object.get(globalThis, "minifyWhitespace")) |flag| {
+ transpiler.minify_whitespace = flag.toBoolean();
+ }
+
if (object.get(globalThis, "sourcemap")) |flag| {
if (flag.isBoolean() or flag.isUndefinedOrNull()) {
if (flag.toBoolean()) {
@@ -785,6 +792,7 @@ pub fn constructor(
bundler.options.macro_remap = transpiler_options.macro_map;
}
+ bundler.options.minify_whitespace = transpiler_options.minify_whitespace;
bundler.options.tree_shaking = transpiler_options.tree_shaking;
bundler.options.trim_unused_imports = transpiler_options.trim_unused_imports;
bundler.options.allow_runtime = transpiler_options.runtime.allow_runtime;
diff --git a/src/bundler.zig b/src/bundler.zig
index 53cbccb91..4099e9d2e 100644
--- a/src/bundler.zig
+++ b/src/bundler.zig
@@ -1155,6 +1155,7 @@ pub const Bundler = struct {
.css_import_behavior = bundler.options.cssImportBehavior(),
.source_map_handler = source_map_context,
.rewrite_require_resolve = bundler.options.platform != .node,
+ .minify_whitespace = bundler.options.minify_whitespace,
},
Linker,
&bundler.linker,
@@ -1176,6 +1177,7 @@ pub const Bundler = struct {
.source_map_handler = source_map_context,
.css_import_behavior = bundler.options.cssImportBehavior(),
.rewrite_require_resolve = bundler.options.platform != .node,
+ .minify_whitespace = bundler.options.minify_whitespace,
},
Linker,
&bundler.linker,
@@ -1197,6 +1199,7 @@ pub const Bundler = struct {
.css_import_behavior = bundler.options.cssImportBehavior(),
.source_map_handler = source_map_context,
.rewrite_require_resolve = bundler.options.platform != .node,
+ .minify_whitespace = bundler.options.minify_whitespace,
},
Linker,
&bundler.linker,
@@ -1218,6 +1221,7 @@ pub const Bundler = struct {
.css_import_behavior = bundler.options.cssImportBehavior(),
.source_map_handler = source_map_context,
.rewrite_require_resolve = bundler.options.platform != .node,
+ .minify_whitespace = bundler.options.minify_whitespace,
},
Linker,
&bundler.linker,
@@ -1239,6 +1243,7 @@ pub const Bundler = struct {
.css_import_behavior = bundler.options.cssImportBehavior(),
.source_map_handler = source_map_context,
.rewrite_require_resolve = bundler.options.platform != .node,
+ .minify_whitespace = bundler.options.minify_whitespace,
},
Linker,
&bundler.linker,
@@ -1260,6 +1265,7 @@ pub const Bundler = struct {
.css_import_behavior = bundler.options.cssImportBehavior(),
.source_map_handler = source_map_context,
.rewrite_require_resolve = bundler.options.platform != .node,
+ .minify_whitespace = bundler.options.minify_whitespace,
},
Linker,
&bundler.linker,
@@ -1717,6 +1723,14 @@ pub const Bundler = struct {
try bundler.configureDefines();
bundler.macro_context = js_ast.Macro.MacroContext.init(&bundler);
+ if (bundler.env.map.get("BUN_CONFIG_MINIFY_WHITESPACE") != null) {
+ bundler.options.minify_whitespace = true;
+ }
+
+ if (bundler.env.map.get("BUN_CONFIG_INLINE") != null) {
+ bundler.options.inlining = true;
+ }
+
var skip_normalize = false;
var load_from_routes = false;
if (bundler.options.routes.routes_enabled and bundler.options.entry_points.len == 0) {
diff --git a/src/js_printer.zig b/src/js_printer.zig
index 618eba09e..5cbff5a15 100644
--- a/src/js_printer.zig
+++ b/src/js_printer.zig
@@ -88,6 +88,8 @@ pub fn canPrintWithoutEscape(comptime CodePointType: type, c: CodePointType, com
}
}
+const indentation_buf = [_]u8{' '} ** 128;
+
pub fn estimateLengthForJSON(input: []const u8, comptime ascii_only: bool) usize {
var remaining = input;
var len: u32 = 2; // for quotes
@@ -410,6 +412,8 @@ pub const Options = struct {
source_map_handler: ?SourceMapHandler = null,
css_import_behavior: Api.CssInJsBehavior = Api.CssInJsBehavior.facade,
+ minify_whitespace: bool = false,
+
// TODO: remove this
// The reason for this is:
// 1. You're bundling a React component
@@ -635,26 +639,33 @@ pub fn NewPrinter(
}
pub fn printIndent(p: *Printer) void {
- if (p.options.indent == 0) {
+ if (p.options.indent == 0 or p.options.minify_whitespace) {
return;
}
- // p.js.growBy(p.options.indent * " ".len) catch unreachable;
- var i: usize = 0;
+ var i: usize = p.options.indent * 2;
- while (i < p.options.indent) : (i += 1) {
- p.unsafePrint(" ");
+ while (i > 0) {
+ const amt = @minimum(i, indentation_buf.len);
+ p.print(indentation_buf[0..amt]);
+ i -= amt;
}
}
pub inline fn printSpace(p: *Printer) void {
- p.print(" ");
+ if (!p.options.minify_whitespace)
+ p.print(" ");
}
pub inline fn printNewline(p: *Printer) void {
- p.print("\n");
+ if (!p.options.minify_whitespace)
+ p.print("\n");
}
pub inline fn printSemicolonAfterStatement(p: *Printer) void {
- p.print(";\n");
+ if (!p.options.minify_whitespace) {
+ p.print(";\n");
+ } else {
+ p.needs_semicolon = true;
+ }
}
pub fn printSemicolonIfNeeded(p: *Printer) void {
if (p.needs_semicolon) {
@@ -663,6 +674,14 @@ pub fn NewPrinter(
}
}
+ fn @"print = "(p: *Printer) void {
+ if (p.options.minify_whitespace) {
+ p.print("=");
+ } else {
+ p.print(" = ");
+ }
+ }
+
fn printBunJestImportStatement(p: *Printer, import: S.Import) void {
if (comptime !is_bun_platform) unreachable;
@@ -702,10 +721,10 @@ pub fn NewPrinter(
p.print("var ");
p.printSymbol(default.ref.?);
if (comptime Statement == void) {
- p.print(" = ");
+ p.@"print = "();
p.printRequireOrImportExpr(import.import_record_index, &.{}, Level.lowest, ExprFlag.None());
} else {
- p.print(" = ");
+ p.@"print = "();
p.print(statement);
}
p.printSemicolonAfterStatement();
@@ -713,10 +732,11 @@ pub fn NewPrinter(
if (import.items.len > 0) {
p.print("var ");
- p.print("{ ");
+ p.print("{");
+ p.printSpace();
if (!import.is_single_line) {
- p.print("\n");
+ p.printNewline();
p.options.indent += 1;
p.printIndent();
}
@@ -727,7 +747,7 @@ pub fn NewPrinter(
p.printSpace();
if (!import.is_single_line) {
- p.print("\n");
+ p.printNewline();
p.printIndent();
}
}
@@ -736,27 +756,30 @@ pub fn NewPrinter(
}
if (!import.is_single_line) {
- p.print("\n");
- p.options.indent -= 1;
+ p.printNewline();
+ p.options.unindent();
} else {
p.printSpace();
}
if (import.star_name_loc == null and import.default_name == null) {
+ p.print("}");
+ p.@"print = "();
+
if (comptime Statement == void) {
- p.print("} = ");
p.printRequireOrImportExpr(import.import_record_index, &.{}, Level.lowest, ExprFlag.None());
} else {
- p.print("} = ");
p.print(statement);
}
} else if (import.default_name) |name| {
- p.print("} =");
- p.printSpaceBeforeIdentifier();
+ p.print("}");
+ p.printSpace();
+ p.print("=");
p.printSymbol(name.ref.?);
} else {
- p.print("} =");
- p.printSpaceBeforeIdentifier();
+ p.print("}");
+ p.printSpace();
+ p.print("=");
p.printSymbol(import.namespace_ref);
}
@@ -802,11 +825,6 @@ pub fn NewPrinter(
p.printBlock(stmt.loc, block.stmts);
p.printNewline();
},
- .s_empty => {
- p.printSpace();
- p.printBlock(stmt.loc, &.{});
- p.printNewline();
- },
else => {
p.printNewline();
p.options.indent += 1;
@@ -898,6 +916,7 @@ pub fn NewPrinter(
pub fn printFnArgs(
p: *Printer,
+ open_paren_loc: ?logger.Loc,
args: []G.Arg,
has_rest_arg: bool,
// is_arrow can be used for minifying later
@@ -906,6 +925,9 @@ pub fn NewPrinter(
const wrap = true;
if (wrap) {
+ if (open_paren_loc) |loc| {
+ p.addSourceMapping(loc);
+ }
p.print("(");
}
@@ -935,7 +957,7 @@ pub fn NewPrinter(
}
pub fn printFunc(p: *Printer, func: G.Fn) void {
- p.printFnArgs(func.args, func.flags.contains(.has_rest_arg), false);
+ p.printFnArgs(func.open_parens_loc, func.args, func.flags.contains(.has_rest_arg), false);
p.printSpace();
p.printBlock(func.body.loc, func.body.stmts);
}
@@ -1468,6 +1490,7 @@ pub fn NewPrinter(
}
p.addSourceMapping(record.range.loc);
+ p.printSpaceBeforeIdentifier();
// Allow it to fail at runtime, if it should
p.print("import(");
p.printImportRecordPath(record);
@@ -1507,7 +1530,9 @@ pub fn NewPrinter(
p.printClauseAlias(item.alias);
if (!strings.eql(name, item.alias)) {
- p.print(" as ");
+ p.print(" as");
+ p.printSpace();
+ p.addSourceMapping(item.alias_loc);
p.printIdentifier(name);
}
} else if (comptime as == .@"var") {
@@ -1516,13 +1541,16 @@ pub fn NewPrinter(
if (!strings.eql(name, item.alias)) {
p.print(":");
p.printSpace();
+
p.printIdentifier(name);
}
} else if (comptime as == .@"export") {
p.printIdentifier(name);
if (!strings.eql(name, item.alias)) {
- p.print(" as ");
+ p.print(" as");
+ p.printSpace();
+ p.addSourceMapping(item.alias_loc);
p.printClauseAlias(item.alias);
}
} else {
@@ -1549,38 +1577,42 @@ pub fn NewPrinter(
}
pub fn printExpr(p: *Printer, expr: Expr, level: Level, _flags: ExprFlag.Set) void {
- p.addSourceMapping(expr.loc);
var flags = _flags;
switch (expr.data) {
.e_missing => {},
.e_undefined => {
- p.printSpaceBeforeIdentifier();
-
+ p.addSourceMapping(expr.loc);
p.printUndefined(level);
},
.e_super => {
p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(expr.loc);
p.print("super");
},
.e_null => {
p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(expr.loc);
p.print("null");
},
.e_this => {
p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(expr.loc);
p.print("this");
},
.e_spread => |e| {
+ p.addSourceMapping(expr.loc);
p.print("...");
p.printExpr(e.value, .comma, ExprFlag.None());
},
.e_new_target => {
p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(expr.loc);
p.print("new.target");
},
.e_import_meta => {
p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(expr.loc);
p.print("import.meta");
},
.e_new => |e| {
@@ -1596,6 +1628,7 @@ pub fn NewPrinter(
}
p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(expr.loc);
p.print("new");
p.printSpace();
p.printExpr(e.target, .new, ExprFlag.ForbidCall());
@@ -1724,6 +1757,7 @@ pub fn NewPrinter(
}
p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(expr.loc);
p.print("import(");
if (e.leading_interior_comments.len > 0) {
p.printNewline();
@@ -1827,11 +1861,12 @@ pub fn NewPrinter(
if (!is_optional_chain_start) {
p.print(".");
}
-
+ p.addSourceMapping(e.index.loc);
p.printSymbol(priv.ref);
},
else => {
p.print("[");
+ p.addSourceMapping(e.index.loc);
p.printExpr(e.index, .lowest, ExprFlag.None());
p.print("]");
},
@@ -1867,12 +1902,13 @@ pub fn NewPrinter(
}
if (e.is_async) {
+ p.addSourceMapping(expr.loc);
p.printSpaceBeforeIdentifier();
p.print("async");
p.printSpace();
}
- p.printFnArgs(e.args, e.has_rest_arg, true);
+ p.printFnArgs(if (e.is_async) null else expr.loc, e.args, e.has_rest_arg, true);
p.printSpace();
p.print("=>");
p.printSpace();
@@ -1918,6 +1954,7 @@ pub fn NewPrinter(
p.printSpaceBeforeIdentifier();
if (e.func.flags.contains(.is_async)) {
+ p.addSourceMapping(expr.loc);
p.print("async ");
}
p.print("function");
@@ -1927,7 +1964,8 @@ pub fn NewPrinter(
}
if (e.func.name) |sym| {
- p.maybePrintSpace();
+ p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(sym.loc);
p.printSymbol(sym.ref orelse Global.panic("internal error: expected E.Function's name symbol to have a ref\n{s}", .{e.func}));
}
@@ -1944,12 +1982,12 @@ pub fn NewPrinter(
}
p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(expr.loc);
p.print("class");
if (e.class_name) |name| {
- p.maybePrintSpace();
+ p.print(" ");
p.addSourceMapping(name.loc);
p.printSymbol(name.ref orelse Global.panic("internal error: expected E.Class's name symbol to have a ref\n{s}", .{e}));
- p.maybePrintSpace();
}
p.printClass(e.*);
if (wrap) {
@@ -1957,6 +1995,7 @@ pub fn NewPrinter(
}
},
.e_array => |e| {
+ p.addSourceMapping(expr.loc);
p.print("[");
const items = e.items.slice();
if (items.len > 0) {
@@ -2006,6 +2045,7 @@ pub fn NewPrinter(
if (wrap) {
p.print("(");
}
+ p.addSourceMapping(expr.loc);
p.print("{");
const props = e.properties.slice();
if (props.len > 0) {
@@ -2043,10 +2083,12 @@ pub fn NewPrinter(
},
.e_boolean => |e| {
p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(expr.loc);
p.print(if (e.value) "true" else "false");
},
.e_string => |e| {
e.resovleRopeIfNeeded(p.options.allocator);
+ p.addSourceMapping(expr.loc);
// If this was originally a template literal, print it as one as long as we're not minifying
if (e.prefer_template) {
@@ -2057,12 +2099,14 @@ pub fn NewPrinter(
}
const c = p.bestQuoteCharForEString(e, true);
+
p.print(c);
p.printStringContent(e, c);
p.print(c);
},
.e_template => |e| {
if (e.tag) |tag| {
+ p.addSourceMapping(expr.loc);
// Optional chains are forbidden in template tags
if (expr.isOptionalChain()) {
p.print("(");
@@ -2071,6 +2115,8 @@ pub fn NewPrinter(
} else {
p.printExpr(tag, .postfix, ExprFlag.None());
}
+ } else {
+ p.addSourceMapping(expr.loc);
}
p.print("`");
@@ -2092,10 +2138,12 @@ pub fn NewPrinter(
p.print("`");
},
.e_reg_exp => |e| {
+ p.addSourceMapping(expr.loc);
p.printRegExpLiteral(e);
},
.e_big_int => |e| {
p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(expr.loc);
p.print(e.value);
p.print('n');
},
@@ -2106,19 +2154,24 @@ pub fn NewPrinter(
if (std.math.isNan(value)) {
p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(expr.loc);
p.print("NaN");
} else if (std.math.isPositiveInf(value)) {
p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(expr.loc);
p.print("Infinity");
} else if (std.math.isNegativeInf(value)) {
if (level.gte(.prefix)) {
+ p.addSourceMapping(expr.loc);
p.print("(-Infinity)");
} else {
p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(expr.loc);
p.print("(-Infinity)");
}
} else if (!std.math.signbit(value)) {
p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(expr.loc);
p.printNonNegativeFloat(absValue);
// Remember the end of the latest number
@@ -2129,11 +2182,13 @@ pub fn NewPrinter(
// "!isNaN(value)" because we need this to be true for "-0" and "-0 < 0"
// is false.
p.print("(-");
+ p.addSourceMapping(expr.loc);
p.printNonNegativeFloat(absValue);
p.print(")");
} else {
p.printSpaceBeforeOperator(Op.Code.un_neg);
p.print("-");
+ p.addSourceMapping(expr.loc);
p.printNonNegativeFloat(absValue);
// Remember the end of the latest number
@@ -2149,6 +2204,7 @@ pub fn NewPrinter(
}
p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(expr.loc);
p.printIdentifier(name);
if (wrap) {
@@ -2161,6 +2217,7 @@ pub fn NewPrinter(
if (p.symbols.getWithLink(e.ref)) |symbol| {
if (symbol.import_item_status == .missing) {
+ p.addSourceMapping(expr.loc);
p.printUndefined(level);
didPrint = true;
} else if (symbol.namespace_alias) |namespace| {
@@ -2177,13 +2234,15 @@ pub fn NewPrinter(
if (wrap) {
p.print("(0, ");
}
-
+ p.addSourceMapping(expr.loc);
p.printNamespaceAlias(import_record, namespace);
if (wrap) {
p.print(")");
}
} else if (import_record.was_originally_require and import_record.path.is_disabled) {
+ p.addSourceMapping(expr.loc);
+
if (import_record.handles_import_errors) {
p.printRequireError(import_record.path.text);
} else {
@@ -2195,6 +2254,7 @@ pub fn NewPrinter(
}
if (!didPrint) {
+ p.addSourceMapping(expr.loc);
p.printSymbol(e.ref);
}
},
@@ -2206,6 +2266,7 @@ pub fn NewPrinter(
}
p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(expr.loc);
p.print("await");
p.printSpace();
p.printExpr(e.value, Level.sub(.prefix, 1), ExprFlag.None());
@@ -2221,6 +2282,7 @@ pub fn NewPrinter(
}
p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(expr.loc);
p.print("yield");
if (e.value) |val| {
@@ -2776,11 +2838,11 @@ pub fn NewPrinter(
}
pub fn printBinding(p: *Printer, binding: Binding) void {
- p.addSourceMapping(binding.loc);
-
switch (binding.data) {
.b_missing => {},
.b_identifier => |b| {
+ p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(binding.loc);
p.printSymbol(b.ref);
},
.b_array => |b| {
@@ -2974,7 +3036,8 @@ pub fn NewPrinter(
p.printSpace();
}
- p.printSpace();
+ p.printSpaceBeforeIdentifier();
+ p.addSourceMapping(name.loc);
p.printSymbol(nameRef);
p.printFunc(s.func);
@@ -2982,7 +3045,7 @@ pub fn NewPrinter(
// p.printSemicolonAfterStatement();
// p.print("var ");
// p.printSymbol(nameRef);
- // p.print(" = ");
+ // p.@"print = "();
// p.printSymbol(nameRef);
// p.printSemicolonAfterStatement();
// } else {
@@ -3011,6 +3074,7 @@ pub fn NewPrinter(
}
p.print("class ");
+ p.addSourceMapping(s.class.class_name.?.loc);
p.printSymbol(nameRef);
p.printClass(s.class);
@@ -3050,7 +3114,7 @@ pub fn NewPrinter(
// this is still necessary for JSON
if (is_inside_bundle) {
p.printModuleExportSymbol();
- p.print(" = ");
+ p.@"print = "();
}
// Functions and classes must be wrapped to avoid confusion with their statement forms
@@ -3067,7 +3131,7 @@ pub fn NewPrinter(
if (is_inside_bundle) {
if (func.func.name == null) {
p.printModuleExportSymbol();
- p.print(" = ");
+ p.@"print = "();
}
}
@@ -3109,7 +3173,7 @@ pub fn NewPrinter(
if (is_inside_bundle) {
if (class.class.class_name == null) {
p.printModuleExportSymbol();
- p.print(" = ");
+ p.@"print = "();
}
}
@@ -3212,7 +3276,9 @@ pub fn NewPrinter(
p.print("(");
p.printModuleExportSymbol();
- p.print(", {");
+ p.print(",");
+ p.printSpace();
+ p.print("{");
p.printSpace();
const last = s.items.len - 1;
for (s.items) |item, i| {
@@ -3296,7 +3362,7 @@ pub fn NewPrinter(
if (import_record.is_bundled or (comptime is_inside_bundle) or namespace.was_originally_property_access) {
p.print("var ");
p.printSymbol(item.name.ref.?);
- p.print(" = ");
+ p.@"print = "();
p.printNamespaceAlias(import_record, namespace);
p.printSemicolonAfterStatement();
_ = array.swapRemove(i);
@@ -3436,7 +3502,9 @@ pub fn NewPrinter(
p.printImportRecordPath(&import_record);
p.print(")");
p.printSemicolonAfterStatement();
- p.print("export {");
+ p.print("export");
+ p.printSpace();
+ p.print("{");
// reset symbol counter back
symbol_counter = p.symbol_counter;
@@ -3768,14 +3836,15 @@ pub fn NewPrinter(
if (s.default_name) |name| {
p.print("var ");
p.printSymbol(name.ref.?);
- p.print(" = ");
+ p.@"print = "();
p.printImportRecordPath(record);
p.printSemicolonAfterStatement();
} else if (record.contains_import_star) {
// this case is particularly important for running files without an extension in bun's runtime
p.print("var ");
p.printSymbol(s.namespace_ref);
- p.print(" = {default:");
+ p.@"print = "();
+ p.print("{default:");
p.printImportRecordPath(record);
p.print("}");
p.printSemicolonAfterStatement();
@@ -3787,7 +3856,9 @@ pub fn NewPrinter(
p.printIndent();
p.print("var ");
p.printSymbol(s.namespace_ref);
- p.print(" = import.meta.require(");
+ p.@"print = "();
+
+ p.print("import.meta.require(");
p.printImportRecordPath(record);
p.print(")");
p.printSemicolonAfterStatement();
@@ -3834,20 +3905,22 @@ pub fn NewPrinter(
p.printModuleId(module_id);
p.print(" from \"");
p.print(record.path.text);
- p.print("\";\n");
+ p.print("\";");
+ p.printNewline();
try p.imported_module_ids.append(module_id);
}
if (record.contains_import_star) {
p.print("var ");
p.printSymbol(s.namespace_ref);
- p.print(" = ");
+ p.@"print = "();
if (!record.path.is_disabled) {
p.printSymbol(require_ref.?);
p.print("(");
p.printModuleId(module_id);
- p.print(");\n");
+ p.print(");");
+ p.printNewline();
} else {
p.printDisabledImport();
p.printSemicolonAfterStatement();
@@ -3857,19 +3930,26 @@ pub fn NewPrinter(
if (s.items.len > 0 or s.default_name != null) {
p.printIndent();
p.printSpaceBeforeIdentifier();
- p.print("var { ");
+ p.print("var");
+ p.printSpace();
+ p.print("{");
if (s.default_name) |default_name| {
- p.print("default: ");
+ p.printSpace();
+ p.print("default:");
+ p.printSpace();
p.printSymbol(default_name.ref.?);
if (s.items.len > 0) {
- p.print(", ");
+ p.printSpace();
+ p.print(",");
+ p.printSpace();
for (s.items) |item, i| {
p.printClauseItemAs(item, .@"var");
if (i < s.items.len - 1) {
- p.print(", ");
+ p.print(",");
+ p.printSpace();
}
}
}
@@ -3878,21 +3958,24 @@ pub fn NewPrinter(
p.printClauseItemAs(item, .@"var");
if (i < s.items.len - 1) {
- p.print(", ");
+ p.print(",");
+ p.printSpace();
}
}
}
- p.print("} = ");
+ p.print("}");
+ p.@"print = "();
if (record.contains_import_star) {
p.printSymbol(s.namespace_ref);
- p.print(";\n");
+ p.printSemicolonAfterStatement();
} else if (!record.path.is_disabled) {
p.printSymbol(require_ref.?);
p.print("(");
p.printModuleId(module_id);
- p.print(");\n");
+ p.print(")");
+ p.printSemicolonAfterStatement();
} else {
p.printDisabledImport();
p.printSemicolonAfterStatement();
@@ -3904,7 +3987,9 @@ pub fn NewPrinter(
if (!record.path.is_disabled) {
if (!p.has_printed_bundled_import_statement) {
p.has_printed_bundled_import_statement = true;
- p.print("import {");
+ p.print("import");
+ p.printSpace();
+ p.print("{");
const last = p.import_records.len - 1;
var needs_comma = false;
@@ -3924,12 +4009,15 @@ pub fn NewPrinter(
}
}
- if (needs_comma) p.print(", ");
+ if (needs_comma) {
+ p.print(",");
+ p.printSpace();
+ }
p.printLoadFromBundleWithoutCall(@truncate(u32, i));
needs_comma = true;
}
- p.print("} from ");
+ p.print("}from ");
p.printQuotedUTF8(record.path.text, false);
p.printSemicolonAfterStatement();
@@ -3938,8 +4026,12 @@ pub fn NewPrinter(
p.print("var ");
p.printLoadFromBundleWithoutCall(s.import_record_index);
-
- p.print(" = () => ({default: {}});\n");
+ if (p.options.minify_whitespace) {
+ p.print("=()=>({default: {}})");
+ p.printSemicolonIfNeeded();
+ } else {
+ p.print(" = () => ({default: {}});\n");
+ }
}
p.printBundledImport(record.*, s);
@@ -3951,9 +4043,9 @@ pub fn NewPrinter(
}
p.print("import");
- p.printSpace();
if (s.default_name) |name| {
+ p.print(" ");
p.printSymbol(name.ref.?);
item_count += 1;
}
@@ -3961,8 +4053,8 @@ pub fn NewPrinter(
if (s.items.len > 0) {
if (item_count > 0) {
p.print(",");
- p.printSpace();
}
+ p.printSpace();
p.print("{");
if (!s.is_single_line) {
@@ -4008,10 +4100,7 @@ pub fn NewPrinter(
}
if (item_count > 0) {
- p.printSpace();
- p.printSpaceBeforeIdentifier();
- p.print("from");
- p.printSpace();
+ p.print(" from ");
}
p.printImportRecordPath(record);
@@ -4144,7 +4233,7 @@ pub fn NewPrinter(
if (!is_disabled) {
p.print("var $");
p.printModuleId(module_id);
- p.print(" = ");
+ p.@"print = "();
p.printLoadFromBundle(s.import_record_index);
if (s.default_name) |default_name| {
@@ -4165,7 +4254,7 @@ pub fn NewPrinter(
if (s.default_name) |default_name| {
p.print("var ");
p.printSymbol(default_name.ref.?);
- p.print(" = ");
+ p.@"print = "();
p.printDisabledImport();
}
}
@@ -4175,13 +4264,14 @@ pub fn NewPrinter(
.import_star_and_import_default => {
p.print("var ");
p.printSymbol(s.namespace_ref);
- p.print(" = ");
+ p.@"print = "();
p.printLoadFromBundle(s.import_record_index);
if (s.default_name) |default_name| {
- p.print(", ");
+ p.print(",");
+ p.printSpace();
p.printSymbol(default_name.ref.?);
- p.print(" = ");
+ p.@"print = "();
if (!is_bun_platform) {
p.print("(");
@@ -4202,7 +4292,7 @@ pub fn NewPrinter(
.import_star => {
p.print("var ");
p.printSymbol(s.namespace_ref);
- p.print(" = ");
+ p.@"print = "();
p.printLoadFromBundle(s.import_record_index);
p.printSemicolonAfterStatement();
},
@@ -4210,7 +4300,7 @@ pub fn NewPrinter(
else => {
p.print("var $");
p.printModuleIdAssumeEnabled(module_id);
- p.print(" = ");
+ p.@"print = "();
p.printLoadFromBundle(s.import_record_index);
p.printSemicolonAfterStatement();
},
@@ -4444,7 +4534,7 @@ pub fn NewPrinter(
p.print(p.options.source_path.?.pretty);
p.print("\nexport var $");
std.fmt.formatInt(p.options.module_hash, 16, .lower, .{}, p) catch unreachable;
- p.print(" = ");
+ p.@"print = "();
p.printExpr(decls[0].value.?, .comma, ExprFlag.None());
p.printSemicolonAfterStatement();
return;
@@ -4466,24 +4556,35 @@ pub fn NewPrinter(
p.print("(");
p.printSpaceBeforeIdentifier();
p.printModuleExportSymbol();
- p.print(", ");
+ p.print(",");
+ p.printSpace();
+
switch (decl.binding.data) {
.b_identifier => |ident| {
- p.print("{ ");
+ p.print("{");
+ p.printSpace();
p.printSymbol(ident.ref);
- p.print(": () => (");
+ if (p.options.minify_whitespace)
+ p.print(":()=>(")
+ else
+ p.print(": () => (");
p.printSymbol(ident.ref);
p.print(") }");
},
.b_object => |obj| {
- p.print("{ ");
+ p.print("{");
+ p.printSpace();
for (obj.properties) |prop| {
switch (prop.value.data) {
.b_identifier => |ident| {
p.printSymbol(ident.ref);
- p.print(": () => (");
+ if (p.options.minify_whitespace)
+ p.print(":()=>(")
+ else
+ p.print(": () => (");
p.printSymbol(ident.ref);
- p.print("),\n");
+ p.print("),");
+ p.printNewline();
},
else => {},
}
@@ -4800,6 +4901,7 @@ pub const DirectWriter = struct {
// Buffered 4k 55ms
const FileWriterInternal = struct {
file: std.fs.File,
+ last_bytes: [2]u8 = [_]u8{ 0, 0 },
threadlocal var buffer: MutableString = undefined;
threadlocal var has_loaded_buffer: bool = false;
@@ -4820,12 +4922,19 @@ const FileWriterInternal = struct {
.file = file,
};
}
- pub fn writeByte(_: *FileWriterInternal, byte: u8) anyerror!usize {
+ pub fn writeByte(this: *FileWriterInternal, byte: u8) anyerror!usize {
try buffer.appendChar(byte);
+
+ this.last_bytes = .{ this.last_bytes[1], byte };
return 1;
}
- pub fn writeAll(_: *FileWriterInternal, bytes: anytype) anyerror!usize {
+ pub fn writeAll(this: *FileWriterInternal, bytes: anytype) anyerror!usize {
try buffer.append(bytes);
+ if (bytes.len >= 2) {
+ this.last_bytes = bytes[bytes.len - 2 ..][0..2].*;
+ } else if (bytes.len >= 1) {
+ this.last_bytes = .{ this.last_bytes[1], bytes[bytes.len - 1] };
+ }
return bytes.len;
}
@@ -4833,22 +4942,27 @@ const FileWriterInternal = struct {
return buffer.list.items;
}
- pub fn getLastByte(_: *const FileWriterInternal) u8 {
- return if (buffer.list.items.len > 0) buffer.list.items[buffer.list.items.len - 1] else 0;
+ pub fn getLastByte(this: *const FileWriterInternal) u8 {
+ return this.last_bytes[1];
}
- pub fn getLastLastByte(_: *const FileWriterInternal) u8 {
- return if (buffer.list.items.len > 1) buffer.list.items[buffer.list.items.len - 2] else 0;
+ pub fn getLastLastByte(this: *const FileWriterInternal) u8 {
+ return this.last_bytes[0];
}
pub fn reserveNext(_: *FileWriterInternal, count: u32) anyerror![*]u8 {
try buffer.growIfNeeded(count);
return @ptrCast([*]u8, &buffer.list.items.ptr[buffer.list.items.len]);
}
- pub fn advanceBy(_: *FileWriterInternal, count: u32) void {
+ pub fn advanceBy(this: *FileWriterInternal, count: u32) void {
if (comptime Environment.isDebug) std.debug.assert(buffer.list.items.len + count <= buffer.list.capacity);
buffer.list.items = buffer.list.items.ptr[0 .. buffer.list.items.len + count];
+ if (count >= 2) {
+ this.last_bytes = buffer.list.items[buffer.list.items.len - 2 ..][0..2].*;
+ } else if (count >= 1) {
+ this.last_bytes = .{ this.last_bytes[1], buffer.list.items[buffer.list.items.len - 1] };
+ }
}
pub fn done(
@@ -4906,6 +5020,7 @@ pub const BufferWriter = struct {
append_null_byte: bool = false,
append_newline: bool = false,
approximate_newline_count: usize = 0,
+ last_bytes: [2]u8 = [_]u8{ 0, 0 },
pub fn getWritten(this: *BufferWriter) []u8 {
return this.buffer.list.items;
@@ -4922,11 +5037,19 @@ pub const BufferWriter = struct {
pub fn writeByte(ctx: *BufferWriter, byte: u8) anyerror!usize {
try ctx.buffer.appendChar(byte);
ctx.approximate_newline_count += @boolToInt(byte == '\n');
+ ctx.last_bytes = .{ ctx.last_bytes[1], byte };
return 1;
}
pub fn writeAll(ctx: *BufferWriter, bytes: anytype) anyerror!usize {
try ctx.buffer.append(bytes);
ctx.approximate_newline_count += @boolToInt(bytes.len > 0 and bytes[bytes.len - 1] == '\n');
+
+ if (bytes.len >= 2) {
+ ctx.last_bytes = bytes[bytes.len - 2 ..][0..2].*;
+ } else if (bytes.len >= 1) {
+ ctx.last_bytes = .{ ctx.last_bytes[1], bytes[bytes.len - 1] };
+ }
+
return bytes.len;
}
@@ -4935,11 +5058,11 @@ pub const BufferWriter = struct {
}
pub fn getLastByte(ctx: *const BufferWriter) u8 {
- return if (ctx.buffer.list.items.len > 0) ctx.buffer.list.items[ctx.buffer.list.items.len - 1] else 0;
+ return ctx.last_bytes[1];
}
pub fn getLastLastByte(ctx: *const BufferWriter) u8 {
- return if (ctx.buffer.list.items.len > 1) ctx.buffer.list.items[ctx.buffer.list.items.len - 2] else 0;
+ return ctx.last_bytes[0];
}
pub fn reserveNext(ctx: *BufferWriter, count: u32) anyerror![*]u8 {
@@ -4950,6 +5073,12 @@ pub const BufferWriter = struct {
if (comptime Environment.isDebug) std.debug.assert(ctx.buffer.list.items.len + count <= ctx.buffer.list.capacity);
ctx.buffer.list.items = ctx.buffer.list.items.ptr[0 .. ctx.buffer.list.items.len + count];
+
+ if (count >= 2) {
+ ctx.last_bytes = ctx.buffer.list.items[ctx.buffer.list.items.len - 2 ..][0..2].*;
+ } else if (count >= 1) {
+ ctx.last_bytes = .{ ctx.last_bytes[1], ctx.buffer.list.items[ctx.buffer.list.items.len - 1] };
+ }
}
pub fn reset(ctx: *BufferWriter) void {
@@ -5061,6 +5190,7 @@ pub fn printAst(
}
if (tree.prepend_part) |part| {
for (part.stmts) |stmt| {
+ printer.printSemicolonIfNeeded();
try printer.printStmt(stmt);
if (printer.writer.getError()) {} else |err| {
return err;
@@ -5070,6 +5200,7 @@ pub fn printAst(
for (tree.parts) |part| {
for (part.stmts) |stmt| {
+ printer.printSemicolonIfNeeded();
try printer.printStmt(stmt);
if (printer.writer.getError()) {} else |err| {
return err;
@@ -5151,6 +5282,7 @@ pub fn printCommonJS(
if (tree.prepend_part) |part| {
for (part.stmts) |stmt| {
+ printer.printSemicolonIfNeeded();
try printer.printStmt(stmt);
if (printer.writer.getError()) {} else |err| {
return err;
@@ -5159,6 +5291,7 @@ pub fn printCommonJS(
}
for (tree.parts) |part| {
for (part.stmts) |stmt| {
+ printer.printSemicolonIfNeeded();
try printer.printStmt(stmt);
if (printer.writer.getError()) {} else |err| {
return err;
@@ -5219,6 +5352,7 @@ pub fn printCommonJSThreaded(
}
if (tree.prepend_part) |part| {
for (part.stmts) |stmt| {
+ printer.printSemicolonIfNeeded();
try printer.printStmt(stmt);
if (printer.writer.getError()) {} else |err| {
return err;
@@ -5228,6 +5362,7 @@ pub fn printCommonJSThreaded(
for (tree.parts) |part| {
for (part.stmts) |stmt| {
+ printer.printSemicolonIfNeeded();
try printer.printStmt(stmt);
if (printer.writer.getError()) {} else |err| {
return err;
diff --git a/src/options.zig b/src/options.zig
index d6e4ecb41..cc0f5d9e4 100644
--- a/src/options.zig
+++ b/src/options.zig
@@ -1242,6 +1242,7 @@ pub const BundleOptions = struct {
install: ?*Api.BunInstall = null,
inlining: bool = false,
+ minify_whitespace: bool = false,
pub inline fn cssImportBehavior(this: *const BundleOptions) Api.CssInJsBehavior {
switch (this.platform) {