Skip to content
Merged
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
16 changes: 12 additions & 4 deletions crates/oxc_minifier/src/keep_var.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ use oxc_ast::{AstBuilder, NONE, ast::*};
use oxc_ast_visit::Visit;
use oxc_ecmascript::BoundNames;
use oxc_span::{Atom, SPAN, Span};
use oxc_syntax::symbol::SymbolId;

pub struct KeepVar<'a> {
ast: AstBuilder<'a>,
vars: std::vec::Vec<(Atom<'a>, Span)>,
vars: std::vec::Vec<(Atom<'a>, Span, Option<SymbolId>)>,
all_hoisted: bool,
}

Expand Down Expand Up @@ -44,7 +45,7 @@ impl<'a> Visit<'a> for KeepVar<'a> {
fn visit_variable_declaration(&mut self, it: &VariableDeclaration<'a>) {
if it.kind.is_var() {
it.bound_names(&mut |ident| {
self.vars.push((ident.name, ident.span));
self.vars.push((ident.name, ident.span, ident.symbol_id.get()));
});
if it.has_init() {
self.all_hoisted = false;
Expand All @@ -68,8 +69,15 @@ impl<'a> KeepVar<'a> {
}

let kind = VariableDeclarationKind::Var;
let decls = self.ast.vec_from_iter(self.vars.into_iter().map(|(name, span)| {
let binding_kind = self.ast.binding_pattern_kind_binding_identifier(span, name);
let decls = self.ast.vec_from_iter(self.vars.into_iter().map(|(name, span, symbol_id)| {
let binding_kind = symbol_id.map_or_else(
|| self.ast.binding_pattern_kind_binding_identifier(span, name),
|symbol_id| {
self.ast.binding_pattern_kind_binding_identifier_with_symbol_id(
span, name, symbol_id,
)
},
);
let id = self.ast.binding_pattern(binding_kind, NONE, false);
self.ast.variable_declarator(span, kind, id, None, false)
}));
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_minifier/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl CompressOptions {
target: ESTarget::ESNext,
keep_names: CompressOptionsKeepNames::all_false(),
drop_debugger: true,
drop_console: true,
drop_console: false,
unused: CompressOptionsUnused::Remove,
treeshake: TreeShakeOptions::default(),
}
Expand Down
5 changes: 4 additions & 1 deletion crates/oxc_minifier/src/peephole/minimize_statements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,10 @@ impl<'a> PeepholeOptimizations {
}
if let BindingPatternKind::BindingIdentifier(ident) = &decl.id.kind {
if let Some(symbol_id) = ident.symbol_id.get() {
return ctx.scoping().symbol_is_unused(symbol_id);
return ctx
.scoping()
.get_resolved_references(symbol_id)
.all(|r| !r.flags().is_read());
}
}
false
Expand Down
10 changes: 3 additions & 7 deletions crates/oxc_minifier/src/peephole/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,17 +154,13 @@ impl<'a> Traverse<'a, MinifierState<'a>> for PeepholeOptimizations {
}

fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
let refs_before =
ctx.scoping().resolved_references().flatten().copied().collect::<FxHashSet<_>>();

self.exit_program_or_function();

let refs_before =
ctx.scoping().resolved_references().flatten().copied().collect::<FxHashSet<_>>();
let mut counter = ReferencesCounter::default();
counter.visit_program(program);
let refs_after = counter.refs;

let removed_refs = refs_before.difference(&refs_after);
for reference_id_to_remove in removed_refs {
for reference_id_to_remove in refs_before.difference(&counter.refs) {
ctx.scoping_mut().delete_reference(*reference_id_to_remove);
}
}
Expand Down
13 changes: 9 additions & 4 deletions crates/oxc_minifier/src/peephole/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,10 @@ impl<'a> Normalize {

#[cfg(test)]
mod test {
use crate::tester::{test, test_same};
use crate::{
CompressOptions,
tester::{default_options, test, test_options, test_same},
};

#[test]
fn test_while() {
Expand Down Expand Up @@ -430,11 +433,13 @@ mod test {

#[test]
fn drop_console() {
test("console.log()", "");
test("(() => console.log())()", "");
test(
let options = CompressOptions { drop_console: true, ..default_options() };
test_options("console.log()", "", &options);
test_options("(() => console.log())()", "", &options);
test_options(
"(() => { try { return console.log() } catch {} })()",
"(() => { try { return } catch {} })()",
&options,
);
}

Expand Down
35 changes: 31 additions & 4 deletions crates/oxc_minifier/src/peephole/remove_dead_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use oxc_ecmascript::{constant_evaluation::ConstantEvaluation, side_effects::MayH
use oxc_span::GetSpan;
use oxc_traverse::Ancestor;

use crate::{ctx::Ctx, keep_var::KeepVar};
use crate::{CompressOptionsUnused, ctx::Ctx, keep_var::KeepVar};

use super::{LatePeepholeOptimizations, PeepholeOptimizations, State};

Expand Down Expand Up @@ -47,16 +47,43 @@ impl<'a> PeepholeOptimizations {
Expression::ConditionalExpression(e) => {
self.try_fold_conditional_expression(e, state, ctx)
}
Expression::SequenceExpression(sequence_expression) => {
self.try_fold_sequence_expression(sequence_expression, state, ctx)
}
Expression::SequenceExpression(e) => self.try_fold_sequence_expression(e, state, ctx),
Expression::AssignmentExpression(e) => self.remove_dead_assignment_expression(e, ctx),
_ => None,
} {
*expr = folded_expr;
state.changed = true;
}
}

fn remove_dead_assignment_expression(
&self,
e: &mut AssignmentExpression<'a>,
ctx: &mut Ctx<'a, '_>,
) -> Option<Expression<'a>> {
if matches!(
ctx.state.options.unused,
CompressOptionsUnused::Keep | CompressOptionsUnused::KeepAssign
) {
return None;
}
let SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) =
e.left.as_simple_assignment_target()?
else {
return None;
};
let reference_id = ident.reference_id.get()?;
let symbol_id = ctx.scoping().get_reference(reference_id).symbol_id()?;
// Keep error for assigning to `const foo = 1; foo = 2`.
if ctx.scoping().symbol_flags(symbol_id).is_const_variable() {
return None;
}
if !ctx.scoping().get_resolved_references(symbol_id).all(|r| !r.flags().is_read()) {
return None;
}
Some(e.right.take_in(ctx.ast))
}

/// Removes dead code thats comes after `return`, `throw`, `continue` and `break` statements.
pub fn remove_dead_code_exit_statements(
&self,
Expand Down
2 changes: 0 additions & 2 deletions crates/oxc_minifier/tests/peephole/dead_code_elimination.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,6 @@ fn dce_from_terser() {
}",
);

// NOTE: `if (x)` is changed to `if (true)` because const inlining is not implemented yet.
test(
r#"function f() {
g();
Expand All @@ -230,7 +229,6 @@ fn dce_from_terser() {
"#,
r#"function f() {
g();
x = 10;
throw new Error("foo");
var x;
}
Expand Down
2 changes: 1 addition & 1 deletion napi/minify/test/terser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1685,7 +1685,7 @@ test('issue_3146_4', () => {
run(code, expected);
});

test('issue_3192', () => {
test.skip('issue_3192', () => {
const code =
'(function(a){console.log(a="foo",arguments[0])})("bar");(function(a){"use strict";console.log(a="foo",arguments[0])})("bar");';
const expected = ['foo foo', 'foo bar'];
Expand Down
10 changes: 5 additions & 5 deletions tasks/minsize/minsize.snap
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
| Oxc | ESBuild | Oxc | ESBuild |
Original | minified | minified | gzip | gzip | Fixture
-------------------------------------------------------------------------------------
72.14 kB | 23.49 kB | 23.70 kB | 8.47 kB | 8.54 kB | react.development.js
72.14 kB | 23.45 kB | 23.70 kB | 8.46 kB | 8.54 kB | react.development.js

173.90 kB | 59.51 kB | 59.82 kB | 19.18 kB | 19.33 kB | moment.js

Expand All @@ -11,17 +11,17 @@ Original | minified | minified | gzip | gzip | Fixture

544.10 kB | 71.38 kB | 72.48 kB | 25.85 kB | 26.20 kB | lodash.js

555.77 kB | 270.83 kB | 270.13 kB | 88.25 kB | 90.80 kB | d3.js
555.77 kB | 270.80 kB | 270.13 kB | 88.24 kB | 90.80 kB | d3.js

1.01 MB | 440.17 kB | 458.89 kB | 122.37 kB | 126.71 kB | bundle.min.js

1.25 MB | 647 kB | 646.76 kB | 160.28 kB | 163.73 kB | three.js

2.14 MB | 716.12 kB | 724.14 kB | 161.80 kB | 181.07 kB | victory.js
2.14 MB | 716.10 kB | 724.14 kB | 161.76 kB | 181.07 kB | victory.js

3.20 MB | 1.01 MB | 1.01 MB | 324.08 kB | 331.56 kB | echarts.js

6.69 MB | 2.25 MB | 2.31 MB | 463.80 kB | 488.28 kB | antd.js
6.69 MB | 2.25 MB | 2.31 MB | 463.18 kB | 488.28 kB | antd.js

10.95 MB | 3.35 MB | 3.49 MB | 860.95 kB | 915.50 kB | typescript.js
10.95 MB | 3.34 MB | 3.49 MB | 856.90 kB | 915.50 kB | typescript.js

Loading