Skip to main content

deno_graph/symbols/
cross_module.rs

1// Copyright 2018-2024 the Deno authors. MIT license.
2
3use std::collections::HashSet;
4use std::collections::VecDeque;
5
6use deno_ast::SourceRange;
7use indexmap::IndexMap;
8
9use crate::ModuleGraph;
10use crate::ModuleSpecifier;
11
12use super::FileDep;
13use super::FileDepName;
14use super::ModuleInfoRef;
15use super::Symbol;
16use super::SymbolDecl;
17use super::SymbolId;
18use super::SymbolNodeDep;
19use super::UniqueSymbolId;
20use super::analyzer::SymbolDeclKind;
21
22#[derive(Debug, Clone)]
23pub enum DefinitionOrUnresolved<'a> {
24  Definition(Definition<'a>),
25  Unresolved(DefinitionUnresolved<'a>),
26}
27
28impl<'a> DefinitionOrUnresolved<'a> {
29  pub fn module(&self) -> ModuleInfoRef<'a> {
30    match self {
31      DefinitionOrUnresolved::Definition(def) => def.module,
32      DefinitionOrUnresolved::Unresolved(unresolved) => unresolved.module,
33    }
34  }
35
36  pub fn symbol(&self) -> Option<&'a Symbol> {
37    match self {
38      DefinitionOrUnresolved::Definition(def) => Some(def.symbol),
39      DefinitionOrUnresolved::Unresolved(_) => None,
40    }
41  }
42}
43
44#[derive(Debug, Clone, PartialEq, Eq, Hash)]
45pub enum DefinitionKind<'a> {
46  ExportStar(&'a FileDep),
47  Definition,
48}
49
50#[derive(Debug, Clone)]
51pub struct Definition<'a> {
52  pub kind: DefinitionKind<'a>,
53  pub module: ModuleInfoRef<'a>,
54  pub symbol: &'a Symbol,
55  pub symbol_decl: &'a SymbolDecl,
56}
57
58impl Definition<'_> {
59  pub fn range(&self) -> &SourceRange {
60    &self.symbol_decl.range
61  }
62
63  pub fn byte_range(&self) -> std::ops::Range<usize> {
64    self
65      .range()
66      .as_byte_range(self.module.text_info().range().start)
67  }
68
69  pub fn text(&self) -> &str {
70    self.module.text_info().range_text(self.range())
71  }
72}
73
74#[derive(Debug, Clone)]
75pub enum DefinitionUnresolvedKind {
76  /// Could not resolve the swc Id.
77  Id(deno_ast::swc::ast::Id),
78  /// Could not resolve the specifier relative to this module via deno_graph.
79  Specifier(String),
80  /// Could not resolve the part on the symbol.
81  Part(String),
82}
83
84/// The point at which a definition could not be resolved.
85#[derive(Debug, Clone)]
86pub struct DefinitionUnresolved<'a> {
87  pub module: ModuleInfoRef<'a>,
88  pub kind: DefinitionUnresolvedKind,
89  pub parts: Vec<String>,
90}
91
92#[derive(Debug, Clone)]
93pub struct DefinitionPathLink<'a> {
94  pub module: ModuleInfoRef<'a>,
95  pub symbol: &'a Symbol,
96  pub symbol_decl: &'a SymbolDecl,
97  pub parts: Vec<String>,
98  pub next: Vec<DefinitionPathNode<'a>>,
99}
100
101#[derive(Debug, Clone)]
102pub enum DefinitionPathNodeResolved<'a> {
103  Link(DefinitionPathLink<'a>),
104  Definition(Definition<'a>),
105}
106
107impl<'a> DefinitionPathNodeResolved<'a> {
108  pub fn symbol(&self) -> Option<&'a Symbol> {
109    match self {
110      Self::Link(link) => Some(link.symbol),
111      Self::Definition(def) => Some(def.symbol),
112    }
113  }
114
115  pub fn module(&self) -> ModuleInfoRef<'a> {
116    match self {
117      Self::Link(link) => link.module,
118      Self::Definition(def) => def.module,
119    }
120  }
121}
122
123/// A graph path to a definition.
124#[derive(Debug, Clone)]
125pub enum DefinitionPathNode<'a> {
126  Resolved(DefinitionPathNodeResolved<'a>),
127  Unresolved(DefinitionUnresolved<'a>),
128}
129
130impl<'a> DefinitionPathNode<'a> {
131  fn definition(definition: Definition<'a>) -> Self {
132    Self::Resolved(DefinitionPathNodeResolved::Definition(definition))
133  }
134
135  fn link(link: DefinitionPathLink<'a>) -> Self {
136    Self::Resolved(DefinitionPathNodeResolved::Link(link))
137  }
138
139  pub fn module(&self) -> ModuleInfoRef<'a> {
140    match self {
141      Self::Resolved(resolved) => resolved.module(),
142      Self::Unresolved(unresolved) => unresolved.module,
143    }
144  }
145
146  pub fn into_definitions(self) -> impl Iterator<Item = Definition<'a>> {
147    self
148      .into_definitions_or_unresolveds()
149      .filter_map(|d| match d {
150        DefinitionOrUnresolved::Definition(d) => Some(d),
151        DefinitionOrUnresolved::Unresolved(_) => None,
152      })
153  }
154
155  pub fn into_definitions_or_unresolveds(
156    self,
157  ) -> impl Iterator<Item = DefinitionOrUnresolved<'a>> {
158    struct IntoIterator<'a> {
159      queue: VecDeque<DefinitionPathNode<'a>>,
160    }
161
162    impl<'a> Iterator for IntoIterator<'a> {
163      type Item = DefinitionOrUnresolved<'a>;
164
165      fn next(&mut self) -> Option<Self::Item> {
166        while let Some(path) = self.queue.pop_front() {
167          match path {
168            DefinitionPathNode::Resolved(DefinitionPathNodeResolved::Link(
169              link,
170            )) => {
171              for child_path in link.next.into_iter().rev() {
172                self.queue.push_front(child_path);
173              }
174            }
175            DefinitionPathNode::Resolved(
176              DefinitionPathNodeResolved::Definition(def),
177            ) => {
178              return Some(DefinitionOrUnresolved::Definition(def));
179            }
180            DefinitionPathNode::Unresolved(unresolved) => {
181              return Some(DefinitionOrUnresolved::Unresolved(unresolved));
182            }
183          }
184        }
185
186        None
187      }
188    }
189
190    IntoIterator {
191      queue: VecDeque::from([self]),
192    }
193  }
194}
195
196/// Finds the path to a definition.
197pub fn find_definition_paths<'a>(
198  module_graph: &'a ModuleGraph,
199  module: ModuleInfoRef<'a>,
200  symbol: &'a Symbol,
201  specifier_to_module: &impl Fn(&ModuleSpecifier) -> Option<ModuleInfoRef<'a>>,
202) -> Vec<DefinitionPathNode<'a>> {
203  find_definition_paths_internal(
204    module_graph,
205    module,
206    symbol,
207    &mut Default::default(),
208    specifier_to_module,
209  )
210}
211
212fn find_definition_paths_internal<'a>(
213  module_graph: &'a ModuleGraph,
214  module: ModuleInfoRef<'a>,
215  symbol: &'a Symbol,
216  visited_symbols: &mut HashSet<UniqueSymbolId>,
217  specifier_to_module: &impl Fn(&ModuleSpecifier) -> Option<ModuleInfoRef<'a>>,
218) -> Vec<DefinitionPathNode<'a>> {
219  debug_assert_eq!(module.module_id(), symbol.module_id());
220  if !visited_symbols.insert(symbol.unique_id()) {
221    return Vec::new();
222  }
223  let mut paths = Vec::with_capacity(symbol.decls().len());
224  for decl in symbol.decls() {
225    match &decl.kind {
226      SymbolDeclKind::Definition(_) => {
227        paths.push(DefinitionPathNode::definition(Definition {
228          module,
229          symbol,
230          symbol_decl: decl,
231          kind: DefinitionKind::Definition,
232        }));
233      }
234      SymbolDeclKind::Target(target_id) => {
235        if let Some(symbol) = module
236          .esm()
237          .unwrap()
238          .symbol_id_from_swc(target_id)
239          .and_then(|id| module.symbol(id))
240        {
241          let inner_paths = find_definition_paths_internal(
242            module_graph,
243            module,
244            symbol,
245            visited_symbols,
246            specifier_to_module,
247          );
248          if !inner_paths.is_empty() {
249            paths.push(DefinitionPathNode::link(DefinitionPathLink {
250              module,
251              symbol,
252              symbol_decl: decl,
253              parts: Vec::new(),
254              next: inner_paths,
255            }));
256          }
257        }
258      }
259      SymbolDeclKind::QualifiedTarget(target_id, parts) => {
260        let inner_paths = go_to_id_and_parts_definition_paths(
261          module_graph,
262          module,
263          target_id,
264          parts,
265          specifier_to_module,
266        );
267        if !inner_paths.is_empty() {
268          paths.push(DefinitionPathNode::link(DefinitionPathLink {
269            module,
270            symbol,
271            symbol_decl: decl,
272            parts: parts.clone(),
273            next: inner_paths,
274          }));
275        }
276      }
277      SymbolDeclKind::FileRef(file_ref) => match &file_ref.name {
278        FileDepName::Star => {
279          paths.push(DefinitionPathNode::definition(Definition {
280            module,
281            symbol,
282            kind: DefinitionKind::ExportStar(file_ref),
283            symbol_decl: decl,
284          }));
285        }
286        FileDepName::Name(export_name) => {
287          let inner_paths = go_to_file_export(
288            module_graph,
289            module,
290            file_ref,
291            export_name,
292            specifier_to_module,
293            visited_symbols,
294          );
295          if !inner_paths.is_empty() {
296            paths.push(DefinitionPathNode::link(DefinitionPathLink {
297              module,
298              symbol,
299              symbol_decl: decl,
300              parts: Vec::new(),
301              next: inner_paths,
302            }));
303          }
304        }
305      },
306    }
307  }
308  paths
309}
310
311fn go_to_file_export<'a>(
312  module_graph: &'a ModuleGraph,
313  referrer_module: ModuleInfoRef<'a>,
314  file_ref: &'a FileDep,
315  export_name: &'a str,
316  specifier_to_module: &impl Fn(&ModuleSpecifier) -> Option<ModuleInfoRef<'a>>,
317  visited_symbols: &mut HashSet<UniqueSymbolId>,
318) -> Vec<DefinitionPathNode<'a>> {
319  let maybe_dep_module = module_graph
320    .resolve_dependency(
321      &file_ref.specifier,
322      referrer_module.specifier(),
323      /* prefer types */ true,
324    )
325    .and_then(specifier_to_module);
326
327  let Some(dep_module) = maybe_dep_module else {
328    return vec![DefinitionPathNode::Unresolved(DefinitionUnresolved {
329      module: referrer_module,
330      kind: DefinitionUnresolvedKind::Specifier(file_ref.specifier.clone()),
331      parts: Vec::new(),
332    })];
333  };
334  let maybe_export_symbol = dep_module
335    .module_symbol()
336    .exports()
337    .get(export_name)
338    .and_then(|symbol_id| dep_module.symbol(*symbol_id));
339  match maybe_export_symbol {
340    Some(export_symbol) => find_definition_paths_internal(
341      module_graph,
342      dep_module,
343      export_symbol,
344      visited_symbols,
345      specifier_to_module,
346    ),
347    None => {
348      // maybe it's in a re-export
349      if let Some(re_export_all_specifiers) =
350        dep_module.re_export_all_specifiers()
351      {
352        for re_export_specifier in re_export_all_specifiers {
353          let maybe_specifier = module_graph.resolve_dependency(
354            re_export_specifier,
355            dep_module.specifier(),
356            /* prefer_types */ true,
357          );
358          let maybe_module = maybe_specifier.and_then(specifier_to_module);
359          let mut visited = HashSet::new();
360          if let Some(module) = maybe_module {
361            // todo(dsherret): this could be optimized to use an iterator
362            let inner = exports_and_re_exports_inner(
363              module_graph,
364              module,
365              specifier_to_module,
366              &mut visited,
367            );
368            for (name, item) in inner.resolved {
369              if name == export_name {
370                let resolved_rexport = item.as_resolved_export();
371                let paths = find_definition_paths_internal(
372                  module_graph,
373                  resolved_rexport.module,
374                  resolved_rexport.symbol(),
375                  visited_symbols,
376                  specifier_to_module,
377                );
378                if !paths.is_empty() {
379                  return paths;
380                }
381                break;
382              }
383            }
384          }
385        }
386      }
387      vec![DefinitionPathNode::Unresolved(DefinitionUnresolved {
388        module: dep_module,
389        kind: DefinitionUnresolvedKind::Part(export_name.to_string()),
390        parts: Vec::new(),
391      })]
392    }
393  }
394}
395
396/// A resolved `SymbolDep`.
397#[derive(Debug)]
398pub enum ResolvedSymbolDepEntry<'a> {
399  /// The path to the definition of the symbol dep.
400  Path(DefinitionPathNode<'a>),
401  /// If the symbol dep was an import type with no property access.
402  ///
403  /// Ex. `type MyType = typeof import("./my_module.ts");`
404  ImportType(ModuleInfoRef<'a>),
405}
406
407pub fn resolve_symbol_dep<'a>(
408  module_graph: &'a ModuleGraph,
409  module: ModuleInfoRef<'a>,
410  dep: &SymbolNodeDep,
411  specifier_to_module: &impl Fn(&ModuleSpecifier) -> Option<ModuleInfoRef<'a>>,
412) -> Vec<ResolvedSymbolDepEntry<'a>> {
413  match dep {
414    SymbolNodeDep::Id(id) => {
415      if let Some(dep_symbol) = module.esm().and_then(|m| m.symbol_from_swc(id))
416      {
417        find_definition_paths(
418          module_graph,
419          module,
420          dep_symbol,
421          specifier_to_module,
422        )
423        .into_iter()
424        .map(ResolvedSymbolDepEntry::Path)
425        .collect()
426      } else {
427        vec![ResolvedSymbolDepEntry::Path(
428          DefinitionPathNode::Unresolved(DefinitionUnresolved {
429            module,
430            kind: DefinitionUnresolvedKind::Id(id.clone()),
431            parts: Vec::new(),
432          }),
433        )]
434      }
435    }
436    SymbolNodeDep::QualifiedId(id, parts) => {
437      go_to_id_and_parts_definition_paths(
438        module_graph,
439        module,
440        id,
441        parts,
442        specifier_to_module,
443      )
444      .into_iter()
445      .map(ResolvedSymbolDepEntry::Path)
446      .collect()
447    }
448    SymbolNodeDep::ImportType(import_specifier, parts) => {
449      let maybe_dep_specifier = module_graph.resolve_dependency(
450        import_specifier,
451        module.specifier(),
452        /* prefer types */ true,
453      );
454      let maybe_module = maybe_dep_specifier.and_then(specifier_to_module);
455      let Some(module) = maybe_module else {
456        return vec![ResolvedSymbolDepEntry::Path(
457          DefinitionPathNode::Unresolved(DefinitionUnresolved {
458            module,
459            kind: DefinitionUnresolvedKind::Specifier(import_specifier.clone()),
460            parts: parts.clone(),
461          }),
462        )];
463      };
464      if parts.is_empty() {
465        // an ImportType includes default exports
466        vec![ResolvedSymbolDepEntry::ImportType(module)]
467      } else {
468        resolve_qualified_export_name(
469          module_graph,
470          module,
471          parts,
472          specifier_to_module,
473        )
474        .into_iter()
475        .map(ResolvedSymbolDepEntry::Path)
476        .collect()
477      }
478    }
479  }
480}
481
482fn go_to_id_and_parts_definition_paths<'a>(
483  module_graph: &'a ModuleGraph,
484  module: ModuleInfoRef<'a>,
485  target_id: &deno_ast::swc::ast::Id,
486  parts: &[String],
487  specifier_to_module: &impl Fn(&ModuleSpecifier) -> Option<ModuleInfoRef<'a>>,
488) -> Vec<DefinitionPathNode<'a>> {
489  if let Some(symbol_id) =
490    module.esm().and_then(|m| m.symbol_id_from_swc(target_id))
491  {
492    resolve_qualified_name(
493      module_graph,
494      module,
495      module.symbol(symbol_id).unwrap(),
496      parts,
497      specifier_to_module,
498    )
499  } else {
500    vec![DefinitionPathNode::Unresolved(DefinitionUnresolved {
501      module,
502      kind: DefinitionUnresolvedKind::Id(target_id.clone()),
503      parts: parts.to_vec(),
504    })]
505  }
506}
507
508fn resolve_qualified_export_name<'a>(
509  graph: &'a ModuleGraph,
510  module: ModuleInfoRef<'a>,
511  parts: &[String],
512  specifier_to_module: &impl Fn(&ModuleSpecifier) -> Option<ModuleInfoRef<'a>>,
513) -> Vec<DefinitionPathNode<'a>> {
514  debug_assert!(!parts.is_empty());
515  resolve_qualified_export_name_internal(
516    graph,
517    module,
518    parts,
519    &mut HashSet::new(),
520    specifier_to_module,
521  )
522}
523
524fn resolve_qualified_export_name_internal<'a>(
525  graph: &'a ModuleGraph,
526  module: ModuleInfoRef<'a>,
527  parts: &[String],
528  visited_symbols: &mut HashSet<UniqueSymbolId>,
529  specifier_to_module: &impl Fn(&ModuleSpecifier) -> Option<ModuleInfoRef<'a>>,
530) -> Vec<DefinitionPathNode<'a>> {
531  debug_assert!(!parts.is_empty());
532  let exports = exports_and_re_exports(graph, module, specifier_to_module);
533  let export_name = &parts[0];
534  if let Some(resolved) = exports.resolved.get(export_name) {
535    let resolved = resolved.as_resolved_export();
536    resolve_qualified_name_internal(
537      graph,
538      resolved.module,
539      resolved.symbol(),
540      &parts[1..],
541      visited_symbols,
542      specifier_to_module,
543    )
544  } else {
545    vec![DefinitionPathNode::Unresolved(DefinitionUnresolved {
546      module,
547      kind: DefinitionUnresolvedKind::Part(export_name.to_string()),
548      parts: parts.to_vec(),
549    })]
550  }
551}
552
553pub fn resolve_qualified_name<'a>(
554  graph: &'a ModuleGraph,
555  module: ModuleInfoRef<'a>,
556  symbol: &'a Symbol,
557  parts: &[String],
558  specifier_to_module: &impl Fn(&ModuleSpecifier) -> Option<ModuleInfoRef<'a>>,
559) -> Vec<DefinitionPathNode<'a>> {
560  resolve_qualified_name_internal(
561    graph,
562    module,
563    symbol,
564    parts,
565    &mut HashSet::new(),
566    specifier_to_module,
567  )
568}
569
570fn resolve_qualified_name_internal<'a>(
571  graph: &'a ModuleGraph,
572  module: ModuleInfoRef<'a>,
573  symbol: &'a Symbol,
574  parts: &[String],
575  visited_symbols: &mut HashSet<UniqueSymbolId>,
576  specifier_to_module: &impl Fn(&ModuleSpecifier) -> Option<ModuleInfoRef<'a>>,
577) -> Vec<DefinitionPathNode<'a>> {
578  fn resolve_paths_with_parts<'a>(
579    paths: Vec<DefinitionPathNode<'a>>,
580    parts: &[String],
581    graph: &'a ModuleGraph,
582    visited_symbols: &mut HashSet<UniqueSymbolId>,
583    specifier_to_module: &impl Fn(&url::Url) -> Option<ModuleInfoRef<'a>>,
584  ) -> Vec<DefinitionPathNode<'a>> {
585    debug_assert!(!parts.is_empty());
586    paths
587      .into_iter()
588      .flat_map(|path| {
589        resolve_path_with_parts(
590          path,
591          parts,
592          graph,
593          visited_symbols,
594          specifier_to_module,
595        )
596      })
597      .collect()
598  }
599
600  fn resolve_path_with_parts<'a>(
601    path: DefinitionPathNode<'a>,
602    parts: &[String],
603    graph: &'a ModuleGraph,
604    visited_symbols: &mut HashSet<UniqueSymbolId>,
605    specifier_to_module: &impl Fn(&url::Url) -> Option<ModuleInfoRef<'a>>,
606  ) -> Option<DefinitionPathNode<'a>> {
607    match path {
608      DefinitionPathNode::Resolved(DefinitionPathNodeResolved::Link(link)) => {
609        let next = resolve_paths_with_parts(
610          link.next,
611          parts,
612          graph,
613          visited_symbols,
614          specifier_to_module,
615        );
616        if next.is_empty() {
617          None
618        } else {
619          Some(DefinitionPathNode::link(DefinitionPathLink {
620            module: link.module,
621            symbol: link.symbol,
622            symbol_decl: link.symbol_decl,
623            parts: parts.to_vec(),
624            next,
625          }))
626        }
627      }
628      DefinitionPathNode::Resolved(DefinitionPathNodeResolved::Definition(
629        definition,
630      )) => {
631        let next_part = &parts[0];
632        let mut next = Vec::new();
633        match definition.kind {
634          DefinitionKind::Definition => {
635            if let Some(export_symbol_id) = definition.symbol.export(next_part)
636            {
637              next.extend(resolve_qualified_name_internal(
638                graph,
639                definition.module,
640                definition.module.symbol(export_symbol_id).unwrap(),
641                &parts[1..],
642                visited_symbols,
643                specifier_to_module,
644              ));
645            } else if next_part == "prototype"
646              && definition.symbol_decl.is_class()
647            {
648              // for now, just resolve to this definition
649              debug_assert!(next.is_empty());
650              return Some(DefinitionPathNode::definition(definition.clone()));
651            } else {
652              next.push(DefinitionPathNode::Unresolved(DefinitionUnresolved {
653                module: definition.module,
654                kind: DefinitionUnresolvedKind::Part(next_part.to_string()),
655                parts: parts.to_vec(),
656              }))
657            }
658          }
659          DefinitionKind::ExportStar(file_dep) => {
660            let maybe_dep_specifier = graph.resolve_dependency(
661              &file_dep.specifier,
662              definition.module.specifier(),
663              /* prefer types */ true,
664            );
665            let specifier_module =
666              maybe_dep_specifier.and_then(specifier_to_module);
667            if let Some(module) = specifier_module {
668              next.extend(resolve_qualified_export_name_internal(
669                graph,
670                module,
671                parts,
672                visited_symbols,
673                specifier_to_module,
674              ));
675            } else {
676              next.push(DefinitionPathNode::Unresolved(DefinitionUnresolved {
677                module: definition.module,
678                kind: DefinitionUnresolvedKind::Specifier(
679                  file_dep.specifier.to_string(),
680                ),
681                parts: parts.to_vec(),
682              }))
683            }
684          }
685        }
686
687        if next.is_empty() {
688          None
689        } else {
690          // convert the definition into a path because the qualified name has yet to be resolved
691          Some(DefinitionPathNode::link(DefinitionPathLink {
692            module: definition.module,
693            symbol: definition.symbol,
694            symbol_decl: definition.symbol_decl,
695            parts: parts.to_vec(),
696            next,
697          }))
698        }
699      }
700      DefinitionPathNode::Unresolved(unresolved) => {
701        Some(DefinitionPathNode::Unresolved(unresolved))
702      }
703    }
704  }
705
706  let paths = find_definition_paths_internal(
707    graph,
708    module,
709    symbol,
710    visited_symbols,
711    specifier_to_module,
712  );
713  if !parts.is_empty() {
714    resolve_paths_with_parts(
715      paths,
716      parts,
717      graph,
718      visited_symbols,
719      specifier_to_module,
720    )
721  } else {
722    paths
723  }
724}
725
726#[derive(Debug, Default, Clone)]
727pub struct ModuleExports<'a> {
728  pub resolved: IndexMap<String, ResolvedExportOrReExportAllPath<'a>>,
729  pub unresolved_specifiers: Vec<UnresolvedSpecifier<'a>>,
730}
731
732/// A resolved export. This lands at the first symbol it finds, which is not
733/// necessarily the declaration symbol. For example, this might be the symbol
734/// for an identifier in an export declaration (ex. `export { foo }`).
735#[derive(Debug, Clone)]
736pub struct ResolvedExport<'a> {
737  pub module: ModuleInfoRef<'a>,
738  pub symbol_id: SymbolId,
739}
740
741impl<'a> ResolvedExport<'a> {
742  pub fn symbol(&self) -> &'a Symbol {
743    self.module.symbol(self.symbol_id).unwrap()
744  }
745}
746
747#[derive(Debug, Clone)]
748pub struct ResolvedReExportAllPath<'a> {
749  /// Module that contains this re-export.
750  pub referrer_module: ModuleInfoRef<'a>,
751  /// Specifier from the referrer that led to the resolved module.
752  pub specifier: &'a str,
753  /// Holds the next resolved export or re-export.
754  pub next: Box<ResolvedExportOrReExportAllPath<'a>>,
755}
756
757impl ResolvedReExportAllPath<'_> {
758  pub fn resolved_module(&self) -> ModuleInfoRef<'_> {
759    match &*self.next {
760      ResolvedExportOrReExportAllPath::Export(e) => e.module,
761      ResolvedExportOrReExportAllPath::ReExportAllPath(e) => e.referrer_module,
762    }
763  }
764}
765
766#[derive(Debug, Clone)]
767pub enum ResolvedExportOrReExportAllPath<'a> {
768  Export(ResolvedExport<'a>),
769  ReExportAllPath(ResolvedReExportAllPath<'a>),
770}
771
772impl<'a> ResolvedExportOrReExportAllPath<'a> {
773  pub fn as_resolved_export(&self) -> &ResolvedExport<'a> {
774    match self {
775      ResolvedExportOrReExportAllPath::Export(export) => export,
776      ResolvedExportOrReExportAllPath::ReExportAllPath(re_export) => {
777        re_export.next.as_resolved_export()
778      }
779    }
780  }
781
782  pub fn iter(
783    &self,
784  ) -> impl Iterator<Item = &ResolvedExportOrReExportAllPath<'a>> {
785    std::iter::successors(Some(self), |last| match last {
786      ResolvedExportOrReExportAllPath::Export(_) => None,
787      ResolvedExportOrReExportAllPath::ReExportAllPath(re_export) => {
788        Some(&re_export.next)
789      }
790    })
791  }
792}
793
794#[derive(Debug, Clone)]
795pub struct UnresolvedSpecifier<'a> {
796  pub referrer: ModuleInfoRef<'a>,
797  pub specifier: &'a str,
798}
799
800pub fn exports_and_re_exports<'a>(
801  module_graph: &'a ModuleGraph,
802  module: ModuleInfoRef<'a>,
803  specifier_to_module: &impl Fn(&ModuleSpecifier) -> Option<ModuleInfoRef<'a>>,
804) -> ModuleExports<'a> {
805  exports_and_re_exports_inner(
806    module_graph,
807    module,
808    specifier_to_module,
809    &mut Default::default(),
810  )
811}
812
813fn exports_and_re_exports_inner<'a>(
814  module_graph: &'a ModuleGraph,
815  module: ModuleInfoRef<'a>,
816  specifier_to_module: &impl Fn(&ModuleSpecifier) -> Option<ModuleInfoRef<'a>>,
817  visited: &mut HashSet<&'a ModuleSpecifier>,
818) -> ModuleExports<'a> {
819  if !visited.insert(module.specifier()) {
820    return ModuleExports::default();
821  }
822
823  let mut unresolved_specifiers = Vec::new();
824  let mut resolved = IndexMap::new();
825  for (name, symbol_id) in module.module_symbol().exports() {
826    resolved.insert(
827      name.clone(),
828      ResolvedExportOrReExportAllPath::Export(ResolvedExport {
829        module,
830        symbol_id: *symbol_id,
831      }),
832    );
833  }
834  if let Some(re_export_all_specifier) = module.re_export_all_specifiers() {
835    let referrer_module = module;
836    for re_export_specifier in re_export_all_specifier {
837      let maybe_specifier = module_graph.resolve_dependency(
838        re_export_specifier,
839        module.specifier(),
840        /* prefer_types */ true,
841      );
842      let maybe_module = maybe_specifier.and_then(specifier_to_module);
843      if let Some(module) = maybe_module {
844        let inner = exports_and_re_exports_inner(
845          module_graph,
846          module,
847          specifier_to_module,
848          visited,
849        );
850        for (name, item) in inner.resolved {
851          if name != "default" && !resolved.contains_key(&name) {
852            resolved.insert(
853              name,
854              ResolvedExportOrReExportAllPath::ReExportAllPath(
855                ResolvedReExportAllPath {
856                  referrer_module,
857                  specifier: re_export_specifier,
858                  next: Box::new(item),
859                },
860              ),
861            );
862          }
863        }
864        unresolved_specifiers.extend(inner.unresolved_specifiers);
865      } else {
866        unresolved_specifiers.push(UnresolvedSpecifier {
867          referrer: module,
868          specifier: re_export_specifier,
869        });
870      }
871    }
872  }
873  ModuleExports {
874    resolved,
875    unresolved_specifiers,
876  }
877}