Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

View file

@ -0,0 +1 @@
{"files":{"Cargo.toml":"d3f12af3d5700c4399ba809d830556b860d66a9a6c62319b27424ca8b202223c","src/decorator_2022_03.rs":"f2dd91b60ce8c94d6afabc10cf301a73d5a6103930a358149360d853e73f41d5","src/decorators/legacy/metadata.rs":"fd1d0d56fb5e4904062889bfef844e06dc31c7945588711f970fef8a3bb7dea6","src/decorators/legacy/mod.rs":"e3cdf3faad988dba570ee892bc04df4d3e75f59a0bb5597d4b856135fd9b5451","src/decorators/mod.rs":"bd23c40d74fdc4d6b8ced9b43da860575b2ba2266b04f01d65a1e4cf931c86ff","src/explicit_resource_management.rs":"7ac7461b39093bb3a9ab2903314b056e87e9b5c72530c72d5ec84794705e8485","src/export_default_from.rs":"053c6d5c11c2e44583bb7ab36f5842539e2edf21cb58ac0c44da7c48baa83444","src/import_assertions.rs":"07180d134c9d8337f3929d6f5fd67e4b8e635ab7e64e76c88364a1e5a6964e1e","src/lib.rs":"49963b6612c1b391593c40f954064a3408bec80fef37a6402618dbb296628614"},"package":"122fd9a69f464694edefbf9c59106b3c15e5cc8cb8575a97836e4fb79018e98f"}

View file

@ -0,0 +1,87 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
name = "swc_ecma_transforms_proposal"
version = "0.166.3"
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
include = [
"Cargo.toml",
"src/**/*.rs",
]
description = "rust port of babel and closure compiler."
documentation = "https://rustdoc.swc.rs/swc_ecma_transforms_proposal/"
license = "Apache-2.0"
repository = "https://github.com/swc-project/swc.git"
[lib]
bench = false
[dependencies.either]
version = "1.6.1"
[dependencies.rustc-hash]
version = "1"
[dependencies.serde]
version = "1.0.118"
features = ["derive"]
[dependencies.smallvec]
version = "1.8.0"
[dependencies.swc_atoms]
version = "0.5.9"
[dependencies.swc_common]
version = "0.32.0"
[dependencies.swc_ecma_ast]
version = "0.109.0"
[dependencies.swc_ecma_loader]
version = "0.44.2"
optional = true
[dependencies.swc_ecma_transforms_base]
version = "0.132.2"
[dependencies.swc_ecma_transforms_classes]
version = "0.121.2"
[dependencies.swc_ecma_transforms_macros]
version = "0.5.3"
[dependencies.swc_ecma_utils]
version = "0.122.0"
[dependencies.swc_ecma_visit]
version = "0.95.0"
[dev-dependencies.serde_json]
version = "1.0.66"
[dev-dependencies.swc_ecma_parser]
version = "0.139.0"
[dev-dependencies.swc_ecma_transforms_compat]
version = "0.158.3"
[dev-dependencies.swc_ecma_transforms_testing]
version = "0.135.2"
[dev-dependencies.testing]
version = "0.34.0"
[features]
default = []
multi-module = ["swc_ecma_loader"]

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,525 @@
use swc_atoms::{js_word, JsWord};
use swc_common::{
collections::AHashMap,
util::{move_map::MoveMap, take::Take},
Spanned, DUMMY_SP,
};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::helper;
use swc_ecma_utils::{quote_ident, undefined, ExprFactory};
use swc_ecma_visit::{VisitMut, VisitMutWith};
use super::EnumKind;
/// https://github.com/leonardfactory/babel-plugin-transform-typescript-metadata/blob/master/src/parameter/parameterVisitor.ts
pub(super) struct ParamMetadata;
impl VisitMut for ParamMetadata {
fn visit_mut_class(&mut self, cls: &mut Class) {
cls.visit_mut_children_with(self);
let mut decorators = cls.decorators.take();
cls.body = cls.body.take().move_map(|m| match m {
ClassMember::Constructor(mut c) => {
for (idx, param) in c.params.iter_mut().enumerate() {
//
match param {
ParamOrTsParamProp::TsParamProp(p) => {
for decorator in p.decorators.drain(..) {
let new_dec = self.create_param_decorator(idx, decorator.expr);
decorators.push(new_dec);
}
}
ParamOrTsParamProp::Param(param) => {
for decorator in param.decorators.drain(..) {
let new_dec = self.create_param_decorator(idx, decorator.expr);
decorators.push(new_dec);
}
}
}
}
ClassMember::Constructor(c)
}
_ => m,
});
cls.decorators = decorators;
}
fn visit_mut_class_method(&mut self, m: &mut ClassMethod) {
for (idx, param) in m.function.params.iter_mut().enumerate() {
for decorator in param.decorators.drain(..) {
let new_dec = self.create_param_decorator(idx, decorator.expr);
m.function.decorators.push(new_dec);
}
}
}
}
impl ParamMetadata {
fn create_param_decorator(&self, param_index: usize, decorator_expr: Box<Expr>) -> Decorator {
Decorator {
span: DUMMY_SP,
expr: Box::new(Expr::Call(CallExpr {
span: decorator_expr.span(),
callee: helper!(ts, ts_param),
args: vec![param_index.as_arg(), decorator_expr.as_arg()],
type_args: Default::default(),
})),
}
}
}
/// https://github.com/leonardfactory/babel-plugin-transform-typescript-metadata/blob/master/src/metadata/metadataVisitor.ts
pub(super) struct Metadata<'a> {
pub(super) enums: &'a AHashMap<JsWord, EnumKind>,
pub(super) class_name: Option<&'a Ident>,
}
impl VisitMut for Metadata<'_> {
fn visit_mut_class(&mut self, c: &mut Class) {
c.visit_mut_children_with(self);
if c.decorators.is_empty() {
return;
}
let constructor = c.body.iter().find_map(|m| match m {
ClassMember::Constructor(c) => Some(c),
_ => None,
});
if constructor.is_none() {
return;
}
{
let dec = self
.create_metadata_design_decorator("design:type", quote_ident!("Function").as_arg());
c.decorators.push(dec);
}
{
let dec = self.create_metadata_design_decorator(
"design:paramtypes",
ArrayLit {
span: DUMMY_SP,
elems: constructor
.as_ref()
.unwrap()
.params
.iter()
.map(|v| match v {
ParamOrTsParamProp::TsParamProp(p) => {
let ann = match &p.param {
TsParamPropParam::Ident(i) => i.type_ann.as_deref(),
TsParamPropParam::Assign(a) => get_type_ann_of_pat(&a.left),
};
Some(serialize_type(self.class_name, ann).as_arg())
}
ParamOrTsParamProp::Param(p) => Some(
serialize_type(self.class_name, get_type_ann_of_pat(&p.pat))
.as_arg(),
),
})
.collect(),
}
.as_arg(),
);
c.decorators.push(dec);
}
}
fn visit_mut_class_method(&mut self, m: &mut ClassMethod) {
if m.function.decorators.is_empty() {
return;
}
{
let dec = self
.create_metadata_design_decorator("design:type", quote_ident!("Function").as_arg());
m.function.decorators.push(dec);
}
{
let dec = self.create_metadata_design_decorator(
"design:paramtypes",
ArrayLit {
span: DUMMY_SP,
elems: m
.function
.params
.iter()
.map(|v| {
Some(
serialize_type(self.class_name, get_type_ann_of_pat(&v.pat))
.as_arg(),
)
})
.collect(),
}
.as_arg(),
);
m.function.decorators.push(dec);
}
}
fn visit_mut_class_prop(&mut self, p: &mut ClassProp) {
if p.decorators.is_empty() {
return;
}
if p.type_ann.is_none() {
return;
}
if let Some(name) = p
.type_ann
.as_ref()
.map(|ty| &ty.type_ann)
.and_then(|type_ann| match &**type_ann {
TsType::TsTypeRef(r) => Some(r),
_ => None,
})
.and_then(|r| match &r.type_name {
TsEntityName::TsQualifiedName(_) => None,
TsEntityName::Ident(i) => Some(i),
})
{
if let Some(kind) = self.enums.get(&name.sym) {
let dec = self.create_metadata_design_decorator(
"design:type",
match kind {
EnumKind::Mixed => quote_ident!("Object").as_arg(),
EnumKind::Str => quote_ident!("String").as_arg(),
EnumKind::Num => quote_ident!("Number").as_arg(),
},
);
p.decorators.push(dec);
return;
}
}
let dec = self.create_metadata_design_decorator(
"design:type",
serialize_type(self.class_name, p.type_ann.as_deref()).as_arg(),
);
p.decorators.push(dec);
}
}
impl Metadata<'_> {
fn create_metadata_design_decorator(&self, design: &str, type_arg: ExprOrSpread) -> Decorator {
Decorator {
span: DUMMY_SP,
expr: Box::new(Expr::Call(CallExpr {
span: DUMMY_SP,
callee: helper!(ts, ts_metadata),
args: vec![design.as_arg(), type_arg],
type_args: Default::default(),
})),
}
}
}
fn serialize_type(class_name: Option<&Ident>, param: Option<&TsTypeAnn>) -> Expr {
fn check_object_existed(expr: Box<Expr>) -> Box<Expr> {
match *expr {
Expr::Member(ref member_expr) => {
let obj_expr = member_expr.obj.clone();
Box::new(Expr::Bin(BinExpr {
span: DUMMY_SP,
left: check_object_existed(obj_expr),
op: op!("||"),
right: Box::new(Expr::Bin(BinExpr {
span: DUMMY_SP,
left: Box::new(Expr::Unary(UnaryExpr {
span: DUMMY_SP,
op: op!("typeof"),
arg: expr,
})),
op: op!("==="),
right: Box::new(Expr::Lit(Lit::Str(Str {
span: DUMMY_SP,
value: "undefined".into(),
raw: None,
}))),
})),
}))
}
_ => Box::new(Expr::Bin(BinExpr {
span: DUMMY_SP,
left: Box::new(Expr::Unary(UnaryExpr {
span: DUMMY_SP,
op: op!("typeof"),
arg: expr,
})),
op: op!("==="),
right: Box::new(Expr::Lit(Lit::Str(Str {
span: DUMMY_SP,
value: "undefined".into(),
raw: None,
}))),
})),
}
}
fn serialize_type_ref(class_name: &str, ty: &TsTypeRef) -> Expr {
match &ty.type_name {
// We should omit references to self (class) since it will throw a ReferenceError at
// runtime due to babel transpile output.
TsEntityName::Ident(i) if &*i.sym == class_name => {
return quote_ident!("Object").into()
}
_ => {}
}
let member_expr = ts_entity_to_member_expr(&ty.type_name);
// We don't know if type is just a type (interface, etc.) or a concrete value
// (class, etc.)
//
// `typeof` operator allows us to use the expression even if it is not defined,
// fallback is just `Object`.
Expr::Cond(CondExpr {
span: DUMMY_SP,
test: check_object_existed(Box::new(member_expr.clone())),
cons: Box::new(quote_ident!("Object").into()),
alt: Box::new(member_expr),
})
}
fn serialize_type_list(class_name: &str, types: &[Box<TsType>]) -> Expr {
let mut u = None;
for ty in types {
// Skip parens if need be
let ty = match &**ty {
TsType::TsParenthesizedType(ty) => &ty.type_ann,
_ => ty,
};
match &**ty {
// Always elide `never` from the union/intersection if possible
TsType::TsKeywordType(TsKeywordType {
kind: TsKeywordTypeKind::TsNeverKeyword,
..
}) => {
continue;
}
// Elide null and undefined from unions for metadata, just like what we did prior to
// the implementation of strict null checks
TsType::TsKeywordType(TsKeywordType {
kind: TsKeywordTypeKind::TsNullKeyword,
..
})
| TsType::TsKeywordType(TsKeywordType {
kind: TsKeywordTypeKind::TsUndefinedKeyword,
..
}) => {
return quote_ident!("Object").into();
}
_ => {}
}
let item = serialize_type_node(class_name, ty);
// One of the individual is global object, return immediately
if let Expr::Ident(Ident {
sym: js_word!("Object"),
..
}) = item
{
return item;
}
// If there exists union that is not void 0 expression, check if the
// the common type is identifier. anything more complex
// and we will just default to Object
//
match &u {
None => {
u = Some(item);
}
Some(prev) => {
// Check for different types
match prev {
Expr::Ident(prev) => match &item {
Expr::Ident(item) if prev.sym == item.sym => {}
_ => return quote_ident!("Object").into(),
},
_ => return quote_ident!("Object").into(),
}
}
}
}
match u {
Some(i) => i,
_ => quote_ident!("Object").into(),
}
}
fn serialize_type_node(class_name: &str, ty: &TsType) -> Expr {
let span = ty.span();
match ty {
TsType::TsKeywordType(TsKeywordType {
kind: TsKeywordTypeKind::TsVoidKeyword,
..
})
| TsType::TsKeywordType(TsKeywordType {
kind: TsKeywordTypeKind::TsUndefinedKeyword,
..
})
| TsType::TsKeywordType(TsKeywordType {
kind: TsKeywordTypeKind::TsNullKeyword,
..
})
| TsType::TsKeywordType(TsKeywordType {
kind: TsKeywordTypeKind::TsNeverKeyword,
..
}) => *undefined(span),
TsType::TsParenthesizedType(ty) => serialize_type_node(class_name, &ty.type_ann),
TsType::TsFnOrConstructorType(_) => quote_ident!("Function").into(),
TsType::TsArrayType(_) | TsType::TsTupleType(_) => quote_ident!("Array").into(),
TsType::TsLitType(TsLitType {
lit: TsLit::Bool(..),
..
})
| TsType::TsTypePredicate(_)
| TsType::TsKeywordType(TsKeywordType {
kind: TsKeywordTypeKind::TsBooleanKeyword,
..
}) => quote_ident!("Boolean").into(),
ty if is_str(ty) => quote_ident!("String").into(),
TsType::TsKeywordType(TsKeywordType {
kind: TsKeywordTypeKind::TsObjectKeyword,
..
}) => quote_ident!("Object").into(),
TsType::TsLitType(TsLitType {
lit: TsLit::Number(..),
..
})
| TsType::TsKeywordType(TsKeywordType {
kind: TsKeywordTypeKind::TsNumberKeyword,
..
}) => quote_ident!("Number").into(),
TsType::TsKeywordType(TsKeywordType {
kind: TsKeywordTypeKind::TsBigIntKeyword,
..
}) => Expr::Cond(CondExpr {
span: DUMMY_SP,
test: check_object_existed(quote_ident!("BigInt").into()),
cons: quote_ident!("Object").into(),
alt: quote_ident!("BigInt").into(),
}),
TsType::TsLitType(ty) => {
// TODO: Proper error reporting
panic!("Bad type for decoration: {:?}", ty);
}
TsType::TsKeywordType(TsKeywordType {
kind: TsKeywordTypeKind::TsSymbolKeyword,
..
}) => quote_ident!("Symbol").into(),
TsType::TsTypeQuery(_)
| TsType::TsTypeOperator(_)
| TsType::TsIndexedAccessType(_)
| TsType::TsTypeLit(_)
| TsType::TsMappedType(_)
| TsType::TsKeywordType(TsKeywordType {
kind: TsKeywordTypeKind::TsAnyKeyword,
..
})
| TsType::TsKeywordType(TsKeywordType {
kind: TsKeywordTypeKind::TsUnknownKeyword,
..
})
| TsType::TsThisType(..) => quote_ident!("Object").into(),
TsType::TsUnionOrIntersectionType(ty) => match ty {
TsUnionOrIntersectionType::TsUnionType(ty) => {
serialize_type_list(class_name, &ty.types)
}
TsUnionOrIntersectionType::TsIntersectionType(ty) => {
serialize_type_list(class_name, &ty.types)
}
},
TsType::TsConditionalType(ty) => {
serialize_type_list(class_name, &[ty.true_type.clone(), ty.false_type.clone()])
}
TsType::TsTypeRef(ty) => serialize_type_ref(class_name, ty),
_ => panic!("Bad type for decorator: {:?}", ty),
}
}
let param = match param {
Some(v) => &v.type_ann,
None => return *undefined(DUMMY_SP),
};
serialize_type_node(class_name.map(|v| &*v.sym).unwrap_or(""), param)
}
fn ts_entity_to_member_expr(type_name: &TsEntityName) -> Expr {
match type_name {
TsEntityName::TsQualifiedName(q) => {
let obj = ts_entity_to_member_expr(&q.left);
Expr::Member(MemberExpr {
span: DUMMY_SP,
obj: obj.into(),
prop: MemberProp::Ident(q.right.clone()),
})
}
TsEntityName::Ident(i) => Expr::Ident(i.clone()),
}
}
fn get_type_ann_of_pat(p: &Pat) -> Option<&TsTypeAnn> {
match p {
Pat::Ident(p) => p.type_ann.as_deref(),
Pat::Array(p) => p.type_ann.as_deref(),
Pat::Rest(p) => p.type_ann.as_deref(),
Pat::Object(p) => p.type_ann.as_deref(),
Pat::Assign(p) => {
return get_type_ann_of_pat(&p.left);
}
Pat::Invalid(_) => None,
Pat::Expr(_) => None,
}
}
fn is_str(ty: &TsType) -> bool {
match ty {
TsType::TsLitType(TsLitType {
lit: TsLit::Str(..) | TsLit::Tpl(..),
..
})
| TsType::TsKeywordType(TsKeywordType {
kind: TsKeywordTypeKind::TsStringKeyword,
..
}) => true,
TsType::TsUnionOrIntersectionType(TsUnionOrIntersectionType::TsUnionType(u)) => {
u.types.iter().all(|ty| is_str(ty))
}
_ => false,
}
}

View file

@ -0,0 +1,544 @@
use swc_atoms::JsWord;
use swc_common::{collections::AHashMap, util::take::Take, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::helper;
use swc_ecma_utils::{
constructor::inject_after_super, default_constructor, private_ident, prop_name_to_expr_value,
quote_ident, replace_ident, undefined, ExprFactory, StmtLike,
};
use swc_ecma_visit::{Visit, VisitMut, VisitMutWith, VisitWith};
use self::metadata::{Metadata, ParamMetadata};
use super::contains_decorator;
mod metadata;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum EnumKind {
Mixed,
Str,
Num,
}
pub(super) fn new(metadata: bool) -> TscDecorator {
TscDecorator {
metadata,
enums: Default::default(),
vars: Default::default(),
appended_exprs: Default::default(),
prepended_exprs: Default::default(),
class_name: Default::default(),
constructor_exprs: Default::default(),
exports: Default::default(),
}
}
pub(super) struct TscDecorator {
metadata: bool,
enums: AHashMap<JsWord, EnumKind>,
/// Used for computed keys, and this variables are not initialized.
vars: Vec<VarDeclarator>,
appended_exprs: Vec<Box<Expr>>,
prepended_exprs: Vec<Box<Expr>>,
class_name: Option<Ident>,
/// Only used if `use_define_for_class_props` is false.
constructor_exprs: Vec<Box<Expr>>,
exports: Vec<ExportSpecifier>,
}
impl TscDecorator {
fn visit_mut_stmt_likes<T>(&mut self, stmts: &mut Vec<T>)
where
T: StmtLike + VisitMutWith<Self>,
{
let old_vars = self.vars.take();
let old_appended_exprs = self.appended_exprs.take();
let old_prepended_exprs = self.prepended_exprs.take();
let mut new = vec![];
for mut s in stmts.take() {
debug_assert!(self.appended_exprs.is_empty());
s.visit_mut_with(self);
if !self.vars.is_empty() {
new.push(T::from_stmt(
VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: Default::default(),
decls: self.vars.take(),
}
.into(),
));
}
new.extend(
self.prepended_exprs
.drain(..)
.map(|expr| {
Stmt::Expr(ExprStmt {
span: DUMMY_SP,
expr,
})
})
.map(T::from_stmt),
);
new.push(s);
new.extend(
self.appended_exprs
.drain(..)
.map(|expr| {
Stmt::Expr(ExprStmt {
span: DUMMY_SP,
expr,
})
})
.map(T::from_stmt),
);
}
*stmts = new;
self.prepended_exprs = old_prepended_exprs;
self.appended_exprs = old_appended_exprs;
self.vars = old_vars;
}
fn key(&mut self, k: &mut PropName) -> Expr {
match k {
PropName::Computed(k) if !k.expr.is_lit() => {
let var_name = private_ident!(k.span, "_key");
// Declare var
self.vars.push(VarDeclarator {
span: DUMMY_SP,
name: Pat::Ident(var_name.clone().into()),
init: None,
definite: Default::default(),
});
// Initialize var
self.prepended_exprs.push(Box::new(Expr::Assign(AssignExpr {
span: DUMMY_SP,
op: op!("="),
left: PatOrExpr::Pat(var_name.clone().into()),
right: k.expr.take(),
})));
k.expr = Box::new(Expr::Ident(var_name.clone()));
return Expr::Ident(var_name);
}
PropName::Ident(i) => {
return Expr::Lit(Lit::Str(Str {
span: DUMMY_SP,
raw: None,
value: i.sym.clone(),
}))
}
_ => {}
}
prop_name_to_expr_value(k.clone())
}
/// Creates `__decorate` calls.
fn add_decorate_call(
&mut self,
decorators: impl IntoIterator<Item = Box<Expr>>,
target: ExprOrSpread,
key: ExprOrSpread,
desc: ExprOrSpread,
) {
let decorators = ArrayLit {
span: DUMMY_SP,
elems: decorators
.into_iter()
.map(|v| v.as_arg())
.map(Some)
.collect(),
}
.as_arg();
self.appended_exprs.push(Box::new(Expr::Call(CallExpr {
span: DUMMY_SP,
callee: helper!(ts, ts_decorate),
args: vec![decorators, target, key, desc],
type_args: Default::default(),
})));
}
}
impl Visit for TscDecorator {
fn visit_ts_enum_decl(&mut self, e: &TsEnumDecl) {
let enum_kind = e
.members
.iter()
.map(|member| member.init.as_ref())
.map(|init| match init {
Some(e) => match &**e {
Expr::Lit(lit) => match lit {
Lit::Str(_) => EnumKind::Str,
Lit::Num(_) => EnumKind::Num,
_ => EnumKind::Mixed,
},
_ => EnumKind::Mixed,
},
None => EnumKind::Num,
})
.fold(None, |opt: Option<EnumKind>, item| {
//
let a = match item {
EnumKind::Mixed => return Some(EnumKind::Mixed),
_ => item,
};
let b = match opt {
Some(EnumKind::Mixed) => return Some(EnumKind::Mixed),
Some(v) => v,
None => return Some(item),
};
if a == b {
Some(a)
} else {
Some(EnumKind::Mixed)
}
});
if let Some(kind) = enum_kind {
self.enums.insert(e.id.sym.clone(), kind);
}
}
}
impl VisitMut for TscDecorator {
fn visit_mut_class(&mut self, n: &mut Class) {
let old_constructor_stmts = self.constructor_exprs.take();
n.visit_mut_with(&mut ParamMetadata);
if self.metadata {
let i = self.class_name.clone();
n.visit_mut_with(&mut Metadata {
enums: &self.enums,
class_name: i.as_ref(),
});
}
n.visit_mut_children_with(self);
if !self.constructor_exprs.is_empty() {
for m in &mut n.body {
if let ClassMember::Constructor(c @ Constructor { body: Some(..), .. }) = m {
inject_after_super(c, self.constructor_exprs.take());
}
}
if !self.constructor_exprs.is_empty() {
let mut c = default_constructor(n.super_class.is_some());
inject_after_super(&mut c, self.constructor_exprs.take());
n.body.insert(0, ClassMember::Constructor(c));
}
}
self.constructor_exprs = old_constructor_stmts;
if let Some(class_name) = self.class_name.clone() {
if !n.decorators.is_empty() {
let decorators = ArrayLit {
span: DUMMY_SP,
elems: n
.decorators
.take()
.into_iter()
.map(|v| v.expr.as_arg())
.map(Some)
.collect(),
}
.as_arg();
let decorated = Box::new(Expr::Call(CallExpr {
span: DUMMY_SP,
callee: helper!(ts, ts_decorate),
args: vec![decorators, class_name.clone().as_arg()],
type_args: Default::default(),
}));
self.appended_exprs.push(Box::new(Expr::Assign(AssignExpr {
span: DUMMY_SP,
op: op!("="),
left: PatOrExpr::Pat(class_name.into()),
right: decorated,
})));
}
}
}
fn visit_mut_class_decl(&mut self, n: &mut ClassDecl) {
let old = self.class_name.take();
self.class_name = Some(n.ident.clone());
n.visit_mut_children_with(self);
self.class_name = old;
}
fn visit_mut_class_expr(&mut self, n: &mut ClassExpr) {
let old = self.class_name.take();
if contains_decorator(n) && n.ident.is_none() {
n.ident = Some(private_ident!("_class"));
}
if let Some(ident) = &n.ident {
self.class_name = Some(ident.clone());
}
n.visit_mut_children_with(self);
self.class_name = old;
}
fn visit_mut_class_method(&mut self, c: &mut ClassMethod) {
c.visit_mut_children_with(self);
if let Some(class_name) = self.class_name.clone() {
if !c.function.decorators.is_empty() {
let key = self.key(&mut c.key);
let target = if c.is_static {
class_name.as_arg()
} else {
class_name.make_member(quote_ident!("prototype")).as_arg()
};
self.add_decorate_call(
c.function.decorators.drain(..).map(|d| d.expr),
target,
key.as_arg(),
Lit::Null(Null::dummy()).as_arg(),
);
}
}
}
fn visit_mut_class_prop(&mut self, c: &mut ClassProp) {
c.visit_mut_children_with(self);
if let Some(class_name) = self.class_name.clone() {
if !c.decorators.is_empty() {
let key = self.key(&mut c.key);
let target = if c.is_static {
class_name.as_arg()
} else {
class_name.make_member(quote_ident!("prototype")).as_arg()
};
self.add_decorate_call(
c.decorators.drain(..).map(|d| d.expr),
target,
key.as_arg(),
undefined(DUMMY_SP).as_arg(),
);
}
}
}
fn visit_mut_decl(&mut self, n: &mut Decl) {
match n {
Decl::Class(decl) => {
let convert_to_let = !decl.class.decorators.is_empty();
decl.visit_mut_with(self);
if convert_to_let {
let inner_ident = private_ident!(decl.ident.sym.clone());
decl.class.body.iter_mut().for_each(|m| match m {
ClassMember::PrivateProp(PrivateProp {
is_static: true, ..
})
| ClassMember::StaticBlock(..)
| ClassMember::ClassProp(ClassProp {
is_static: true, ..
}) => {
replace_ident(m, decl.ident.to_id(), &inner_ident);
}
_ => {}
});
let d = VarDeclarator {
span: DUMMY_SP,
name: decl.ident.clone().into(),
init: Some(Box::new(Expr::Class(ClassExpr {
ident: Some(inner_ident),
class: decl.class.take(),
}))),
definite: Default::default(),
};
*n = Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Let,
declare: Default::default(),
decls: vec![d],
}));
}
}
_ => {
n.visit_mut_children_with(self);
}
}
}
fn visit_mut_module(&mut self, n: &mut Module) {
n.visit_with(self);
n.visit_mut_children_with(self);
}
fn visit_mut_module_item(&mut self, module_item: &mut ModuleItem) {
match module_item {
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(n)) => {
let export_decl_span = n.span;
match &mut n.decl {
Decl::Class(decl) => {
let convert_to_let = !decl.class.decorators.is_empty();
decl.visit_mut_with(self);
if convert_to_let {
let inner_ident = private_ident!(decl.ident.sym.clone());
decl.class.body.iter_mut().for_each(|m| match m {
ClassMember::PrivateProp(PrivateProp {
is_static: true, ..
})
| ClassMember::StaticBlock(..)
| ClassMember::ClassProp(ClassProp {
is_static: true, ..
}) => {
replace_ident(m, decl.ident.to_id(), &inner_ident);
}
_ => {}
});
let d = VarDeclarator {
span: DUMMY_SP,
name: decl.ident.clone().into(),
init: Some(Box::new(Expr::Class(ClassExpr {
ident: Some(inner_ident),
class: decl.class.take(),
}))),
definite: Default::default(),
};
let let_decl = VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Let,
declare: Default::default(),
decls: vec![d],
}
.into();
*module_item =
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
span: export_decl_span,
decl: let_decl,
}));
}
}
_ => {
module_item.visit_mut_children_with(self);
}
}
}
ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(n)) => match &mut n.decl {
DefaultDecl::Class(decl) => {
let convert_to_let = !decl.class.decorators.is_empty();
decl.visit_mut_with(self);
if convert_to_let {
let ident = decl.ident.clone().unwrap();
let inner_ident = private_ident!(ident.sym.clone());
decl.class.body.iter_mut().for_each(|m| match m {
ClassMember::PrivateProp(PrivateProp {
is_static: true, ..
})
| ClassMember::StaticBlock(..)
| ClassMember::ClassProp(ClassProp {
is_static: true, ..
}) => {
replace_ident(m, ident.to_id(), &inner_ident);
}
_ => {}
});
let d = VarDeclarator {
span: DUMMY_SP,
name: ident.clone().into(),
init: Some(Box::new(Expr::Class(ClassExpr {
ident: Some(inner_ident),
..decl.take()
}))),
definite: Default::default(),
};
*module_item = VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Let,
declare: Default::default(),
decls: vec![d],
}
.into();
self.exports
.push(ExportSpecifier::Named(ExportNamedSpecifier {
span: DUMMY_SP,
orig: ModuleExportName::Ident(ident),
exported: Some(ModuleExportName::Ident(quote_ident!("default"))),
is_type_only: Default::default(),
}));
}
}
_ => {
module_item.visit_mut_children_with(self);
}
},
_ => {
module_item.visit_mut_children_with(self);
}
}
}
fn visit_mut_module_items(&mut self, s: &mut Vec<ModuleItem>) {
self.visit_mut_stmt_likes(s);
if !self.exports.is_empty() {
s.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
NamedExport {
span: DUMMY_SP,
specifiers: self.exports.take(),
src: None,
type_only: Default::default(),
with: Default::default(),
},
)));
}
}
fn visit_mut_script(&mut self, n: &mut Script) {
n.visit_with(self);
n.visit_mut_children_with(self);
}
fn visit_mut_stmts(&mut self, s: &mut Vec<Stmt>) {
self.visit_mut_stmt_likes(s)
}
}

View file

@ -0,0 +1,661 @@
use std::{iter, mem::take};
use either::Either;
use serde::Deserialize;
use swc_common::{Spanned, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::helper;
use swc_ecma_transforms_classes::super_field::SuperFieldAccessFolder;
use swc_ecma_utils::{
alias_ident_for, constructor::inject_after_super, default_constructor, prepend_stmt,
private_ident, prop_name_to_expr, prop_name_to_expr_value, quote_ident, quote_str, undefined,
ExprFactory, IdentExt,
};
use swc_ecma_visit::{as_folder, noop_fold_type, Fold, FoldWith, Visit, VisitWith};
mod legacy;
/// ## Simple class decorator
///
/// ```js
///
/// @annotation
/// class MyClass { }
///
/// function annotation(target) {
/// target.annotated = true;
/// }
/// ```
///
/// ## Class decorator
///
/// ```js
/// @isTestable(true)
/// class MyClass { }
///
/// function isTestable(value) {
/// return function decorator(target) {
/// target.isTestable = value;
/// }
/// }
/// ```
///
/// ## Class method decorator
///
/// ```js
/// class C {
/// @enumerable(false)
/// method() { }
/// }
///
/// function enumerable(value) {
/// return function (target, key, descriptor) {
/// descriptor.enumerable = value;
/// return descriptor;
/// }
/// }
/// ```
pub fn decorators(c: Config) -> impl Fold {
if c.legacy {
Either::Left(as_folder(self::legacy::new(c.emit_metadata)))
} else {
if c.emit_metadata {
unimplemented!("emitting decorator metadata while using new proposal")
}
Either::Right(Decorators {
is_in_strict: false,
vars: Default::default(),
})
}
}
#[derive(Debug, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Config {
pub legacy: bool,
#[serde(default)]
pub emit_metadata: bool,
pub use_define_for_class_fields: bool,
}
#[derive(Debug, Default)]
struct Decorators {
is_in_strict: bool,
vars: Vec<VarDeclarator>,
}
/// TODO: VisitMut
impl Fold for Decorators {
noop_fold_type!();
fn fold_decl(&mut self, decl: Decl) -> Decl {
let decl = decl.fold_children_with(self);
match decl {
Decl::Class(ClassDecl {
ident,
declare: false,
class,
}) => {
if !contains_decorator(&class) {
return Decl::Class(ClassDecl {
ident,
declare: false,
class,
});
}
let decorate_call = Box::new(self.fold_class_inner(ident.clone(), class));
VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Let,
declare: false,
decls: vec![VarDeclarator {
span: DUMMY_SP,
name: ident.into(),
definite: false,
init: Some(decorate_call),
}],
}
.into()
}
_ => decl,
}
}
fn fold_expr(&mut self, expr: Expr) -> Expr {
let expr = expr.fold_children_with(self);
match expr {
Expr::Class(ClassExpr { ident, class }) => {
if !contains_decorator(&class) {
return Expr::Class(ClassExpr { ident, class });
}
self.fold_class_inner(ident.unwrap_or_else(|| quote_ident!("_class")), class)
}
_ => expr,
}
}
fn fold_module_decl(&mut self, decl: ModuleDecl) -> ModuleDecl {
let decl = decl.fold_children_with(self);
match decl {
ModuleDecl::ExportDefaultDecl(ExportDefaultDecl {
span,
decl: DefaultDecl::Class(ClassExpr { ident, class }),
..
}) => {
let decorate_call = Box::new(
self.fold_class_inner(ident.unwrap_or_else(|| quote_ident!("_class")), class),
);
ModuleDecl::ExportDefaultExpr(ExportDefaultExpr {
span,
expr: decorate_call,
})
}
_ => decl,
}
}
fn fold_module_items(&mut self, items: Vec<ModuleItem>) -> Vec<ModuleItem> {
if !contains_decorator(&items) {
return items;
}
let old_strict = self.is_in_strict;
self.is_in_strict = true;
let mut buf = Vec::with_capacity(items.len() + 4);
items.into_iter().for_each(|item| {
if !contains_decorator(&item) {
buf.push(item);
return;
}
macro_rules! handle_class {
($cls:expr, $ident:expr) => {{
let class = $cls;
let ident = $ident;
let decorate_call = Box::new(self.fold_class_inner(ident.clone(), class));
buf.push(
VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Let,
declare: false,
decls: vec![VarDeclarator {
span: DUMMY_SP,
name: ident.clone().into(),
init: Some(decorate_call),
definite: false,
}],
}
.into(),
);
// export { Foo as default }
buf.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
NamedExport {
span: DUMMY_SP,
specifiers: vec![ExportNamedSpecifier {
span: DUMMY_SP,
orig: ModuleExportName::Ident(ident),
exported: Some(ModuleExportName::Ident(quote_ident!("default"))),
is_type_only: false,
}
.into()],
src: None,
type_only: false,
with: None,
},
)));
}};
}
//
match item {
ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(ExportDefaultDecl {
decl:
DefaultDecl::Class(ClassExpr {
ident: Some(ident),
class,
}),
..
})) => handle_class!(class, ident),
ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr(ExportDefaultExpr {
span,
expr,
..
})) => match *expr {
Expr::Class(ClassExpr {
ident: Some(ident),
class,
}) => handle_class!(class, ident),
_ => {
let item = ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr(
ExportDefaultExpr { span, expr },
));
buf.push(item.fold_with(self));
}
},
_ => {
buf.push(item.fold_with(self));
}
}
});
self.is_in_strict = old_strict;
if !self.vars.is_empty() {
prepend_stmt(
&mut buf,
VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: false,
decls: take(&mut self.vars),
}
.into(),
)
}
buf
}
}
impl Decorators {
#[allow(clippy::boxed_local)]
fn fold_class_inner(&mut self, ident: Ident, mut class: Box<Class>) -> Expr {
let initialize = private_ident!("_initialize");
let super_class_ident = class
.super_class
.as_ref()
.map(|expr| alias_ident_for(expr, "_super"));
let super_class_expr = class.super_class;
class.super_class = super_class_ident.clone().map(|i| Box::new(Expr::Ident(i)));
let constructor = {
let initialize_call = Box::new(Expr::Call(CallExpr {
span: DUMMY_SP,
callee: initialize.clone().as_callee(),
args: vec![ThisExpr { span: DUMMY_SP }.as_arg()],
type_args: Default::default(),
}));
// Inject initialize
let pos = class.body.iter().position(|member| {
matches!(
*member,
ClassMember::Constructor(Constructor { body: Some(..), .. })
)
});
match pos {
Some(pos) => {
let mut c = match class.body.remove(pos) {
ClassMember::Constructor(c) => c,
_ => unreachable!(),
};
inject_after_super(&mut c, vec![initialize_call]);
ClassMember::Constructor(c)
}
None => {
let mut c = default_constructor(super_class_ident.is_some());
c.body
.as_mut()
.unwrap()
.stmts
.push(initialize_call.into_stmt());
ClassMember::Constructor(c)
}
}
};
let mut vars = vec![];
macro_rules! fold_method {
($method:expr, $fn_name:expr, $key_prop_value:expr) => {{
let fn_name = $fn_name;
let method = $method;
let mut folder = swc_ecma_visit::as_folder(SuperFieldAccessFolder {
class_name: &ident,
vars: &mut vars,
constructor_this_mark: None,
is_static: method.is_static,
folding_constructor: false,
in_nested_scope: false,
in_injected_define_property_call: false,
this_alias_mark: None,
// TODO: loose mode
constant_super: false,
super_class: &None,
in_pat: false,
});
let method = method.fold_with(&mut folder);
// kind: "method",
// key: getKeyJ(),
// value: function () {
// return 2;
// }
Some(
ObjectLit {
span: DUMMY_SP,
props: iter::once(PropOrSpread::Prop(Box::new(Prop::KeyValue(
KeyValueProp {
key: PropName::Ident(quote_ident!("kind")),
value: Box::new(Expr::Lit(Lit::Str(quote_str!(
match method.kind {
MethodKind::Method => "method",
MethodKind::Getter => "get",
MethodKind::Setter => "set",
}
)))),
},
))))
.chain(if method.is_static {
Some(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(quote_ident!("static")),
value: true.into(),
}))))
} else {
None
})
.chain({
//
if method.function.decorators.is_empty() {
None
} else {
Some(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(quote_ident!("decorators")),
value: Box::new(Expr::Array(ArrayLit {
span: DUMMY_SP,
elems: method
.function
.decorators
.into_iter()
.map(|dec| dec.expr.as_arg())
.map(Some)
.collect(),
})),
}))))
}
})
.chain(iter::once(PropOrSpread::Prop(Box::new(Prop::KeyValue(
KeyValueProp {
key: PropName::Ident(quote_ident!("key")),
value: $key_prop_value,
},
)))))
.chain(iter::once(PropOrSpread::Prop(Box::new(Prop::KeyValue(
KeyValueProp {
key: PropName::Ident(quote_ident!("value")),
value: Box::new(
FnExpr {
ident: fn_name.map(IdentExt::private),
function: Function {
decorators: vec![],
..*method.function
}
.into(),
}
.into(),
),
},
)))))
.collect(),
}
.as_arg(),
)
}};
}
let descriptors = class
.body
.into_iter()
.filter_map(|member| {
//
match member {
ClassMember::Constructor(_) => unreachable!("multiple constructor?"),
ClassMember::TsIndexSignature(_) => None,
ClassMember::Method(method) => {
let fn_name = match method.key {
PropName::Ident(ref i) => Some(i.clone()),
PropName::Str(ref s) => Some(Ident::new(s.value.clone(), s.span)),
_ => None,
};
let key_prop_value = Box::new(prop_name_to_expr_value(method.key.clone()));
fold_method!(method, fn_name, key_prop_value)
}
ClassMember::PrivateMethod(method) => {
let fn_name = Ident::new(
format!("_{}", method.key.id.sym).into(),
method.key.id.span,
);
let key_prop_value = Box::new(Expr::Lit(Lit::Str(Str {
span: method.key.id.span,
raw: None,
value: method.key.id.sym.clone(),
})));
fold_method!(method, Some(fn_name), key_prop_value)
}
ClassMember::ClassProp(prop) => {
let prop_span = prop.span();
let key_prop_value = match prop.key {
PropName::Ident(i) => Box::new(Expr::Lit(Lit::Str(Str {
span: i.span,
raw: None,
value: i.sym,
}))),
_ => prop_name_to_expr(prop.key).into(),
};
//
Some(
ObjectLit {
span: prop_span,
props: iter::once(PropOrSpread::Prop(Box::new(Prop::KeyValue(
KeyValueProp {
key: PropName::Ident(quote_ident!("kind")),
value: Box::new(Expr::Lit(Lit::Str(quote_str!("field")))),
},
))))
.chain(if prop.is_static {
Some(PropOrSpread::Prop(Box::new(Prop::KeyValue(
KeyValueProp {
key: PropName::Ident(quote_ident!("static")),
value: true.into(),
},
))))
} else {
None
})
.chain({
//
if prop.decorators.is_empty() {
None
} else {
Some(PropOrSpread::Prop(Box::new(Prop::KeyValue(
KeyValueProp {
key: PropName::Ident(quote_ident!("decorators")),
value: Box::new(Expr::Array(ArrayLit {
span: DUMMY_SP,
elems: prop
.decorators
.into_iter()
.map(|dec| dec.expr.as_arg())
.map(Some)
.collect(),
})),
},
))))
}
})
.chain(iter::once(PropOrSpread::Prop(Box::new(Prop::KeyValue(
KeyValueProp {
key: PropName::Ident(quote_ident!("key")),
value: key_prop_value,
},
)))))
.chain(iter::once(PropOrSpread::Prop(Box::new(match prop.value {
Some(value) => Prop::Method(MethodProp {
key: PropName::Ident(quote_ident!("value")),
function: Function {
span: DUMMY_SP,
is_async: false,
is_generator: false,
decorators: vec![],
params: vec![],
body: Some(BlockStmt {
span: DUMMY_SP,
stmts: vec![Stmt::Return(ReturnStmt {
span: DUMMY_SP,
arg: Some(value),
})],
}),
type_params: Default::default(),
return_type: Default::default(),
}
.into(),
}),
_ => Prop::KeyValue(KeyValueProp {
key: PropName::Ident(quote_ident!("value")),
value: undefined(DUMMY_SP),
}),
}))))
.collect(),
}
.as_arg(),
)
}
_ => unimplemented!("ClassMember::{:?}", member,),
}
})
.map(Some)
.collect();
self.vars.extend(vars);
Expr::Call(make_decorate_call(
class.decorators,
iter::once({
// function(_initialize) {}
Function {
span: DUMMY_SP,
params: iter::once(Pat::Ident(initialize.into()))
.chain(super_class_ident.map(Pat::from))
.map(|pat| Param {
span: DUMMY_SP,
decorators: vec![],
pat,
})
.collect(),
decorators: Default::default(),
is_async: false,
is_generator: false,
body: Some(BlockStmt {
span: DUMMY_SP,
stmts: if !self.is_in_strict {
// 'use strict';
Some(Lit::Str(quote_str!("use strict")).into_stmt())
} else {
None
}
.into_iter()
.chain(iter::once(Stmt::Decl(Decl::Class(ClassDecl {
ident: ident.clone(),
class: Class {
decorators: Default::default(),
body: vec![constructor],
..*class
}
.into(),
declare: false,
}))))
.chain(iter::once(Stmt::Return(ReturnStmt {
span: DUMMY_SP,
arg: Some(Box::new(Expr::Object(ObjectLit {
span: DUMMY_SP,
props: vec![
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(quote_ident!("F")),
value: Box::new(Expr::Ident(ident)),
}))),
PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
key: PropName::Ident(quote_ident!("d")),
value: Box::new(Expr::Array(ArrayLit {
span: DUMMY_SP,
elems: descriptors,
})),
}))),
],
}))),
})))
.collect(),
}),
return_type: Default::default(),
type_params: Default::default(),
}
.as_arg()
})
.chain(super_class_expr.map(|e| e.as_arg())),
))
}
}
fn make_decorate_call(
decorators: Vec<Decorator>,
args: impl Iterator<Item = ExprOrSpread>,
) -> CallExpr {
CallExpr {
span: DUMMY_SP,
callee: helper!(decorate),
args: iter::once(
ArrayLit {
span: DUMMY_SP,
elems: decorators
.into_iter()
.map(|dec| Some(dec.expr.as_arg()))
.collect(),
}
.as_arg(),
)
.chain(args)
.collect(),
type_args: Default::default(),
}
}
struct DecoratorFinder {
found: bool,
}
impl Visit for DecoratorFinder {
fn visit_decorator(&mut self, _: &Decorator) {
self.found = true
}
}
fn contains_decorator<N>(node: &N) -> bool
where
N: VisitWith<DecoratorFinder>,
{
let mut v = DecoratorFinder { found: false };
node.visit_with(&mut v);
v.found
}

View file

@ -0,0 +1,332 @@
use std::iter::once;
use swc_common::{util::take::Take, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::helper;
use swc_ecma_utils::{private_ident, ExprFactory, ModuleItemLike, StmtLike};
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
pub fn explicit_resource_management() -> impl Fold + VisitMut {
as_folder(ExplicitResourceManagement::default())
}
#[derive(Default)]
struct ExplicitResourceManagement {
state: Option<State>,
is_not_top_level: bool,
}
struct State {
stack: Ident,
has_error: Ident,
error_var: Ident,
catch_var: Ident,
has_await: bool,
}
impl Default for State {
fn default() -> Self {
Self {
stack: private_ident!("_stack"),
has_error: private_ident!("_hasError"),
error_var: private_ident!("_error"),
catch_var: private_ident!("_"),
has_await: false,
}
}
}
impl ExplicitResourceManagement {
fn visit_mut_stmt_likes<T>(&mut self, stmts: &mut Vec<T>)
where
T: StmtLike + ModuleItemLike,
Vec<T>: VisitMutWith<Self>,
{
let old_state = self.state.take();
stmts.visit_mut_children_with(self);
if let Some(state) = self.state.take() {
self.wrap_with_try(state, stmts);
}
self.state = old_state;
}
fn wrap_with_try<T>(&mut self, state: State, stmts: &mut Vec<T>)
where
T: StmtLike + ModuleItemLike,
{
let mut new = vec![];
let mut try_body = vec![];
let stack_var_decl = VarDeclarator {
span: DUMMY_SP,
name: state.stack.clone().into(),
init: Some(
ArrayLit {
span: DUMMY_SP,
elems: vec![],
}
.into(),
),
definite: Default::default(),
};
try_body.push(Stmt::Decl(Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: false,
decls: vec![stack_var_decl],
}))));
for stmt in stmts.take() {
match stmt.try_into_stmt() {
Ok(stmt @ Stmt::Decl(Decl::Fn(..))) => {
new.push(T::from_stmt(stmt));
}
Ok(stmt) => try_body.push(stmt),
Err(stmt) => match stmt.try_into_module_decl() {
Ok(ModuleDecl::ExportDefaultDecl(decl)) => {
let ident = match &decl.decl {
DefaultDecl::Class(c) => c.ident.clone(),
DefaultDecl::Fn(f) => f.ident.clone(),
DefaultDecl::TsInterfaceDecl(_) => unreachable!(),
};
let ident = ident.unwrap_or_else(|| private_ident!("_default"));
// export { C as default }
new.push(
T::try_from_module_decl(ModuleDecl::ExportNamed(NamedExport {
span: DUMMY_SP,
specifiers: vec![ExportSpecifier::Named(ExportNamedSpecifier {
span: DUMMY_SP,
orig: ModuleExportName::Ident(ident.clone()),
exported: Some(ModuleExportName::Ident(Ident::new(
"default".into(),
DUMMY_SP,
))),
is_type_only: Default::default(),
})],
src: None,
type_only: Default::default(),
with: None,
}))
.unwrap(),
);
try_body.push(Stmt::Decl(Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: Default::default(),
decls: vec![VarDeclarator {
span: DUMMY_SP,
name: ident.into(),
init: Some(match decl.decl {
DefaultDecl::Class(c) => Box::new(Expr::Class(c)),
DefaultDecl::Fn(f) => Box::new(Expr::Fn(f)),
DefaultDecl::TsInterfaceDecl(_) => unreachable!(),
}),
definite: Default::default(),
}],
}))));
}
Ok(ModuleDecl::ExportDefaultExpr(decl)) => {
let ident = private_ident!("_default");
// export { _default as default }
new.push(
T::try_from_module_decl(ModuleDecl::ExportNamed(NamedExport {
span: DUMMY_SP,
specifiers: vec![ExportSpecifier::Named(ExportNamedSpecifier {
span: DUMMY_SP,
orig: ModuleExportName::Ident(ident.clone()),
exported: Some(ModuleExportName::Ident(Ident::new(
"default".into(),
DUMMY_SP,
))),
is_type_only: Default::default(),
})],
src: None,
type_only: Default::default(),
with: None,
}))
.unwrap(),
);
try_body.push(Stmt::Decl(Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: Default::default(),
decls: vec![VarDeclarator {
span: DUMMY_SP,
name: ident.into(),
init: Some(decl.expr),
definite: Default::default(),
}],
}))));
}
Ok(stmt) => new.push(T::try_from_module_decl(stmt).unwrap()),
Err(stmt) => new.push(stmt),
},
}
}
// Drop `;`
try_body.retain(|stmt| !matches!(stmt, Stmt::Empty(..)));
// var error = $catch_var
let error_catch_var = Stmt::Decl(Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: false,
decls: vec![VarDeclarator {
span: DUMMY_SP,
name: state.error_var.clone().into(),
init: Some(state.catch_var.clone().into()),
definite: false,
}],
})));
// var has_error = true
let has_error_true = Stmt::Decl(Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: false,
decls: vec![VarDeclarator {
span: DUMMY_SP,
name: state.has_error.clone().into(),
init: Some(true.into()),
definite: false,
}],
})));
let dispose_expr = CallExpr {
span: DUMMY_SP,
callee: helper!(dispose),
args: vec![
state.stack.as_arg(),
state.error_var.as_arg(),
state.has_error.as_arg(),
],
type_args: Default::default(),
};
let dispose_stmt = if state.has_await {
Expr::Await(AwaitExpr {
span: DUMMY_SP,
arg: Box::new(dispose_expr.into()),
})
} else {
dispose_expr.into()
}
.into_stmt();
let try_stmt = TryStmt {
span: DUMMY_SP,
block: BlockStmt {
span: DUMMY_SP,
stmts: try_body,
},
handler: Some(CatchClause {
span: DUMMY_SP,
param: Some(state.catch_var.into()),
body: BlockStmt {
span: DUMMY_SP,
stmts: vec![error_catch_var, has_error_true],
},
}),
finalizer: Some(BlockStmt {
span: DUMMY_SP,
stmts: vec![dispose_stmt],
}),
};
new.push(T::from_stmt(Stmt::Try(Box::new(try_stmt))));
*stmts = new;
}
}
impl VisitMut for ExplicitResourceManagement {
noop_visit_mut_type!();
fn visit_mut_for_of_stmt(&mut self, n: &mut ForOfStmt) {
n.visit_mut_children_with(self);
if let ForHead::UsingDecl(decl) = &mut n.left {
let state = State::default();
n.left = ForHead::VarDecl(Box::new(VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Const,
declare: false,
decls: decl.decls.take(),
}));
let mut body = vec![*n.body.take()];
self.wrap_with_try(state, &mut body);
n.body = Box::new(Stmt::Block(BlockStmt {
span: DUMMY_SP,
stmts: body,
}))
}
}
fn visit_mut_module_items(&mut self, stmts: &mut Vec<ModuleItem>) {
self.visit_mut_stmt_likes(stmts)
}
fn visit_mut_stmt(&mut self, s: &mut Stmt) {
s.visit_mut_children_with(self);
if let Stmt::Decl(Decl::Using(decl)) = s {
let state = self.state.get_or_insert_with(Default::default);
state.has_await |= decl.is_await;
*s = Stmt::Decl(Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
kind: if self.is_not_top_level {
VarDeclKind::Const
} else {
VarDeclKind::Var
},
declare: Default::default(),
decls: decl
.decls
.take()
.into_iter()
.map(|d| {
let init = CallExpr {
span: decl.span,
callee: helper!(using),
args: once(state.stack.clone().as_arg())
.chain(once(d.init.unwrap().as_arg()))
.chain(if decl.is_await {
Some(true.as_arg())
} else {
None
})
.collect(),
type_args: Default::default(),
};
VarDeclarator {
init: Some(init.into()),
..d
}
})
.collect(),
})));
}
}
fn visit_mut_stmts(&mut self, stmts: &mut Vec<Stmt>) {
let old_is_not_top_level = self.is_not_top_level;
self.is_not_top_level = true;
self.visit_mut_stmt_likes(stmts);
self.is_not_top_level = old_is_not_top_level;
}
}

View file

@ -0,0 +1,106 @@
use swc_common::DUMMY_SP;
use swc_ecma_ast::*;
use swc_ecma_utils::quote_ident;
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut};
/// `@babel/plugin-proposal-export-default-from`
pub fn export_default_from() -> impl Fold + VisitMut {
as_folder(ExportDefaultFrom)
}
struct ExportDefaultFrom;
impl VisitMut for ExportDefaultFrom {
noop_visit_mut_type!();
fn visit_mut_module_items(&mut self, items: &mut Vec<ModuleItem>) {
let count = items
.iter()
.filter(|m| {
matches!(m, ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(NamedExport {
specifiers,
src: Some(..),
type_only: false,
..
})) if specifiers.iter().any(|s| s.is_default()))
})
.count();
if count == 0 {
return;
}
let mut stmts = Vec::<ModuleItem>::with_capacity(items.len() + count);
for item in items.drain(..) {
match item {
ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(NamedExport {
span,
specifiers,
src: Some(src),
type_only: false,
with,
})) if specifiers.iter().any(|s| s.is_default()) => {
let mut origin_specifiers = vec![];
let mut export_specifiers = vec![];
let mut has_namespace = false;
for s in specifiers.into_iter() {
match s {
ExportSpecifier::Default(ExportDefaultSpecifier { exported }) => {
export_specifiers.push(ExportSpecifier::Named(
ExportNamedSpecifier {
span: DUMMY_SP,
orig: quote_ident!(exported.span, "default").into(),
exported: Some(exported.into()),
is_type_only: false,
},
));
}
ExportSpecifier::Namespace(..) => {
has_namespace = true;
origin_specifiers.push(s);
}
ExportSpecifier::Named(..) => {
if has_namespace {
origin_specifiers.push(s);
} else {
export_specifiers.push(s);
}
}
}
}
stmts.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
NamedExport {
span,
specifiers: export_specifiers,
src: Some(src.clone()),
type_only: false,
with: None,
},
)));
if !origin_specifiers.is_empty() {
stmts.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
NamedExport {
span,
specifiers: origin_specifiers,
src: Some(src),
type_only: false,
with,
},
)));
}
}
_ => {
stmts.push(item);
}
}
}
*items = stmts;
}
}

View file

@ -0,0 +1,23 @@
use swc_ecma_ast::{ExportAll, ImportDecl, NamedExport};
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut};
pub fn import_assertions() -> impl VisitMut + Fold {
as_folder(ImportAssertions)
}
struct ImportAssertions;
impl VisitMut for ImportAssertions {
noop_visit_mut_type!();
fn visit_mut_import_decl(&mut self, n: &mut ImportDecl) {
n.with = None;
}
fn visit_mut_export_all(&mut self, n: &mut ExportAll) {
n.with = None;
}
fn visit_mut_named_export(&mut self, n: &mut NamedExport) {
n.with = None;
}
}

View file

@ -0,0 +1,13 @@
#![deny(clippy::all)]
#![allow(clippy::vec_box)]
pub use self::{
decorators::decorators, export_default_from::export_default_from,
import_assertions::import_assertions,
};
pub mod decorator_2022_03;
pub mod decorators;
pub mod explicit_resource_management;
mod export_default_from;
mod import_assertions;