mirror of
https://github.com/swc-project/swc.git
synced 2024-09-19 04:38:13 +03:00
Add const_modules pass (#268)
swc_ecma_transforms: - use fxhash for inline_globals pass - handle member expression in inlne_globals pass - add const_modules pass
This commit is contained in:
parent
c1de0a5c86
commit
a785ecc960
186
ecmascript/transforms/src/const_modules.rs
Normal file
186
ecmascript/transforms/src/const_modules.rs
Normal file
@ -0,0 +1,186 @@
|
||||
use crate::{pass::Pass, util::State};
|
||||
use ast::*;
|
||||
use fxhash::FxHashMap;
|
||||
use std::sync::Arc;
|
||||
use swc_atoms::JsWord;
|
||||
use swc_common::{util::move_map::MoveMap, Fold, FoldWith};
|
||||
|
||||
pub fn const_modules(
|
||||
globals: FxHashMap<JsWord, FxHashMap<JsWord, Arc<Expr>>>,
|
||||
) -> impl Pass + Clone {
|
||||
ConstModules {
|
||||
globals,
|
||||
scope: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ConstModules {
|
||||
globals: FxHashMap<JsWord, FxHashMap<JsWord, Arc<Expr>>>,
|
||||
scope: State<Scope>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Scope {
|
||||
imported: FxHashMap<JsWord, Arc<Expr>>,
|
||||
}
|
||||
|
||||
impl Fold<Vec<ModuleItem>> for ConstModules {
|
||||
fn fold(&mut self, items: Vec<ModuleItem>) -> Vec<ModuleItem> {
|
||||
items.move_flat_map(|item| match item {
|
||||
ModuleItem::ModuleDecl(ModuleDecl::Import(import)) => {
|
||||
let entry = self.globals.get(&import.src.value);
|
||||
|
||||
if let Some(entry) = entry {
|
||||
for s in &import.specifiers {
|
||||
let i = match *s {
|
||||
ImportSpecifier::Specific(ref s) => &s.local,
|
||||
ImportSpecifier::Namespace(..) => unimplemented!(
|
||||
"const modules does not support namespace import yet"
|
||||
),
|
||||
ImportSpecifier::Default(..) => {
|
||||
panic!("const_modules does not support default import")
|
||||
}
|
||||
};
|
||||
let value = entry.get(&i.sym).cloned().unwrap_or_else(|| {
|
||||
panic!(
|
||||
"const_modules: {} does not contain flags named {}",
|
||||
import.src.value, i.sym
|
||||
)
|
||||
});
|
||||
self.scope.value.imported.insert(i.sym.clone(), value);
|
||||
}
|
||||
|
||||
None
|
||||
} else {
|
||||
Some(ModuleItem::ModuleDecl(ModuleDecl::Import(import)))
|
||||
}
|
||||
}
|
||||
_ => Some(item.fold_with(self)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Fold<Expr> for ConstModules {
|
||||
fn fold(&mut self, expr: Expr) -> Expr {
|
||||
let expr = match expr {
|
||||
Expr::Member(expr) => {
|
||||
if expr.computed {
|
||||
Expr::Member(MemberExpr {
|
||||
obj: expr.obj.fold_with(self),
|
||||
prop: expr.prop.fold_with(self),
|
||||
..expr
|
||||
})
|
||||
} else {
|
||||
Expr::Member(MemberExpr {
|
||||
obj: expr.obj.fold_with(self),
|
||||
..expr
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => expr.fold_children(self),
|
||||
};
|
||||
match expr {
|
||||
Expr::Ident(Ident { ref sym, .. }) => {
|
||||
// It's ok because we don't recurse into member expressions.
|
||||
if let Some(value) = self.scope.imported.get(sym) {
|
||||
return (**value).clone();
|
||||
} else {
|
||||
expr
|
||||
}
|
||||
}
|
||||
_ => expr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::tests::Tester;
|
||||
|
||||
fn tr(tester: &mut Tester, sources: &[(&str, &[(&str, &str)])]) -> impl Fold<Module> {
|
||||
let mut m = FxHashMap::default();
|
||||
|
||||
for (src, values) in sources {
|
||||
let values = values
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
let mut v = tester
|
||||
.apply_transform(
|
||||
::testing::DropSpan,
|
||||
"global.js",
|
||||
::swc_ecma_parser::Syntax::default(),
|
||||
v,
|
||||
)
|
||||
.unwrap();
|
||||
let v = match v.body.pop().unwrap() {
|
||||
ModuleItem::Stmt(Stmt::Expr(box expr)) => expr,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
((*k).into(), Arc::new(v))
|
||||
})
|
||||
.collect();
|
||||
|
||||
m.insert((*src).into(), values);
|
||||
}
|
||||
|
||||
const_modules(m)
|
||||
}
|
||||
|
||||
test!(
|
||||
::swc_ecma_parser::Syntax::default(),
|
||||
|tester| tr(tester, &[("@ember/env-flags", &[("DEBUG", "true")])]),
|
||||
simple_flags,
|
||||
r#"import { DEBUG } from '@ember/env-flags';
|
||||
if (DEBUG) {
|
||||
console.log('Foo!');
|
||||
}"#,
|
||||
r#"
|
||||
if (true) {
|
||||
console.log('Foo!');
|
||||
}"#
|
||||
);
|
||||
|
||||
test!(
|
||||
::swc_ecma_parser::Syntax::default(),
|
||||
|tester| tr(
|
||||
tester,
|
||||
&[
|
||||
("@ember/env-flags", &[("DEBUG", "true")]),
|
||||
(
|
||||
"@ember/features",
|
||||
&[("FEATURE_A", "false"), ("FEATURE_B", "true")]
|
||||
)
|
||||
]
|
||||
),
|
||||
complex_multiple,
|
||||
"
|
||||
import { DEBUG } from '@ember/env-flags';
|
||||
import { FEATURE_A, FEATURE_B } from '@ember/features';
|
||||
if (DEBUG) {
|
||||
console.log('Foo!');
|
||||
}
|
||||
|
||||
let woot;
|
||||
if (FEATURE_A) {
|
||||
woot = () => 'woot';
|
||||
} else if (FEATURE_B) {
|
||||
woot = () => 'toow';
|
||||
}
|
||||
",
|
||||
"
|
||||
if (true) {
|
||||
console.log('Foo!');
|
||||
}
|
||||
|
||||
let woot;
|
||||
if (false) {
|
||||
woot = () => 'woot';
|
||||
} else if (true) {
|
||||
woot = () => 'toow';
|
||||
}
|
||||
"
|
||||
);
|
||||
}
|
@ -1,19 +1,31 @@
|
||||
use ast::*;
|
||||
use std::collections::HashMap;
|
||||
use fxhash::FxHashMap;
|
||||
use swc_atoms::JsWord;
|
||||
use swc_common::{Fold, FoldWith};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InlineGlobals {
|
||||
pub envs: HashMap<JsWord, Expr>,
|
||||
pub globals: HashMap<JsWord, Expr>,
|
||||
pub envs: FxHashMap<JsWord, Expr>,
|
||||
pub globals: FxHashMap<JsWord, Expr>,
|
||||
}
|
||||
|
||||
impl Fold<Expr> for InlineGlobals {
|
||||
fn fold(&mut self, expr: Expr) -> Expr {
|
||||
let expr = match expr {
|
||||
// Don't recurese into member expression.
|
||||
Expr::Member(..) => expr,
|
||||
Expr::Member(expr) => {
|
||||
if expr.computed {
|
||||
Expr::Member(MemberExpr {
|
||||
obj: expr.obj.fold_with(self),
|
||||
prop: expr.prop.fold_with(self),
|
||||
..expr
|
||||
})
|
||||
} else {
|
||||
Expr::Member(MemberExpr {
|
||||
obj: expr.obj.fold_with(self),
|
||||
..expr
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => expr.fold_children(self),
|
||||
};
|
||||
|
||||
@ -21,7 +33,7 @@ impl Fold<Expr> for InlineGlobals {
|
||||
Expr::Ident(Ident { ref sym, .. }) => {
|
||||
// It's ok because we don't recurse into member expressions.
|
||||
if let Some(value) = self.globals.get(sym) {
|
||||
return value.clone();
|
||||
return value.clone().fold_with(self);
|
||||
} else {
|
||||
expr
|
||||
}
|
||||
@ -82,8 +94,8 @@ mod tests {
|
||||
tester: &mut crate::tests::Tester,
|
||||
values: &[(&str, &str)],
|
||||
is_env: bool,
|
||||
) -> HashMap<JsWord, Expr> {
|
||||
let mut m = HashMap::new();
|
||||
) -> FxHashMap<JsWord, Expr> {
|
||||
let mut m = FxHashMap::default();
|
||||
|
||||
for (k, v) in values {
|
||||
let v = if is_env {
|
||||
@ -112,14 +124,14 @@ mod tests {
|
||||
m
|
||||
}
|
||||
|
||||
fn envs(tester: &mut crate::tests::Tester, values: &[(&str, &str)]) -> HashMap<JsWord, Expr> {
|
||||
fn envs(tester: &mut crate::tests::Tester, values: &[(&str, &str)]) -> FxHashMap<JsWord, Expr> {
|
||||
mk_map(tester, values, true)
|
||||
}
|
||||
|
||||
fn globals(
|
||||
tester: &mut crate::tests::Tester,
|
||||
values: &[(&str, &str)],
|
||||
) -> HashMap<JsWord, Expr> {
|
||||
) -> FxHashMap<JsWord, Expr> {
|
||||
mk_map(tester, values, false)
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,8 @@ extern crate serde;
|
||||
extern crate unicode_xid;
|
||||
|
||||
pub use self::{
|
||||
fixer::fixer, hygiene::hygiene, inline_globals::InlineGlobals, simplify::simplifier,
|
||||
const_modules::const_modules, fixer::fixer, hygiene::hygiene, inline_globals::InlineGlobals,
|
||||
simplify::simplifier,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
@ -57,6 +58,7 @@ mod macros;
|
||||
#[macro_use]
|
||||
mod hygiene;
|
||||
pub mod compat;
|
||||
mod const_modules;
|
||||
mod fixer;
|
||||
mod inline_globals;
|
||||
pub mod modules;
|
||||
|
Loading…
Reference in New Issue
Block a user