ezno_parser/declarations/
import.rs

1use 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/// Side effects is represented under the Parts variant where the vector is empty
13#[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
175/// Covers import and exports
176pub(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}