Skip to main content

boa_ast/module_item_list/
mod.rs

1//! Module item list AST nodes.
2//!
3//! More information:
4//!  - [ECMAScript specification][spec]
5//!
6//! [spec]: https://tc39.es/ecma262/#sec-modules
7
8use crate::{
9    StatementListItem,
10    declaration::{
11        ExportDeclaration, ExportEntry, ExportSpecifier, ImportAttribute, ImportDeclaration,
12        ImportEntry, ImportKind, ImportName, IndirectExportEntry, LocalExportEntry,
13        ModuleSpecifier, ReExportImportName, ReExportKind,
14    },
15    operations::{BoundNamesVisitor, bound_names},
16    visitor::{VisitWith, Visitor, VisitorMut},
17};
18use boa_interner::Sym;
19use indexmap::IndexSet;
20use rustc_hash::{FxHashSet, FxHasher};
21use std::{convert::Infallible, hash::BuildHasherDefault, ops::ControlFlow};
22
23/// Module item list AST node.
24///
25/// It contains a list of module items.
26///
27/// More information:
28///  - [ECMAScript specification][spec]
29///
30/// [spec]: https://tc39.es/ecma262/#prod-ModuleItemList
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32#[derive(Clone, Debug, Default, PartialEq)]
33pub struct ModuleItemList {
34    items: Box<[ModuleItem]>,
35}
36
37impl ModuleItemList {
38    /// Gets the list of module items.
39    #[inline]
40    #[must_use]
41    pub const fn items(&self) -> &[ModuleItem] {
42        &self.items
43    }
44
45    /// Abstract operation [`ExportedNames`][spec].
46    ///
47    /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-exportednames
48    #[inline]
49    #[must_use]
50    pub fn exported_names(&self) -> Vec<Sym> {
51        #[derive(Debug)]
52        struct ExportedItemsVisitor<'vec>(&'vec mut Vec<Sym>);
53
54        impl<'ast> Visitor<'ast> for ExportedItemsVisitor<'_> {
55            type BreakTy = Infallible;
56
57            fn visit_import_declaration(
58                &mut self,
59                _: &'ast ImportDeclaration,
60            ) -> ControlFlow<Self::BreakTy> {
61                ControlFlow::Continue(())
62            }
63            fn visit_statement_list_item(
64                &mut self,
65                _: &'ast StatementListItem,
66            ) -> ControlFlow<Self::BreakTy> {
67                ControlFlow::Continue(())
68            }
69            fn visit_export_specifier(
70                &mut self,
71                node: &'ast ExportSpecifier,
72            ) -> ControlFlow<Self::BreakTy> {
73                self.0.push(node.alias());
74                ControlFlow::Continue(())
75            }
76            fn visit_export_declaration(
77                &mut self,
78                node: &'ast ExportDeclaration,
79            ) -> ControlFlow<Self::BreakTy> {
80                match node {
81                    ExportDeclaration::ReExport { kind, .. } => {
82                        match kind {
83                            ReExportKind::Namespaced { name: Some(name) } => self.0.push(*name),
84                            ReExportKind::Namespaced { name: None } => {}
85                            ReExportKind::Named { names } => {
86                                for specifier in &**names {
87                                    self.visit_export_specifier(specifier)?;
88                                }
89                            }
90                        }
91                        ControlFlow::Continue(())
92                    }
93                    ExportDeclaration::List(list) => {
94                        for specifier in &**list {
95                            self.visit_export_specifier(specifier)?;
96                        }
97                        ControlFlow::Continue(())
98                    }
99                    ExportDeclaration::VarStatement(var) => {
100                        BoundNamesVisitor(self.0).visit_var_declaration(var)
101                    }
102                    ExportDeclaration::Declaration(decl) => {
103                        BoundNamesVisitor(self.0).visit_declaration(decl)
104                    }
105                    ExportDeclaration::DefaultFunctionDeclaration(_)
106                    | ExportDeclaration::DefaultGeneratorDeclaration(_)
107                    | ExportDeclaration::DefaultAsyncFunctionDeclaration(_)
108                    | ExportDeclaration::DefaultAsyncGeneratorDeclaration(_)
109                    | ExportDeclaration::DefaultClassDeclaration(_)
110                    | ExportDeclaration::DefaultAssignmentExpression(_) => {
111                        self.0.push(Sym::DEFAULT);
112                        ControlFlow::Continue(())
113                    }
114                }
115            }
116        }
117
118        let mut names = Vec::new();
119
120        let _ = ExportedItemsVisitor(&mut names).visit_module_item_list(self);
121
122        names
123    }
124
125    /// Abstract operation [`ExportedBindings`][spec].
126    ///
127    /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-exportedbindings
128    #[inline]
129    #[must_use]
130    pub fn exported_bindings(&self) -> FxHashSet<Sym> {
131        #[derive(Debug)]
132        struct ExportedBindingsVisitor<'vec>(&'vec mut FxHashSet<Sym>);
133
134        impl<'ast> Visitor<'ast> for ExportedBindingsVisitor<'_> {
135            type BreakTy = Infallible;
136
137            fn visit_import_declaration(
138                &mut self,
139                _: &'ast ImportDeclaration,
140            ) -> ControlFlow<Self::BreakTy> {
141                ControlFlow::Continue(())
142            }
143            fn visit_statement_list_item(
144                &mut self,
145                _: &'ast StatementListItem,
146            ) -> ControlFlow<Self::BreakTy> {
147                ControlFlow::Continue(())
148            }
149            fn visit_export_specifier(
150                &mut self,
151                node: &'ast ExportSpecifier,
152            ) -> ControlFlow<Self::BreakTy> {
153                self.0.insert(node.private_name());
154                ControlFlow::Continue(())
155            }
156            fn visit_export_declaration(
157                &mut self,
158                node: &'ast ExportDeclaration,
159            ) -> ControlFlow<Self::BreakTy> {
160                let name = match node {
161                    ExportDeclaration::ReExport { .. } => return ControlFlow::Continue(()),
162                    ExportDeclaration::List(list) => {
163                        for specifier in &**list {
164                            self.visit_export_specifier(specifier)?;
165                        }
166                        return ControlFlow::Continue(());
167                    }
168                    ExportDeclaration::DefaultAssignmentExpression(expr) => {
169                        return BoundNamesVisitor(self.0).visit_expression(expr);
170                    }
171                    ExportDeclaration::VarStatement(var) => {
172                        return BoundNamesVisitor(self.0).visit_var_declaration(var);
173                    }
174                    ExportDeclaration::Declaration(decl) => {
175                        return BoundNamesVisitor(self.0).visit_declaration(decl);
176                    }
177                    ExportDeclaration::DefaultFunctionDeclaration(f) => f.name(),
178                    ExportDeclaration::DefaultGeneratorDeclaration(g) => g.name(),
179                    ExportDeclaration::DefaultAsyncFunctionDeclaration(af) => af.name(),
180                    ExportDeclaration::DefaultAsyncGeneratorDeclaration(ag) => ag.name(),
181                    ExportDeclaration::DefaultClassDeclaration(cl) => cl.name(),
182                };
183
184                self.0.insert(name.sym());
185
186                ControlFlow::Continue(())
187            }
188        }
189
190        let mut names = FxHashSet::default();
191
192        let _ = ExportedBindingsVisitor(&mut names).visit_module_item_list(self);
193
194        names
195    }
196
197    /// Operation [`ModuleRequests`][spec].
198    ///
199    /// Gets the list of modules that need to be fetched by the module resolver to link this module.
200    ///
201    /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-modulerequests
202    #[inline]
203    #[must_use]
204    pub fn requests(&self) -> IndexSet<Sym, BuildHasherDefault<FxHasher>> {
205        #[derive(Debug)]
206        struct RequestsVisitor<'vec>(&'vec mut IndexSet<Sym, BuildHasherDefault<FxHasher>>);
207
208        impl<'ast> Visitor<'ast> for RequestsVisitor<'_> {
209            type BreakTy = Infallible;
210
211            fn visit_statement_list_item(
212                &mut self,
213                _: &'ast StatementListItem,
214            ) -> ControlFlow<Self::BreakTy> {
215                ControlFlow::Continue(())
216            }
217            fn visit_module_specifier(
218                &mut self,
219                node: &'ast ModuleSpecifier,
220            ) -> ControlFlow<Self::BreakTy> {
221                self.0.insert(node.sym());
222                ControlFlow::Continue(())
223            }
224        }
225
226        let mut requests = IndexSet::default();
227
228        let _ = RequestsVisitor(&mut requests).visit_module_item_list(self);
229
230        requests
231    }
232
233    /// Operation [`ImportEntries`][spec].
234    ///
235    /// Gets the list of import entries of this module.
236    ///
237    /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-importentries
238    #[inline]
239    #[must_use]
240    pub fn import_entries(&self) -> Vec<ImportEntry> {
241        #[derive(Debug)]
242        struct ImportEntriesVisitor<'vec>(&'vec mut Vec<ImportEntry>);
243
244        impl<'ast> Visitor<'ast> for ImportEntriesVisitor<'_> {
245            type BreakTy = Infallible;
246
247            fn visit_module_item(&mut self, node: &'ast ModuleItem) -> ControlFlow<Self::BreakTy> {
248                match node {
249                    ModuleItem::ImportDeclaration(import) => self.visit_import_declaration(import),
250                    ModuleItem::ExportDeclaration(_) | ModuleItem::StatementListItem(_) => {
251                        ControlFlow::Continue(())
252                    }
253                }
254            }
255
256            fn visit_import_declaration(
257                &mut self,
258                node: &'ast ImportDeclaration,
259            ) -> ControlFlow<Self::BreakTy> {
260                let module = node.specifier().sym();
261                let attributes: Box<[ImportAttribute]> = Box::from(node.attributes());
262
263                if let Some(default) = node.default() {
264                    self.0.push(ImportEntry::new(
265                        module,
266                        ImportName::Name(Sym::DEFAULT),
267                        default,
268                        attributes.clone(),
269                    ));
270                }
271
272                match node.kind() {
273                    ImportKind::DefaultOrUnnamed => {}
274                    ImportKind::Namespaced { binding } => {
275                        self.0.push(ImportEntry::new(
276                            module,
277                            ImportName::Namespace,
278                            *binding,
279                            attributes.clone(),
280                        ));
281                    }
282                    ImportKind::Named { names } => {
283                        for name in &**names {
284                            self.0.push(ImportEntry::new(
285                                module,
286                                ImportName::Name(name.export_name()),
287                                name.binding(),
288                                attributes.clone(),
289                            ));
290                        }
291                    }
292                }
293
294                ControlFlow::Continue(())
295            }
296        }
297
298        let mut entries = Vec::default();
299
300        let _ = ImportEntriesVisitor(&mut entries).visit_module_item_list(self);
301
302        entries
303    }
304
305    /// Operation [`ExportEntries`][spec].
306    ///
307    /// Gets the list of export entries of this module.
308    ///
309    /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-exportentries
310    #[inline]
311    #[must_use]
312    pub fn export_entries(&self) -> Vec<ExportEntry> {
313        #[derive(Debug)]
314        struct ExportEntriesVisitor<'vec>(&'vec mut Vec<ExportEntry>);
315
316        impl<'ast> Visitor<'ast> for ExportEntriesVisitor<'_> {
317            type BreakTy = Infallible;
318
319            fn visit_module_item(&mut self, node: &'ast ModuleItem) -> ControlFlow<Self::BreakTy> {
320                match node {
321                    ModuleItem::ExportDeclaration(import) => self.visit_export_declaration(import),
322                    ModuleItem::ImportDeclaration(_) | ModuleItem::StatementListItem(_) => {
323                        ControlFlow::Continue(())
324                    }
325                }
326            }
327
328            fn visit_export_declaration(
329                &mut self,
330                node: &'ast ExportDeclaration,
331            ) -> ControlFlow<Self::BreakTy> {
332                let name = match node {
333                    ExportDeclaration::ReExport {
334                        kind,
335                        specifier,
336                        attributes,
337                    } => {
338                        let module = specifier.sym();
339                        let attrs = attributes.clone();
340
341                        match kind {
342                            ReExportKind::Namespaced { name: Some(name) } => {
343                                self.0.push(
344                                    IndirectExportEntry::new(
345                                        module,
346                                        ReExportImportName::Star,
347                                        *name,
348                                        attrs.clone(),
349                                    )
350                                    .into(),
351                                );
352                            }
353                            ReExportKind::Namespaced { name: None } => {
354                                self.0.push(ExportEntry::StarReExport {
355                                    module_request: module,
356                                    attributes: attrs.clone(),
357                                });
358                            }
359
360                            ReExportKind::Named { names } => {
361                                for name in &**names {
362                                    self.0.push(
363                                        IndirectExportEntry::new(
364                                            module,
365                                            ReExportImportName::Name(name.private_name()),
366                                            name.alias(),
367                                            attrs.clone(),
368                                        )
369                                        .into(),
370                                    );
371                                }
372                            }
373                        }
374
375                        return ControlFlow::Continue(());
376                    }
377                    ExportDeclaration::List(names) => {
378                        for name in &**names {
379                            self.0.push(
380                                LocalExportEntry::new(name.private_name(), name.alias()).into(),
381                            );
382                        }
383                        return ControlFlow::Continue(());
384                    }
385                    ExportDeclaration::VarStatement(var) => {
386                        for name in bound_names(var) {
387                            self.0.push(LocalExportEntry::new(name, name).into());
388                        }
389                        return ControlFlow::Continue(());
390                    }
391                    ExportDeclaration::Declaration(decl) => {
392                        for name in bound_names(decl) {
393                            self.0.push(LocalExportEntry::new(name, name).into());
394                        }
395                        return ControlFlow::Continue(());
396                    }
397                    ExportDeclaration::DefaultFunctionDeclaration(f) => f.name().sym(),
398                    ExportDeclaration::DefaultGeneratorDeclaration(g) => g.name().sym(),
399                    ExportDeclaration::DefaultAsyncFunctionDeclaration(af) => af.name().sym(),
400                    ExportDeclaration::DefaultAsyncGeneratorDeclaration(ag) => ag.name().sym(),
401                    ExportDeclaration::DefaultClassDeclaration(c) => c.name().sym(),
402                    ExportDeclaration::DefaultAssignmentExpression(_) => Sym::DEFAULT_EXPORT,
403                };
404
405                self.0
406                    .push(LocalExportEntry::new(name, Sym::DEFAULT).into());
407
408                ControlFlow::Continue(())
409            }
410        }
411
412        let mut entries = Vec::default();
413
414        let _ = ExportEntriesVisitor(&mut entries).visit_module_item_list(self);
415
416        entries
417    }
418}
419
420impl<T> From<T> for ModuleItemList
421where
422    T: Into<Box<[ModuleItem]>>,
423{
424    #[inline]
425    fn from(items: T) -> Self {
426        Self {
427            items: items.into(),
428        }
429    }
430}
431
432impl VisitWith for ModuleItemList {
433    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
434    where
435        V: Visitor<'a>,
436    {
437        for item in &*self.items {
438            visitor.visit_module_item(item)?;
439        }
440
441        ControlFlow::Continue(())
442    }
443
444    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
445    where
446        V: VisitorMut<'a>,
447    {
448        for item in &mut *self.items {
449            visitor.visit_module_item_mut(item)?;
450        }
451
452        ControlFlow::Continue(())
453    }
454}
455
456/// Module item AST node.
457///
458/// This is an extension over a [`StatementList`](crate::StatementList), which can also include
459/// multiple [`ImportDeclaration`] and [`ExportDeclaration`] nodes, along with
460/// [`StatementListItem`] nodes.
461///
462/// More information:
463///  - [ECMAScript specification][spec]
464///
465/// [spec]: https://tc39.es/ecma262/#prod-ModuleItem
466#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
467#[derive(Clone, Debug, PartialEq)]
468pub enum ModuleItem {
469    /// See [`ImportDeclaration`].
470    ImportDeclaration(ImportDeclaration),
471    /// See [`ExportDeclaration`].
472    ExportDeclaration(Box<ExportDeclaration>),
473    /// See [`StatementListItem`].
474    StatementListItem(StatementListItem),
475}
476
477impl VisitWith for ModuleItem {
478    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
479    where
480        V: Visitor<'a>,
481    {
482        match self {
483            Self::ImportDeclaration(i) => visitor.visit_import_declaration(i),
484            Self::ExportDeclaration(e) => visitor.visit_export_declaration(e),
485            Self::StatementListItem(s) => visitor.visit_statement_list_item(s),
486        }
487    }
488
489    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
490    where
491        V: VisitorMut<'a>,
492    {
493        match self {
494            Self::ImportDeclaration(i) => visitor.visit_import_declaration_mut(i),
495            Self::ExportDeclaration(e) => visitor.visit_export_declaration_mut(e),
496            Self::StatementListItem(s) => visitor.visit_statement_list_item_mut(s),
497        }
498    }
499}