mirror of
https://github.com/swc-project/swc.git
synced 2024-12-25 06:36:08 +03:00
feat(es/transforms): Support namespace imports from const_modules
(#7013)
This commit is contained in:
parent
b7cef34637
commit
3cb03d5299
@ -1,20 +1,28 @@
|
|||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use swc_atoms::JsWord;
|
use swc_atoms::JsWord;
|
||||||
use swc_common::{errors::HANDLER, sync::Lrc, util::move_map::MoveMap, FileName, SourceMap};
|
use swc_common::{
|
||||||
|
errors::HANDLER,
|
||||||
|
sync::Lrc,
|
||||||
|
util::{move_map::MoveMap, take::Take},
|
||||||
|
FileName, SourceMap,
|
||||||
|
};
|
||||||
use swc_ecma_ast::*;
|
use swc_ecma_ast::*;
|
||||||
use swc_ecma_parser::parse_file_as_expr;
|
use swc_ecma_parser::parse_file_as_expr;
|
||||||
use swc_ecma_utils::drop_span;
|
use swc_ecma_utils::drop_span;
|
||||||
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith};
|
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
|
||||||
|
|
||||||
pub fn const_modules(
|
pub fn const_modules(
|
||||||
cm: Lrc<SourceMap>,
|
cm: Lrc<SourceMap>,
|
||||||
globals: FxHashMap<JsWord, FxHashMap<JsWord, String>>,
|
globals: FxHashMap<JsWord, FxHashMap<JsWord, String>>,
|
||||||
) -> impl Fold {
|
) -> impl Fold {
|
||||||
ConstModules {
|
as_folder(ConstModules {
|
||||||
globals: globals
|
globals: globals
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(src, map)| {
|
.map(|(src, map)| {
|
||||||
@ -31,7 +39,7 @@ pub fn const_modules(
|
|||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
scope: Default::default(),
|
scope: Default::default(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_option(cm: &SourceMap, name: &str, src: String) -> Arc<Expr> {
|
fn parse_option(cm: &SourceMap, name: &str, src: String) -> Arc<Expr> {
|
||||||
@ -77,36 +85,45 @@ struct ConstModules {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Scope {
|
struct Scope {
|
||||||
|
namespace: HashSet<Id>,
|
||||||
imported: HashMap<JsWord, Arc<Expr>>,
|
imported: HashMap<JsWord, Arc<Expr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: VisitMut
|
impl VisitMut for ConstModules {
|
||||||
impl Fold for ConstModules {
|
noop_visit_mut_type!();
|
||||||
noop_fold_type!();
|
|
||||||
|
|
||||||
fn fold_module_items(&mut self, items: Vec<ModuleItem>) -> Vec<ModuleItem> {
|
fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
|
||||||
items.move_flat_map(|item| match item {
|
*n = n.take().move_flat_map(|item| match item {
|
||||||
ModuleItem::ModuleDecl(ModuleDecl::Import(import)) => {
|
ModuleItem::ModuleDecl(ModuleDecl::Import(import)) => {
|
||||||
let entry = self.globals.get(&import.src.value);
|
let entry = self.globals.get(&import.src.value);
|
||||||
|
|
||||||
if let Some(entry) = entry {
|
if let Some(entry) = entry {
|
||||||
for s in &import.specifiers {
|
for s in &import.specifiers {
|
||||||
let i = match *s {
|
match *s {
|
||||||
ImportSpecifier::Named(ref s) => &s.local,
|
ImportSpecifier::Named(ref s) => {
|
||||||
ImportSpecifier::Namespace(..) => unimplemented!(
|
let imported = s
|
||||||
"const modules does not support namespace import yet"
|
.imported
|
||||||
),
|
.as_ref()
|
||||||
ImportSpecifier::Default(..) => {
|
.map(|m| match m {
|
||||||
panic!("const_modules does not support default import")
|
ModuleExportName::Ident(id) => &id.sym,
|
||||||
}
|
ModuleExportName::Str(s) => &s.value,
|
||||||
};
|
})
|
||||||
let value = entry.get(&i.sym).cloned().unwrap_or_else(|| {
|
.unwrap_or(&s.local.sym);
|
||||||
|
let value = entry.get(imported).cloned().unwrap_or_else(|| {
|
||||||
panic!(
|
panic!(
|
||||||
"const_modules: {} does not contain flags named {}",
|
"The requested const_module `{}` does not provide an \
|
||||||
import.src.value, i.sym
|
export named `{}`",
|
||||||
|
import.src.value, imported
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
self.scope.imported.insert(i.sym.clone(), value);
|
self.scope.imported.insert(imported.clone(), value);
|
||||||
|
}
|
||||||
|
ImportSpecifier::Namespace(ref s) => {
|
||||||
|
self.scope.namespace.insert(s.local.to_id());
|
||||||
|
}
|
||||||
|
ImportSpecifier::Default(..) => {
|
||||||
|
panic!("const_module does not support default import")
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
@ -114,42 +131,68 @@ impl Fold for ConstModules {
|
|||||||
Some(ModuleItem::ModuleDecl(ModuleDecl::Import(import)))
|
Some(ModuleItem::ModuleDecl(ModuleDecl::Import(import)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Some(item.fold_with(self)),
|
_ => Some(item),
|
||||||
})
|
});
|
||||||
|
|
||||||
|
if self.scope.imported.is_empty() && self.scope.namespace.is_empty() {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_expr(&mut self, expr: Expr) -> Expr {
|
n.iter_mut().for_each(|item| {
|
||||||
let expr = match expr {
|
if let ModuleItem::Stmt(stmt) = item {
|
||||||
Expr::Member(expr) => Expr::Member(MemberExpr {
|
stmt.visit_mut_with(self);
|
||||||
obj: expr.obj.fold_with(self),
|
}
|
||||||
prop: if let MemberProp::Computed(c) = expr.prop {
|
});
|
||||||
MemberProp::Computed(c.fold_with(self))
|
}
|
||||||
} else {
|
|
||||||
expr.prop
|
|
||||||
},
|
|
||||||
..expr
|
|
||||||
}),
|
|
||||||
|
|
||||||
Expr::SuperProp(expr) => Expr::SuperProp(SuperPropExpr {
|
fn visit_mut_expr(&mut self, n: &mut Expr) {
|
||||||
prop: if let SuperProp::Computed(c) = expr.prop {
|
match n {
|
||||||
SuperProp::Computed(c.fold_with(self))
|
Expr::Ident(ref id @ Ident { ref sym, .. }) => {
|
||||||
} else {
|
|
||||||
expr.prop
|
|
||||||
},
|
|
||||||
..expr
|
|
||||||
}),
|
|
||||||
_ => expr.fold_children_with(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) {
|
if let Some(value) = self.scope.imported.get(sym) {
|
||||||
(**value).clone()
|
*n = (**value).clone();
|
||||||
} else {
|
return;
|
||||||
expr
|
}
|
||||||
|
|
||||||
|
if let Some(..) = self.scope.namespace.get(&id.to_id()) {
|
||||||
|
panic!(
|
||||||
|
"The const_module namespace `{}` cannot be used without member accessor",
|
||||||
|
sym
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => expr,
|
Expr::Member(MemberExpr { obj, prop, .. }) if obj.is_ident() => {
|
||||||
|
let member_obj = obj.as_ident().unwrap();
|
||||||
|
|
||||||
|
if self.scope.namespace.contains(&member_obj.to_id()) {
|
||||||
|
let module_name = &member_obj.sym;
|
||||||
|
|
||||||
|
let imported_name = match prop {
|
||||||
|
MemberProp::Ident(ref id) => &id.sym,
|
||||||
|
MemberProp::Computed(ref p) => match &*p.expr {
|
||||||
|
Expr::Lit(Lit::Str(s)) => &s.value,
|
||||||
|
_ => return,
|
||||||
|
},
|
||||||
|
MemberProp::PrivateName(..) => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let value = self
|
||||||
|
.globals
|
||||||
|
.get(module_name)
|
||||||
|
.and_then(|entry| entry.get(imported_name))
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
panic!(
|
||||||
|
"The requested const_module `{}` does not provide an export named \
|
||||||
|
`{}`",
|
||||||
|
module_name, imported_name
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
*n = (**value).clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
e => {
|
||||||
|
e.visit_mut_children_with(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,23 @@ test!(
|
|||||||
}"#
|
}"#
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test!(
|
||||||
|
::swc_ecma_parser::Syntax::default(),
|
||||||
|
|tester| tr(tester, &[("@ember/env-flags", &[("DEBUG", "true")])]),
|
||||||
|
imports_hoisted,
|
||||||
|
r#"
|
||||||
|
if (DEBUG) {
|
||||||
|
console.log('Foo!');
|
||||||
|
}
|
||||||
|
|
||||||
|
import { DEBUG } from '@ember/env-flags';
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
if (true) {
|
||||||
|
console.log('Foo!');
|
||||||
|
}"#
|
||||||
|
);
|
||||||
|
|
||||||
test!(
|
test!(
|
||||||
::swc_ecma_parser::Syntax::default(),
|
::swc_ecma_parser::Syntax::default(),
|
||||||
|tester| tr(
|
|tester| tr(
|
||||||
@ -73,3 +90,29 @@ if (false) {
|
|||||||
}
|
}
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test!(
|
||||||
|
::swc_ecma_parser::Syntax::default(),
|
||||||
|
|tester| tr(tester, &[("foo", &[("bar", "true")])]),
|
||||||
|
namespace_import,
|
||||||
|
r#"
|
||||||
|
import * as foo from 'foo';
|
||||||
|
console.log(foo.bar)
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
console.log(true);
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
test!(
|
||||||
|
::swc_ecma_parser::Syntax::default(),
|
||||||
|
|tester| tr(tester, &[("foo", &[("bar", "true")])]),
|
||||||
|
namespace_import_computed,
|
||||||
|
r#"
|
||||||
|
import * as foo from 'foo';
|
||||||
|
console.log(foo["bar"])
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
console.log(true);
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user