swc_typescript/fast_dts/util/
ast_ext.rs

1use std::borrow::Cow;
2
3use swc_atoms::Atom;
4use swc_common::Mark;
5use swc_ecma_ast::{
6    BindingIdent, ComputedPropName, Expr, Ident, Lit, MemberProp, ObjectPatProp, Pat, Prop,
7    PropName, TsTypeAnn,
8};
9
10pub trait ExprExit {
11    fn get_root_ident(&self) -> Option<&Ident>;
12    fn get_global_symbol_prop(&self, unresolved_mark: Mark) -> Option<&MemberProp>;
13}
14
15impl ExprExit for Expr {
16    fn get_root_ident(&self) -> Option<&Ident> {
17        match self {
18            Expr::Member(member_expr) => member_expr.obj.get_root_ident(),
19            Expr::Ident(ident) => Some(ident),
20            Expr::OptChain(opt_chain_expr) => opt_chain_expr
21                .base
22                .as_member()
23                .and_then(|member_expr| member_expr.obj.get_root_ident()),
24            _ => None,
25        }
26    }
27
28    fn get_global_symbol_prop(&self, unresolved_mark: Mark) -> Option<&MemberProp> {
29        let (obj, prop) = (match self {
30            Expr::Member(member) => Some((&member.obj, &member.prop)),
31            Expr::OptChain(opt_chain) => opt_chain
32                .base
33                .as_member()
34                .map(|member| (&member.obj, &member.prop)),
35            _ => None,
36        })?;
37
38        // https://github.com/microsoft/TypeScript/blob/cbac1ddfc73ca3b9d8741c1b51b74663a0f24695/src/compiler/transformers/declarations.ts#L1011
39        if let Some(ident) = obj.as_ident() {
40            // Exactly `Symbol.something` and `Symbol` either does not resolve
41            // or definitely resolves to the global Symbol
42            return if ident.sym.as_str() == "Symbol" && ident.ctxt.has_mark(unresolved_mark) {
43                Some(prop)
44            } else {
45                None
46            };
47        }
48
49        if let Some(member_expr) = obj.as_member() {
50            // Exactly `globalThis.Symbol.something` and `globalThis` resolves
51            // to the global `globalThis`
52            if let Some(ident) = member_expr.obj.as_ident() {
53                if ident.sym.as_str() == "globalThis"
54                    && ident.ctxt.has_mark(unresolved_mark)
55                    && member_expr.prop.is_ident_with("Symbol")
56                {
57                    return Some(prop);
58                }
59            }
60        }
61
62        None
63    }
64}
65
66pub trait PatExt {
67    fn get_type_ann(&self) -> &Option<Box<TsTypeAnn>>;
68    fn set_type_ann(&mut self, type_anno: Option<Box<TsTypeAnn>>);
69    fn bound_names<F: FnMut(&BindingIdent)>(&self, f: &mut F);
70}
71
72impl PatExt for Pat {
73    fn get_type_ann(&self) -> &Option<Box<TsTypeAnn>> {
74        let pat = match self {
75            Pat::Assign(assign_pat) => &assign_pat.left,
76            _ => self,
77        };
78
79        match pat {
80            Pat::Ident(binding_ident) => &binding_ident.type_ann,
81            Pat::Array(array_pat) => &array_pat.type_ann,
82            Pat::Rest(rest_pat) => &rest_pat.type_ann,
83            Pat::Object(object_pat) => &object_pat.type_ann,
84            Pat::Assign(_) | Pat::Invalid(_) | Pat::Expr(_) => &None,
85            #[cfg(swc_ast_unknown)]
86            _ => panic!("unable to access unknown nodes"),
87        }
88    }
89
90    fn set_type_ann(&mut self, type_anno: Option<Box<TsTypeAnn>>) {
91        let pat = match self {
92            Pat::Assign(assign_pat) => &mut assign_pat.left,
93            _ => self,
94        };
95
96        match pat {
97            Pat::Ident(binding_ident) => binding_ident.type_ann = type_anno,
98            Pat::Array(array_pat) => array_pat.type_ann = type_anno,
99            Pat::Rest(rest_pat) => rest_pat.type_ann = type_anno,
100            Pat::Object(object_pat) => object_pat.type_ann = type_anno,
101            Pat::Assign(_) | Pat::Invalid(_) | Pat::Expr(_) => {}
102            #[cfg(swc_ast_unknown)]
103            _ => panic!("unable to access unknown nodes"),
104        }
105    }
106
107    fn bound_names<F: FnMut(&BindingIdent)>(&self, f: &mut F) {
108        match self {
109            Pat::Ident(binding_ident) => f(binding_ident),
110            Pat::Array(array_pat) => {
111                for pat in array_pat.elems.iter().flatten() {
112                    pat.bound_names(f);
113                }
114            }
115            Pat::Rest(rest_pat) => rest_pat.arg.bound_names(f),
116            Pat::Object(object_pat) => {
117                for pat in &object_pat.props {
118                    match pat {
119                        ObjectPatProp::KeyValue(key_value_pat_prop) => {
120                            key_value_pat_prop.value.bound_names(f)
121                        }
122                        ObjectPatProp::Assign(assign_pat_prop) => f(&assign_pat_prop.key),
123                        ObjectPatProp::Rest(rest_pat) => rest_pat.arg.bound_names(f),
124                        #[cfg(swc_ast_unknown)]
125                        _ => panic!("unable to access unknown nodes"),
126                    }
127                }
128            }
129            Pat::Assign(assign_pat) => assign_pat.left.bound_names(f),
130            Pat::Invalid(_) | Pat::Expr(_) => todo!(),
131            #[cfg(swc_ast_unknown)]
132            _ => panic!("unable to access unknown nodes"),
133        }
134    }
135}
136
137pub trait PropNameExit {
138    fn static_name(&self) -> Option<Cow<str>>;
139    fn static_prop(&self, unresolved_mark: Mark) -> Option<StaticProp>;
140}
141
142impl PropNameExit for PropName {
143    fn static_name(&self) -> Option<Cow<str>> {
144        match self {
145            PropName::Ident(ident_name) => Some(Cow::Borrowed(ident_name.sym.as_str())),
146            PropName::Str(string) => Some(Cow::Borrowed(string.value.as_str()?)),
147            PropName::Num(number) => Some(Cow::Owned(number.value.to_string())),
148            PropName::BigInt(big_int) => Some(Cow::Owned(big_int.value.to_string())),
149            PropName::Computed(computed_prop_name) => computed_prop_name.static_name(),
150            #[cfg(swc_ast_unknown)]
151            _ => panic!("unable to access unknown nodes"),
152        }
153    }
154
155    fn static_prop(&self, unresolved_mark: Mark) -> Option<StaticProp> {
156        match self {
157            PropName::Computed(c) => c.static_prop(unresolved_mark),
158            prop => prop.static_name().map(Into::into).map(StaticProp::Name),
159        }
160    }
161}
162
163impl PropNameExit for ComputedPropName {
164    fn static_name(&self) -> Option<Cow<str>> {
165        match self.expr.as_ref() {
166            Expr::Lit(lit) => match lit {
167                Lit::Str(string) => Some(Cow::Borrowed(string.value.as_str()?)),
168                Lit::Bool(b) => Some(Cow::Owned(b.value.to_string())),
169                Lit::Null(_) => Some(Cow::Borrowed("null")),
170                Lit::Num(number) => Some(Cow::Owned(number.value.to_string())),
171                Lit::BigInt(big_int) => Some(Cow::Owned(big_int.value.to_string())),
172                Lit::Regex(regex) => Some(Cow::Owned(regex.exp.to_string())),
173                Lit::JSXText(_) => None,
174                #[cfg(swc_ast_unknown)]
175                _ => panic!("unable to access unknown nodes"),
176            },
177            Expr::Tpl(tpl) if tpl.exprs.is_empty() => tpl
178                .quasis
179                .first()
180                .and_then(|e| e.cooked.as_ref())
181                .and_then(|atom| atom.as_str().map(Cow::Borrowed)),
182            _ => None,
183        }
184    }
185
186    fn static_prop(&self, unresolved_mark: Mark) -> Option<StaticProp> {
187        match self.expr.as_ref() {
188            Expr::Member(..) | Expr::OptChain(..) => self
189                .expr
190                .get_global_symbol_prop(unresolved_mark)
191                .and_then(|prop| match prop {
192                    MemberProp::Ident(ident_name) => {
193                        Some(StaticProp::Symbol(ident_name.sym.clone()))
194                    }
195                    MemberProp::Computed(c) => {
196                        c.static_name().map(Into::into).map(StaticProp::Symbol)
197                    }
198                    MemberProp::PrivateName(..) => None,
199                    #[cfg(swc_ast_unknown)]
200                    _ => panic!("unable to access unknown nodes"),
201                }),
202            _ => self.static_name().map(Into::into).map(StaticProp::Name),
203        }
204    }
205}
206
207impl PropNameExit for Prop {
208    fn static_name(&self) -> Option<Cow<str>> {
209        match self {
210            Self::Shorthand(ident_name) => Some(Cow::Borrowed(ident_name.sym.as_str())),
211            Self::KeyValue(key_value_prop) => key_value_prop.key.static_name(),
212            Self::Assign(..) => None,
213            Self::Getter(getter_prop) => getter_prop.key.static_name(),
214            Self::Setter(setter_prop) => setter_prop.key.static_name(),
215            Self::Method(method_prop) => method_prop.key.static_name(),
216            #[cfg(swc_ast_unknown)]
217            _ => panic!("unable to access unknown nodes"),
218        }
219    }
220
221    fn static_prop(&self, unresolved_mark: Mark) -> Option<StaticProp> {
222        match self {
223            Self::Shorthand(ident_name) => Some(StaticProp::Name(ident_name.sym.clone())),
224            Self::KeyValue(key_value_prop) => key_value_prop.key.static_prop(unresolved_mark),
225            Self::Assign(..) => None,
226            Self::Getter(getter_prop) => getter_prop.key.static_prop(unresolved_mark),
227            Self::Setter(setter_prop) => setter_prop.key.static_prop(unresolved_mark),
228            Self::Method(method_prop) => method_prop.key.static_prop(unresolved_mark),
229            #[cfg(swc_ast_unknown)]
230            _ => panic!("unable to access unknown nodes"),
231        }
232    }
233}
234
235pub trait MemberPropExt {
236    fn static_name(&self) -> Option<&Atom>;
237}
238
239impl MemberPropExt for MemberProp {
240    fn static_name(&self) -> Option<&Atom> {
241        match self {
242            MemberProp::Ident(ident_name) => Some(&ident_name.sym),
243            MemberProp::Computed(computed_prop_name) => match computed_prop_name.expr.as_ref() {
244                Expr::Lit(Lit::Str(s)) => s.value.as_atom(),
245                Expr::Tpl(tpl) if tpl.quasis.len() == 1 && tpl.exprs.is_empty() => {
246                    Some(&tpl.quasis[0].raw)
247                }
248                _ => None,
249            },
250            MemberProp::PrivateName(_) => None,
251            #[cfg(swc_ast_unknown)]
252            _ => panic!("unable to access unknown nodes"),
253        }
254    }
255}
256
257#[derive(Eq, Hash, PartialEq, Clone)]
258pub(crate) enum StaticProp {
259    Name(Atom),
260    Symbol(Atom),
261}