1use serde::{Deserialize, Serialize};
2
3use super::{ModuleId, SourceSpan};
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
7pub enum ImportSpecifier {
8 Named(String),
10 Default,
12 Namespace(String),
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
18pub enum ImportKind {
19 Static,
21 Dynamic,
23 Require,
25 TypeOnly,
27 ReExport,
29}
30
31impl ImportKind {
32 pub fn is_runtime(&self) -> bool {
34 !matches!(self, Self::TypeOnly)
35 }
36
37 pub fn is_static(&self) -> bool {
39 matches!(self, Self::Static | Self::Require | Self::ReExport)
40 }
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct Import {
46 pub source: String,
47 pub specifiers: Vec<ImportSpecifier>,
48 pub kind: ImportKind,
49 pub resolved_to: Option<ModuleId>,
50 pub span: SourceSpan,
51}
52
53impl Import {
54 pub fn new(
56 source: impl Into<String>,
57 specifiers: Vec<ImportSpecifier>,
58 kind: ImportKind,
59 resolved_to: Option<ModuleId>,
60 span: SourceSpan,
61 ) -> Self {
62 Self {
63 source: source.into(),
64 specifiers,
65 kind,
66 resolved_to,
67 span,
68 }
69 }
70
71 pub fn is_side_effect_only(&self) -> bool {
73 self.specifiers.is_empty() && self.kind.is_runtime()
74 }
75
76 pub fn is_external(&self) -> bool {
78 !self.source.is_empty()
79 && !self.source.starts_with('.')
80 && !self.source.starts_with('/')
81 && !self.source.starts_with('\\')
82 && !self.source.starts_with("virtual:")
83 }
84
85 pub fn is_type_only(&self) -> bool {
87 matches!(self.kind, ImportKind::TypeOnly)
88 }
89
90 pub fn is_namespace_import(&self) -> bool {
92 self.specifiers
93 .iter()
94 .any(|spec| matches!(spec, ImportSpecifier::Namespace(_)))
95 }
96
97 pub fn namespace_name(&self) -> Option<&str> {
101 self.specifiers.iter().find_map(|spec| {
102 if let ImportSpecifier::Namespace(name) = spec {
103 Some(name.as_str())
104 } else {
105 None
106 }
107 })
108 }
109
110 pub fn has_runtime_effect(&self) -> bool {
118 self.kind.is_runtime()
119 }
120}