Skip to content

Commit 783a544

Browse files
committed
fix(minifier): avoid removing function declaration from KeepVar
1 parent eaddc8f commit 783a544

File tree

5 files changed

+79
-35
lines changed

5 files changed

+79
-35
lines changed

crates/oxc_ast_macros/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@ doctest = false
2222

2323
[dependencies]
2424
quote = { workspace = true }
25-
syn = { workspace = true, features = ["full", "parsing", "proc-macro", "printing"] }
25+
syn = { workspace = true, features = ["full", "parsing", "printing", "proc-macro"] }
2626
proc-macro2 = { workspace = true }

crates/oxc_minifier/src/ast_passes/remove_dead_code.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,29 @@ impl<'a> RemoveDeadCode<'a> {
5555
}
5656

5757
let Some(index) = index else { return };
58+
if index == stmts.len() - 1 {
59+
return;
60+
}
5861

5962
let mut keep_var = KeepVar::new(self.ast);
6063

6164
for stmt in stmts.iter().skip(index + 1) {
6265
keep_var.visit_statement(stmt);
6366
}
6467

65-
stmts.drain(index + 1..);
68+
let mut i = 0;
69+
stmts.retain(|s| {
70+
i += 1;
71+
if i - 1 <= index {
72+
return true;
73+
}
74+
// keep function declaration
75+
if matches!(s.as_declaration(), Some(Declaration::FunctionDeclaration(_))) {
76+
return true;
77+
}
78+
false
79+
});
80+
6681
if let Some(stmt) = keep_var.get_variable_declaration_statement() {
6782
stmts.push(stmt);
6883
}

crates/oxc_minifier/src/keep_var.rs

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,48 @@
11
#[allow(clippy::wildcard_imports)]
22
use oxc_ast::{ast::*, syntax_directed_operations::BoundNames, AstBuilder, Visit};
33
use oxc_span::{Atom, Span, SPAN};
4-
use oxc_syntax::scope::ScopeFlags;
54

65
pub struct KeepVar<'a> {
76
ast: AstBuilder<'a>,
87
vars: std::vec::Vec<(Atom<'a>, Span)>,
98
}
109

1110
impl<'a> Visit<'a> for KeepVar<'a> {
12-
fn visit_variable_declaration(&mut self, decl: &VariableDeclaration<'a>) {
13-
if decl.kind.is_var() {
14-
decl.bound_names(&mut |ident| {
15-
self.vars.push((ident.name.clone(), ident.span));
16-
});
11+
fn visit_statement(&mut self, it: &Statement<'a>) {
12+
// Only visit blocks where vars could be hoisted
13+
match it {
14+
Statement::BlockStatement(it) => self.visit_block_statement(it),
15+
Statement::BreakStatement(it) => self.visit_break_statement(it),
16+
Statement::ContinueStatement(it) => self.visit_continue_statement(it),
17+
// Statement::DebuggerStatement(it) => self.visit_debugger_statement(it),
18+
Statement::DoWhileStatement(it) => self.visit_do_while_statement(it),
19+
// Statement::EmptyStatement(it) => self.visit_empty_statement(it),
20+
// Statement::ExpressionStatement(it) => self.visit_expression_statement(it),
21+
Statement::ForInStatement(it) => self.visit_for_in_statement(it),
22+
Statement::ForOfStatement(it) => self.visit_for_of_statement(it),
23+
Statement::ForStatement(it) => self.visit_for_statement(it),
24+
Statement::IfStatement(it) => self.visit_if_statement(it),
25+
Statement::LabeledStatement(it) => self.visit_labeled_statement(it),
26+
// Statement::ReturnStatement(it) => self.visit_return_statement(it),
27+
Statement::SwitchStatement(it) => self.visit_switch_statement(it),
28+
// Statement::ThrowStatement(it) => self.visit_throw_statement(it),
29+
Statement::TryStatement(it) => self.visit_try_statement(it),
30+
Statement::WhileStatement(it) => self.visit_while_statement(it),
31+
Statement::WithStatement(it) => self.visit_with_statement(it),
32+
// match_declaration!(Statement) => visitor.visit_declaration(it.to_declaration()),
33+
// match_module_declaration!(Statement) => {
34+
// visitor.visit_module_declaration(it.to_module_declaration())
35+
// }
36+
Statement::VariableDeclaration(decl) => {
37+
if decl.kind.is_var() {
38+
decl.bound_names(&mut |ident| {
39+
self.vars.push((ident.name.clone(), ident.span));
40+
});
41+
}
42+
}
43+
_ => {}
1744
}
1845
}
19-
20-
fn visit_function(&mut self, _it: &Function<'a>, _flags: ScopeFlags) {
21-
/* skip functions */
22-
}
23-
24-
fn visit_arrow_function_expression(&mut self, _it: &ArrowFunctionExpression<'a>) {}
25-
26-
fn visit_class(&mut self, _it: &Class<'a>) {
27-
/* skip classes */
28-
}
2946
}
3047

3148
impl<'a> KeepVar<'a> {

crates/oxc_minifier/tests/oxc/remove_dead_code.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,6 @@ fn test(source_text: &str, expected: &str) {
2424
assert_eq!(minified, expected, "for source {source_text}");
2525
}
2626

27-
fn test_same(source_text: &str) {
28-
let minified = print(source_text, true);
29-
let expected = print(source_text, false);
30-
assert_eq!(minified, expected, "for source {source_text}");
31-
}
32-
3327
#[test]
3428
fn dce_if_statement() {
3529
test("if (true) { foo }", "{ foo }");
@@ -127,18 +121,36 @@ fn dce_logical_expression() {
127121

128122
#[test]
129123
fn dce_var_hoisting() {
130-
test_same(
124+
test(
125+
"function f() {
126+
return () => {
127+
var x;
128+
}
129+
REMOVE;
130+
function KEEP() {}
131+
REMOVE;
132+
}",
131133
"function f() {
132134
return () => {
133135
var x;
134136
}
137+
function KEEP() {}
135138
}",
136139
);
137-
test_same(
140+
test(
141+
"function f() {
142+
return function g() {
143+
var x;
144+
}
145+
REMOVE;
146+
function KEEP() {}
147+
REMOVE;
148+
}",
138149
"function f() {
139150
return function g() {
140151
var x;
141152
}
153+
function KEEP() {}
142154
}",
143155
);
144156
}

tasks/minsize/minsize.snap

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,25 @@ Original | Minified | esbuild | Gzip | esbuild
22

33
72.14 kB | 24.32 kB | 23.70 kB | 8.72 kB | 8.54 kB | react.development.js
44

5-
173.90 kB | 61.80 kB | 59.82 kB | 19.57 kB | 19.33 kB | moment.js
5+
173.90 kB | 61.80 kB | 59.82 kB | 19.58 kB | 19.33 kB | moment.js
66

77
287.63 kB | 92.91 kB | 90.07 kB | 32.33 kB | 31.95 kB | jquery.js
88

9-
342.15 kB | 122.97 kB | 118.14 kB | 45.08 kB | 44.37 kB | vue.js
9+
342.15 kB | 122.97 kB | 118.14 kB | 45.05 kB | 44.37 kB | vue.js
1010

11-
544.10 kB | 74.71 kB | 72.48 kB | 26.24 kB | 26.20 kB | lodash.js
11+
544.10 kB | 74.71 kB | 72.48 kB | 26.22 kB | 26.20 kB | lodash.js
1212

13-
555.77 kB | 274.92 kB | 270.13 kB | 91.50 kB | 90.80 kB | d3.js
13+
555.77 kB | 274.82 kB | 270.13 kB | 91.39 kB | 90.80 kB | d3.js
1414

15-
1.01 MB | 471.72 kB | 458.89 kB | 127.60 kB | 126.71 kB | bundle.min.js
15+
1.01 MB | 471.74 kB | 458.89 kB | 127.57 kB | 126.71 kB | bundle.min.js
1616

17-
1.25 MB | 673.73 kB | 646.76 kB | 166.77 kB | 163.73 kB | three.js
17+
1.25 MB | 673.75 kB | 646.76 kB | 166.76 kB | 163.73 kB | three.js
1818

19-
2.14 MB | 743.50 kB | 724.14 kB | 182.06 kB | 181.07 kB | victory.js
19+
2.14 MB | 743.40 kB | 724.14 kB | 181.95 kB | 181.07 kB | victory.js
2020

21-
3.20 MB | 1.03 MB | 1.01 MB | 332.77 kB | 331.56 kB | echarts.js
21+
3.20 MB | 1.03 MB | 1.01 MB | 332.46 kB | 331.56 kB | echarts.js
2222

2323
6.69 MB | 2.42 MB | 2.31 MB | 503.31 kB | 488.28 kB | antd.js
2424

25-
10.95 MB | 3.57 MB | 3.49 MB | 912.65 kB | 915.50 kB | typescript.js
25+
10.95 MB | 3.57 MB | 3.49 MB | 912.42 kB | 915.50 kB | typescript.js
2626

0 commit comments

Comments
 (0)