mirror of
https://github.com/swc-project/swc.git
synced 2024-11-27 04:47:03 +03:00
feat(es/minifier): Implement static evaluator (#2176)
swc_ecma_minifier: - Add an api to evaluate constants statically.
This commit is contained in:
parent
ee16139a19
commit
11fe35dbd1
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -2628,7 +2628,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_ecma_minifier"
|
||||
version = "0.23.0"
|
||||
version = "0.23.1"
|
||||
dependencies = [
|
||||
"ansi_term 0.12.1",
|
||||
"anyhow",
|
||||
@ -3400,7 +3400,7 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "wasm"
|
||||
version = "1.2.83"
|
||||
version = "1.2.84"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"console_error_panic_hook",
|
||||
|
@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs", "src/lists/*.json"]
|
||||
license = "Apache-2.0/MIT"
|
||||
name = "swc_ecma_minifier"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.23.0"
|
||||
version = "0.23.1"
|
||||
|
||||
[features]
|
||||
debug = []
|
||||
|
@ -1,3 +1,4 @@
|
||||
pub(crate) use self::pure::pure_optimizer;
|
||||
use self::{
|
||||
drop_console::drop_console,
|
||||
hoist_decls::DeclHoisterConfig,
|
||||
@ -5,9 +6,10 @@ use self::{
|
||||
};
|
||||
use crate::{
|
||||
analyzer::{analyze, ProgramData, UsageAnalyzer},
|
||||
compress::{hoist_decls::decl_hoister, pure::pure_optimizer},
|
||||
compress::hoist_decls::decl_hoister,
|
||||
debug::{dump, invoke},
|
||||
marks::Marks,
|
||||
mode::Mode,
|
||||
option::CompressOptions,
|
||||
util::{now, unit::CompileUnit, Optional},
|
||||
MAX_PAR_DEPTH,
|
||||
@ -41,11 +43,15 @@ mod optimize;
|
||||
mod pure;
|
||||
mod util;
|
||||
|
||||
pub(crate) fn compressor<'a>(
|
||||
pub(crate) fn compressor<'a, M>(
|
||||
globals: &'a Globals,
|
||||
marks: Marks,
|
||||
options: &'a CompressOptions,
|
||||
) -> impl 'a + JsPass {
|
||||
mode: &'a M,
|
||||
) -> impl 'a + JsPass
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
let console_remover = Optional {
|
||||
enabled: options.drop_console,
|
||||
visitor: drop_console(),
|
||||
@ -59,12 +65,16 @@ pub(crate) fn compressor<'a>(
|
||||
data: None,
|
||||
optimizer_state: Default::default(),
|
||||
left_parallel_depth: 0,
|
||||
mode,
|
||||
};
|
||||
|
||||
chain!(console_remover, as_folder(compressor), expr_simplifier())
|
||||
}
|
||||
|
||||
struct Compressor<'a> {
|
||||
struct Compressor<'a, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
globals: &'a Globals,
|
||||
marks: Marks,
|
||||
options: &'a CompressOptions,
|
||||
@ -74,15 +84,23 @@ struct Compressor<'a> {
|
||||
optimizer_state: OptimizerState,
|
||||
/// `0` means we should not create more threads.
|
||||
left_parallel_depth: u8,
|
||||
|
||||
mode: &'a M,
|
||||
}
|
||||
|
||||
impl CompilerPass for Compressor<'_> {
|
||||
impl<M> CompilerPass for Compressor<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
fn name() -> Cow<'static, str> {
|
||||
"compressor".into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Compressor<'_> {
|
||||
impl<M> Compressor<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
fn handle_stmt_likes<T>(&mut self, stmts: &mut Vec<T>)
|
||||
where
|
||||
T: StmtLike,
|
||||
@ -114,7 +132,7 @@ impl Compressor<'_> {
|
||||
/// Optimize a bundle in a parallel.
|
||||
fn visit_par<N>(&mut self, nodes: &mut Vec<N>)
|
||||
where
|
||||
N: Send + Sync + for<'aa> VisitMutWith<Compressor<'aa>>,
|
||||
N: Send + Sync + for<'aa> VisitMutWith<Compressor<'aa, M>>,
|
||||
{
|
||||
log::debug!("visit_par(left_depth = {})", self.left_parallel_depth);
|
||||
|
||||
@ -129,6 +147,7 @@ impl Compressor<'_> {
|
||||
data: None,
|
||||
optimizer_state: Default::default(),
|
||||
left_parallel_depth: 0,
|
||||
mode: self.mode,
|
||||
};
|
||||
node.visit_mut_with(&mut v);
|
||||
|
||||
@ -148,6 +167,7 @@ impl Compressor<'_> {
|
||||
data: None,
|
||||
optimizer_state: Default::default(),
|
||||
left_parallel_depth: self.left_parallel_depth - 1,
|
||||
mode: self.mode,
|
||||
};
|
||||
node.visit_mut_with(&mut v);
|
||||
|
||||
@ -164,7 +184,7 @@ impl Compressor<'_> {
|
||||
|
||||
fn optimize_unit_repeatedly<N>(&mut self, n: &mut N)
|
||||
where
|
||||
N: CompileUnit + VisitWith<UsageAnalyzer> + for<'aa> VisitMutWith<Compressor<'aa>>,
|
||||
N: CompileUnit + VisitWith<UsageAnalyzer> + for<'aa> VisitMutWith<Compressor<'aa, M>>,
|
||||
{
|
||||
if cfg!(feature = "debug") {
|
||||
log::debug!(
|
||||
@ -210,7 +230,7 @@ impl Compressor<'_> {
|
||||
/// Optimize a module. `N` can be [Module] or [FnExpr].
|
||||
fn optimize_unit<N>(&mut self, n: &mut N)
|
||||
where
|
||||
N: CompileUnit + VisitWith<UsageAnalyzer> + for<'aa> VisitMutWith<Compressor<'aa>>,
|
||||
N: CompileUnit + VisitWith<UsageAnalyzer> + for<'aa> VisitMutWith<Compressor<'aa, M>>,
|
||||
{
|
||||
self.data = Some(analyze(&*n, Some(self.marks)));
|
||||
|
||||
@ -282,7 +302,7 @@ impl Compressor<'_> {
|
||||
|
||||
let start_time = now();
|
||||
|
||||
let mut visitor = pure_optimizer(&self.options, self.marks);
|
||||
let mut visitor = pure_optimizer(&self.options, self.marks, self.mode);
|
||||
n.apply(&mut visitor);
|
||||
self.changed |= visitor.changed();
|
||||
|
||||
@ -312,6 +332,7 @@ impl Compressor<'_> {
|
||||
self.options,
|
||||
self.data.as_ref().unwrap(),
|
||||
&mut self.optimizer_state,
|
||||
self.mode,
|
||||
);
|
||||
n.apply(&mut visitor);
|
||||
self.changed |= visitor.changed();
|
||||
@ -368,7 +389,10 @@ impl Compressor<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitMut for Compressor<'_> {
|
||||
impl<M> VisitMut for Compressor<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
noop_visit_mut_type!();
|
||||
|
||||
fn visit_mut_fn_expr(&mut self, n: &mut FnExpr) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::Optimizer;
|
||||
use crate::compress::optimize::is_left_access_to_arguments;
|
||||
use crate::{compress::optimize::is_left_access_to_arguments, mode::Mode};
|
||||
use std::iter::repeat_with;
|
||||
use swc_atoms::js_word;
|
||||
use swc_common::DUMMY_SP;
|
||||
@ -8,7 +8,10 @@ use swc_ecma_utils::{find_ids, ident::IdentLike, private_ident, Id};
|
||||
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
|
||||
|
||||
/// Methods related to the option `arguments`.
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
///
|
||||
/// - `arguments['foo']` => `arguments.foo`
|
||||
pub(super) fn optimize_str_access_to_arguments(&mut self, e: &mut Expr) {
|
||||
|
@ -2,6 +2,7 @@ use super::Optimizer;
|
||||
use crate::{
|
||||
compress::{optimize::Ctx, util::negate_cost},
|
||||
debug::dump,
|
||||
mode::Mode,
|
||||
};
|
||||
use swc_atoms::js_word;
|
||||
use swc_common::Spanned;
|
||||
@ -10,7 +11,10 @@ use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
use swc_ecma_utils::{ident::IdentLike, undefined, ExprExt, Type, Value::Known};
|
||||
|
||||
/// Methods related to the options `bools` and `bool_as_ints`.
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
/// **This negates bool**.
|
||||
///
|
||||
/// Returns true if it's negated.
|
||||
@ -115,7 +119,7 @@ impl Optimizer<'_> {
|
||||
///
|
||||
/// - `"undefined" == typeof value;` => `void 0 === value`
|
||||
pub(super) fn compress_typeof_undefined(&mut self, e: &mut BinExpr) {
|
||||
fn opt(o: &mut Optimizer, l: &mut Expr, r: &mut Expr) -> bool {
|
||||
fn opt<M>(o: &mut Optimizer<M>, l: &mut Expr, r: &mut Expr) -> bool {
|
||||
match (&mut *l, &mut *r) {
|
||||
(
|
||||
Expr::Lit(Lit::Str(Str {
|
||||
|
@ -1,11 +1,15 @@
|
||||
use super::Optimizer;
|
||||
use crate::mode::Mode;
|
||||
use fxhash::FxHashMap;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_utils::{ident::IdentLike, Id};
|
||||
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
|
||||
|
||||
/// Methods related to the option `collapse_vars`.
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
pub(super) fn collapse_assignment_to_vars(&mut self, e: &mut Expr) {
|
||||
if !self.options.collapse_vars {
|
||||
return;
|
||||
|
@ -4,6 +4,7 @@ use crate::{
|
||||
optimize::Ctx,
|
||||
util::{always_terminates, negate_cost},
|
||||
},
|
||||
mode::Mode,
|
||||
util::SpanExt,
|
||||
DISABLE_BUGGY_PASSES,
|
||||
};
|
||||
@ -15,7 +16,10 @@ use swc_ecma_utils::{ident::IdentLike, ExprExt, ExprFactory, StmtLike};
|
||||
|
||||
/// Methods related to the option `conditionals`. All methods are noop if
|
||||
/// `conditionals` is false.
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
/// Negates the condition of a `if` statement to reduce body size.
|
||||
pub(super) fn negate_if_stmt(&mut self, stmt: &mut IfStmt) {
|
||||
let alt = match stmt.alt.as_deref_mut() {
|
||||
|
@ -1,10 +1,14 @@
|
||||
use super::Optimizer;
|
||||
use crate::mode::Mode;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
use swc_ecma_utils::ident::IdentLike;
|
||||
|
||||
/// Methods related to option `dead_code`.
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
/// Optimize return value or argument of throw.
|
||||
///
|
||||
/// This methods removes some useless assignments.
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::Optimizer;
|
||||
use crate::{compress::util::eval_as_number, DISABLE_BUGGY_PASSES};
|
||||
use crate::{compress::util::eval_as_number, mode::Mode, DISABLE_BUGGY_PASSES};
|
||||
use std::num::FpCategory;
|
||||
use swc_atoms::js_word;
|
||||
use swc_common::{Spanned, SyntaxContext, DUMMY_SP};
|
||||
@ -8,7 +8,10 @@ use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
use swc_ecma_utils::{ident::IdentLike, undefined, ExprExt, Value::Known};
|
||||
|
||||
/// Methods related to the option `evaludate`.
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
/// Evaludate expression if possible.
|
||||
///
|
||||
/// This method call apppropriate methods for each ast types.
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::Optimizer;
|
||||
use crate::{
|
||||
compress::util::is_directive,
|
||||
mode::Mode,
|
||||
util::{sort::is_sorted_by, MoudleItemExt},
|
||||
DISABLE_BUGGY_PASSES,
|
||||
};
|
||||
@ -8,7 +9,10 @@ use std::cmp::Ordering;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
/// Calls `reorder_stmts_inner` after splitting stmts.
|
||||
pub(super) fn reorder_stmts<T>(&mut self, stmts: &mut Vec<T>)
|
||||
where
|
||||
|
@ -1,9 +1,13 @@
|
||||
use super::Optimizer;
|
||||
use crate::mode::Mode;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_utils::ident::IdentLike;
|
||||
|
||||
/// Methods related to the option `hoist_props`.
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
/// Store values of properties so we can replace property accesses with the
|
||||
/// values.
|
||||
pub(super) fn store_var_for_prop_hoisting(&mut self, n: &mut VarDeclarator) {
|
||||
|
@ -2,6 +2,7 @@ use super::Optimizer;
|
||||
use crate::{
|
||||
compress::{optimize::Ctx, util::is_pure_undefined},
|
||||
debug::dump,
|
||||
mode::Mode,
|
||||
util::ExprOptExt,
|
||||
};
|
||||
use swc_common::{Spanned, DUMMY_SP};
|
||||
@ -12,7 +13,10 @@ use swc_ecma_visit::{noop_visit_type, Node, Visit, VisitWith};
|
||||
|
||||
/// Methods related to the option `if_return`. All methods are noop if
|
||||
/// `if_return` is false.
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
/// # Input
|
||||
///
|
||||
/// ```js
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::Optimizer;
|
||||
use crate::{
|
||||
compress::optimize::Ctx,
|
||||
mode::Mode,
|
||||
util::{idents_used_by, make_number},
|
||||
};
|
||||
use fxhash::FxHashMap;
|
||||
@ -16,7 +17,10 @@ use swc_ecma_utils::{ident::IdentLike, undefined, ExprFactory, Id};
|
||||
use swc_ecma_visit::VisitMutWith;
|
||||
|
||||
/// Methods related to the option `negate_iife`.
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
/// Negates iife, while ignore return value.
|
||||
pub(super) fn negate_iife_ignoring_ret(&mut self, e: &mut Expr) {
|
||||
if !self.options.negate_iife || self.ctx.in_bang_arg || self.ctx.dont_use_negated_iife {
|
||||
@ -116,7 +120,10 @@ impl Optimizer<'_> {
|
||||
}
|
||||
|
||||
/// Methods related to iife.
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
/// # Exmaple
|
||||
///
|
||||
/// ## Input
|
||||
|
@ -2,6 +2,7 @@ use super::Optimizer;
|
||||
use crate::{
|
||||
compress::optimize::util::{class_has_side_effect, is_valid_for_lhs},
|
||||
debug::dump,
|
||||
mode::Mode,
|
||||
util::idents_used_by,
|
||||
};
|
||||
use swc_atoms::js_word;
|
||||
@ -11,7 +12,10 @@ use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
use swc_ecma_utils::{ident::IdentLike, ExprExt, UsageFinder};
|
||||
|
||||
/// Methods related to option `inline`.
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
/// Stores the value of a variable to inline it.
|
||||
///
|
||||
/// This method may remove value of initializer. It mean that the value will
|
||||
@ -135,6 +139,11 @@ impl Optimizer<'_> {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if !usage.mutated {
|
||||
self.mode.store(i.to_id(), &*init);
|
||||
}
|
||||
|
||||
// No use => doppred
|
||||
if usage.ref_count == 0 {
|
||||
if init.may_have_side_effects() {
|
||||
@ -165,6 +174,8 @@ impl Optimizer<'_> {
|
||||
_ => false,
|
||||
}
|
||||
{
|
||||
self.mode.store(i.to_id(), &*init);
|
||||
|
||||
if self.options.inline != 0
|
||||
&& !should_preserve
|
||||
&& match &**init {
|
||||
|
@ -1,12 +1,14 @@
|
||||
use crate::compress::util::is_directive;
|
||||
|
||||
use super::Optimizer;
|
||||
use crate::{compress::util::is_directive, mode::Mode};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
use swc_ecma_utils::StmtLike;
|
||||
|
||||
/// Methods related to option `join_vars`.
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
/// Join variables.
|
||||
///
|
||||
/// This method may move variables to head of for statements like
|
||||
|
@ -1,11 +1,17 @@
|
||||
use crate::compress::optimize::{unused::UnreachableHandler, Optimizer};
|
||||
use crate::{
|
||||
compress::optimize::{unused::UnreachableHandler, Optimizer},
|
||||
mode::Mode,
|
||||
};
|
||||
use swc_common::DUMMY_SP;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
use swc_ecma_utils::{ExprExt, Value::Known};
|
||||
|
||||
/// Methods related to the option `loops`.
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
/// `for(a;b;c;) break;` => `a;b;`
|
||||
pub(super) fn optimize_loops_with_break(&mut self, s: &mut Stmt) {
|
||||
if !self.options.loops {
|
||||
|
@ -1,8 +1,10 @@
|
||||
use self::util::replace_id_with_expr;
|
||||
use crate::{
|
||||
analyzer::{ProgramData, UsageAnalyzer},
|
||||
compress::util::is_pure_undefined,
|
||||
debug::dump,
|
||||
marks::Marks,
|
||||
mode::Mode,
|
||||
option::CompressOptions,
|
||||
util::{contains_leaping_yield, MoudleItemExt},
|
||||
};
|
||||
@ -20,8 +22,6 @@ use swc_ecma_utils::{
|
||||
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith, VisitWith};
|
||||
use Value::Known;
|
||||
|
||||
use self::util::replace_id_with_expr;
|
||||
|
||||
mod arguments;
|
||||
mod bools;
|
||||
mod collapse_vars;
|
||||
@ -49,12 +49,16 @@ pub(super) struct OptimizerState {
|
||||
}
|
||||
|
||||
/// This pass is simillar to `node.optimize` of terser.
|
||||
pub(super) fn optimizer<'a>(
|
||||
pub(super) fn optimizer<'a, M>(
|
||||
marks: Marks,
|
||||
options: &'a CompressOptions,
|
||||
data: &'a ProgramData,
|
||||
state: &'a mut OptimizerState,
|
||||
) -> impl 'a + VisitMut + Repeated {
|
||||
mode: &'a M,
|
||||
) -> impl 'a + VisitMut + Repeated
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
assert!(
|
||||
options.top_retain.iter().all(|s| s.trim() != ""),
|
||||
"top_retain should not contain empty string"
|
||||
@ -79,6 +83,7 @@ pub(super) fn optimizer<'a>(
|
||||
done,
|
||||
done_ctxt,
|
||||
label: Default::default(),
|
||||
mode,
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,7 +183,7 @@ impl Ctx {
|
||||
}
|
||||
}
|
||||
|
||||
struct Optimizer<'a> {
|
||||
struct Optimizer<'a, M> {
|
||||
marks: Marks,
|
||||
|
||||
changed: bool,
|
||||
@ -213,9 +218,11 @@ struct Optimizer<'a> {
|
||||
|
||||
/// Closest label.
|
||||
label: Option<Id>,
|
||||
|
||||
mode: &'a M,
|
||||
}
|
||||
|
||||
impl Repeated for Optimizer<'_> {
|
||||
impl<M> Repeated for Optimizer<'_, M> {
|
||||
fn changed(&self) -> bool {
|
||||
self.changed
|
||||
}
|
||||
@ -225,7 +232,10 @@ impl Repeated for Optimizer<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
fn handle_stmt_likes<T>(&mut self, stmts: &mut Vec<T>)
|
||||
where
|
||||
T: StmtLike + ModuleItemLike + MoudleItemExt + VisitMutWith<Self>,
|
||||
@ -1427,7 +1437,10 @@ impl Optimizer<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitMut for Optimizer<'_> {
|
||||
impl<M> VisitMut for Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
noop_visit_mut_type!();
|
||||
|
||||
fn visit_mut_arrow_expr(&mut self, n: &mut ArrowExpr) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::Optimizer;
|
||||
use crate::{
|
||||
compress::util::negate,
|
||||
mode::Mode,
|
||||
util::{make_bool, ValueExt},
|
||||
};
|
||||
use swc_atoms::js_word;
|
||||
@ -10,7 +11,10 @@ use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
use swc_ecma_utils::{ident::IdentLike, ExprExt, Type, Value};
|
||||
use Value::Known;
|
||||
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
///
|
||||
/// - `'12' === `foo` => '12' == 'foo'`
|
||||
pub(super) fn optimize_bin_operator(&mut self, e: &mut BinExpr) {
|
||||
|
@ -5,6 +5,7 @@ use crate::{
|
||||
util::{get_lhs_ident, get_lhs_ident_mut, is_directive, is_ident_used_by},
|
||||
},
|
||||
debug::dump,
|
||||
mode::Mode,
|
||||
option::CompressOptions,
|
||||
util::{idents_used_by, idents_used_by_ignoring_nested, ExprOptExt, MoudleItemExt},
|
||||
};
|
||||
@ -19,7 +20,10 @@ use swc_ecma_visit::{noop_visit_type, Node, Visit, VisitWith};
|
||||
|
||||
/// Methods related to the option `sequences`. All methods are noop if
|
||||
/// `sequences` is false.
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
///
|
||||
/// # Exmaple
|
||||
///
|
||||
|
@ -1,11 +1,15 @@
|
||||
use super::Optimizer;
|
||||
use crate::mode::Mode;
|
||||
use swc_atoms::js_word;
|
||||
use swc_common::Spanned;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
use swc_ecma_utils::{ident::IdentLike, ExprExt, Value::Known};
|
||||
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
pub(super) fn optimize_expr_in_str_ctx_unsafely(&mut self, e: &mut Expr) {
|
||||
if !self.options.unsafe_passes {
|
||||
return;
|
||||
|
@ -1,7 +1,6 @@
|
||||
use std::mem::take;
|
||||
|
||||
use super::Optimizer;
|
||||
use crate::util::ExprOptExt;
|
||||
use crate::{mode::Mode, util::ExprOptExt};
|
||||
use std::mem::take;
|
||||
use swc_common::{EqIgnoreSpan, DUMMY_SP};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
@ -9,7 +8,10 @@ use swc_ecma_utils::{ident::IdentLike, prepend, ExprExt, StmtExt, Type, Value::K
|
||||
use swc_ecma_visit::{noop_visit_type, Node, Visit, VisitWith};
|
||||
|
||||
/// Methods related to option `switches`.
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
/// Handle switches in the case where we can know which branch will be
|
||||
/// taken.
|
||||
pub(super) fn optimize_const_switches(&mut self, s: &mut Stmt) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::Optimizer;
|
||||
use crate::{
|
||||
compress::optimize::util::class_has_side_effect, debug::dump, option::PureGetterOption,
|
||||
compress::optimize::util::class_has_side_effect, debug::dump, mode::Mode,
|
||||
option::PureGetterOption,
|
||||
};
|
||||
use swc_atoms::js_word;
|
||||
use swc_common::{Span, DUMMY_SP};
|
||||
@ -10,7 +11,10 @@ use swc_ecma_utils::{contains_ident_ref, ident::IdentLike};
|
||||
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
|
||||
|
||||
/// Methods related to the option `unused`.
|
||||
impl Optimizer<'_> {
|
||||
impl<M> Optimizer<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
pub(super) fn drop_unused_var_declarator(&mut self, var: &mut VarDeclarator) {
|
||||
match &mut var.init {
|
||||
Some(init) => match &**init {
|
||||
|
@ -1,3 +1,5 @@
|
||||
use crate::mode::Mode;
|
||||
|
||||
use super::{Ctx, Optimizer};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use swc_atoms::JsWord;
|
||||
@ -6,7 +8,10 @@ use swc_ecma_ast::*;
|
||||
use swc_ecma_utils::{prop_name_eq, ExprExt, Id};
|
||||
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
|
||||
|
||||
impl<'b> Optimizer<'b> {
|
||||
impl<'b, M> Optimizer<'b, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
pub(super) fn access_property<'e>(
|
||||
&mut self,
|
||||
expr: &'e mut Expr,
|
||||
@ -77,7 +82,7 @@ impl<'b> Optimizer<'b> {
|
||||
|
||||
/// RAII guard to change context temporarically
|
||||
#[inline]
|
||||
pub(super) fn with_ctx(&mut self, ctx: Ctx) -> WithCtx<'_, 'b> {
|
||||
pub(super) fn with_ctx(&mut self, ctx: Ctx) -> WithCtx<'_, 'b, M> {
|
||||
let orig_ctx = self.ctx;
|
||||
self.ctx = ctx;
|
||||
WithCtx {
|
||||
@ -87,26 +92,26 @@ impl<'b> Optimizer<'b> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct WithCtx<'a, 'b> {
|
||||
reducer: &'a mut Optimizer<'b>,
|
||||
pub(super) struct WithCtx<'a, 'b, M> {
|
||||
reducer: &'a mut Optimizer<'b, M>,
|
||||
orig_ctx: Ctx,
|
||||
}
|
||||
|
||||
impl<'b> Deref for WithCtx<'_, 'b> {
|
||||
type Target = Optimizer<'b>;
|
||||
impl<'b, M> Deref for WithCtx<'_, 'b, M> {
|
||||
type Target = Optimizer<'b, M>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.reducer
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for WithCtx<'_, '_> {
|
||||
impl<M> DerefMut for WithCtx<'_, '_, M> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.reducer
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WithCtx<'_, '_> {
|
||||
impl<M> Drop for WithCtx<'_, '_, M> {
|
||||
fn drop(&mut self) {
|
||||
self.reducer.ctx = self.orig_ctx;
|
||||
}
|
||||
|
@ -1,11 +1,15 @@
|
||||
use super::Pure;
|
||||
use crate::mode::Mode;
|
||||
use swc_common::Spanned;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
use swc_ecma_utils::contains_this_expr;
|
||||
|
||||
/// Methods related to the option `arrows`.
|
||||
impl Pure<'_> {
|
||||
impl<M> Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
pub(super) fn optimize_arrow_body(&mut self, b: &mut BlockStmtOrExpr) {
|
||||
if !self.options.arrows {
|
||||
return;
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::Pure;
|
||||
use crate::{
|
||||
compress::util::{is_pure_undefined, negate, negate_cost},
|
||||
mode::Mode,
|
||||
util::make_bool,
|
||||
};
|
||||
use std::mem::swap;
|
||||
@ -10,7 +11,10 @@ use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
use swc_ecma_utils::{ExprExt, Type, Value};
|
||||
|
||||
impl Pure<'_> {
|
||||
impl<M> Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
pub(super) fn negate_twice(&mut self, e: &mut Expr) {
|
||||
self.changed = true;
|
||||
negate(e, false);
|
||||
|
@ -1,12 +1,15 @@
|
||||
use super::Pure;
|
||||
use crate::{compress::util::negate_cost, debug::dump, util::make_bool};
|
||||
use crate::{compress::util::negate_cost, debug::dump, mode::Mode, util::make_bool};
|
||||
use std::mem::swap;
|
||||
use swc_common::{EqIgnoreSpan, Spanned};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
use swc_ecma_utils::{ExprExt, Type, Value};
|
||||
|
||||
impl Pure<'_> {
|
||||
impl<M> Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
///
|
||||
/// - `foo ? bar : false` => `!!foo && bar`
|
||||
/// - `!foo ? true : bar` => `!foo || bar`
|
||||
|
@ -23,10 +23,10 @@ pub(super) struct Ctx {
|
||||
pub in_first_expr: bool,
|
||||
}
|
||||
|
||||
impl<'b> Pure<'b> {
|
||||
impl<'b, M> Pure<'b, M> {
|
||||
/// RAII guard to change context temporarically
|
||||
#[inline]
|
||||
pub(super) fn with_ctx(&mut self, ctx: Ctx) -> WithCtx<'_, 'b> {
|
||||
pub(super) fn with_ctx(&mut self, ctx: Ctx) -> WithCtx<'_, 'b, M> {
|
||||
let orig_ctx = self.ctx;
|
||||
self.ctx = ctx;
|
||||
WithCtx {
|
||||
@ -36,26 +36,26 @@ impl<'b> Pure<'b> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct WithCtx<'a, 'b> {
|
||||
pass: &'a mut Pure<'b>,
|
||||
pub(super) struct WithCtx<'a, 'b, M> {
|
||||
pass: &'a mut Pure<'b, M>,
|
||||
orig_ctx: Ctx,
|
||||
}
|
||||
|
||||
impl<'b> Deref for WithCtx<'_, 'b> {
|
||||
type Target = Pure<'b>;
|
||||
impl<'b, M> Deref for WithCtx<'_, 'b, M> {
|
||||
type Target = Pure<'b, M>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.pass
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for WithCtx<'_, '_> {
|
||||
impl<M> DerefMut for WithCtx<'_, '_, M> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.pass
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WithCtx<'_, '_> {
|
||||
impl<M> Drop for WithCtx<'_, '_, M> {
|
||||
fn drop(&mut self) {
|
||||
self.pass.ctx = self.orig_ctx;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::Pure;
|
||||
use crate::compress::util::always_terminates;
|
||||
use crate::{compress::util::always_terminates, mode::Mode};
|
||||
use swc_atoms::js_word;
|
||||
use swc_common::DUMMY_SP;
|
||||
use swc_ecma_ast::*;
|
||||
@ -7,7 +7,10 @@ use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
use swc_ecma_utils::{ExprExt, StmtLike, Value};
|
||||
|
||||
/// Methods related to option `dead_code`.
|
||||
impl Pure<'_> {
|
||||
impl<M> Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
pub(super) fn drop_useless_blocks<T>(&mut self, stmts: &mut Vec<T>)
|
||||
where
|
||||
T: StmtLike,
|
||||
|
@ -1,12 +1,18 @@
|
||||
use super::Pure;
|
||||
use crate::compress::util::{eval_as_number, is_pure_undefined_or_null};
|
||||
use crate::{
|
||||
compress::util::{eval_as_number, is_pure_undefined_or_null},
|
||||
mode::Mode,
|
||||
};
|
||||
use swc_atoms::js_word;
|
||||
use swc_common::{Spanned, SyntaxContext};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
use swc_ecma_utils::{undefined, ExprExt, Value};
|
||||
|
||||
impl Pure<'_> {
|
||||
impl<M> Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
pub(super) fn eval_array_method_call(&mut self, e: &mut Expr) {
|
||||
if !self.options.evaluate {
|
||||
return;
|
||||
@ -351,7 +357,10 @@ impl Pure<'_> {
|
||||
}
|
||||
|
||||
/// Evaluation of strings.
|
||||
impl Pure<'_> {
|
||||
impl<M> Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
/// Handle calls on string literals, like `'foo'.toUpperCase()`.
|
||||
pub(super) fn eval_str_method_call(&mut self, e: &mut Expr) {
|
||||
if !self.options.evaluate {
|
||||
|
@ -1,10 +1,14 @@
|
||||
use super::Pure;
|
||||
use crate::mode::Mode;
|
||||
use swc_common::Spanned;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
use swc_ecma_utils::{ExprExt, Value};
|
||||
|
||||
impl Pure<'_> {
|
||||
impl<M> Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
///
|
||||
/// - `while(test);` => `for(;;test);
|
||||
/// - `do; while(true)` => `for(;;);
|
||||
|
@ -1,8 +1,11 @@
|
||||
use super::Pure;
|
||||
use crate::compress::util::is_pure_undefined;
|
||||
use crate::{compress::util::is_pure_undefined, mode::Mode};
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
impl Pure<'_> {
|
||||
impl<M> Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
pub(super) fn drop_undefined_from_return_arg(&mut self, s: &mut ReturnStmt) {
|
||||
match s.arg.as_deref() {
|
||||
Some(e) => {
|
||||
|
@ -1,5 +1,7 @@
|
||||
use self::ctx::Ctx;
|
||||
use crate::{marks::Marks, option::CompressOptions, util::MoudleItemExt, MAX_PAR_DEPTH};
|
||||
use crate::{
|
||||
marks::Marks, mode::Mode, option::CompressOptions, util::MoudleItemExt, MAX_PAR_DEPTH,
|
||||
};
|
||||
use rayon::prelude::*;
|
||||
use swc_common::{pass::Repeated, DUMMY_SP};
|
||||
use swc_ecma_ast::*;
|
||||
@ -20,26 +22,32 @@ mod strings;
|
||||
mod unsafes;
|
||||
mod vars;
|
||||
|
||||
pub(super) fn pure_optimizer<'a>(
|
||||
pub(crate) fn pure_optimizer<'a, M>(
|
||||
options: &'a CompressOptions,
|
||||
marks: Marks,
|
||||
) -> impl 'a + VisitMut + Repeated {
|
||||
mode: &'a M,
|
||||
) -> impl 'a + VisitMut + Repeated
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
Pure {
|
||||
options,
|
||||
marks,
|
||||
ctx: Default::default(),
|
||||
changed: Default::default(),
|
||||
mode,
|
||||
}
|
||||
}
|
||||
|
||||
struct Pure<'a> {
|
||||
struct Pure<'a, M> {
|
||||
options: &'a CompressOptions,
|
||||
marks: Marks,
|
||||
ctx: Ctx,
|
||||
changed: bool,
|
||||
mode: &'a M,
|
||||
}
|
||||
|
||||
impl Repeated for Pure<'_> {
|
||||
impl<M> Repeated for Pure<'_, M> {
|
||||
fn changed(&self) -> bool {
|
||||
self.changed
|
||||
}
|
||||
@ -50,7 +58,10 @@ impl Repeated for Pure<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Pure<'_> {
|
||||
impl<M> Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
fn handle_stmt_likes<T>(&mut self, stmts: &mut Vec<T>)
|
||||
where
|
||||
T: MoudleItemExt,
|
||||
@ -70,7 +81,7 @@ impl Pure<'_> {
|
||||
/// Visit `nodes`, maybe in parallel.
|
||||
fn visit_par<N>(&mut self, nodes: &mut Vec<N>)
|
||||
where
|
||||
N: for<'aa> VisitMutWith<Pure<'aa>> + Send + Sync,
|
||||
N: for<'aa> VisitMutWith<Pure<'aa, M>> + Send + Sync,
|
||||
{
|
||||
if self.ctx.par_depth >= MAX_PAR_DEPTH * 2 || cfg!(target_arch = "wasm32") {
|
||||
for node in nodes {
|
||||
@ -79,6 +90,7 @@ impl Pure<'_> {
|
||||
marks: self.marks,
|
||||
ctx: self.ctx,
|
||||
changed: false,
|
||||
mode: self.mode,
|
||||
};
|
||||
node.visit_mut_with(&mut v);
|
||||
|
||||
@ -96,6 +108,7 @@ impl Pure<'_> {
|
||||
..self.ctx
|
||||
},
|
||||
changed: false,
|
||||
mode: self.mode,
|
||||
};
|
||||
node.visit_mut_with(&mut v);
|
||||
|
||||
@ -110,7 +123,10 @@ impl Pure<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitMut for Pure<'_> {
|
||||
impl<M> VisitMut for Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
noop_visit_mut_type!();
|
||||
|
||||
fn visit_mut_assign_expr(&mut self, e: &mut AssignExpr) {
|
||||
|
@ -1,8 +1,12 @@
|
||||
use super::Pure;
|
||||
use crate::mode::Mode;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
|
||||
impl Pure<'_> {
|
||||
impl<M> Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
pub(super) fn optimize_expr_in_num_ctx(&mut self, e: &mut Expr) {
|
||||
match e {
|
||||
Expr::Lit(Lit::Str(Str { span, value, .. })) => {
|
||||
|
@ -1,11 +1,14 @@
|
||||
use super::Pure;
|
||||
use crate::{compress::util::is_valid_identifier, util::deeply_contains_this_expr};
|
||||
use crate::{compress::util::is_valid_identifier, mode::Mode, util::deeply_contains_this_expr};
|
||||
use swc_atoms::js_word;
|
||||
use swc_common::SyntaxContext;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_utils::{prop_name_eq, ExprExt};
|
||||
|
||||
impl Pure<'_> {
|
||||
impl<M> Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
pub(super) fn optimize_property_of_member_expr(&mut self, e: &mut MemberExpr) {
|
||||
if !e.computed {
|
||||
return;
|
||||
|
@ -1,12 +1,14 @@
|
||||
use crate::compress::util::get_lhs_ident;
|
||||
|
||||
use super::Pure;
|
||||
use crate::{compress::util::get_lhs_ident, mode::Mode};
|
||||
use swc_common::{SyntaxContext, DUMMY_SP};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
use swc_ecma_utils::{ExprExt, ExprFactory};
|
||||
|
||||
impl Pure<'_> {
|
||||
impl<M> Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
pub(super) fn drop_useless_ident_ref_in_seq(&mut self, seq: &mut SeqExpr) {
|
||||
if !self.options.collapse_vars {
|
||||
return;
|
||||
|
@ -1,13 +1,16 @@
|
||||
use std::mem::take;
|
||||
|
||||
use super::Pure;
|
||||
use crate::mode::Mode;
|
||||
use std::mem::take;
|
||||
use swc_atoms::{js_word, JsWord};
|
||||
use swc_common::DUMMY_SP;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
use swc_ecma_utils::{ExprExt, Type, Value};
|
||||
|
||||
impl Pure<'_> {
|
||||
impl<M> Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
/// Converts template literals to string if `exprs` of [Tpl] is empty.
|
||||
pub(super) fn convert_tpl_to_str(&mut self, e: &mut Expr) {
|
||||
match e {
|
||||
@ -15,6 +18,7 @@ impl Pure<'_> {
|
||||
if let Some(c) = &t.quasis[0].cooked {
|
||||
if c.value.chars().all(|c| match c {
|
||||
'\u{0020}'..='\u{007e}' => true,
|
||||
'\n' | '\r' => M::force_str_for_tpl(),
|
||||
_ => false,
|
||||
}) {
|
||||
*e = Expr::Lit(Lit::Str(c.clone()));
|
||||
|
@ -1,9 +1,13 @@
|
||||
use super::Pure;
|
||||
use crate::mode::Mode;
|
||||
use swc_atoms::js_word;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_utils::ExprExt;
|
||||
|
||||
impl Pure<'_> {
|
||||
impl<M> Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
/// Drop arguments of `Symbol()` call.
|
||||
pub(super) fn drop_arguemtns_of_symbol_call(&mut self, e: &mut CallExpr) {
|
||||
if !self.options.unsafe_symbols {
|
||||
|
@ -1,3 +1,5 @@
|
||||
use super::Pure;
|
||||
use crate::mode::Mode;
|
||||
use swc_common::DUMMY_SP;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
@ -6,9 +8,10 @@ use swc_ecma_visit::{
|
||||
noop_visit_mut_type, noop_visit_type, Node, Visit, VisitMut, VisitMutWith, VisitWith,
|
||||
};
|
||||
|
||||
use super::Pure;
|
||||
|
||||
impl Pure<'_> {
|
||||
impl<M> Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
/// Collapse single-use non-constant variables, side effects permitting.
|
||||
///
|
||||
/// This merges all variables to first variable declartion with an
|
||||
|
251
ecmascript/minifier/src/eval.rs
Normal file
251
ecmascript/minifier/src/eval.rs
Normal file
@ -0,0 +1,251 @@
|
||||
use crate::{
|
||||
compress::{compressor, pure_optimizer},
|
||||
marks::Marks,
|
||||
mode::Mode,
|
||||
};
|
||||
use fxhash::FxHashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use swc_atoms::js_word;
|
||||
use swc_common::DUMMY_SP;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms::optimization::simplify::expr_simplifier;
|
||||
use swc_ecma_transforms_base::ext::MapWithMut;
|
||||
use swc_ecma_utils::{ident::IdentLike, undefined, ExprExt, ExprFactory, Id};
|
||||
use swc_ecma_visit::{FoldWith, VisitMutWith};
|
||||
|
||||
pub struct Evaluator {
|
||||
module: Module,
|
||||
marks: Marks,
|
||||
data: Eval,
|
||||
/// We run minification only once.
|
||||
done: bool,
|
||||
}
|
||||
|
||||
impl Evaluator {
|
||||
pub fn new(module: Module, marks: Marks) -> Self {
|
||||
Evaluator {
|
||||
module,
|
||||
marks,
|
||||
data: Default::default(),
|
||||
done: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct Eval {
|
||||
store: Arc<Mutex<EvalStore>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct EvalStore {
|
||||
cache: FxHashMap<Id, Box<Expr>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum EvalResult {
|
||||
Lit(Lit),
|
||||
Undefined,
|
||||
}
|
||||
|
||||
impl Mode for Eval {
|
||||
fn store(&self, id: Id, value: &Expr) {
|
||||
let mut w = self.store.lock().unwrap();
|
||||
w.cache.insert(id, Box::new(value.clone()));
|
||||
}
|
||||
|
||||
fn force_str_for_tpl() -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Evaluator {
|
||||
fn run(&mut self) {
|
||||
if !self.done {
|
||||
self.done = true;
|
||||
|
||||
let marks = self.marks;
|
||||
let data = self.data.clone();
|
||||
self.module.map_with_mut(|m| {
|
||||
//
|
||||
swc_common::GLOBALS.with(|globals| {
|
||||
//
|
||||
m.fold_with(&mut compressor(
|
||||
&globals,
|
||||
marks,
|
||||
&serde_json::from_str("{}").unwrap(),
|
||||
&data,
|
||||
))
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval(&mut self, e: &Expr) -> Option<EvalResult> {
|
||||
match e {
|
||||
Expr::Seq(s) => return self.eval(s.exprs.last()?),
|
||||
|
||||
Expr::Lit(l @ Lit::Null(..))
|
||||
| Expr::Lit(l @ Lit::Num(..) | l @ Lit::Str(..) | l @ Lit::BigInt(..)) => {
|
||||
return Some(EvalResult::Lit(l.clone()))
|
||||
}
|
||||
|
||||
Expr::Tpl(t) => {
|
||||
return self.eval_tpl(&t);
|
||||
}
|
||||
|
||||
Expr::TaggedTpl(t) => {
|
||||
// Handle `String.raw`
|
||||
|
||||
match &*t.tag {
|
||||
Expr::Member(MemberExpr {
|
||||
obj: ExprOrSuper::Expr(tag_obj),
|
||||
prop,
|
||||
computed: false,
|
||||
..
|
||||
}) if tag_obj.is_ident_ref_to("String".into())
|
||||
&& prop.is_ident_ref_to("raw".into()) =>
|
||||
{
|
||||
return self.eval_tpl(&t.tpl);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Expr::Cond(c) => {
|
||||
let test = self.eval(&c.test)?;
|
||||
|
||||
if is_truthy(&test)? {
|
||||
return self.eval(&c.cons);
|
||||
} else {
|
||||
return self.eval(&c.alt);
|
||||
}
|
||||
}
|
||||
|
||||
// TypeCastExpression, ExpressionStatement etc
|
||||
Expr::TsTypeAssertion(e) => {
|
||||
return self.eval(&e.expr);
|
||||
}
|
||||
|
||||
Expr::TsConstAssertion(e) => {
|
||||
return self.eval(&e.expr);
|
||||
}
|
||||
|
||||
// "foo".length
|
||||
Expr::Member(MemberExpr {
|
||||
obj: ExprOrSuper::Expr(obj),
|
||||
prop,
|
||||
computed: false,
|
||||
..
|
||||
}) if obj.is_lit() && prop.is_ident_ref_to("length".into()) => {}
|
||||
|
||||
Expr::Unary(UnaryExpr {
|
||||
op: op!("void"), ..
|
||||
}) => return Some(EvalResult::Undefined),
|
||||
|
||||
Expr::Unary(UnaryExpr {
|
||||
op: op!("!"), arg, ..
|
||||
}) => {
|
||||
let arg = self.eval(&arg)?;
|
||||
|
||||
if is_truthy(&arg)? {
|
||||
return Some(EvalResult::Lit(Lit::Bool(Bool {
|
||||
span: DUMMY_SP,
|
||||
value: false,
|
||||
})));
|
||||
} else {
|
||||
return Some(EvalResult::Lit(Lit::Bool(Bool {
|
||||
span: DUMMY_SP,
|
||||
value: true,
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Some(EvalResult::Lit(self.eval_as_expr(e)?.lit()?))
|
||||
}
|
||||
|
||||
fn eval_as_expr(&mut self, e: &Expr) -> Option<Box<Expr>> {
|
||||
match e {
|
||||
Expr::Ident(i) => {
|
||||
self.run();
|
||||
|
||||
let lock = self.data.store.lock().ok()?;
|
||||
let val = lock.cache.get(&i.to_id())?;
|
||||
|
||||
return Some(val.clone());
|
||||
}
|
||||
|
||||
Expr::Member(MemberExpr {
|
||||
span,
|
||||
obj: ExprOrSuper::Expr(obj),
|
||||
prop,
|
||||
computed: false,
|
||||
..
|
||||
}) => {
|
||||
let obj = self.eval_as_expr(&obj)?;
|
||||
|
||||
let mut e = Expr::Member(MemberExpr {
|
||||
span: *span,
|
||||
obj: obj.as_obj(),
|
||||
prop: prop.clone(),
|
||||
computed: false,
|
||||
});
|
||||
|
||||
e.visit_mut_with(&mut expr_simplifier());
|
||||
return Some(Box::new(e));
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn eval_tpl(&mut self, q: &Tpl) -> Option<EvalResult> {
|
||||
self.run();
|
||||
|
||||
let mut exprs = vec![];
|
||||
|
||||
for expr in &q.exprs {
|
||||
let res = self.eval(expr)?;
|
||||
exprs.push(match res {
|
||||
EvalResult::Lit(v) => Box::new(Expr::Lit(v)),
|
||||
EvalResult::Undefined => undefined(DUMMY_SP),
|
||||
});
|
||||
}
|
||||
|
||||
let mut e = Expr::Tpl(Tpl {
|
||||
span: q.span,
|
||||
exprs,
|
||||
quasis: q.quasis.clone(),
|
||||
});
|
||||
|
||||
{
|
||||
let data = self.data.clone();
|
||||
e.visit_mut_with(&mut pure_optimizer(
|
||||
&serde_json::from_str("{}").unwrap(),
|
||||
self.marks,
|
||||
&data,
|
||||
));
|
||||
}
|
||||
|
||||
Some(EvalResult::Lit(e.lit()?))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_truthy(lit: &EvalResult) -> Option<bool> {
|
||||
match lit {
|
||||
EvalResult::Lit(v) => match v {
|
||||
Lit::Str(v) => Some(v.value != js_word!("")),
|
||||
Lit::Bool(v) => Some(v.value),
|
||||
Lit::Null(_) => Some(false),
|
||||
Lit::Num(v) => Some(v.value != 0.0 && v.value != -0.0),
|
||||
_ => None,
|
||||
},
|
||||
EvalResult::Undefined => Some(false),
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@ use crate::{
|
||||
},
|
||||
util::now,
|
||||
};
|
||||
use mode::Minification;
|
||||
use pass::postcompress::postcompress_optimizer;
|
||||
use std::time::Instant;
|
||||
use swc_common::{comments::Comments, sync::Lrc, SourceMap, GLOBALS};
|
||||
@ -38,8 +39,10 @@ use timing::Timings;
|
||||
mod analyzer;
|
||||
mod compress;
|
||||
mod debug;
|
||||
pub mod eval;
|
||||
pub mod marks;
|
||||
mod metadata;
|
||||
mod mode;
|
||||
pub mod option;
|
||||
mod pass;
|
||||
pub mod timing;
|
||||
@ -105,7 +108,7 @@ pub fn optimize(
|
||||
|
||||
// Noop.
|
||||
// https://github.com/mishoo/UglifyJS2/issues/2794
|
||||
if options.rename && false {
|
||||
if options.rename && DISABLE_BUGGY_PASSES {
|
||||
// toplevel.figure_out_scope(options.mangle);
|
||||
// TODO: Pass `options.mangle` to name expander.
|
||||
m.visit_mut_with(&mut name_expander());
|
||||
@ -116,7 +119,8 @@ pub fn optimize(
|
||||
}
|
||||
if let Some(options) = &options.compress {
|
||||
let start = now();
|
||||
m = GLOBALS.with(|globals| m.fold_with(&mut compressor(globals, marks, &options)));
|
||||
m = GLOBALS
|
||||
.with(|globals| m.fold_with(&mut compressor(globals, marks, &options, &Minification)));
|
||||
if let Some(start) = start {
|
||||
log::info!("compressor took {:?}", Instant::now() - start);
|
||||
}
|
||||
|
20
ecmascript/minifier/src/mode.rs
Normal file
20
ecmascript/minifier/src/mode.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_utils::Id;
|
||||
|
||||
pub(crate) trait Mode: Send + Sync {
|
||||
fn store(&self, id: Id, value: &Expr);
|
||||
|
||||
/// If this returns true, template literals with `\n` or `\r` will be
|
||||
/// converted to [Lit::Str].
|
||||
fn force_str_for_tpl() -> bool;
|
||||
}
|
||||
|
||||
pub struct Minification;
|
||||
|
||||
impl Mode for Minification {
|
||||
fn store(&self, _: Id, _: &Expr) {}
|
||||
|
||||
fn force_str_for_tpl() -> bool {
|
||||
false
|
||||
}
|
||||
}
|
468
ecmascript/minifier/tests/eval.rs
Normal file
468
ecmascript/minifier/tests/eval.rs
Normal file
@ -0,0 +1,468 @@
|
||||
use swc_common::{input::SourceFileInput, sync::Lrc, FileName, SourceMap};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_codegen::{text_writer::JsWriter, Emitter};
|
||||
use swc_ecma_minifier::{
|
||||
eval::{EvalResult, Evaluator},
|
||||
marks::Marks,
|
||||
};
|
||||
use swc_ecma_parser::{lexer::Lexer, EsConfig, Parser, Syntax};
|
||||
use swc_ecma_transforms::resolver;
|
||||
use swc_ecma_utils::ExprExt;
|
||||
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
|
||||
use testing::{assert_eq, DebugUsingDisplay};
|
||||
|
||||
fn eval(module: &str, expr: &str) -> Option<String> {
|
||||
testing::run_test2(false, |cm, _handler| {
|
||||
let fm = cm.new_source_file(FileName::Anon, module.to_string());
|
||||
let marks = Marks::new();
|
||||
|
||||
let module_ast = {
|
||||
let lexer = Lexer::new(
|
||||
Default::default(),
|
||||
EsVersion::latest(),
|
||||
SourceFileInput::from(&*fm),
|
||||
None,
|
||||
);
|
||||
let mut parser = Parser::new_from(lexer);
|
||||
parser.parse_module().unwrap()
|
||||
};
|
||||
|
||||
let expr_ast = {
|
||||
let fm = cm.new_source_file(FileName::Anon, expr.to_string());
|
||||
|
||||
let lexer = Lexer::new(
|
||||
Default::default(),
|
||||
EsVersion::latest(),
|
||||
SourceFileInput::from(&*fm),
|
||||
None,
|
||||
);
|
||||
let mut parser = Parser::new_from(lexer);
|
||||
parser.parse_expr().unwrap()
|
||||
};
|
||||
|
||||
let mut evaluator = Evaluator::new(module_ast, marks);
|
||||
|
||||
let res = evaluator.eval(&expr_ast);
|
||||
|
||||
match res {
|
||||
Some(res) => match res {
|
||||
EvalResult::Lit(l) => match l {
|
||||
swc_ecma_ast::Lit::Str(v) => Ok(Some(v.value.to_string())),
|
||||
swc_ecma_ast::Lit::Bool(v) => Ok(Some(v.value.to_string())),
|
||||
swc_ecma_ast::Lit::Num(v) => Ok(Some(v.value.to_string())),
|
||||
swc_ecma_ast::Lit::Null(_) => Ok(Some("null".into())),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
EvalResult::Undefined => Ok(Some("undefined".into())),
|
||||
},
|
||||
None => Ok(None),
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
fn simple() {
|
||||
assert_eq!(eval("const foo = 4", "foo").unwrap(), "4");
|
||||
}
|
||||
|
||||
struct PartialInliner {
|
||||
marks: Marks,
|
||||
eval: Option<Evaluator>,
|
||||
}
|
||||
|
||||
impl PartialInliner {
|
||||
fn run_test<F>(src: &str, op: F)
|
||||
where
|
||||
F: FnOnce(Lrc<SourceMap>, Module, &mut PartialInliner),
|
||||
{
|
||||
testing::run_test2(false, |cm, _handler| {
|
||||
let fm = cm.new_source_file(FileName::Anon, src.to_string());
|
||||
let marks = Marks::new();
|
||||
|
||||
let mut module = {
|
||||
let lexer = Lexer::new(
|
||||
Syntax::Es(EsConfig {
|
||||
jsx: true,
|
||||
..Default::default()
|
||||
}),
|
||||
EsVersion::latest(),
|
||||
SourceFileInput::from(&*fm),
|
||||
None,
|
||||
);
|
||||
let mut parser = Parser::new_from(lexer);
|
||||
parser.parse_module().unwrap()
|
||||
};
|
||||
module.visit_mut_with(&mut resolver());
|
||||
|
||||
let mut inliner = PartialInliner {
|
||||
marks,
|
||||
eval: Default::default(),
|
||||
};
|
||||
|
||||
op(cm.clone(), module, &mut inliner);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn expect(src: &str, expected: &str) {
|
||||
PartialInliner::run_test(src, |cm, mut module, inliner| {
|
||||
//
|
||||
module.visit_mut_with(inliner);
|
||||
|
||||
let expected_module = {
|
||||
let fm = cm.new_source_file(FileName::Anon, expected.to_string());
|
||||
let lexer = Lexer::new(
|
||||
Syntax::Es(EsConfig {
|
||||
jsx: true,
|
||||
..Default::default()
|
||||
}),
|
||||
EsVersion::latest(),
|
||||
SourceFileInput::from(&*fm),
|
||||
None,
|
||||
);
|
||||
let mut parser = Parser::new_from(lexer);
|
||||
parser.parse_module().unwrap()
|
||||
};
|
||||
let expected = {
|
||||
let mut buf = vec![];
|
||||
|
||||
{
|
||||
let mut emitter = Emitter {
|
||||
cfg: Default::default(),
|
||||
cm: cm.clone(),
|
||||
comments: None,
|
||||
wr: Box::new(JsWriter::new(cm.clone(), "\n", &mut buf, None)),
|
||||
};
|
||||
|
||||
emitter.emit_module(&expected_module).unwrap();
|
||||
}
|
||||
String::from_utf8(buf).unwrap()
|
||||
};
|
||||
|
||||
let actual = {
|
||||
let mut buf = vec![];
|
||||
|
||||
{
|
||||
let mut emitter = Emitter {
|
||||
cfg: Default::default(),
|
||||
cm: cm.clone(),
|
||||
comments: None,
|
||||
wr: Box::new(JsWriter::new(cm.clone(), "\n", &mut buf, None)),
|
||||
};
|
||||
|
||||
emitter.emit_module(&module).unwrap();
|
||||
}
|
||||
String::from_utf8(buf).unwrap()
|
||||
};
|
||||
|
||||
assert_eq!(DebugUsingDisplay(&expected), DebugUsingDisplay(&actual));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitMut for PartialInliner {
|
||||
noop_visit_mut_type!();
|
||||
|
||||
fn visit_mut_expr(&mut self, e: &mut Expr) {
|
||||
e.visit_mut_children_with(self);
|
||||
|
||||
if let Some(evaluator) = self.eval.as_mut() {
|
||||
match e {
|
||||
Expr::TaggedTpl(tt) => {
|
||||
if tt.tag.is_ident_ref_to("css".into()) {
|
||||
let res = evaluator.eval_tpl(&tt.tpl);
|
||||
|
||||
let res = match res {
|
||||
Some(v) => v,
|
||||
None => return,
|
||||
};
|
||||
|
||||
match res {
|
||||
EvalResult::Lit(Lit::Str(s)) => {
|
||||
let el = TplElement {
|
||||
span: s.span,
|
||||
tail: true,
|
||||
cooked: Some(s.clone()),
|
||||
raw: s,
|
||||
};
|
||||
tt.tpl = Tpl {
|
||||
span: el.span,
|
||||
exprs: Default::default(),
|
||||
quasis: vec![el],
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_module(&mut self, module: &mut Module) {
|
||||
self.eval = Some(Evaluator::new(module.clone(), self.marks));
|
||||
module.visit_mut_children_with(self);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_1() {
|
||||
PartialInliner::expect(
|
||||
"
|
||||
const color = 'red'
|
||||
|
||||
export const foo = css`
|
||||
div {
|
||||
color: ${color};
|
||||
}
|
||||
`
|
||||
",
|
||||
"
|
||||
const color = 'red'
|
||||
|
||||
export const foo = css`
|
||||
div {
|
||||
color: red;
|
||||
}
|
||||
`
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_2() {
|
||||
PartialInliner::expect(
|
||||
"
|
||||
export default css`
|
||||
div {
|
||||
font-size: 3em;
|
||||
}
|
||||
p {
|
||||
color: ${props.color};
|
||||
}
|
||||
`
|
||||
",
|
||||
"
|
||||
export default css`
|
||||
div {
|
||||
font-size: 3em;
|
||||
}
|
||||
p {
|
||||
color: ${props.color};
|
||||
}
|
||||
`
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_3() {
|
||||
PartialInliner::expect(
|
||||
"
|
||||
const darken = c => c
|
||||
const color = 'red'
|
||||
const otherColor = 'green'
|
||||
const mediumScreen = '680px'
|
||||
const animationDuration = '200ms'
|
||||
const animationName = 'my-cool-animation'
|
||||
const obj = { display: 'block' }
|
||||
|
||||
export const s1 = css`
|
||||
p.${color} {
|
||||
color: ${otherColor};
|
||||
display: ${obj.display};
|
||||
}
|
||||
`;
|
||||
",
|
||||
"
|
||||
const darken = c => c
|
||||
const color = 'red'
|
||||
const otherColor = 'green'
|
||||
const mediumScreen = '680px'
|
||||
const animationDuration = '200ms'
|
||||
const animationName = 'my-cool-animation'
|
||||
const obj = { display: 'block' }
|
||||
|
||||
export const s1 = css`
|
||||
p.red {
|
||||
color: green;
|
||||
display: block;
|
||||
}
|
||||
`
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn partial_4() {
|
||||
PartialInliner::expect(
|
||||
"
|
||||
const darken = c => c
|
||||
const color = 'red'
|
||||
const otherColor = 'green'
|
||||
const mediumScreen = '680px'
|
||||
const animationDuration = '200ms'
|
||||
const animationName = 'my-cool-animation'
|
||||
const obj = { display: 'block' }
|
||||
|
||||
export const s2 = css`
|
||||
p.${color} {
|
||||
color: ${darken(color)};
|
||||
}
|
||||
`;
|
||||
",
|
||||
"
|
||||
const darken = c => c
|
||||
const color = 'red'
|
||||
const otherColor = 'green'
|
||||
const mediumScreen = '680px'
|
||||
const animationDuration = '200ms'
|
||||
const animationName = 'my-cool-animation'
|
||||
const obj = { display: 'block' }
|
||||
|
||||
export const s2 = css`
|
||||
p.red {
|
||||
color: 'red';
|
||||
}
|
||||
`
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn partial_5() {
|
||||
PartialInliner::expect(
|
||||
"
|
||||
const darken = c => c
|
||||
const color = 'red'
|
||||
const otherColor = 'green'
|
||||
const mediumScreen = '680px'
|
||||
const animationDuration = '200ms'
|
||||
const animationName = 'my-cool-animation'
|
||||
const obj = { display: 'block' }
|
||||
|
||||
export const s3 = css`
|
||||
p.${color} {
|
||||
color: ${darken(color) + 2};
|
||||
}
|
||||
`;
|
||||
",
|
||||
"
|
||||
const darken = c => c
|
||||
const color = 'red'
|
||||
const otherColor = 'green'
|
||||
const mediumScreen = '680px'
|
||||
const animationDuration = '200ms'
|
||||
const animationName = 'my-cool-animation'
|
||||
const obj = { display: 'block' }
|
||||
|
||||
export const s3 = css`
|
||||
p.red {
|
||||
color: 'red2';
|
||||
}
|
||||
`;
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_6() {
|
||||
PartialInliner::expect(
|
||||
"
|
||||
const darken = c => c
|
||||
const color = 'red'
|
||||
const otherColor = 'green'
|
||||
const mediumScreen = '680px'
|
||||
const animationDuration = '200ms'
|
||||
const animationName = 'my-cool-animation'
|
||||
const obj = { display: 'block' }
|
||||
|
||||
export const s4 = css`
|
||||
@media (min-width: ${mediumScreen}) {
|
||||
p {
|
||||
color: green;
|
||||
}
|
||||
p {
|
||||
color: ${`red`};
|
||||
}
|
||||
}
|
||||
p {
|
||||
color: red;
|
||||
}
|
||||
`;
|
||||
",
|
||||
"
|
||||
const darken = c => c
|
||||
const color = 'red'
|
||||
const otherColor = 'green'
|
||||
const mediumScreen = '680px'
|
||||
const animationDuration = '200ms'
|
||||
const animationName = 'my-cool-animation'
|
||||
const obj = { display: 'block' }
|
||||
|
||||
export const s4 = css`
|
||||
@media (min-width: 680px) {
|
||||
p {
|
||||
color: green;
|
||||
}
|
||||
p {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
p {
|
||||
color: red;
|
||||
}
|
||||
`;
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_7() {
|
||||
PartialInliner::expect(
|
||||
"
|
||||
const darken = c => c
|
||||
const color = 'red'
|
||||
const otherColor = 'green'
|
||||
const mediumScreen = '680px'
|
||||
const animationDuration = '200ms'
|
||||
const animationName = 'my-cool-animation'
|
||||
const obj = { display: 'block' }
|
||||
|
||||
export default ({ display }) => (
|
||||
css`
|
||||
span {
|
||||
display: ${display ? 'block' : 'none'};
|
||||
}
|
||||
`
|
||||
)
|
||||
",
|
||||
"
|
||||
const darken = c => c
|
||||
const color = 'red'
|
||||
const otherColor = 'green'
|
||||
const mediumScreen = '680px'
|
||||
const animationDuration = '200ms'
|
||||
const animationName = 'my-cool-animation'
|
||||
const obj = { display: 'block' }
|
||||
|
||||
export default ({ display }) => (
|
||||
css`
|
||||
span {
|
||||
display: ${display ? 'block' : 'none'};
|
||||
}
|
||||
`
|
||||
)
|
||||
",
|
||||
);
|
||||
}
|
@ -13,7 +13,7 @@ mod tests;
|
||||
const LOG: bool = false;
|
||||
|
||||
/// See [resolver_with_mark] for docs.
|
||||
pub fn resolver() -> impl 'static + Fold {
|
||||
pub fn resolver() -> impl 'static + Fold + VisitMut {
|
||||
resolver_with_mark(Mark::fresh(Mark::root()))
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@swc/core",
|
||||
"version": "1.2.83",
|
||||
"version": "1.2.84",
|
||||
"description": "Super-fast alternative for babel",
|
||||
"homepage": "https://swc.rs",
|
||||
"main": "./index.js",
|
||||
|
@ -6,7 +6,7 @@ license = "Apache-2.0 AND MIT"
|
||||
name = "wasm"
|
||||
publish = false
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "1.2.83"
|
||||
version = "1.2.84"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
Loading…
Reference in New Issue
Block a user