ezno_parser/declarations/
import.rs1use source_map::Span;
2use tokenizer_lib::{sized_tokens::TokenStart, Token, TokenReader};
3
4use crate::{
5 ast::object_literal::ObjectLiteral, derive_ASTNode, parse_bracketed, throw_unexpected_token,
6 ASTNode, ParseOptions, ParseResult, ParsingState, TSXKeyword, TSXToken, VariableIdentifier,
7};
8use visitable_derive::Visitable;
9
10use super::{ImportExportPart, ImportLocation};
11
12#[derive(Debug, Clone, PartialEq, Visitable)]
14#[apply(derive_ASTNode)]
15pub enum ImportedItems {
16 Parts(Option<Vec<ImportExportPart<ImportDeclaration>>>),
17 All { under: VariableIdentifier },
18}
19
20#[apply(derive_ASTNode)]
21#[derive(Debug, Clone, PartialEq, Visitable, get_field_by_type::GetFieldByType)]
22#[get_field_by_type_target(Span)]
23pub struct ImportDeclaration {
24 #[cfg(feature = "extras")]
25 pub is_deferred: bool,
26 #[cfg(feature = "full-typescript")]
27 pub is_type_annotation_import_only: bool,
28 pub default: Option<VariableIdentifier>,
29 pub items: ImportedItems,
30 pub from: ImportLocation,
31 pub with: Option<ObjectLiteral>,
32 pub position: Span,
33 #[cfg(feature = "extras")]
34 pub reversed: bool,
35}
36
37impl ASTNode for ImportDeclaration {
38 fn from_reader(
39 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
40 state: &mut crate::ParsingState,
41 options: &ParseOptions,
42 ) -> ParseResult<Self> {
43 let out = parse_import_specifier_and_parts(reader, state, options)?;
44
45 let start = if matches!(out.items, ImportedItems::Parts(None)) && out.default.is_none() {
46 out.start
47 } else {
48 state.expect_keyword(reader, TSXKeyword::From)?
49 };
50
51 let (from, end) = ImportLocation::from_reader(reader, state, options, Some(start))?;
52
53 let with = reader
54 .conditional_next(|t| matches!(t, TSXToken::Keyword(TSXKeyword::With)))
55 .is_some()
56 .then(|| ObjectLiteral::from_reader(reader, state, options))
57 .transpose()?;
58
59 Ok(ImportDeclaration {
60 default: out.default,
61 items: out.items,
62 #[cfg(feature = "full-typescript")]
63 is_type_annotation_import_only: out.is_type_annotation_import_only,
64 #[cfg(feature = "extras")]
65 is_deferred: out.is_deferred,
66 from,
67 with,
68 position: out.start.union(end),
69 #[cfg(feature = "extras")]
70 reversed: false,
71 })
72 }
73
74 fn to_string_from_buffer<T: source_map::ToString>(
75 &self,
76 buf: &mut T,
77 options: &crate::ToStringOptions,
78 local: crate::LocalToStringInformation,
79 ) {
80 buf.push_str("import");
81
82 #[cfg(feature = "full-typescript")]
83 if self.is_type_annotation_import_only && options.include_type_annotations {
84 buf.push_str(" type");
85 }
86
87 if let Some(ref default) = self.default {
88 buf.push(' ');
89 default.to_string_from_buffer(buf, options, local);
90 if matches!(self.items, ImportedItems::Parts(None)) {
91 buf.push(' ');
92 }
93 } else {
94 options.push_gap_optionally(buf);
95 }
96
97 match self.items {
98 ImportedItems::All { ref under } => {
99 if self.default.is_some() {
100 buf.push_str(", ");
101 }
102 buf.push_str("* as ");
103 under.to_string_from_buffer(buf, options, local);
104 buf.push(' ');
105 }
106 ImportedItems::Parts(ref parts) => {
107 if let Some(parts) = parts {
108 if !parts.is_empty() {
109 if self.default.is_some() {
110 buf.push_str(", ");
111 }
112 super::import_export_parts_to_string_from_buffer(
113 parts, buf, options, local,
114 );
115 options.push_gap_optionally(buf);
116 }
117 }
118 }
119 }
120 if !(matches!(self.items, ImportedItems::Parts(None)) && self.default.is_none()) {
121 buf.push_str("from");
122 options.push_gap_optionally(buf);
123 }
124 self.from.to_string_from_buffer(buf);
125 }
126
127 fn get_position(&self) -> Span {
128 self.position
129 }
130}
131
132impl ImportDeclaration {
133 #[cfg(feature = "extras")]
134 pub fn reversed_from_reader(
135 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
136 state: &mut crate::ParsingState,
137 options: &ParseOptions,
138 ) -> ParseResult<Self> {
139 let start = state.expect_keyword(reader, TSXKeyword::From)?;
140
141 let (from, _end) = ImportLocation::from_reader(reader, state, options, Some(start))?;
142
143 let out = parse_import_specifier_and_parts(reader, state, options)?;
144
145 let with = reader
146 .conditional_next(|t| matches!(t, TSXToken::Keyword(TSXKeyword::Assert)))
147 .is_some()
148 .then(|| ObjectLiteral::from_reader(reader, state, options))
149 .transpose()?;
150
151 Ok(ImportDeclaration {
152 default: out.default,
153 items: out.items,
154 is_type_annotation_import_only: out.is_type_annotation_import_only,
155 with,
156 #[cfg(feature = "extras")]
157 is_deferred: out.is_deferred,
158 from,
159 position: start.union(out.end),
160 reversed: true,
161 })
162 }
163}
164
165pub(crate) struct PartsResult {
166 pub start: TokenStart,
167 #[cfg(feature = "extras")]
168 pub is_deferred: bool,
169 pub is_type_annotation_import_only: bool,
170 pub default: Option<VariableIdentifier>,
171 pub items: ImportedItems,
172 pub end: source_map::End,
173}
174
175pub(crate) fn parse_import_specifier_and_parts(
177 reader: &mut impl TokenReader<TSXToken, TokenStart>,
178 state: &mut ParsingState,
179 options: &ParseOptions,
180) -> Result<PartsResult, crate::ParseError> {
181 let start = state.expect_keyword(reader, TSXKeyword::Import)?;
182
183 #[cfg(feature = "extras")]
184 let is_deferred = state.optionally_expect_keyword(reader, TSXKeyword::Deferred).is_some();
185
186 let is_type_annotation_import_only =
187 state.optionally_expect_keyword(reader, TSXKeyword::Type).is_some();
188
189 let peek = reader.peek();
190
191 let default = if let Some(Token(
192 TSXToken::OpenBrace | TSXToken::Multiply | TSXToken::StringLiteral(..),
193 _,
194 )) = peek
195 {
196 None
197 } else {
198 let default_identifier = VariableIdentifier::from_reader(reader, state, options)?;
199 if reader.conditional_next(|t| matches!(t, TSXToken::Comma)).is_some() {
200 Some(default_identifier)
201 } else {
202 let end = default_identifier.get_position().get_end();
203 return Ok(PartsResult {
204 start,
205 #[cfg(feature = "extras")]
206 is_deferred,
207 is_type_annotation_import_only,
208 default: Some(default_identifier),
209 items: ImportedItems::Parts(None),
210 end,
211 });
212 }
213 };
214
215 let peek = reader.peek();
216 let (items, end) = if let Some(Token(TSXToken::Multiply, _)) = peek {
217 reader.next();
218 state.expect_keyword(reader, TSXKeyword::As)?;
219 let under = VariableIdentifier::from_reader(reader, state, options)?;
220 let end = under.get_position().get_end();
221 (ImportedItems::All { under }, end)
222 } else if let Some(Token(TSXToken::OpenBrace, _)) = peek {
223 let (parts, _, end) = parse_bracketed::<ImportExportPart<_>>(
224 reader,
225 state,
226 options,
227 Some(TSXToken::OpenBrace),
228 TSXToken::CloseBrace,
229 )?;
230 (ImportedItems::Parts(Some(parts)), end)
231 } else if let Some(Token(TSXToken::StringLiteral(..), _)) = peek {
232 (ImportedItems::Parts(None), start.get_end_after(6))
233 } else {
234 return throw_unexpected_token(reader, &[TSXToken::Multiply, TSXToken::OpenBrace]);
235 };
236
237 Ok(PartsResult {
238 start,
239 #[cfg(feature = "extras")]
240 is_deferred,
241 is_type_annotation_import_only,
242 default,
243 items,
244 end,
245 })
246}