Skip to content

Commit

Permalink
bundler: Add --ignore-dce-annotations, and other DCE annotation rel…
Browse files Browse the repository at this point in the history
…ated stuff (#12808)

Co-authored-by: paperdave <[email protected]>
Co-authored-by: Jarred Sumner <[email protected]>
  • Loading branch information
3 people authored Aug 1, 2024
1 parent 9911407 commit c2cf528
Show file tree
Hide file tree
Showing 14 changed files with 286 additions and 166 deletions.
3 changes: 1 addition & 2 deletions docs/bundler/vs-esbuild.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,7 @@ In Bun's CLI, simple boolean flags like `--minify` do not accept an argument. Ot
---
- `--ignore-annotations`
- n/a
- Not supported
- `--ignore-dce-annotations`
---
Expand Down
3 changes: 3 additions & 0 deletions src/api/schema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1687,6 +1687,9 @@ pub const Api = struct {
/// packages
packages: ?PackagesMode = null,

/// ignore_dce_annotations
ignore_dce_annotations: bool,

pub fn decode(reader: anytype) anyerror!TransformOptions {
var this = std.mem.zeroes(TransformOptions);

Expand Down
2 changes: 2 additions & 0 deletions src/bun_js.zig
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub const Run = struct {

b.options.minify_identifiers = ctx.bundler_options.minify_identifiers;
b.options.minify_whitespace = ctx.bundler_options.minify_whitespace;
b.options.ignore_dce_annotations = ctx.bundler_options.ignore_dce_annotations;
b.resolver.opts.minify_identifiers = ctx.bundler_options.minify_identifiers;
b.resolver.opts.minify_whitespace = ctx.bundler_options.minify_whitespace;

Expand Down Expand Up @@ -232,6 +233,7 @@ pub const Run = struct {

b.options.minify_identifiers = ctx.bundler_options.minify_identifiers;
b.options.minify_whitespace = ctx.bundler_options.minify_whitespace;
b.options.ignore_dce_annotations = ctx.bundler_options.ignore_dce_annotations;
b.resolver.opts.minify_identifiers = ctx.bundler_options.minify_identifiers;
b.resolver.opts.minify_whitespace = ctx.bundler_options.minify_whitespace;

Expand Down
5 changes: 5 additions & 0 deletions src/bundler.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,7 @@ pub const Bundler = struct {
.minify_identifiers = bundler.options.minify_identifiers,
.transform_only = bundler.options.transform_only,
.runtime_transpiler_cache = runtime_transpiler_cache,
.print_dce_annotations = bundler.options.emit_dce_annotations,
},
enable_source_map,
),
Expand All @@ -1167,6 +1168,7 @@ pub const Bundler = struct {
.transform_only = bundler.options.transform_only,
.import_meta_ref = ast.import_meta_ref,
.runtime_transpiler_cache = runtime_transpiler_cache,
.print_dce_annotations = bundler.options.emit_dce_annotations,
},
enable_source_map,
),
Expand Down Expand Up @@ -1199,6 +1201,7 @@ pub const Bundler = struct {
.inline_require_and_import_errors = false,
.import_meta_ref = ast.import_meta_ref,
.runtime_transpiler_cache = runtime_transpiler_cache,
.print_dce_annotations = bundler.options.emit_dce_annotations,
},
enable_source_map,
),
Expand Down Expand Up @@ -1405,6 +1408,8 @@ pub const Bundler = struct {
opts.features.runtime_transpiler_cache = this_parse.runtime_transpiler_cache;
opts.transform_only = bundler.options.transform_only;

opts.ignore_dce_annotations = bundler.options.ignore_dce_annotations;

// @bun annotation
opts.features.dont_bundle_twice = this_parse.dont_bundle_twice;

Expand Down
9 changes: 9 additions & 0 deletions src/bundler/bundle_v2.zig
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,9 @@ pub const BundleV2 = struct {
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;
generator.linker.options.emit_dce_annotations = bundler.options.emit_dce_annotations;
generator.linker.options.ignore_dce_annotations = bundler.options.ignore_dce_annotations;

generator.linker.options.source_maps = bundler.options.source_map;
generator.linker.options.tree_shaking = bundler.options.tree_shaking;
generator.linker.options.public_path = bundler.options.public_path;
Expand Down Expand Up @@ -1627,6 +1630,7 @@ pub const BundleV2 = struct {
.extension_order = &.{},
.env_files = &.{},
.conditions = config.conditions.map.keys(),
.ignore_dce_annotations = bundler.options.ignore_dce_annotations,
},
completion.env,
);
Expand Down Expand Up @@ -2874,6 +2878,7 @@ pub const ParseTask = struct {
opts.features.minify_syntax = bundler.options.minify_syntax;
opts.features.minify_identifiers = bundler.options.minify_identifiers;
opts.features.emit_decorator_metadata = bundler.options.emit_decorator_metadata;
opts.ignore_dce_annotations = bundler.options.ignore_dce_annotations and !source.index.isRuntime();

opts.tree_shaking = if (source.index.isRuntime()) true else bundler.options.tree_shaking;
opts.module_type = task.module_type;
Expand Down Expand Up @@ -3851,6 +3856,7 @@ pub const LinkerContext = struct {
pub const LinkerOptions = struct {
output_format: options.OutputFormat = .esm,
ignore_dce_annotations: bool = false,
emit_dce_annotations: bool = true,
tree_shaking: bool = true,
minify_whitespace: bool = false,
minify_syntax: bool = false,
Expand Down Expand Up @@ -6804,6 +6810,7 @@ pub const LinkerContext = struct {
.minify_whitespace = c.options.minify_whitespace,
.minify_identifiers = c.options.minify_identifiers,
.minify_syntax = c.options.minify_syntax,
.print_dce_annotations = c.options.emit_dce_annotations,
// .const_values = c.graph.const_values,
};

Expand Down Expand Up @@ -7714,6 +7721,7 @@ pub const LinkerContext = struct {
.require_or_import_meta_for_source_callback = js_printer.RequireOrImportMeta.Callback.init(LinkerContext, requireOrImportMetaForSource, c),

.minify_whitespace = c.options.minify_whitespace,
.print_dce_annotations = c.options.emit_dce_annotations,
.minify_syntax = c.options.minify_syntax,
// .const_values = c.graph.const_values,
};
Expand Down Expand Up @@ -9015,6 +9023,7 @@ pub const LinkerContext = struct {
.minify_whitespace = c.options.minify_whitespace,
.minify_syntax = c.options.minify_syntax,
.module_type = c.options.output_format,
.print_dce_annotations = c.options.emit_dce_annotations,
.has_run_symbol_renamer = true,

.allocator = allocator,
Expand Down
13 changes: 11 additions & 2 deletions src/cli.zig
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ pub const Arguments = struct {
clap.parseParam("--jsx-fragment <STR> Changes the function called when compiling JSX fragments") catch unreachable,
clap.parseParam("--jsx-import-source <STR> Declares the module specifier to be used for importing the jsx and jsxs factory functions. Default: \"react\"") catch unreachable,
clap.parseParam("--jsx-runtime <STR> \"automatic\" (default) or \"classic\"") catch unreachable,
clap.parseParam("--ignore-dce-annotations Ignore tree-shaking annotations such as @__PURE__") catch unreachable,
};
const runtime_params_ = [_]ParamType{
clap.parseParam("--watch Automatically restart the process on file change") catch unreachable,
Expand Down Expand Up @@ -238,7 +239,7 @@ pub const Arguments = struct {
clap.parseParam("--no-clear-screen Disable clearing the terminal screen on reload when --watch is enabled") catch unreachable,
clap.parseParam("--target <STR> The intended execution environment for the bundle. \"browser\", \"bun\" or \"node\"") catch unreachable,
clap.parseParam("--outdir <STR> Default to \"dist\" if multiple files") catch unreachable,
clap.parseParam("--outfile <STR> Write to a file") catch unreachable,
clap.parseParam("--outfile <STR> Write to a file") catch unreachable,
clap.parseParam("--sourcemap <STR>? Build with sourcemaps - 'inline', 'external', or 'none'") catch unreachable,
clap.parseParam("--format <STR> Specifies the module format to build to. Only \"esm\" is supported.") catch unreachable,
clap.parseParam("--root <STR> Root directory used for multiple entry points") catch unreachable,
Expand All @@ -251,10 +252,11 @@ pub const Arguments = struct {
clap.parseParam("--asset-naming <STR> Customize asset filenames. Defaults to \"[name]-[hash].[ext]\"") catch unreachable,
clap.parseParam("--server-components Enable React Server Components (experimental)") catch unreachable,
clap.parseParam("--no-bundle Transpile file only, do not bundle") catch unreachable,
clap.parseParam("--emit-dce-annotations Re-emit DCE annotations in bundles. Enabled by default unless --minify-whitespace is passed.") catch unreachable,
clap.parseParam("--minify Enable all minification flags") catch unreachable,
clap.parseParam("--minify-syntax Minify syntax and inline data") catch unreachable,
clap.parseParam("--minify-whitespace Minify whitespace") catch unreachable,
clap.parseParam("--minify-identifiers Minify identifiers") catch unreachable,
clap.parseParam("--minify-identifiers Minify identifiers") catch unreachable,
clap.parseParam("--dump-environment-variables") catch unreachable,
clap.parseParam("--conditions <STR>... Pass custom conditions to resolve") catch unreachable,
};
Expand Down Expand Up @@ -697,6 +699,8 @@ pub const Arguments = struct {
const output_dir: ?string = null;
const output_file: ?string = null;

ctx.bundler_options.ignore_dce_annotations = args.flag("--ignore-dce-annotations");

if (cmd == .BuildCommand) {
ctx.bundler_options.transform_only = args.flag("--no-bundle");

Expand All @@ -709,6 +713,9 @@ pub const Arguments = struct {
ctx.bundler_options.minify_whitespace = minify_flag or args.flag("--minify-whitespace");
ctx.bundler_options.minify_identifiers = minify_flag or args.flag("--minify-identifiers");

ctx.bundler_options.emit_dce_annotations = args.flag("--emit-dce-annotations") or
!ctx.bundler_options.minify_whitespace;

if (args.options("--external").len > 0) {
var externals = try allocator.alloc([]u8, args.options("--external").len);
for (args.options("--external"), 0..) |external, i| {
Expand Down Expand Up @@ -1275,6 +1282,8 @@ pub const Command = struct {
minify_syntax: bool = false,
minify_whitespace: bool = false,
minify_identifiers: bool = false,
ignore_dce_annotations: bool = false,
emit_dce_annotations: bool = true,
};

pub fn create(allocator: std.mem.Allocator, log: *logger.Log, comptime command: Command.Tag) anyerror!Context {
Expand Down
6 changes: 6 additions & 0 deletions src/cli/build_command.zig
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ pub const BuildCommand = struct {
this_bundler.options.minify_identifiers = ctx.bundler_options.minify_identifiers;
this_bundler.resolver.opts.minify_identifiers = ctx.bundler_options.minify_identifiers;

this_bundler.options.emit_dce_annotations = ctx.bundler_options.emit_dce_annotations;
this_bundler.resolver.opts.emit_dce_annotations = ctx.bundler_options.emit_dce_annotations;

this_bundler.options.ignore_dce_annotations = ctx.bundler_options.ignore_dce_annotations;
this_bundler.resolver.opts.ignore_dce_annotations = ctx.bundler_options.ignore_dce_annotations;

if (ctx.bundler_options.compile) {
if (ctx.bundler_options.code_splitting) {
Output.prettyErrorln("<r><red>error<r><d>:<r> cannot use --compile with --splitting", .{});
Expand Down
40 changes: 14 additions & 26 deletions src/js_parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1935,13 +1935,14 @@ pub const SideEffects = enum(u1) {
}
},

.e_call => |call| {

inline .e_call, .e_new => |call| {
// A call that has been marked "__PURE__" can be removed if all arguments
// can be removed. The annotation causes us to ignore the target.
if (call.can_be_unwrapped_if_unused) {
if (call.args.len > 0) {
return Expr.joinAllWithCommaCallback(call.args.slice(), @TypeOf(p), p, comptime simplifyUnusedExpr, p.allocator);
} else {
return Expr.empty;
}
}
},
Expand Down Expand Up @@ -2071,23 +2072,6 @@ pub const SideEffects = enum(u1) {
);
},

.e_new => |call| {
// A constructor call that has been marked "__PURE__" can be removed if all arguments
// can be removed. The annotation causes us to ignore the target.
if (call.can_be_unwrapped_if_unused) {
if (call.args.len > 0) {
return Expr.joinAllWithCommaCallback(
call.args.slice(),
@TypeOf(p),
p,
comptime simplifyUnusedExpr,
p.allocator,
);
}

return null;
}
},
else => {},
}

Expand Down Expand Up @@ -3122,6 +3106,10 @@ pub const Parser = struct {
hasher.update("NO_TS");
}

if (this.ignore_dce_annotations) {
hasher.update("no_dce");
}

this.features.hashForRuntimeTranspiler(hasher);
}

Expand Down Expand Up @@ -13201,12 +13189,6 @@ fn NewParser_(
.e_new => |ex| {
ex.can_be_unwrapped_if_unused = true;
},

// this is specifically added only to support our implementation
// of '__require' for --target=node, for /* @__PURE__ */ import.meta.url
.e_dot => |ex| {
ex.can_be_removed_if_unused = true;
},
else => {},
}
}
Expand Down Expand Up @@ -20860,7 +20842,13 @@ fn NewParser_(
E.Call{
.target = target,
.args = ExprNodeList.init(args_list),
.can_be_unwrapped_if_unused = all_values_are_pure,
// TODO: make these fully tree-shakable. this annotation
// as-is is incorrect. This would be done by changing all
// enum wrappers into `var Enum = ...` instead of two
// separate statements. This way, the @__PURE__ annotation
// is attached to the variable binding.
//
// .can_be_unwrapped_if_unused = all_values_are_pure,
},
stmt_loc,
);
Expand Down
16 changes: 10 additions & 6 deletions src/js_printer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ const ascii_only_always_on_unless_minifying = true;
fn formatUnsignedIntegerBetween(comptime len: u16, buf: *[len]u8, val: u64) void {
comptime var i: u16 = len;
var remainder = val;
// Write out the number from the end to the front

// Write out the number from the end to the front
inline while (i > 0) {
comptime i -= 1;
buf[comptime i] = @as(u8, @intCast((remainder % 10))) + '0';
Expand Down Expand Up @@ -536,6 +536,8 @@ pub const Options = struct {
minify_whitespace: bool = false,
minify_identifiers: bool = false,
minify_syntax: bool = false,
print_dce_annotations: bool = true,

transform_only: bool = false,
inline_require_and_import_errors: bool = true,
has_run_symbol_renamer: bool = false,
Expand All @@ -544,7 +546,7 @@ pub const Options = struct {

module_type: options.OutputFormat = .preserve,

/// Used for cross-module inlining of import items when bundling
// /// Used for cross-module inlining of import items when bundling
// const_values: Ast.ConstValuesMap = .{},
ts_enums: Ast.TsEnumsMap = .{},

Expand Down Expand Up @@ -2146,8 +2148,10 @@ fn NewPrinter(
return;
}

// noop for now
pub inline fn printPure(_: *Printer) void {}
pub inline fn printPure(p: *Printer) void {
if (Environment.allow_assert) assert(p.options.print_dce_annotations);
p.printWhitespacer(ws("/* @__PURE__ */ "));
}

pub fn printQuotedUTF8(p: *Printer, str: string, allow_backtick: bool) void {
const quote = if (comptime !is_json)
Expand Down Expand Up @@ -2353,7 +2357,7 @@ fn NewPrinter(
}
},
.e_new => |e| {
const has_pure_comment = e.can_be_unwrapped_if_unused;
const has_pure_comment = e.can_be_unwrapped_if_unused and p.options.print_dce_annotations;
const wrap = level.gte(.call) or (has_pure_comment and level.gte(.postfix));

if (wrap) {
Expand Down Expand Up @@ -2403,7 +2407,7 @@ fn NewPrinter(
wrap = true;
}

const has_pure_comment = e.can_be_unwrapped_if_unused;
const has_pure_comment = e.can_be_unwrapped_if_unused and p.options.print_dce_annotations;
if (has_pure_comment and level.gte(.postfix)) {
wrap = true;
}
Expand Down
3 changes: 3 additions & 0 deletions src/options.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1515,6 +1515,9 @@ pub const BundleOptions = struct {
minify_identifiers: bool = false,
dead_code_elimination: bool = true,

ignore_dce_annotations: bool = false,
emit_dce_annotations: bool = false,

code_coverage: bool = false,
debugger: bool = false,

Expand Down
Loading

0 comments on commit c2cf528

Please sign in to comment.