Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add preserve_imports_with_side_effects option for simplify optimization #6962

Merged
merged 14 commits into from
Mar 5, 2023
9 changes: 8 additions & 1 deletion crates/swc/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,10 @@ impl Options {
.as_ref()
.map(|v| v.simplify.into_bool())
.unwrap_or_default();
let drop_unused_imports = optimizer
.as_ref()
.map(|v| v.drop_unused_imports.into_bool())
.unwrap_or_default();

let optimization = {
if let Some(opts) = optimizer.and_then(|o| o.globals) {
Expand All @@ -472,7 +476,7 @@ impl Options {
optimization,
Optional::new(export_default_from(), syntax.export_default_from()),
Optional::new(
simplifier(top_level_mark, Default::default()),
simplifier(top_level_mark, drop_unused_imports, Default::default()),
enable_simplifier
),
json_parse_pass
Expand Down Expand Up @@ -1481,6 +1485,9 @@ pub struct OptimizerConfig {

#[serde(default)]
pub jsonify: Option<JsonifyOption>,

#[serde(default)]
pub drop_unused_imports: BoolConfig<false>,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a way to modify the simplify option directly, but I added it in this way to avoid breaking change. What do you think?

Copy link
Member

@kdy1 kdy1 Feb 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this to the simplify option?

}

#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
Expand Down
1 change: 1 addition & 0 deletions crates/swc_bundler/src/bundler/optimize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ where
top_retain: Default::default(),
},
self.unresolved_mark,
false,
kdy1 marked this conversation as resolved.
Show resolved Hide resolved
)));
}
node
Expand Down
1 change: 1 addition & 0 deletions crates/swc_ecma_minifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ fn perform_dce(m: &mut Program, options: &CompressOptions, extra: &ExtraOptions)
top_retain: options.top_retain.clone(),
},
extra.unresolved_mark,
false,
);

loop {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use crate::debug_assert_valid;
pub fn dce(
config: Config,
unresolved_mark: Mark,
drop_unused_imports: bool,
nissy-dev marked this conversation as resolved.
Show resolved Hide resolved
) -> impl Fold + VisitMut + Repeated + CompilerPass {
as_folder(TreeShaker {
expr_ctx: ExprCtx {
Expand All @@ -44,6 +45,7 @@ pub fn dce(
var_decl_kind: None,
data: Default::default(),
bindings: Default::default(),
drop_unused_imports,
})
}

Expand Down Expand Up @@ -89,6 +91,8 @@ struct TreeShaker {
data: Arc<Data>,

bindings: Arc<AHashSet<Id>>,

drop_unused_imports: bool,
}

impl CompilerPass for TreeShaker {
Expand Down Expand Up @@ -820,6 +824,27 @@ impl VisitMut for TreeShaker {
}
}

fn visit_mut_import_specifiers(&mut self, ss: &mut Vec<ImportSpecifier>) {
ss.retain(|s| {
let local = match s {
ImportSpecifier::Named(l) => &l.local,
ImportSpecifier::Default(l) => &l.local,
ImportSpecifier::Namespace(l) => &l.local,
};

if self.drop_unused_imports && self.can_drop_binding(local.to_id(), false) {
debug!(
"Dropping import specifier `{}` because it's not used",
local
);
self.changed = true;
return false;
}

true
});
}

fn visit_mut_module(&mut self, m: &mut Module) {
debug_assert_valid(m);

Expand Down Expand Up @@ -883,7 +908,22 @@ impl VisitMut for TreeShaker {
}

fn visit_mut_module_item(&mut self, n: &mut ModuleItem) {
n.visit_mut_children_with(self);
match n {
ModuleItem::ModuleDecl(ModuleDecl::Import(i)) => {
let is_for_side_effect = i.specifiers.is_empty();

i.visit_mut_with(self);

if self.drop_unused_imports && !is_for_side_effect && i.specifiers.is_empty() {
debug!("Dropping an import because it's not used");
self.changed = true;
*n = ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP }));
}
}
_ => {
n.visit_mut_children_with(self);
}
}
debug_assert_valid(n);
}

Expand Down
8 changes: 6 additions & 2 deletions crates/swc_ecma_transforms_optimization/src/simplify/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ pub struct Config {

/// Performs simplify-expr, inlining, remove-dead-branch and dce until nothing
/// changes.
pub fn simplifier(unresolved_mark: Mark, c: Config) -> impl RepeatedJsPass {
pub fn simplifier(
unresolved_mark: Mark,
drop_unused_imports: bool,
c: Config,
) -> impl RepeatedJsPass {
Repeat::new(chain!(
expr_simplifier(unresolved_mark, c.expr),
dead_branch_remover(unresolved_mark),
dce::dce(c.dce, unresolved_mark)
dce::dce(c.dce, unresolved_mark, drop_unused_imports)
))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use swc_common::{chain, pass::Repeat, Mark};
use swc_ecma_parser::{EsConfig, Syntax};
use swc_ecma_transforms_base::resolver;
use swc_ecma_transforms_optimization::simplify::dce::{dce, Config};
use swc_ecma_transforms_testing::test;
use swc_ecma_visit::Fold;

fn tr() -> impl Fold {
Repeat::new(dce(
Config {
top_level: true,
..Default::default()
},
Mark::new(),
true,
))
}

macro_rules! to {
($name:ident, $src:expr, $expected:expr) => {
test!(
Syntax::Es(EsConfig {
decorators: true,
..Default::default()
}),
|_| chain!(resolver(Mark::new(), Mark::new(), false), tr()),
$name,
$src,
$expected
);
};
}

macro_rules! optimized_out {
($name:ident, $src:expr) => {
to!($name, $src, "");
};
}

macro_rules! noop {
($name:ident, $src:expr) => {
to!($name, $src, $src);
};
}

to!(
single_pass,
"
const a = 1;

if (a) {
const b = 2;
}
",
"
const a = 1;
if (a) {}
"
);

optimized_out!(import_default_unused, "import foo from 'foo'");

optimized_out!(import_specific_unused, "import {foo} from 'foo'");

optimized_out!(import_mixed_unused, "import foo, { bar } from 'foo'");

noop!(
import_export_named,
"import foo from 'src'; export { foo };"
);

to!(
import_unused_export_named,
"import foo, { bar } from 'src'; export { foo }; ",
"import foo from 'src'; export { foo }; "
);
6 changes: 3 additions & 3 deletions crates/swc_ecma_transforms_optimization/tests/fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fn dce_single_pass(input: PathBuf) {
&|t| {
let unresolved_mark = Mark::new();

chain!(remover(t), dce(Default::default(), unresolved_mark))
chain!(remover(t), dce(Default::default(), unresolved_mark, false))
},
&input,
&output,
Expand All @@ -45,7 +45,7 @@ fn dce_repeated(input: PathBuf) {
&|t| {
chain!(
remover(t),
Repeat::new(dce(Default::default(), Mark::new()))
Repeat::new(dce(Default::default(), Mark::new(), false))
)
},
&input,
Expand All @@ -64,7 +64,7 @@ fn dce_jsx(input: PathBuf) {
jsx: true,
..Default::default()
}),
&|t| chain!(remover(t), dce(Default::default(), Mark::new())),
&|t| chain!(remover(t), dce(Default::default(), Mark::new(), false)),
&input,
&output,
Default::default(),
Expand Down
8 changes: 4 additions & 4 deletions crates/swc_ecma_transforms_optimization/tests/simplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ fn test(src: &str, expected: &str) {
expr_simplifier(unresolved_mark, Default::default()),
inlining::inlining(Default::default()),
dead_branch_remover(unresolved_mark),
dce::dce(Default::default(), unresolved_mark)
dce::dce(Default::default(), unresolved_mark, false)
)),
)
},
Expand Down Expand Up @@ -57,7 +57,7 @@ macro_rules! to {
expr_simplifier(unresolved_mark, Default::default()),
inlining::inlining(Default::default()),
dead_branch_remover(unresolved_mark),
dce::dce(Default::default(), unresolved_mark)
dce::dce(Default::default(), unresolved_mark, false)
)),
)
},
Expand Down Expand Up @@ -514,7 +514,7 @@ test!(
..Default::default()
}
),
dce(Default::default(), unresolved_mark),
dce(Default::default(), unresolved_mark, false),
inlining(Default::default())
)
},
Expand Down Expand Up @@ -590,7 +590,7 @@ test!(
expr_simplifier(unresolved_mark, Default::default()),
inlining::inlining(Default::default()),
dead_branch_remover(unresolved_mark),
dce::dce(Default::default(), unresolved_mark)
dce::dce(Default::default(), unresolved_mark, false)
)),
es2018(Default::default()),
es2017(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ fn tr() -> impl Fold {
..Default::default()
},
Mark::new(),
false,
))
}

Expand Down