Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1247,7 +1247,7 @@ impl Expr {
// will potentially be simplified to avoid generating unnecessary extra "!"
// operators. For example, calling this with "!!x" will return "!x" instead
// of returning "!!!x".
pub(crate) fn not(&self, bump: &Bump) -> Expr {
pub fn not(&self, bump: &Bump) -> Expr {
self.maybe_simplify_not(bump).unwrap_or_else(|| {
Expr::init(
E::Unary {
Expand Down Expand Up @@ -2491,6 +2491,15 @@ impl Data {
break 'brk PrimitiveType::Mixed; // Can be number or bigint or string (or an exception)
}

crate::OpCode::BinAddAssign => {
if binary.right.data.known_primitive_with_check(stack_check)
== PrimitiveType::String
{
break 'brk PrimitiveType::String;
}
break 'brk PrimitiveType::Mixed; // Can be number or bigint or string (or an exception)
}

crate::OpCode::BinSub
| crate::OpCode::BinSubAssign
| crate::OpCode::BinMul
Expand Down Expand Up @@ -2629,7 +2638,7 @@ impl Data {
}
}

pub(crate) fn extract_numeric_value(&self) -> Option<f64> {
pub fn extract_numeric_value(&self) -> Option<f64> {
match self {
Data::ENumber(n) => Some(n.value()),
Data::EInlinedEnum(inlined) => match &inlined.value.data {
Expand Down
2 changes: 2 additions & 0 deletions src/bundler/ParseTask.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2517,6 +2517,8 @@ pub mod parse_worker {
options::Format::InternalBakeDev => js_parser::options::Format::InternalBakeDev,
};
opts.features.minify_syntax = topts.minify_syntax;
// A bundle is a build: it gets every syntax pass.
opts.features.minify_syntax_statements = topts.minify_syntax;
opts.features.minify_identifiers = topts.minify_identifiers;
opts.features.minify_keep_names = topts.keep_names;
opts.features.minify_whitespace = topts.minify_whitespace;
Expand Down
6 changes: 6 additions & 0 deletions src/bundler/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1308,6 +1308,10 @@ pub struct BundleOptions<'a> {
pub inline_entrypoint_import_meta_main: bool,
pub minify_whitespace: bool,
pub minify_syntax: bool,
/// Statement restructuring on top of `minify_syntax`. Set by `bun build`
/// only (the bundler's parse task derives it from `minify_syntax` itself).
/// See `bun_js_parser::RuntimeFeatures::minify_syntax_statements`.
pub minify_syntax_statements: bool,
pub minify_identifiers: bool,
pub keep_names: bool,
pub dead_code_elimination: bool,
Expand Down Expand Up @@ -1519,6 +1523,7 @@ impl<'a> BundleOptions<'a> {
inline_entrypoint_import_meta_main: self.inline_entrypoint_import_meta_main,
minify_whitespace: self.minify_whitespace,
minify_syntax: self.minify_syntax,
minify_syntax_statements: self.minify_syntax_statements,
minify_identifiers: self.minify_identifiers,
keep_names: self.keep_names,
dead_code_elimination: self.dead_code_elimination,
Expand Down Expand Up @@ -1768,6 +1773,7 @@ impl<'a> BundleOptions<'a> {
inline_entrypoint_import_meta_main: false,
minify_whitespace: false,
minify_syntax: false,
minify_syntax_statements: false,
minify_identifiers: false,
keep_names: false,
dead_code_elimination: true,
Expand Down
1 change: 1 addition & 0 deletions src/bundler/transpiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1644,6 +1644,7 @@ impl<'a> Transpiler<'a> {

opts.features.inject_jest_globals = this_parse.inject_jest_globals;
opts.features.minify_syntax = self.options.minify_syntax;
opts.features.minify_syntax_statements = self.options.minify_syntax_statements;
opts.features.minify_identifiers = self.options.minify_identifiers;
opts.features.dead_code_elimination = self.options.dead_code_elimination;
opts.features.remove_cjs_module_wrapper = this_parse.remove_cjs_module_wrapper;
Expand Down
67 changes: 30 additions & 37 deletions src/js_parser/p.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,12 @@ pub struct P<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> {
/// See [`bun_react_compiler::PendingCompile`]. Set by `visit_func` /
/// arrow-visit, consumed inside `visit_stmts` between visit and mangle.
pub(crate) react_compiler_pending: Option<bun_react_compiler::PendingCompile>,
/// True while visiting the body of a function the React Compiler may
/// compile. See `full_minify_syntax`.
pub(crate) in_react_compiler_candidate: bool,
/// Nesting of the `if (x) return; rest` to `if (!x) { rest }` rewrite in
/// `mangle_stmts`, bounded by `MAX_MANGLE_NESTING`.
pub(crate) mangle_jump_depth: u32,
/// Compiled args/flags written by the `visit_stmts` hook for `visit_func` /
/// arrow-visit to apply to the original `G::Fn` / `E::Arrow`.
pub(crate) react_compiler_result: Option<bun_react_compiler::CompileResult>,
Expand Down Expand Up @@ -508,6 +514,10 @@ pub struct P<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> {
pub(crate) stmt_expr_value: js_ast::ExprData,
pub(crate) call_target: js_ast::ExprData,
pub(crate) delete_target: js_ast::ExprData,
/// The tag of the tagged template being visited. A tag call binds `this`
/// like a method call, so a rewrite that turns a tag into a property access
/// must keep `this` undefined, as `call_target` does for a call.
pub(crate) template_tag: js_ast::ExprData,
pub(crate) loop_body: js_ast::StmtData,
pub(crate) module_scope: js_ast::StoreRef<js_ast::Scope>,
pub(crate) module_scope_directive_loc: bun_ast::Loc,
Expand Down Expand Up @@ -5727,6 +5737,23 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
true
}

/// `minify_syntax_statements` (see `RuntimeFeatures`): every pass that
/// merges or restructures statements (comma joins, `if` to `&&`/`?:`,
/// return and throw chains, `while` to `for`, arrow bodies to expressions)
/// and every expression rewrite that changes how a function prints checks
/// this instead of `minify_syntax`.
///
/// It is off inside a function body the React Compiler may compile: the
/// compiler sees the body after its statements were visited, and it does
/// not lower some shapes the statement mangler produces (a `for (;;)` loop
/// made from a `while`, `ref.current == null && (ref.current = x)` made
/// from an `if`).
pub(crate) fn full_minify_syntax(&self) -> bool {
self.options.features.minify_syntax
&& self.options.features.minify_syntax_statements
&& !self.in_react_compiler_candidate
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// TODO:
// When React Fast Refresh is enabled, anything that's a JSX component should not be removable
// This is to improve the reliability of fast refresh between page loads.
Expand Down Expand Up @@ -6346,43 +6373,6 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
}
}

pub(crate) fn append_if_body_preserving_scope(
&mut self,
stmts: &mut ListManaged<'a, Stmt>,
body: Stmt,
) -> Result<(), crate::Error> {
if let js_ast::StmtData::SBlock(block) = &body.data {
// `S::Block.stmts` is `StoreSlice<Stmt>` arena-owned for parser 'a; no aliasing &mut.
let block_stmts: &[Stmt] = block.stmts.slice();
let mut keep_block = false;
for stmt in block_stmts {
if statement_cares_about_scope(stmt) {
keep_block = true;
break;
}
}
if !keep_block && !block_stmts.is_empty() {
stmts.extend_from_slice(block_stmts);
return Ok(());
}
}

if statement_cares_about_scope(&body) {
let block_stmts = self.arena.alloc_slice_copy(&[body]);
stmts.push(self.s(
S::Block {
stmts: block_stmts.into(),
close_brace_loc: bun_ast::Loc::EMPTY,
},
body.loc,
));
return Ok(());
}

stmts.push(body);
Ok(())
}

#[cold]
#[inline(never)]
fn mark_exported_binding_inside_namespace(&mut self, r#ref: Ref, binding: BindingNodeIndex) {
Expand Down Expand Up @@ -9174,6 +9164,7 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O

call_target: null_expr_data(),
delete_target: null_expr_data(),
template_tag: null_expr_data(),
stmt_expr_value: null_expr_data(),
loop_body: null_stmt_data(),
define,
Expand Down Expand Up @@ -9265,6 +9256,8 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O
react_compiler_candidate_name: None,
react_compiler_in_react_hoc: false,
react_compiler_pending: None,
in_react_compiler_candidate: false,
mangle_jump_depth: 0,
react_compiler_result: None,
server_components_wrap_ref: Ref::NONE,
jest: Jest::default(),
Expand Down
7 changes: 1 addition & 6 deletions src/js_parser/parse/parse_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ impl<'a> Options<'a> {
no_macros: f.no_macros,
commonjs_named_exports: f.commonjs_named_exports,
minify_syntax: f.minify_syntax,
minify_syntax_statements: f.minify_syntax_statements,
minify_identifiers: f.minify_identifiers,
minify_keep_names: f.minify_keep_names,
minify_whitespace: f.minify_whitespace,
Expand Down Expand Up @@ -268,12 +269,6 @@ impl<'a> Options<'a> {
self.features.hash_for_runtime_transpiler(hasher);
}

// Used to determine if `joinWithComma` should be called in `visitStmts`. We do this
// to avoid changing line numbers too much to make source mapping more readable
pub(crate) fn runtime_merge_adjacent_expression_statements(&self) -> bool {
self.bundle
}

pub fn init(jsx: options::JSX::Pragma, loader: options::Loader) -> Options<'static> {
// `macro_context` is `None`
// (see field comment); caller overwrites before use.
Expand Down
12 changes: 11 additions & 1 deletion src/js_parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,14 @@ pub mod Runtime {
pub commonjs_named_exports: bool,

pub minify_syntax: bool,
/// The rest of esbuild's `--minify-syntax`: statement restructuring
/// (`if` chains to `?:`/`&&`/`||`, `while` to `for`, merged returns)
/// and the expression rewrites that change how a function prints
/// (`===` to `==`, `a === null || a === void 0` to `a == null`).
/// Only `bun build` sets it. `minify_syntax` alone keeps one output
/// statement per source statement, which `bun run` relies on for
/// source maps, coverage and `Function.prototype.toString()`.
pub minify_syntax_statements: bool,
pub minify_identifiers: bool,
/// Preserve function/class names during minification (CLI: --keep-names)
pub minify_keep_names: bool,
Expand Down Expand Up @@ -289,6 +297,7 @@ pub mod Runtime {
no_macros: false,
commonjs_named_exports: true,
minify_syntax: false,
minify_syntax_statements: false,
minify_identifiers: false,
minify_keep_names: false,
minify_whitespace: false,
Expand Down Expand Up @@ -359,13 +368,14 @@ pub mod Runtime {
pub(crate) fn hash_for_runtime_transpiler(&self, hasher: &mut Wyhash) {
debug_assert!(self.runtime_transpiler_cache.is_some());

let bools: [bool; 17] = [
let bools: [bool; 18] = [
self.top_level_await,
self.auto_import_jsx,
self.allow_runtime,
self.inlining,
self.commonjs_named_exports,
self.minify_syntax,
self.minify_syntax_statements,
self.minify_identifiers,
self.minify_keep_names,
self.dead_code_elimination,
Expand Down
Loading
Loading