refactor(es/module): Reimplement some functions of module/typescript (#8063)

This commit is contained in:
magic-akari 2023-10-05 04:15:27 -05:00 committed by GitHub
parent 70ec648a63
commit 3e5b062cd2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 406 additions and 368 deletions

View File

@ -5,20 +5,22 @@ use swc_atoms::{js_word, JsWord};
use swc_common::{
comments::{CommentKind, Comments},
util::take::Take,
FileName, Mark, Span, Spanned, DUMMY_SP,
FileName, Mark, Span, DUMMY_SP,
};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::{feature::FeatureFlag, helper_expr};
use swc_ecma_utils::{
member_expr, private_ident, quote_ident, quote_str, ExprFactory, FunctionFactory, IsDirective,
member_expr, private_ident, quote_ident, quote_str, undefined, ExprFactory, FunctionFactory,
IsDirective,
};
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
pub use super::util::Config as InnerConfig;
use crate::{
module_decl_strip::{Export, Link, LinkFlag, LinkItem, LinkSpecifierReducer, ModuleDeclStrip},
module_ref_rewriter::{ImportMap, ModuleRefRewriter},
module_ref_rewriter::{rewrite_import_bindings, ImportMap},
path::{ImportResolver, Resolver},
top_level_this::top_level_this,
util::{
define_es_module, emit_export_stmts, local_name_for_src, use_strict, ImportInterop,
VecStmtLike,
@ -130,17 +132,10 @@ where
noop_visit_mut_type!();
fn visit_mut_module(&mut self, n: &mut Module) {
if let Some(first) = n.body.first() {
if self.module_id.is_none() {
self.module_id = self.get_amd_module_id_from_comments(first.span());
}
if self.module_id.is_none() {
self.module_id = self.get_amd_module_id_from_comments(n.span);
}
let import_interop = self.config.import_interop();
let mut strip = ModuleDeclStrip::new(self.const_var_kind);
n.body.visit_mut_with(&mut strip);
let mut stmts: Vec<Stmt> = Vec::with_capacity(n.body.len() + 4);
// Collect directives
@ -158,6 +153,15 @@ where
stmts.push(use_strict());
}
if !self.config.allow_top_level_this {
top_level_this(&mut n.body, *undefined(DUMMY_SP));
}
let import_interop = self.config.import_interop();
let mut strip = ModuleDeclStrip::new(self.const_var_kind);
n.body.visit_mut_with(&mut strip);
let ModuleDeclStrip {
link,
export,
@ -197,11 +201,7 @@ where
stmts.visit_mut_children_with(self);
}
stmts.visit_mut_children_with(&mut ModuleRefRewriter::new(
import_map,
Default::default(),
self.config.allow_top_level_this,
));
rewrite_import_bindings(&mut stmts, import_map, Default::default());
// ====================
// Emit
@ -348,7 +348,7 @@ where
let mut stmts = Vec::with_capacity(link.len());
let mut export_obj_prop_list = export.into_iter().map(From::from).collect();
let mut export_obj_prop_list = export.into_iter().collect();
link.into_iter().for_each(
|(src, LinkItem(src_span, link_specifier_set, mut link_flag))| {
@ -429,7 +429,7 @@ where
let mut export_stmts = Default::default();
if !export_obj_prop_list.is_empty() && !is_export_assign {
export_obj_prop_list.sort_by_cached_key(|v| v.key().clone());
export_obj_prop_list.sort_by_cached_key(|(key, ..)| key.clone());
let exports = self.exports();

View File

@ -1,23 +1,26 @@
use swc_atoms::js_word;
use swc_common::{
collections::AHashSet, comments::Comments, util::take::Take, FileName, Mark, Span, Spanned,
DUMMY_SP,
collections::AHashSet, comments::Comments, util::take::Take, FileName, Mark, Span, DUMMY_SP,
};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::{feature::FeatureFlag, helper_expr};
use swc_ecma_utils::{
member_expr, private_ident, quote_expr, quote_ident, ExprFactory, FunctionFactory, IsDirective,
member_expr, private_ident, quote_expr, quote_ident, undefined, ExprFactory, FunctionFactory,
IsDirective,
};
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
pub use super::util::Config;
use crate::{
module_decl_strip::{Export, Link, LinkFlag, LinkItem, LinkSpecifierReducer, ModuleDeclStrip},
module_ref_rewriter::{ImportMap, ModuleRefRewriter},
module_decl_strip::{
Export, ExportKV, Link, LinkFlag, LinkItem, LinkSpecifierReducer, ModuleDeclStrip,
},
module_ref_rewriter::{rewrite_import_bindings, ImportMap},
path::{ImportResolver, Resolver},
top_level_this::top_level_this,
util::{
define_es_module, emit_export_stmts, local_name_for_src, prop_name, use_strict,
ImportInterop, ObjPropKeyIdent, VecStmtLike,
ImportInterop, VecStmtLike,
},
};
@ -91,6 +94,26 @@ where
noop_visit_mut_type!();
fn visit_mut_module(&mut self, n: &mut Module) {
let mut stmts: Vec<ModuleItem> = Vec::with_capacity(n.body.len() + 6);
// Collect directives
stmts.extend(
&mut n
.body
.iter_mut()
.take_while(|i| i.directive_continue())
.map(|i| i.take()),
);
// "use strict";
if self.config.strict_mode && !stmts.has_use_strict() {
stmts.push(use_strict().into());
}
if !self.config.allow_top_level_this {
top_level_this(&mut n.body, *undefined(DUMMY_SP));
}
let import_interop = self.config.import_interop();
let mut module_map = Default::default();
@ -111,22 +134,6 @@ where
let mut strip = ModuleDeclStrip::new(self.const_var_kind);
n.body.visit_mut_with(&mut strip);
let mut stmts: Vec<ModuleItem> = Vec::with_capacity(n.body.len() + 6);
// Collect directives
stmts.extend(
&mut n
.body
.iter_mut()
.take_while(|i| i.directive_continue())
.map(|i| i.take()),
);
// "use strict";
if self.config.strict_mode && !stmts.has_use_strict() {
stmts.push(use_strict().into());
}
let ModuleDeclStrip {
link,
export,
@ -181,11 +188,7 @@ where
stmts.visit_mut_children_with(self);
}
stmts.visit_mut_children_with(&mut ModuleRefRewriter::new(
module_map,
lazy_record,
self.config.allow_top_level_this,
));
rewrite_import_bindings(&mut stmts, module_map, lazy_record);
n.body = stmts;
}
@ -271,7 +274,7 @@ where
let mut stmts = Vec::with_capacity(link.len());
let mut export_obj_prop_list = export.into_iter().map(From::from).collect();
let mut export_obj_prop_list = export.into_iter().collect();
let lexer_reexport = if export_interop_annotation {
self.emit_lexer_reexport(&link)
@ -364,7 +367,7 @@ where
let mut export_stmts: Vec<Stmt> = Default::default();
if !export_obj_prop_list.is_empty() && !is_export_assign {
export_obj_prop_list.sort_by_cached_key(|v| v.key().clone());
export_obj_prop_list.sort_by_cached_key(|(key, ..)| key.clone());
let mut features = self.available_features;
let exports = self.exports();
@ -457,17 +460,17 @@ where
/// 0 && (exports.foo = 0);
/// 0 && (module.exports = { foo: _, bar: _ });
/// ```
fn emit_lexer_exports_init(&mut self, export_id_list: &[ObjPropKeyIdent]) -> Option<Stmt> {
fn emit_lexer_exports_init(&mut self, export_id_list: &[ExportKV]) -> Option<Stmt> {
match export_id_list.len() {
0 => None,
1 => {
let expr: Expr = 0.into();
let key_value = &export_id_list[0];
let prop = prop_name(key_value.key(), DUMMY_SP).into();
let (key, export_item) = &export_id_list[0];
let prop = prop_name(key, DUMMY_SP).into();
let export_binding = MemberExpr {
obj: Box::new(self.exports().into()),
span: key_value.span(),
span: export_item.export_name_span(),
prop,
};
let expr = expr.make_assign_to(op!("="), export_binding.as_pat_or_expr());
@ -483,7 +486,7 @@ where
_ => {
let props = export_id_list
.iter()
.map(|key_value| prop_name(key_value.key(), DUMMY_SP))
.map(|(key, ..)| prop_name(key, DUMMY_SP))
.map(|key| KeyValueProp {
key: key.into(),
// `cjs-module-lexer` only support identifier as value

View File

@ -17,6 +17,7 @@ pub(crate) mod module_ref_rewriter;
pub mod path;
pub mod rewriter;
pub mod system_js;
mod top_level_this;
pub mod umd;
#[derive(Debug, Default, Clone, Serialize, Deserialize)]

View File

@ -9,10 +9,12 @@ use swc_ecma_ast::*;
use swc_ecma_utils::{find_pat_ids, ident::IdentLike, private_ident, quote_ident, ExprFactory};
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
use crate::{module_ref_rewriter::ImportMap, util::ObjPropKeyIdent};
use crate::module_ref_rewriter::ImportMap;
/// key: module path
pub type Link = IndexMap<JsWord, LinkItem>;
pub type Export = AHashMap<(JsWord, Span), Ident>;
/// key: export binding name
pub type Export = AHashMap<JsWord, ExportItem>;
#[derive(Debug)]
pub struct ModuleDeclStrip {
@ -135,17 +137,18 @@ impl VisitMut for ModuleDeclStrip {
fn visit_mut_export_decl(&mut self, n: &mut ExportDecl) {
match &n.decl {
Decl::Class(ClassDecl { ident, .. }) | Decl::Fn(FnDecl { ident, .. }) => {
let ident = ident.clone();
self.export.insert((ident.sym.clone(), ident.span), ident);
self.export.insert(
ident.sym.clone(),
ExportItem::new(ident.span, ident.clone()),
);
}
Decl::Var(v) => {
self.export
.extend(find_pat_ids::<_, Ident>(&v.decls).into_iter().map(|id| {
let ident = id.clone();
((id.sym, id.span), ident)
}));
self.export.extend(
find_pat_ids::<_, Ident>(&v.decls)
.into_iter()
.map(|id| (id.sym.clone(), ExportItem::new(id.span, id))),
);
}
_ => {}
};
@ -189,15 +192,14 @@ impl VisitMut for ModuleDeclStrip {
};
if let Some(exported) = exported {
let exported = match exported {
let (export_name, export_name_span) = match exported {
ModuleExportName::Ident(Ident { span, sym, .. }) => (sym, span),
ModuleExportName::Str(Str { span, value, .. }) => (value, span),
};
(exported, orig)
(export_name, ExportItem::new(export_name_span, orig))
} else {
let exported = orig.sym.clone();
((exported, orig.span), orig)
(orig.sym.clone(), ExportItem::new(orig.span, orig))
}
}
}))
@ -225,7 +227,8 @@ impl VisitMut for ModuleDeclStrip {
.get_or_insert_with(|| private_ident!(n.span, "_default"))
.clone();
self.export.insert((js_word!("default"), n.span), ident);
self.export
.insert(js_word!("default"), ExportItem::new(n.span, ident));
}
DefaultDecl::Fn(fn_expr) => {
let ident = fn_expr
@ -233,7 +236,8 @@ impl VisitMut for ModuleDeclStrip {
.get_or_insert_with(|| private_ident!(n.span, "_default"))
.clone();
self.export.insert((js_word!("default"), n.span), ident);
self.export
.insert(js_word!("default"), ExportItem::new(n.span, ident));
}
DefaultDecl::TsInterfaceDecl(_) => {}
}
@ -252,7 +256,7 @@ impl VisitMut for ModuleDeclStrip {
let ident = private_ident!(n.span, "_default");
self.export
.insert((js_word!("default"), n.span), ident.clone());
.insert(js_word!("default"), ExportItem::new(n.span, ident.clone()));
self.export_default = Some(Stmt::Decl(
n.expr
@ -306,7 +310,8 @@ impl VisitMut for ModuleDeclStrip {
}) = module_ref
{
if *is_export {
self.export.insert((id.sym.clone(), id.span), id.clone());
self.export
.insert(id.sym.clone(), ExportItem::new(id.span, id.clone()));
}
self.link
@ -623,7 +628,7 @@ impl LinkItem {
}
}
pub(crate) type ExportObjPropList = Vec<ObjPropKeyIdent>;
pub(crate) type ExportObjPropList = Vec<ExportKV>;
/// Reduce self to generate ImportMap and ExportObjPropList
pub(crate) trait LinkSpecifierReducer {
@ -687,10 +692,13 @@ impl LinkSpecifierReducer for AHashSet<LinkSpecifier> {
// foo -> mod.foo
import_map.insert(orig.to_id(), (mod_ident.clone(), Some(orig.0.clone())));
let exported = exported.unwrap_or_else(|| orig.clone());
let (export_name, export_name_span) = exported.unwrap_or_else(|| orig.clone());
// bar -> foo
export_obj_prop_list.push((exported, quote_ident!(orig.1, orig.0)).into())
export_obj_prop_list.push((
export_name,
ExportItem::new(export_name_span, quote_ident!(orig.1, orig.0)),
))
}
LinkSpecifier::ExportDefaultAs(_, key, span) => {
*ref_to_mod_ident = true;
@ -702,20 +710,19 @@ impl LinkSpecifierReducer for AHashSet<LinkSpecifier> {
// export { foo };
// ```
let exported = (key.clone(), span);
// foo -> mod.default
import_map.insert(
exported.to_id(),
(key.clone(), span).to_id(),
(mod_ident.clone(), Some("default".into())),
);
export_obj_prop_list.push((exported, quote_ident!(span, key)).into())
export_obj_prop_list
.push((key.clone(), ExportItem::new(span, quote_ident!(span, key))));
}
LinkSpecifier::ExportStarAs(key, span) => {
*ref_to_mod_ident = true;
export_obj_prop_list.push((key, span, mod_ident.clone()).into())
export_obj_prop_list.push((key, ExportItem::new(span, mod_ident.clone())));
}
LinkSpecifier::ExportStar => {}
LinkSpecifier::ImportEqual(id) => {
@ -733,3 +740,22 @@ impl LinkSpecifierReducer for AHashSet<LinkSpecifier> {
})
}
}
#[derive(Debug)]
pub struct ExportItem(Span, Ident);
impl ExportItem {
pub fn new(export_name_span: Span, local_ident: Ident) -> Self {
Self(export_name_span, local_ident)
}
pub fn export_name_span(&self) -> Span {
self.0
}
pub fn into_local_ident(self) -> Ident {
self.1
}
}
pub type ExportKV = (JsWord, ExportItem);

View File

@ -1,19 +1,18 @@
use swc_atoms::JsWord;
use swc_common::{
collections::{AHashMap, AHashSet},
util::take::Take,
SyntaxContext, DUMMY_SP,
};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::helpers::HELPERS;
use swc_ecma_utils::{undefined, ExprFactory, IntoIndirectCall};
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
use swc_ecma_utils::{ExprFactory, QueryRef, RefRewriter};
use swc_ecma_visit::VisitMutWith;
use crate::util::prop_name;
pub type ImportMap = AHashMap<Id, (Ident, Option<JsWord>)>;
pub(crate) struct ModuleRefRewriter {
pub(crate) struct ImportQuery {
/// ```javascript
/// import foo, { a as b, c } from "mod";
/// import * as x from "x";
@ -32,167 +31,18 @@ pub(crate) struct ModuleRefRewriter {
/// x => (_x, None),
/// )
/// ```
pub import_map: ImportMap,
pub lazy_record: AHashSet<Id>,
pub allow_top_level_this: bool,
is_global_this: bool,
import_map: ImportMap,
lazy_record: AHashSet<Id>,
helper_ctxt: Option<SyntaxContext>,
}
impl ModuleRefRewriter {
pub fn new(
import_map: ImportMap,
lazy_record: AHashSet<Id>,
allow_top_level_this: bool,
) -> Self {
Self {
import_map,
lazy_record,
allow_top_level_this,
is_global_this: true,
helper_ctxt: {
HELPERS
.is_set()
.then(|| HELPERS.with(|helper| helper.mark()))
.map(|mark| SyntaxContext::empty().apply_mark(mark))
},
}
}
}
impl VisitMut for ModuleRefRewriter {
noop_visit_mut_type!();
/// replace bar in binding pattern
/// const foo = { bar }
fn visit_mut_prop(&mut self, n: &mut Prop) {
match n {
Prop::Shorthand(shorthand) => {
if let Some(expr) = self.map_module_ref_ident(shorthand) {
*n = KeyValueProp {
key: shorthand.take().into(),
value: Box::new(expr),
}
.into()
}
}
_ => n.visit_mut_children_with(self),
}
}
fn visit_mut_expr(&mut self, n: &mut Expr) {
match n {
Expr::Ident(ref_ident) => {
if let Some(expr) = self.map_module_ref_ident(ref_ident) {
*n = expr;
}
}
Expr::This(ThisExpr { span }) => {
if !self.allow_top_level_this && self.is_global_this {
*n = *undefined(*span);
}
}
_ => n.visit_mut_children_with(self),
};
}
fn visit_mut_callee(&mut self, n: &mut Callee) {
match n {
Callee::Expr(e) if e.is_ident() => {
let is_indirect_callee = e
.as_ident()
.filter(|ident| self.helper_ctxt.iter().all(|ctxt| ctxt != &ident.span.ctxt))
.and_then(|ident| self.import_map.get(&ident.to_id()))
.map(|(_, prop)| prop.is_some())
.unwrap_or_default();
e.visit_mut_with(self);
if is_indirect_callee {
*n = n.take().into_indirect()
}
}
_ => n.visit_mut_children_with(self),
}
}
fn visit_mut_tagged_tpl(&mut self, n: &mut TaggedTpl) {
let is_indirect = n
.tag
.as_ident()
.filter(|ident| self.helper_ctxt.iter().all(|ctxt| ctxt != &ident.span.ctxt))
.and_then(|ident| self.import_map.get(&ident.to_id()))
.map(|(_, prop)| prop.is_some())
.unwrap_or_default();
n.visit_mut_children_with(self);
if is_indirect {
*n = n.take().into_indirect()
}
}
fn visit_mut_function(&mut self, n: &mut Function) {
self.visit_mut_with_non_global_this(n);
}
fn visit_mut_constructor(&mut self, n: &mut Constructor) {
self.visit_mut_with_non_global_this(n);
}
fn visit_mut_class_prop(&mut self, n: &mut ClassProp) {
n.key.visit_mut_with(self);
self.visit_mut_with_non_global_this(&mut n.value);
}
fn visit_mut_private_prop(&mut self, n: &mut PrivateProp) {
n.key.visit_mut_with(self);
self.visit_mut_with_non_global_this(&mut n.value);
}
fn visit_mut_getter_prop(&mut self, n: &mut GetterProp) {
n.key.visit_mut_with(self);
self.visit_mut_with_non_global_this(&mut n.body);
}
fn visit_mut_setter_prop(&mut self, n: &mut SetterProp) {
n.key.visit_mut_with(self);
self.visit_mut_with_non_global_this(&mut n.body);
}
fn visit_mut_static_block(&mut self, n: &mut StaticBlock) {
self.visit_mut_with_non_global_this(n);
}
}
impl ModuleRefRewriter {
fn visit_mut_with_non_global_this<T>(&mut self, n: &mut T)
where
T: VisitMutWith<Self>,
{
let top_level = self.is_global_this;
self.is_global_this = false;
n.visit_mut_children_with(self);
self.is_global_this = top_level;
}
fn map_module_ref_ident(&mut self, ref_ident: &Ident) -> Option<Expr> {
impl QueryRef for ImportQuery {
fn query_ref(&self, ident: &Ident) -> Option<Expr> {
self.import_map
.get(&ref_ident.to_id())
.get(&ident.to_id())
.map(|(mod_ident, mod_prop)| -> Expr {
let mut mod_ident = mod_ident.clone();
let span = ref_ident.span.with_ctxt(mod_ident.span.ctxt);
let span = ident.span.with_ctxt(mod_ident.span.ctxt);
mod_ident.span = span;
let mod_expr = if self.lazy_record.contains(&mod_ident.to_id()) {
@ -215,4 +65,43 @@ impl ModuleRefRewriter {
}
})
}
fn query_lhs(&self, _: &Ident) -> Option<Expr> {
// import binding cannot be used as lhs
None
}
fn should_fix_this(&self, ident: &Ident) -> bool {
if self.helper_ctxt.iter().any(|ctxt| ctxt == &ident.span.ctxt) {
return false;
}
self.import_map
.get(&ident.to_id())
.map(|(_, prop)| prop.is_some())
.unwrap_or_default()
}
}
pub(crate) fn rewrite_import_bindings<V>(
node: &mut V,
import_map: ImportMap,
lazy_record: AHashSet<Id>,
) where
V: VisitMutWith<RefRewriter<ImportQuery>>,
{
let mut v = RefRewriter {
query: ImportQuery {
import_map,
lazy_record,
helper_ctxt: {
HELPERS
.is_set()
.then(|| HELPERS.with(|helper| helper.mark()))
.map(|mark| SyntaxContext::empty().apply_mark(mark))
},
},
};
node.visit_mut_with(&mut v);
}

View File

@ -0,0 +1,84 @@
use swc_ecma_ast::*;
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
pub struct TopLevelThis {
found: bool,
this: Expr,
}
pub(crate) fn top_level_this<V>(node: &mut V, replace_with: Expr) -> bool
where
V: VisitMutWith<TopLevelThis>,
{
let mut v = TopLevelThis {
this: replace_with,
found: false,
};
node.visit_mut_with(&mut v);
v.found
}
impl VisitMut for TopLevelThis {
noop_visit_mut_type!();
noop_visit_mut_type!(visit_mut_function, Function);
fn visit_mut_class_member(&mut self, n: &mut ClassMember) {
match n {
ClassMember::Method(ClassMethod {
key: PropName::Computed(computed),
..
})
| ClassMember::ClassProp(ClassProp {
key: PropName::Computed(computed),
..
}) => {
computed.visit_mut_with(self);
}
_ => {}
}
}
fn visit_mut_prop(&mut self, n: &mut Prop) {
match n {
Prop::KeyValue(..) => {
n.visit_mut_children_with(self);
}
Prop::Getter(GetterProp {
key: PropName::Computed(computed),
..
})
| Prop::Setter(SetterProp {
key: PropName::Computed(computed),
..
})
| Prop::Method(MethodProp {
key: PropName::Computed(computed),
..
}) => computed.visit_mut_children_with(self),
_ => {}
}
}
fn visit_mut_expr(&mut self, n: &mut Expr) {
if let Expr::This(ThisExpr { span }) = n {
self.found = true;
let mut this = self.this.clone();
match &mut this {
// for void 0
Expr::Unary(unary_expr) => unary_expr.span = *span,
Expr::Ident(ident) => ident.span = *span,
Expr::Member(member) => member.span = *span,
_ => {
unimplemented!();
}
}
*n = this;
} else {
n.visit_mut_children_with(self);
}
}
}

View File

@ -6,7 +6,7 @@ use swc_common::{
use swc_ecma_ast::*;
use swc_ecma_transforms_base::{feature::FeatureFlag, helper_expr};
use swc_ecma_utils::{
is_valid_prop_ident, private_ident, quote_ident, quote_str, ExprFactory, IsDirective,
is_valid_prop_ident, private_ident, quote_ident, quote_str, undefined, ExprFactory, IsDirective,
};
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
@ -14,8 +14,9 @@ use self::config::BuiltConfig;
pub use self::config::Config;
use crate::{
module_decl_strip::{Export, Link, LinkFlag, LinkItem, LinkSpecifierReducer, ModuleDeclStrip},
module_ref_rewriter::{ImportMap, ModuleRefRewriter},
module_ref_rewriter::{rewrite_import_bindings, ImportMap},
path::{ImportResolver, Resolver},
top_level_this::top_level_this,
util::{
define_es_module, emit_export_stmts, local_name_for_src, use_strict, ImportInterop,
VecStmtLike,
@ -107,13 +108,8 @@ where
noop_visit_mut_type!();
fn visit_mut_module(&mut self, module: &mut Module) {
let import_interop = self.config.config.import_interop();
let module_items = &mut module.body;
let mut strip = ModuleDeclStrip::new(self.const_var_kind);
module_items.visit_mut_with(&mut strip);
let mut stmts: Vec<Stmt> = Vec::with_capacity(module_items.len() + 4);
// Collect directives
@ -130,6 +126,15 @@ where
stmts.push(use_strict());
}
if !self.config.config.allow_top_level_this {
top_level_this(module_items, *undefined(DUMMY_SP));
}
let import_interop = self.config.config.import_interop();
let mut strip = ModuleDeclStrip::new(self.const_var_kind);
module_items.visit_mut_with(&mut strip);
let ModuleDeclStrip {
link,
export,
@ -165,11 +170,7 @@ where
stmts.push(return_stmt.into())
}
stmts.visit_mut_children_with(&mut ModuleRefRewriter::new(
import_map,
Default::default(),
self.config.config.allow_top_level_this,
));
rewrite_import_bindings(&mut stmts, import_map, Default::default());
// ====================
// Emit
@ -220,7 +221,7 @@ where
let mut stmts = Vec::with_capacity(link.len());
let mut export_obj_prop_list = export.into_iter().map(From::from).collect();
let mut export_obj_prop_list = export.into_iter().collect();
link.into_iter().for_each(
|(src, LinkItem(src_span, link_specifier_set, mut link_flag))| {
@ -301,7 +302,7 @@ where
let mut export_stmts = Default::default();
if !export_obj_prop_list.is_empty() && !is_export_assign {
export_obj_prop_list.sort_by_cached_key(|v| v.key().clone());
export_obj_prop_list.sort_by_cached_key(|(key, ..)| key.clone());
let exports = self.exports();

View File

@ -2,13 +2,15 @@ use is_macro::Is;
use serde::{Deserialize, Serialize};
use swc_atoms::{js_word, JsWord};
use swc_cached::regex::CachedRegex;
use swc_common::{Span, Spanned, DUMMY_SP};
use swc_common::{Span, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_utils::{
is_valid_prop_ident, member_expr, private_ident, quote_ident, quote_str, ExprFactory,
FunctionFactory, IsDirective,
};
use crate::module_decl_strip::{ExportItem, ExportKV};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct Config {
@ -324,15 +326,19 @@ pub(crate) fn esm_export() -> Function {
}
}
pub(crate) fn emit_export_stmts(exports: Ident, mut prop_list: Vec<ObjPropKeyIdent>) -> Vec<Stmt> {
pub(crate) fn emit_export_stmts(exports: Ident, mut prop_list: Vec<ExportKV>) -> Vec<Stmt> {
match prop_list.len() {
0 | 1 => prop_list
.pop()
.map(|obj_prop| {
.map(|(export_name, export_item)| {
object_define_enumerable(
exports.as_arg(),
quote_str!(obj_prop.span(), obj_prop.key()).as_arg(),
prop_function((js_word!("get"), DUMMY_SP, obj_prop.2.clone()).into()).into(),
quote_str!(export_item.export_name_span(), export_name).as_arg(),
prop_function((
js_word!("get"),
ExportItem::new(DUMMY_SP, export_item.into_local_ident()),
))
.into(),
)
.into_stmt()
})
@ -397,39 +403,6 @@ impl From<IdentOrStr> for MemberProp {
}
}
/// {
/// "key": ident,
/// }
pub(crate) struct ObjPropKeyIdent(JsWord, Span, Ident);
impl From<((JsWord, Span), Ident)> for ObjPropKeyIdent {
fn from(((key, span), ident): ((JsWord, Span), Ident)) -> Self {
Self(key, span, ident)
}
}
impl From<(JsWord, Span, Ident)> for ObjPropKeyIdent {
fn from((key, span, ident): (JsWord, Span, Ident)) -> Self {
Self(key, span, ident)
}
}
impl Spanned for ObjPropKeyIdent {
fn span(&self) -> Span {
self.1
}
}
impl ObjPropKeyIdent {
pub fn key(&self) -> &JsWord {
&self.0
}
pub fn into_expr(self) -> Expr {
self.2.into()
}
}
/// ```javascript
/// {
/// key: function() {
@ -437,13 +410,14 @@ impl ObjPropKeyIdent {
/// },
/// }
/// ```
pub(crate) fn prop_function(prop: ObjPropKeyIdent) -> Prop {
let key = prop_name(prop.key(), prop.span()).into();
pub(crate) fn prop_function((key, export_item): ExportKV) -> Prop {
let key = prop_name(&key, export_item.export_name_span()).into();
KeyValueProp {
key,
value: Box::new(
prop.into_expr()
export_item
.into_local_ident()
.into_lazy_fn(Default::default())
.into_fn_expr(None)
.into(),

View File

@ -7,12 +7,10 @@ use swc_common::{
};
use swc_ecma_ast::*;
use swc_ecma_utils::{
alias_ident_for, constructor::inject_after_super, is_literal, member_expr, private_ident,
quote_ident, quote_str, ExprFactory,
};
use swc_ecma_visit::{
as_folder, noop_visit_mut_type, noop_visit_type, Fold, Visit, VisitMut, VisitMutWith, VisitWith,
alias_ident_for, constructor::inject_after_super, find_pat_ids, is_literal, member_expr,
private_ident, quote_ident, quote_str, ExprFactory, QueryRef, RefRewriter,
};
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
use crate::{
config::TsImportExportAssignConfig,
@ -468,10 +466,7 @@ impl Transform {
}
if !mutable_export_ids.is_empty() {
stmts.visit_mut_with(&mut ExportRefRewrriter {
namesapce_id: id,
export_id_list: mutable_export_ids,
});
rewrite_export_bindings(&mut stmts, id, mutable_export_ids);
}
BlockStmt { span, stmts }
@ -638,9 +633,7 @@ impl Transform {
) -> Option<Stmt> {
debug_assert!(!var_decl.declare);
let mut collector = ExportedIdentCollector::default();
var_decl.visit_with(&mut collector);
mutable_export_ids.extend(collector.export_list);
mutable_export_ids.extend(find_pat_ids(&var_decl));
var_decl.decls.visit_mut_with(&mut ExportedPatRewriter {
id: id.clone().into(),
@ -1154,79 +1147,42 @@ impl VisitMut for ExportedPatRewriter {
}
}
#[derive(Default)]
struct ExportedIdentCollector {
export_list: Vec<Id>,
}
impl Visit for ExportedIdentCollector {
noop_visit_type!();
fn visit_binding_ident(&mut self, n: &BindingIdent) {
self.export_list.push(n.to_id());
}
fn visit_assign_pat_prop(&mut self, n: &AssignPatProp) {
self.export_list.push(n.key.to_id());
n.visit_children_with(self);
}
fn visit_var_declarator(&mut self, n: &VarDeclarator) {
n.name.visit_with(self);
}
}
struct ExportRefRewrriter {
struct ExportQuery {
namesapce_id: Id,
export_id_list: AHashSet<Id>,
}
impl VisitMut for ExportRefRewrriter {
noop_visit_mut_type!();
fn visit_mut_pat(&mut self, n: &mut Pat) {
match n {
Pat::Ident(BindingIdent { id, .. }) => {
if self.export_id_list.contains(&id.to_id()) {
*n = Pat::Expr(Box::new(self.namesapce_id.clone().make_member(id.take())));
}
}
_ => n.visit_mut_children_with(self),
}
impl QueryRef for ExportQuery {
fn query_ref(&self, ident: &Ident) -> Option<Expr> {
self.export_id_list
.contains(&ident.to_id())
.then(|| self.namesapce_id.clone().make_member(ident.clone()))
}
fn visit_mut_expr(&mut self, n: &mut Expr) {
if let Expr::Ident(ref_ident) = n {
if self.export_id_list.contains(&ref_ident.to_id()) {
*n = self.namesapce_id.clone().make_member(ref_ident.clone());
}
return;
}
n.visit_mut_children_with(self);
fn query_lhs(&self, ident: &Ident) -> Option<Expr> {
self.query_ref(ident)
}
fn visit_mut_prop(&mut self, n: &mut Prop) {
match n {
Prop::Shorthand(shorthand) => {
if self.export_id_list.contains(&shorthand.to_id()) {
*n = KeyValueProp {
key: shorthand.clone().into(),
value: self
.namesapce_id
.clone()
.make_member(shorthand.take())
.into(),
}
.into()
}
}
_ => n.visit_mut_children_with(self),
}
fn should_fix_this(&self, _: &Ident) -> bool {
// tsc does not care about `this` in namespace.
false
}
}
fn rewrite_export_bindings<V>(node: &mut V, namesapce_id: Id, export_id_list: AHashSet<Id>)
where
V: VisitMutWith<RefRewriter<ExportQuery>>,
{
let mut v = RefRewriter {
query: ExportQuery {
namesapce_id,
export_id_list,
},
};
node.visit_mut_with(&mut v);
}
struct EnumMemberItem {
span: Span,
name: JsWord,

View File

@ -20,7 +20,9 @@ use std::{
use rustc_hash::FxHashMap;
use swc_atoms::{js_word, JsWord};
use swc_common::{collections::AHashSet, Mark, Span, Spanned, SyntaxContext, DUMMY_SP};
use swc_common::{
collections::AHashSet, util::take::Take, Mark, Span, Spanned, SyntaxContext, DUMMY_SP,
};
use swc_ecma_ast::*;
use swc_ecma_visit::{
noop_visit_mut_type, noop_visit_type, visit_mut_obj_and_computed, visit_obj_and_computed,
@ -3052,6 +3054,108 @@ impl VisitMut for IdentRenamer<'_> {
}
}
pub trait QueryRef {
fn query_ref(&self, ident: &Ident) -> Option<Expr>;
fn query_lhs(&self, ident: &Ident) -> Option<Expr>;
/// when `foo()` is replaced with `bar.baz()`,
/// should `bar.baz` be indirect call?
fn should_fix_this(&self, ident: &Ident) -> bool;
}
/// Replace `foo` with `bar` or `bar.baz`
pub struct RefRewriter<T>
where
T: QueryRef,
{
pub query: T,
}
impl<T> VisitMut for RefRewriter<T>
where
T: QueryRef,
{
noop_visit_mut_type!();
/// replace bar in binding pattern
/// input:
/// ```JavaScript
/// const foo = { bar }
/// ```
/// output:
/// ```JavaScript
/// cobst foo = { bar: baz }
/// ```
fn visit_mut_prop(&mut self, n: &mut Prop) {
match n {
Prop::Shorthand(shorthand) => {
if let Some(expr) = self.query.query_ref(shorthand) {
*n = KeyValueProp {
key: shorthand.take().into(),
value: Box::new(expr),
}
.into()
}
}
_ => n.visit_mut_children_with(self),
}
}
fn visit_mut_pat(&mut self, n: &mut Pat) {
match n {
Pat::Ident(BindingIdent { id, .. }) => {
if let Some(expr) = self.query.query_lhs(id) {
*n = Pat::Expr(Box::new(expr));
}
}
_ => n.visit_mut_children_with(self),
}
}
fn visit_mut_expr(&mut self, n: &mut Expr) {
match n {
Expr::Ident(ref_ident) => {
if let Some(expr) = self.query.query_ref(ref_ident) {
*n = expr;
}
}
_ => n.visit_mut_children_with(self),
};
}
fn visit_mut_callee(&mut self, n: &mut Callee) {
match n {
Callee::Expr(e)
if e.as_ident()
.map(|ident| self.query.should_fix_this(ident))
.unwrap_or_default() =>
{
e.visit_mut_with(self);
if e.is_member() {
*n = n.take().into_indirect()
}
}
_ => n.visit_mut_children_with(self),
}
}
fn visit_mut_tagged_tpl(&mut self, n: &mut TaggedTpl) {
let should_fix_this = n
.tag
.as_ident()
.map(|ident| self.query.should_fix_this(ident))
.unwrap_or_default();
n.visit_mut_children_with(self);
if should_fix_this && n.tag.is_member() {
*n = n.take().into_indirect()
}
}
}
#[cfg(test)]
mod test {
use swc_common::{input::StringInput, BytePos};