Skip to main content

ecma_syntax_cat/
module.rs

1//! ECMAScript module-level items: imports and exports.
2
3use crate::expression::Expression;
4use crate::identifier::Identifier;
5use crate::span::Spanned;
6use crate::statement::Statement;
7
8/// One top-level item inside a module (either a statement or a module
9/// declaration).
10pub type ModuleItem = Spanned<ModuleItemKind>;
11
12/// The shape of a module-level item.
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum ModuleItemKind {
15    /// A regular statement (variable declaration, function, expression
16    /// statement, etc.).
17    Statement(Statement),
18    /// `import ... from "..."`
19    Import(ImportDeclaration),
20    /// `export { ... }`, `export let x = 1;`, etc.
21    Export(ExportDeclaration),
22}
23
24impl std::fmt::Display for ModuleItemKind {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        match self {
27            Self::Statement(stmt) => write!(f, "{stmt}"),
28            Self::Import(decl) => write!(f, "{decl}"),
29            Self::Export(decl) => write!(f, "{decl}"),
30        }
31    }
32}
33
34/// An `import` declaration.
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct ImportDeclaration {
37    specifiers: Vec<ImportSpecifier>,
38    source: String,
39}
40
41impl ImportDeclaration {
42    /// Build an import declaration.  `source` is the module specifier
43    /// string (e.g. `"./foo.js"`).
44    #[must_use]
45    pub fn new(specifiers: Vec<ImportSpecifier>, source: impl Into<String>) -> Self {
46        Self {
47            specifiers,
48            source: source.into(),
49        }
50    }
51
52    /// The import specifiers (named, default, namespace).
53    #[must_use]
54    pub fn specifiers(&self) -> &[ImportSpecifier] {
55        &self.specifiers
56    }
57
58    /// The module specifier string.
59    #[must_use]
60    pub fn source(&self) -> &str {
61        &self.source
62    }
63}
64
65impl std::fmt::Display for ImportDeclaration {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        let specs = self
68            .specifiers
69            .iter()
70            .map(|s| format!("{s}"))
71            .collect::<Vec<_>>()
72            .join(", ");
73        write!(f, "import {specs} from {:?};", self.source)
74    }
75}
76
77/// One specifier inside an `import` declaration.
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub enum ImportSpecifier {
80    /// `import { name as local } from ...`.  When `imported == local` this
81    /// is the unaliased form `import { name } from ...`.
82    Named {
83        /// The name exported by the source module.
84        imported: Identifier,
85        /// The local binding name in this module.
86        local: Identifier,
87    },
88    /// `import defaultName from ...`.
89    Default {
90        /// The local binding name for the default export.
91        local: Identifier,
92    },
93    /// `import * as ns from ...`.
94    Namespace {
95        /// The local binding name for the namespace object.
96        local: Identifier,
97    },
98}
99
100impl std::fmt::Display for ImportSpecifier {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        match self {
103            Self::Named { imported, local } => {
104                if imported == local {
105                    write!(f, "{{ {imported} }}")
106                } else {
107                    write!(f, "{{ {imported} as {local} }}")
108                }
109            }
110            Self::Default { local } => write!(f, "{local}"),
111            Self::Namespace { local } => write!(f, "* as {local}"),
112        }
113    }
114}
115
116/// An `export` declaration.
117#[derive(Debug, Clone, PartialEq, Eq)]
118pub enum ExportDeclaration {
119    /// `export { a, b as c } from? "..."?`
120    Named {
121        /// Named specifiers.
122        specifiers: Vec<ExportSpecifier>,
123        /// Optional re-export source.
124        source: Option<String>,
125    },
126    /// `export default expression` or `export default declaration`.
127    Default {
128        /// The exported expression or declaration.
129        declaration: ExportDefault,
130    },
131    /// `export let x = ...` or `export function ...`.
132    Declaration {
133        /// The accompanying statement (typically a function/class/variable
134        /// declaration).
135        declaration: Statement,
136    },
137    /// `export * from "..."` or `export * as ns from "..."`.
138    All {
139        /// Optional namespace name for `export * as ns`.
140        exported: Option<Identifier>,
141        /// The source module specifier.
142        source: String,
143    },
144}
145
146impl std::fmt::Display for ExportDeclaration {
147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148        match self {
149            Self::Named { specifiers, source } => {
150                write_export_named(f, specifiers, source.as_deref())
151            }
152            Self::Default { declaration } => write!(f, "export default {declaration};"),
153            Self::Declaration { declaration } => write!(f, "export {declaration}"),
154            Self::All { exported, source } => write_export_all(f, exported.as_ref(), source),
155        }
156    }
157}
158
159fn write_export_named(
160    f: &mut std::fmt::Formatter<'_>,
161    specifiers: &[ExportSpecifier],
162    source: Option<&str>,
163) -> std::fmt::Result {
164    let specs = specifiers
165        .iter()
166        .map(|s| format!("{s}"))
167        .collect::<Vec<_>>()
168        .join(", ");
169    match source {
170        Some(src) => write!(f, "export {{ {specs} }} from {src:?};"),
171        None => write!(f, "export {{ {specs} }};"),
172    }
173}
174
175fn write_export_all(
176    f: &mut std::fmt::Formatter<'_>,
177    exported: Option<&Identifier>,
178    source: &str,
179) -> std::fmt::Result {
180    match exported {
181        Some(name) => write!(f, "export * as {name} from {source:?};"),
182        None => write!(f, "export * from {source:?};"),
183    }
184}
185
186/// One specifier inside a named-export declaration.
187#[derive(Debug, Clone, PartialEq, Eq)]
188pub struct ExportSpecifier {
189    local: Identifier,
190    exported: Identifier,
191}
192
193impl ExportSpecifier {
194    /// Build an export specifier.
195    #[must_use]
196    pub fn new(local: Identifier, exported: Identifier) -> Self {
197        Self { local, exported }
198    }
199
200    /// The local binding being exported.
201    #[must_use]
202    pub fn local(&self) -> &Identifier {
203        &self.local
204    }
205
206    /// The name under which it is exported.  When `local == exported` the
207    /// `as` clause was omitted.
208    #[must_use]
209    pub fn exported(&self) -> &Identifier {
210        &self.exported
211    }
212}
213
214impl std::fmt::Display for ExportSpecifier {
215    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216        if self.local == self.exported {
217            write!(f, "{}", self.local)
218        } else {
219            write!(f, "{} as {}", self.local, self.exported)
220        }
221    }
222}
223
224/// What may appear after `export default`.
225#[derive(Debug, Clone, PartialEq, Eq)]
226pub enum ExportDefault {
227    /// `export default <expression>;`
228    Expression(Expression),
229    /// `export default function ...` (may be anonymous).
230    Function(crate::function::Function),
231    /// `export default class ...` (may be anonymous).
232    Class(crate::class::Class),
233}
234
235impl std::fmt::Display for ExportDefault {
236    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237        match self {
238            Self::Expression(expr) => write!(f, "{expr}"),
239            Self::Function(func) => write!(f, "{func}"),
240            Self::Class(class) => write!(f, "{class}"),
241        }
242    }
243}