ezno_parser/declarations/
export.rs

1use crate::{
2	derive_ASTNode, errors::parse_lexing_error, throw_unexpected_token,
3	type_annotations::TypeAnnotationFunctionParameters, types::enum_declaration::EnumDeclaration,
4	ASTNode, Expression, ParseError, ParseOptions, ParseResult, Span, StatementPosition,
5	TSXKeyword, TSXToken, Token, TypeAnnotation, VariableIdentifier,
6};
7
8use super::{
9	variable::VariableDeclaration, ClassDeclaration, ImportExportPart, ImportLocation,
10	InterfaceDeclaration, StatementFunction, TypeAlias,
11};
12
13use get_field_by_type::GetFieldByType;
14use tokenizer_lib::TokenReader;
15use visitable_derive::Visitable;
16
17/// [See](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export)
18#[apply(derive_ASTNode)]
19#[derive(Debug, PartialEq, Clone, Visitable, get_field_by_type::GetFieldByType)]
20#[get_field_by_type_target(Span)]
21pub enum ExportDeclaration {
22	Item {
23		exported: Exportable,
24		position: Span,
25	},
26	// `export default ...`
27	Default {
28		expression: Box<Expression>,
29		position: Span,
30	},
31	DefaultFunction {
32		/// Technically not allowed in TypeScript
33		is_async: bool,
34		identifier: Option<VariableIdentifier>,
35		#[visit_skip_field]
36		parameters: TypeAnnotationFunctionParameters,
37		return_type: Option<TypeAnnotation>,
38		position: Span,
39	},
40}
41
42#[apply(derive_ASTNode)]
43#[derive(Debug, PartialEq, Clone, Visitable)]
44pub enum Exportable {
45	Class(ClassDeclaration<StatementPosition>),
46	Function(StatementFunction),
47	Variable(VariableDeclaration),
48	Interface(InterfaceDeclaration),
49	TypeAlias(TypeAlias),
50	EnumDeclaration(EnumDeclaration),
51	Parts(Vec<ImportExportPart<ExportDeclaration>>),
52	ImportAll {
53		r#as: Option<VariableIdentifier>,
54		from: ImportLocation,
55	},
56	ImportParts {
57		// yah `super::ImportDeclaration` here
58		parts: Vec<ImportExportPart<super::ImportDeclaration>>,
59		from: ImportLocation,
60		type_definitions_only: bool,
61	},
62}
63
64impl ASTNode for ExportDeclaration {
65	fn get_position(&self) -> Span {
66		*self.get()
67	}
68
69	fn from_reader(
70		reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
71		state: &mut crate::ParsingState,
72		options: &ParseOptions,
73	) -> ParseResult<Self> {
74		let start = state.expect_keyword(reader, TSXKeyword::Export)?;
75
76		match reader.peek().ok_or_else(parse_lexing_error)? {
77			Token(TSXToken::Keyword(TSXKeyword::Default), _) => {
78				reader.next();
79				if options.type_definition_module
80					&& reader.peek().map_or(
81						false,
82						|t| matches!(t.0, TSXToken::Keyword(kw) if kw.is_in_function_header()),
83					) {
84					let is_async = reader
85						.conditional_next(|t| matches!(t, TSXToken::Keyword(TSXKeyword::Async)))
86						.is_some();
87
88					#[allow(unused)]
89					let token = reader.next();
90					debug_assert!(matches!(
91						token.unwrap().0,
92						TSXToken::Keyword(TSXKeyword::Function)
93					));
94
95					let identifier =
96						if let Some(Token(TSXToken::OpenParentheses, _)) = reader.peek() {
97							None
98						} else {
99							Some(VariableIdentifier::from_reader(reader, state, options)?)
100						};
101
102					let parameters =
103						TypeAnnotationFunctionParameters::from_reader(reader, state, options)?;
104
105					let return_type = reader
106						.conditional_next(|tok| matches!(tok, TSXToken::Colon))
107						.is_some()
108						.then(|| TypeAnnotation::from_reader(reader, state, options))
109						.transpose()?;
110
111					let position = start.union(
112						return_type.as_ref().map_or(parameters.position, ASTNode::get_position),
113					);
114
115					Ok(ExportDeclaration::DefaultFunction {
116						position,
117						is_async,
118						identifier,
119						parameters,
120						return_type,
121					})
122				} else {
123					let expression = Expression::from_reader(reader, state, options)?;
124					let position = start.union(expression.get_position());
125					Ok(ExportDeclaration::Default { expression: Box::new(expression), position })
126				}
127			}
128			Token(TSXToken::Multiply, _) => {
129				reader.next();
130				let r#as = if let Some(Token(TSXToken::Keyword(TSXKeyword::As), _)) = reader.peek()
131				{
132					state.append_keyword_at_pos(reader.next().unwrap().1 .0, TSXKeyword::As);
133					Some(VariableIdentifier::from_reader(reader, state, options)?)
134				} else {
135					None
136				};
137				let start = state.expect_keyword(reader, TSXKeyword::From)?;
138
139				let (from, end) = ImportLocation::from_reader(reader, state, options, Some(start))?;
140
141				Ok(ExportDeclaration::Item {
142					exported: Exportable::ImportAll { r#as, from },
143					position: start.union(end),
144				})
145			}
146			Token(TSXToken::Keyword(TSXKeyword::Class), _) => {
147				let Token(_, start) = reader.next().unwrap();
148				state.append_keyword_at_pos(start.0, TSXKeyword::Class);
149				let class_declaration =
150					ClassDeclaration::from_reader_sub_class_keyword(reader, state, options, start)?;
151				let position = start.union(class_declaration.get_position());
152				Ok(Self::Item { exported: Exportable::Class(class_declaration), position })
153			}
154			Token(TSXToken::Keyword(TSXKeyword::Enum), _) => {
155				let Token(_, start) = reader.next().unwrap();
156				let enum_declaration = EnumDeclaration::from_reader(reader, state, options)?;
157				let position = start.union(enum_declaration.get_position());
158				Ok(Self::Item { exported: Exportable::EnumDeclaration(enum_declaration), position })
159			}
160			Token(TSXToken::Keyword(TSXKeyword::Const | TSXKeyword::Let), _) => {
161				let variable_declaration =
162					VariableDeclaration::from_reader(reader, state, options)?;
163				let position = start.union(variable_declaration.get_position());
164				Ok(Self::Item { exported: Exportable::Variable(variable_declaration), position })
165			}
166			Token(TSXToken::Keyword(TSXKeyword::Interface), _) => {
167				let interface_declaration =
168					InterfaceDeclaration::from_reader(reader, state, options)?;
169				let position = start.union(interface_declaration.get_position());
170				Ok(Self::Item { exported: Exportable::Interface(interface_declaration), position })
171			}
172			Token(TSXToken::Keyword(TSXKeyword::Type), _) => {
173				if let Token(TSXToken::OpenBrace, _) =
174					reader.peek_n(1).ok_or_else(parse_lexing_error)?
175				{
176					state.append_keyword_at_pos(reader.next().unwrap().1 .0, TSXKeyword::Type);
177					let Token(_, start) = reader.next().unwrap(); // OpenBrace
178
179					let (parts, _, _end) = crate::parse_bracketed::<ImportExportPart<_>>(
180						reader,
181						state,
182						options,
183						None,
184						TSXToken::CloseBrace,
185					)?;
186
187					let from_pos = state.expect_keyword(reader, TSXKeyword::From)?;
188
189					let (from, end) =
190						ImportLocation::from_reader(reader, state, options, Some(from_pos))?;
191
192					Ok(Self::Item {
193						exported: Exportable::ImportParts {
194							parts,
195							from,
196							type_definitions_only: true,
197						},
198						position: start.union(end),
199					})
200				} else {
201					let type_alias = TypeAlias::from_reader(reader, state, options)?;
202					let position = start.union(type_alias.get_position());
203					Ok(Self::Item { exported: Exportable::TypeAlias(type_alias), position })
204				}
205			}
206			Token(TSXToken::OpenBrace, _) => {
207				let Token(_, start) = reader.next().unwrap();
208				let mut bracket_depth = 1;
209				let after_bracket = reader.scan(|token, _| match token {
210					TSXToken::OpenBrace => {
211						bracket_depth += 1;
212						false
213					}
214					TSXToken::CloseBrace => {
215						bracket_depth -= 1;
216						bracket_depth == 0
217					}
218					_ => false,
219				});
220				if let Some(Token(token_type, _)) = after_bracket {
221					if let TSXToken::Keyword(TSXKeyword::From) = token_type {
222						let (parts, _, _end) = crate::parse_bracketed::<ImportExportPart<_>>(
223							reader,
224							state,
225							options,
226							None,
227							TSXToken::CloseBrace,
228						)?;
229						let Token(_from_kw, start) = reader.next().unwrap();
230						state.append_keyword_at_pos(start.0, TSXKeyword::From);
231
232						let (from, end) =
233							ImportLocation::from_reader(reader, state, options, Some(start))?;
234						Ok(Self::Item {
235							exported: Exportable::ImportParts {
236								parts,
237								from,
238								type_definitions_only: false,
239							},
240							position: start.union(end),
241						})
242					} else {
243						let (parts, _, end) = crate::parse_bracketed::<ImportExportPart<_>>(
244							reader,
245							state,
246							options,
247							None,
248							TSXToken::CloseBrace,
249						)?;
250						Ok(Self::Item {
251							exported: Exportable::Parts(parts),
252							position: start.union(end),
253						})
254					}
255				} else {
256					Err(ParseError::new(
257						crate::ParseErrors::UnmatchedBrackets,
258						start.with_length(1),
259					))
260				}
261			}
262			Token(TSXToken::Keyword(kw), _) if kw.is_in_function_header() => {
263				let function_declaration = StatementFunction::from_reader(reader, state, options)?;
264				let position = start.union(function_declaration.get_position());
265				Ok(Self::Item { exported: Exportable::Function(function_declaration), position })
266			}
267			_ => throw_unexpected_token(
268				reader,
269				&[
270					TSXToken::Keyword(TSXKeyword::Class),
271					TSXToken::Keyword(TSXKeyword::Function),
272					TSXToken::Keyword(TSXKeyword::Const),
273					TSXToken::Keyword(TSXKeyword::Let),
274					TSXToken::Keyword(TSXKeyword::Interface),
275					TSXToken::Keyword(TSXKeyword::Type),
276					TSXToken::OpenBrace,
277				],
278			),
279		}
280	}
281
282	fn to_string_from_buffer<T: source_map::ToString>(
283		&self,
284		buf: &mut T,
285		options: &crate::ToStringOptions,
286		local: crate::LocalToStringInformation,
287	) {
288		match self {
289			ExportDeclaration::Item { exported, .. } => {
290				buf.push_str("export ");
291				match exported {
292					Exportable::Class(class_declaration) => {
293						class_declaration.to_string_from_buffer(buf, options, local);
294					}
295					Exportable::Function(function_declaration) => {
296						function_declaration.to_string_from_buffer(buf, options, local);
297					}
298					Exportable::Interface(interface_declaration) => {
299						interface_declaration.to_string_from_buffer(buf, options, local);
300					}
301					Exportable::Variable(variable_dec_stmt) => {
302						variable_dec_stmt.to_string_from_buffer(buf, options, local);
303					}
304					Exportable::TypeAlias(type_alias) => {
305						type_alias.to_string_from_buffer(buf, options, local);
306					}
307					Exportable::EnumDeclaration(enum_declaration) => {
308						enum_declaration.to_string_from_buffer(buf, options, local);
309					}
310					Exportable::Parts(parts) => {
311						super::import_export_parts_to_string_from_buffer(
312							parts, buf, options, local,
313						);
314					}
315					Exportable::ImportAll { r#as, from } => {
316						buf.push_str("* ");
317						if let Some(r#as) = r#as {
318							buf.push_str("as ");
319							r#as.to_string_from_buffer(buf, options, local);
320							buf.push(' ');
321						}
322						buf.push_str("from \"");
323						from.to_string_from_buffer(buf);
324						buf.push('"');
325					}
326					Exportable::ImportParts { parts, from, type_definitions_only } => {
327						if *type_definitions_only {
328							buf.push_str("type ");
329						}
330						super::import_export_parts_to_string_from_buffer(
331							parts, buf, options, local,
332						);
333						options.push_gap_optionally(buf);
334						buf.push_str("from \"");
335						from.to_string_from_buffer(buf);
336						buf.push('"');
337					}
338				}
339			}
340			ExportDeclaration::Default { expression, position: _ } => {
341				buf.push_str("export default ");
342				expression.to_string_from_buffer(buf, options, local);
343			}
344			ExportDeclaration::DefaultFunction {
345				is_async,
346				identifier,
347				parameters,
348				return_type,
349				position: _,
350			} => {
351				if options.include_type_annotations {
352					buf.push_str("export default ");
353					if *is_async {
354						buf.push_str("async ");
355					}
356					buf.push_str("function ");
357					if let Some(ref identifier) = identifier {
358						identifier.to_string_from_buffer(buf, options, local);
359						buf.push(' ');
360					}
361					parameters.to_string_from_buffer(buf, options, local);
362					if let Some(ref return_type) = return_type {
363						buf.push_str(": ");
364						return_type.to_string_from_buffer(buf, options, local);
365					}
366				}
367			}
368		}
369	}
370}