Skip to main content

swc_ecma_ast/
module_decl.rs

1use std::borrow::Cow;
2
3use is_macro::Is;
4use swc_atoms::Atom;
5use swc_common::{ast_node, util::take::Take, EqIgnoreSpan, Span, DUMMY_SP};
6
7use crate::{
8    decl::Decl,
9    expr::{ClassExpr, Expr, FnExpr},
10    ident::Ident,
11    lit::Str,
12    typescript::{TsExportAssignment, TsImportEqualsDecl, TsInterfaceDecl, TsNamespaceExportDecl},
13    BindingIdent, IdentName, ObjectLit,
14};
15
16#[ast_node]
17#[derive(Eq, Hash, Is, EqIgnoreSpan)]
18#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
19#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
20pub enum ModuleDecl {
21    #[tag("ImportDeclaration")]
22    Import(ImportDecl),
23
24    #[tag("ExportDeclaration")]
25    ExportDecl(ExportDecl),
26
27    #[tag("ExportNamedDeclaration")]
28    ExportNamed(NamedExport),
29
30    #[tag("ExportDefaultDeclaration")]
31    ExportDefaultDecl(ExportDefaultDecl),
32
33    #[tag("ExportDefaultExpression")]
34    ExportDefaultExpr(ExportDefaultExpr),
35
36    #[tag("ExportAllDeclaration")]
37    ExportAll(ExportAll),
38
39    #[tag("TsImportEqualsDeclaration")]
40    TsImportEquals(Box<TsImportEqualsDecl>),
41
42    #[tag("TsExportAssignment")]
43    TsExportAssignment(TsExportAssignment),
44
45    #[tag("TsNamespaceExportDeclaration")]
46    TsNamespaceExport(TsNamespaceExportDecl),
47}
48
49boxed!(ModuleDecl, [TsImportEqualsDecl]);
50
51macro_rules! module_decl {
52    ([$($variant:ty),*]) => {
53        $(
54            bridge_from!(crate::ModuleItem, crate::ModuleDecl, $variant);
55        )*
56    };
57}
58
59module_decl!([
60    ImportDecl,
61    ExportDecl,
62    NamedExport,
63    ExportDefaultDecl,
64    ExportDefaultExpr,
65    ExportAll,
66    TsImportEqualsDecl,
67    TsExportAssignment,
68    TsNamespaceExportDecl
69]);
70
71impl Take for ModuleDecl {
72    fn dummy() -> Self {
73        ImportDecl::dummy().into()
74    }
75}
76
77/// Default exports other than **direct** function expression or class
78/// expression.
79///
80///
81/// # Note
82///
83/// ```ts
84/// export default function Foo() {
85/// }
86/// ```
87///
88/// is [`ExportDefaultDecl`] and it's hoisted.
89///
90/// ```ts
91/// export default (function Foo() {
92/// })
93/// ```
94///
95/// is [`ExportDefaultExpr`] and it's not hoisted.
96#[ast_node("ExportDefaultExpression")]
97#[derive(Eq, Hash, EqIgnoreSpan)]
98#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
99#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
100pub struct ExportDefaultExpr {
101    pub span: Span,
102
103    #[cfg_attr(feature = "serde-impl", serde(rename = "expression"))]
104    pub expr: Box<Expr>,
105}
106
107#[ast_node("ExportDeclaration")]
108#[derive(Eq, Hash, EqIgnoreSpan)]
109#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
110#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
111pub struct ExportDecl {
112    pub span: Span,
113
114    #[cfg_attr(feature = "serde-impl", serde(rename = "declaration"))]
115    pub decl: Decl,
116}
117
118#[ast_node("ImportDeclaration")]
119#[derive(Eq, Hash, EqIgnoreSpan)]
120#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
121#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
122pub struct ImportDecl {
123    pub span: Span,
124
125    #[cfg_attr(feature = "serde-impl", serde(default))]
126    pub specifiers: Vec<ImportSpecifier>,
127
128    #[cfg_attr(feature = "serde-impl", serde(rename = "source"))]
129    pub src: Box<Str>,
130
131    #[cfg_attr(feature = "serde-impl", serde(default, rename = "typeOnly"))]
132    pub type_only: bool,
133
134    #[cfg_attr(feature = "serde-impl", serde(default))]
135    #[cfg_attr(
136        feature = "encoding-impl",
137        encoding(with = "cbor4ii::core::types::Maybe")
138    )]
139    pub with: Option<Box<ObjectLit>>,
140
141    #[cfg_attr(feature = "serde-impl", serde(default))]
142    pub phase: ImportPhase,
143}
144
145#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default, EqIgnoreSpan)]
146#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
147#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
148#[cfg_attr(feature = "serde-impl", derive(serde::Serialize, serde::Deserialize))]
149#[cfg_attr(
150    feature = "encoding-impl",
151    derive(::swc_common::Encode, ::swc_common::Decode)
152)]
153#[cfg_attr(swc_ast_unknown, non_exhaustive)]
154pub enum ImportPhase {
155    #[default]
156    #[cfg_attr(feature = "serde-impl", serde(rename = "evaluation"))]
157    Evaluation,
158    #[cfg_attr(feature = "serde-impl", serde(rename = "source"))]
159    Source,
160    #[cfg_attr(feature = "serde-impl", serde(rename = "defer"))]
161    Defer,
162}
163
164impl Take for ImportDecl {
165    fn dummy() -> Self {
166        ImportDecl {
167            span: DUMMY_SP,
168            specifiers: Take::dummy(),
169            src: Take::dummy(),
170            type_only: Default::default(),
171            with: Take::dummy(),
172            phase: Default::default(),
173        }
174    }
175}
176
177/// `export * from 'mod'`
178#[ast_node("ExportAllDeclaration")]
179#[derive(Eq, Hash, EqIgnoreSpan)]
180#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
181#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
182pub struct ExportAll {
183    pub span: Span,
184
185    #[cfg_attr(feature = "serde-impl", serde(rename = "source"))]
186    pub src: Box<Str>,
187
188    #[cfg_attr(feature = "serde-impl", serde(rename = "typeOnly"))]
189    pub type_only: bool,
190
191    #[cfg_attr(feature = "serde-impl", serde(default))]
192    #[cfg_attr(
193        feature = "encoding-impl",
194        encoding(with = "cbor4ii::core::types::Maybe")
195    )]
196    pub with: Option<Box<ObjectLit>>,
197}
198
199impl Take for ExportAll {
200    fn dummy() -> Self {
201        Self {
202            span: DUMMY_SP,
203            src: Take::dummy(),
204            type_only: Default::default(),
205            with: Take::dummy(),
206        }
207    }
208}
209
210/// `export { foo } from 'mod'`
211/// `export { foo as bar } from 'mod'`
212#[ast_node("ExportNamedDeclaration")]
213#[derive(Eq, Hash, EqIgnoreSpan)]
214#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
215#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
216pub struct NamedExport {
217    pub span: Span,
218
219    pub specifiers: Vec<ExportSpecifier>,
220
221    #[cfg_attr(feature = "serde-impl", serde(rename = "source"))]
222    #[cfg_attr(
223        feature = "encoding-impl",
224        encoding(with = "cbor4ii::core::types::Maybe")
225    )]
226    pub src: Option<Box<Str>>,
227
228    #[cfg_attr(feature = "serde-impl", serde(rename = "typeOnly"))]
229    pub type_only: bool,
230
231    #[cfg_attr(feature = "serde-impl", serde(default))]
232    #[cfg_attr(
233        feature = "encoding-impl",
234        encoding(with = "cbor4ii::core::types::Maybe")
235    )]
236    pub with: Option<Box<ObjectLit>>,
237}
238
239impl Take for NamedExport {
240    fn dummy() -> Self {
241        Self {
242            span: DUMMY_SP,
243            specifiers: Take::dummy(),
244            src: Take::dummy(),
245            type_only: Default::default(),
246            with: Take::dummy(),
247        }
248    }
249}
250
251#[ast_node("ExportDefaultDeclaration")]
252#[derive(Eq, Hash, EqIgnoreSpan)]
253#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
254#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
255pub struct ExportDefaultDecl {
256    pub span: Span,
257
258    pub decl: DefaultDecl,
259}
260
261#[ast_node]
262#[derive(Eq, Hash, Is, EqIgnoreSpan)]
263#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
264#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
265pub enum DefaultDecl {
266    #[tag("ClassExpression")]
267    Class(ClassExpr),
268
269    #[tag("FunctionExpression")]
270    #[is(name = "fn_expr")]
271    Fn(FnExpr),
272
273    #[tag("TsInterfaceDeclaration")]
274    TsInterfaceDecl(Box<TsInterfaceDecl>),
275}
276
277#[ast_node]
278#[derive(Eq, Hash, Is, EqIgnoreSpan)]
279#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
280#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
281pub enum ImportSpecifier {
282    #[tag("ImportSpecifier")]
283    Named(ImportNamedSpecifier),
284    #[tag("ImportDefaultSpecifier")]
285    Default(ImportDefaultSpecifier),
286    #[tag("ImportNamespaceSpecifier")]
287    Namespace(ImportStarAsSpecifier),
288}
289
290impl ImportSpecifier {
291    pub fn is_type_only(&self) -> bool {
292        match self {
293            ImportSpecifier::Named(named) => named.is_type_only,
294            ImportSpecifier::Default(..) | ImportSpecifier::Namespace(..) => false,
295            #[cfg(all(swc_ast_unknown, feature = "encoding-impl"))]
296            _ => swc_common::unknown!(),
297        }
298    }
299
300    pub fn local(&self) -> &Ident {
301        match self {
302            ImportSpecifier::Named(named) => &named.local,
303            ImportSpecifier::Default(default) => &default.local,
304            ImportSpecifier::Namespace(ns) => &ns.local,
305            #[cfg(all(swc_ast_unknown, feature = "encoding-impl"))]
306            _ => swc_common::unknown!(),
307        }
308    }
309
310    pub fn local_mut(&mut self) -> &mut Ident {
311        match self {
312            ImportSpecifier::Named(named) => &mut named.local,
313            ImportSpecifier::Default(default) => &mut default.local,
314            ImportSpecifier::Namespace(ns) => &mut ns.local,
315            #[cfg(all(swc_ast_unknown, feature = "encoding-impl"))]
316            _ => swc_common::unknown!(),
317        }
318    }
319}
320
321/// e.g. `import foo from 'mod.js'`
322#[ast_node("ImportDefaultSpecifier")]
323#[derive(Eq, Hash, EqIgnoreSpan)]
324#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
325#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
326pub struct ImportDefaultSpecifier {
327    pub span: Span,
328
329    pub local: Ident,
330}
331/// e.g. `import * as foo from 'mod.js'`.
332#[ast_node("ImportNamespaceSpecifier")]
333#[derive(Eq, Hash, EqIgnoreSpan)]
334#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
335#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
336pub struct ImportStarAsSpecifier {
337    pub span: Span,
338
339    pub local: Ident,
340}
341/// e.g. local = foo, imported = None `import { foo } from 'mod.js'`
342/// e.g. local = bar, imported = Some(foo) for `import { foo as bar } from
343/// 'mod.js'`
344#[ast_node("ImportSpecifier")]
345#[derive(Eq, Hash, EqIgnoreSpan)]
346#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
347#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
348pub struct ImportNamedSpecifier {
349    pub span: Span,
350
351    pub local: Ident,
352
353    #[cfg_attr(feature = "serde-impl", serde(default))]
354    #[cfg_attr(
355        feature = "encoding-impl",
356        encoding(with = "cbor4ii::core::types::Maybe")
357    )]
358    pub imported: Option<ModuleExportName>,
359
360    #[cfg_attr(feature = "serde-impl", serde(default))]
361    pub is_type_only: bool,
362}
363
364#[ast_node]
365#[derive(Eq, Hash, Is, EqIgnoreSpan)]
366#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
367#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
368pub enum ExportSpecifier {
369    #[tag("ExportNamespaceSpecifier")]
370    Namespace(ExportNamespaceSpecifier),
371
372    #[tag("ExportDefaultSpecifier")]
373    Default(ExportDefaultSpecifier),
374
375    #[tag("ExportSpecifier")]
376    Named(ExportNamedSpecifier),
377}
378
379/// `export * as foo from 'src';`
380#[ast_node("ExportNamespaceSpecifier")]
381#[derive(Eq, Hash, EqIgnoreSpan)]
382#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
383#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
384pub struct ExportNamespaceSpecifier {
385    pub span: Span,
386
387    pub name: ModuleExportName,
388}
389
390// export v from 'mod';
391#[ast_node("ExportDefaultSpecifier")]
392#[derive(Eq, Hash, EqIgnoreSpan)]
393#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
394#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
395pub struct ExportDefaultSpecifier {
396    #[span]
397    pub exported: Ident,
398}
399
400#[ast_node("ExportSpecifier")]
401#[derive(Eq, Hash, EqIgnoreSpan)]
402#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
403#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
404pub struct ExportNamedSpecifier {
405    pub span: Span,
406    /// `foo` in `export { foo as bar }`
407    pub orig: ModuleExportName,
408    /// `Some(bar)` in `export { foo as bar }`
409    #[cfg_attr(feature = "serde-impl", serde(default))]
410    #[cfg_attr(
411        feature = "encoding-impl",
412        encoding(with = "cbor4ii::core::types::Maybe")
413    )]
414    pub exported: Option<ModuleExportName>,
415    /// `type` in `export { type foo as bar }`
416    #[cfg_attr(feature = "serde-impl", serde(default))]
417    pub is_type_only: bool,
418}
419
420#[ast_node]
421#[derive(Eq, Hash, EqIgnoreSpan)]
422#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
423#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
424// https://tc39.es/ecma262/#prod-ModuleExportName
425pub enum ModuleExportName {
426    #[tag("Identifier")]
427    Ident(Ident),
428
429    #[tag("StringLiteral")]
430    Str(Str),
431}
432
433bridge_from!(ModuleExportName, Ident, BindingIdent);
434bridge_from!(ModuleExportName, Ident, IdentName);
435
436impl ModuleExportName {
437    /// Get the atom of the export name.
438    ///
439    /// If the name is a string literal that has ill-formed UTF16, it will be
440    /// converted to a UTF-8 valid string using lossy conversion (Unpaired
441    /// surrogates are replaced with Replacement Character).  This is
442    /// a SyntaxError if it's fully complied to the spec.
443    /// See: https://tc39.es/ecma262/#sec-module-semantics-static-semantics-early-errors
444    pub fn atom(&self) -> Cow<'_, Atom> {
445        match self {
446            ModuleExportName::Ident(i) => Cow::Borrowed(&i.sym),
447            ModuleExportName::Str(s) => match s.value.clone().try_into_atom() {
448                Ok(atom) => Cow::Owned(atom),
449                Err(original) => Cow::Owned(Atom::from(&*original.to_string_lossy())),
450            },
451            #[cfg(all(swc_ast_unknown, feature = "encoding-impl"))]
452            _ => swc_common::unknown!(),
453        }
454    }
455}