1pub mod classes;
2pub mod export;
3pub mod import;
4pub mod variable;
5
6pub use super::types::{
7 declare_variable::*,
8 enum_declaration::{EnumDeclaration, EnumMember},
9 interface::InterfaceDeclaration,
10 type_alias::TypeAlias,
11};
12
13use derive_enum_from_into::{EnumFrom, EnumTryInto};
14use get_field_by_type::GetFieldByType;
15use source_map::Span;
16use tokenizer_lib::{sized_tokens::TokenStart, Token};
17use visitable_derive::Visitable;
18
19use crate::{
20 derive_ASTNode, errors::parse_lexing_error, extensions::decorators,
21 throw_unexpected_token_with_token, Decorated, Marker, ParseError, ParseErrors, ParseOptions,
22 Quoted, StatementPosition, TSXKeyword, TSXToken,
23};
24
25pub use self::{
26 classes::ClassDeclaration,
27 export::ExportDeclaration,
28 import::ImportDeclaration,
29 variable::{VariableDeclaration, VariableDeclarationItem},
30};
31
32pub type StatementFunctionBase = crate::functions::GeneralFunctionBase<StatementPosition>;
33pub type StatementFunction = crate::FunctionBase<StatementFunctionBase>;
34
35#[cfg_attr(target_family = "wasm", wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section))]
36#[allow(dead_code)]
37const TYPES_STATEMENT_FUNCTION: &str = r"
38 export interface StatementFunction extends FunctionBase {
39 header: FunctionHeader,
40 parameters: FunctionParameters<ThisParameter | null, null>,
41 body: Block,
42 name: StatementPosition
43 }
44";
45
46#[apply(derive_ASTNode)]
47#[derive(
48 Debug, Clone, Visitable, EnumFrom, EnumTryInto, PartialEq, get_field_by_type::GetFieldByType,
49)]
50#[get_field_by_type_target(Span)]
51#[try_into_references(&, &mut)]
52pub enum Declaration {
53 Variable(VariableDeclaration),
54 Function(Decorated<StatementFunction>),
55 Class(Decorated<ClassDeclaration<StatementPosition>>),
56 Enum(Decorated<EnumDeclaration>),
57 Interface(Decorated<InterfaceDeclaration>),
58 TypeAlias(TypeAlias),
59 DeclareVariable(DeclareVariableDeclaration),
61 #[cfg(feature = "full-typescript")]
62 Namespace(crate::types::namespace::Namespace),
63 Import(ImportDeclaration),
65 Export(Decorated<ExportDeclaration>),
66}
67
68impl Declaration {
69 pub(crate) fn is_declaration_start(
72 reader: &mut impl tokenizer_lib::TokenReader<crate::TSXToken, crate::TokenStart>,
73 options: &ParseOptions,
74 ) -> bool {
75 let Some(Token(token, _)) = reader.peek() else { return false };
76
77 let result = matches!(
78 token,
79 TSXToken::Keyword(
80 TSXKeyword::Let
81 | TSXKeyword::Const
82 | TSXKeyword::Function
83 | TSXKeyword::Class
84 | TSXKeyword::Export
85 ) | TSXToken::At,
86 );
87
88 #[cfg(feature = "extras")]
89 return result
90 || matches!(token, TSXToken::Keyword(kw) if options.custom_function_headers && kw.is_special_function_header())
91 || (matches!(token, TSXToken::Keyword(TSXKeyword::Namespace) if cfg!(feature = "full-typescript")))
92 || {
93 let TSXToken::Keyword(token) = *token else { return false };
94 let Some(Token(after, _)) = reader.peek_n(1) else { return false };
95
96 #[allow(clippy::match_same_arms)]
97 match (token, after) {
98 (
100 TSXKeyword::Import,
101 TSXToken::OpenBrace
102 | TSXToken::Keyword(..)
103 | TSXToken::Identifier(..)
104 | TSXToken::StringLiteral(..)
105 | TSXToken::Multiply,
106 ) => true,
107 (TSXKeyword::Declare | TSXKeyword::Interface, _) => options.type_annotations,
108 (TSXKeyword::Async, TSXToken::Keyword(TSXKeyword::Function)) => true,
109 (TSXKeyword::Async, TSXToken::Keyword(kw)) => {
110 options.custom_function_headers && kw.is_special_function_header()
111 }
112 (TSXKeyword::From, TSXToken::StringLiteral(..)) => true,
114 (..) => false,
115 }
116 };
117
118 #[cfg(not(feature = "extras"))]
119 return result || {
120 let TSXToken::Keyword(token) = *token else { return false };
121
122 matches!(token, TSXKeyword::Import)
124 && matches!(
125 reader.peek_n(1),
126 Some(Token(
127 TSXToken::OpenBrace
128 | TSXToken::Keyword(..)
129 | TSXToken::Identifier(..)
130 | TSXToken::StringLiteral(..)
131 | TSXToken::Multiply,
132 _
133 ))
134 )
135 };
136 }
137}
138
139#[apply(derive_ASTNode)]
140#[derive(Debug, Clone, PartialEq, Eq)]
141pub enum ImportLocation {
142 Quoted(String, Quoted),
143 #[cfg_attr(feature = "self-rust-tokenize", self_tokenize_field(0))]
144 Marker(
145 #[cfg_attr(target_family = "wasm", tsify(type = "Marker<ImportLocation>"))] Marker<Self>,
146 ),
147}
148
149impl ImportLocation {
150 pub(crate) fn from_reader(
151 reader: &mut impl tokenizer_lib::TokenReader<crate::TSXToken, crate::TokenStart>,
152 state: &mut crate::ParsingState,
153 options: &crate::ParseOptions,
154 start: Option<TokenStart>,
155 ) -> crate::ParseResult<(Self, source_map::End)> {
156 if let (true, Some(start), Some(Token(peek, at))) =
157 (options.partial_syntax, start, reader.peek())
158 {
159 let next_is_not_location_like = peek.is_statement_or_declaration_start()
160 && state
161 .line_starts
162 .byte_indexes_on_different_lines(start.0 as usize, at.0 as usize);
163
164 if next_is_not_location_like {
165 return Ok((
166 ImportLocation::Marker(state.new_partial_point_marker(*at)),
167 source_map::End(start.0),
168 ));
169 }
170 }
171
172 let token = reader.next().ok_or_else(parse_lexing_error)?;
173 if let Token(TSXToken::StringLiteral(content, quoted), start) = token {
174 let with_length = start.get_end_after(content.len() + 1);
175 Ok((ImportLocation::Quoted(content, quoted), with_length))
176 } else if options.interpolation_points
177 && matches!(&token.0, TSXToken::Identifier(i) if i == crate::marker::MARKER)
178 {
179 Ok((Self::Marker(state.new_partial_point_marker(token.1)), source_map::End(token.1 .0)))
180 } else {
181 Err(ParseError::new(
182 ParseErrors::ExpectedStringLiteral { found: token.0 },
183 token.1.with_length(0),
184 ))
185 }
186 }
187
188 pub(crate) fn to_string_from_buffer<T: source_map::ToString>(&self, buf: &mut T) {
189 match self {
190 ImportLocation::Quoted(inner, quoted) => {
191 buf.push(quoted.as_char());
192 buf.push_str(inner);
193 buf.push(quoted.as_char());
194 }
195 ImportLocation::Marker(_) => {}
196 }
197 }
198
199 #[must_use]
201 pub fn get_path(&self) -> Option<&str> {
202 if let Self::Quoted(name, _) = self {
203 Some(name)
204 } else {
205 None
206 }
207 }
208}
209
210impl crate::ASTNode for Declaration {
211 fn from_reader(
212 reader: &mut impl tokenizer_lib::TokenReader<crate::TSXToken, crate::TokenStart>,
213 state: &mut crate::ParsingState,
214 options: &crate::ParseOptions,
215 ) -> crate::ParseResult<Self> {
216 let decorators = decorators::decorators_from_reader(reader, state, options)?;
219
220 match reader.peek().ok_or_else(parse_lexing_error)?.0 {
221 TSXToken::Keyword(TSXKeyword::Const) => {
223 let after_const = reader.peek_n(2);
224 if let Some(Token(TSXToken::Keyword(TSXKeyword::Enum), _)) = after_const {
225 EnumDeclaration::from_reader(reader, state, options)
226 .map(|on| Declaration::Enum(Decorated::new(decorators, on)))
227 } else {
228 let declaration = VariableDeclaration::from_reader(reader, state, options)?;
229 Ok(Declaration::Variable(declaration))
230 }
231 }
232 TSXToken::Keyword(TSXKeyword::Let) => {
233 let declaration = VariableDeclaration::from_reader(reader, state, options)?;
234 Ok(Declaration::Variable(declaration))
235 }
236 TSXToken::Keyword(TSXKeyword::Enum) => {
237 EnumDeclaration::from_reader(reader, state, options)
238 .map(|on| Declaration::Enum(Decorated::new(decorators, on)))
239 }
240 #[cfg(feature = "extras")]
241 TSXToken::Keyword(ref kw) if kw.is_special_function_header() => {
242 let function = StatementFunction::from_reader(reader, state, options)?;
243 Ok(Declaration::Function(Decorated::new(decorators, function)))
244 }
245 TSXToken::Keyword(TSXKeyword::Function | TSXKeyword::Async) => {
246 let function = StatementFunction::from_reader(reader, state, options)?;
247 Ok(Declaration::Function(Decorated::new(decorators, function)))
248 }
249 TSXToken::Keyword(TSXKeyword::Class) => {
250 let Token(_, start) = reader.next().unwrap();
251 state.append_keyword_at_pos(start.0, TSXKeyword::Class);
252 ClassDeclaration::from_reader_sub_class_keyword(reader, state, options, start)
253 .map(|on| Declaration::Class(Decorated::new(decorators, on)))
254 }
255 TSXToken::Keyword(TSXKeyword::Export) => {
256 ExportDeclaration::from_reader(reader, state, options)
257 .map(|on| Declaration::Export(Decorated::new(decorators, on)))
258 }
259 TSXToken::Keyword(TSXKeyword::Import) => {
260 ImportDeclaration::from_reader(reader, state, options).map(Into::into)
261 }
262 TSXToken::Keyword(TSXKeyword::Interface) if options.type_annotations => {
263 InterfaceDeclaration::from_reader(reader, state, options)
264 .map(|on| Declaration::Interface(Decorated::new(decorators, on)))
265 }
266 TSXToken::Keyword(TSXKeyword::Type) if options.type_annotations => {
267 TypeAlias::from_reader(reader, state, options).map(Into::into)
268 }
269 TSXToken::Keyword(TSXKeyword::Declare) if options.type_annotations => {
270 let Token(_, start) = reader.next().unwrap();
271 match reader.peek().ok_or_else(parse_lexing_error)?.0 {
272 TSXToken::Keyword(TSXKeyword::Let | TSXKeyword::Const | TSXKeyword::Var) => {
273 DeclareVariableDeclaration::from_reader_sub_declare(
274 reader,
275 state,
276 options,
277 Some(start),
278 decorators,
279 )
280 .map(Into::into)
281 }
282 TSXToken::Keyword(TSXKeyword::Class) => {
283 let mut class = ClassDeclaration::<StatementPosition>::from_reader(
284 reader, state, options,
285 )?;
286 class.name.is_declare = true;
287 class.position.start = start.0;
288 Ok(Declaration::Class(Decorated::new(decorators, class)))
289 }
290 TSXToken::Keyword(TSXKeyword::Function) => {
291 let mut function = StatementFunction::from_reader(reader, state, options)?;
292 function.name.is_declare = true;
293 function.position.start = start.0;
294 Ok(Declaration::Function(Decorated::new(decorators, function)))
295 }
296 TSXToken::Keyword(TSXKeyword::Type) => {
297 let mut alias = TypeAlias::from_reader(reader, state, options)?;
298 alias.name.is_declare = true;
299 alias.position.start = start.0;
300 Ok(Declaration::TypeAlias(alias))
301 }
302 #[cfg(feature = "full-typescript")]
303 TSXToken::Keyword(TSXKeyword::Namespace) => {
304 let mut namespace = crate::types::namespace::Namespace::from_reader(
305 reader, state, options,
306 )?;
307 namespace.is_declare = true;
308 namespace.position.start = start.0;
309 Ok(Declaration::Namespace(namespace))
310 }
311 _ => throw_unexpected_token_with_token(
312 reader.next().ok_or_else(parse_lexing_error)?,
313 &[
314 TSXToken::Keyword(TSXKeyword::Let),
315 TSXToken::Keyword(TSXKeyword::Const),
316 TSXToken::Keyword(TSXKeyword::Var),
317 TSXToken::Keyword(TSXKeyword::Function),
318 TSXToken::Keyword(TSXKeyword::Class),
319 TSXToken::Keyword(TSXKeyword::Type),
320 TSXToken::Keyword(TSXKeyword::Namespace),
321 ],
322 ),
323 }
324 }
325 #[cfg(feature = "extras")]
326 TSXToken::Keyword(TSXKeyword::From) => {
327 ImportDeclaration::reversed_from_reader(reader, state, options).map(Into::into)
328 }
329 #[cfg(feature = "full-typescript")]
330 TSXToken::Keyword(TSXKeyword::Namespace) => {
331 crate::types::namespace::Namespace::from_reader(reader, state, options)
332 .map(Into::into)
333 }
334 _ => throw_unexpected_token_with_token(
335 reader.next().ok_or_else(parse_lexing_error)?,
336 &[
337 TSXToken::Keyword(TSXKeyword::Let),
338 TSXToken::Keyword(TSXKeyword::Const),
339 TSXToken::Keyword(TSXKeyword::Function),
340 TSXToken::Keyword(TSXKeyword::Class),
341 TSXToken::Keyword(TSXKeyword::Enum),
342 TSXToken::Keyword(TSXKeyword::Type),
343 TSXToken::Keyword(TSXKeyword::Declare),
344 TSXToken::Keyword(TSXKeyword::Import),
345 TSXToken::Keyword(TSXKeyword::Export),
346 TSXToken::Keyword(TSXKeyword::Async),
347 #[cfg(feature = "extras")]
348 TSXToken::Keyword(TSXKeyword::Generator),
349 ],
350 ),
351 }
352 }
353
354 fn to_string_from_buffer<T: source_map::ToString>(
355 &self,
356 buf: &mut T,
357 options: &crate::ToStringOptions,
358 local: crate::LocalToStringInformation,
359 ) {
360 match self {
361 Declaration::Variable(var) => var.to_string_from_buffer(buf, options, local),
362 Declaration::Class(cls) => cls.to_string_from_buffer(buf, options, local),
363 Declaration::Import(is) => is.to_string_from_buffer(buf, options, local),
364 Declaration::Export(es) => es.to_string_from_buffer(buf, options, local),
365 Declaration::Function(f) => f.to_string_from_buffer(buf, options, local),
366 Declaration::Interface(id) => id.to_string_from_buffer(buf, options, local),
367 Declaration::TypeAlias(ta) => ta.to_string_from_buffer(buf, options, local),
368 Declaration::Enum(r#enum) => r#enum.to_string_from_buffer(buf, options, local),
369 Declaration::DeclareVariable(dvd) => dvd.to_string_from_buffer(buf, options, local),
370 #[cfg(feature = "full-typescript")]
371 Declaration::Namespace(ns) => ns.to_string_from_buffer(buf, options, local),
372 }
373 }
374
375 fn get_position(&self) -> Span {
376 *self.get()
377 }
378}
379
380pub trait ImportOrExport: std::fmt::Debug + Clone + PartialEq + Sync + Send + 'static {
381 const PREFIX: bool;
382}
383
384impl ImportOrExport for ImportDeclaration {
385 const PREFIX: bool = true;
386}
387
388impl ImportOrExport for ExportDeclaration {
389 const PREFIX: bool = false;
390}
391
392#[derive(Debug, Clone, PartialEq, Visitable, GetFieldByType)]
394#[get_field_by_type_target(Span)]
395#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))]
396pub struct ImportExportPart<T: ImportOrExport> {
397 pub just_type: bool,
398 pub name: crate::VariableIdentifier,
399 pub alias: Option<ImportExportName>,
400 pub position: Span,
401 #[visit_skip_field]
402 pub _marker: std::marker::PhantomData<T>,
403}
404
405#[cfg_attr(target_family = "wasm", wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section))]
406#[allow(dead_code)]
407const IMPORT_EXPORT_PART_TYPE: &str = r"
408 type ImportExportPart<_T> = { just_type: boolean, name: VariableIdentifier, alias: ImportExportName | null, position: Span };
409";
410
411impl<T: ImportOrExport> crate::ListItem for ImportExportPart<T> {
412 type LAST = ();
413}
414
415impl<U: ImportOrExport> crate::ASTNode for ImportExportPart<U> {
416 fn get_position(&self) -> Span {
417 *GetFieldByType::get(self)
418 }
419
420 fn from_reader(
422 reader: &mut impl crate::TokenReader<TSXToken, crate::TokenStart>,
423 state: &mut crate::ParsingState,
424 options: &ParseOptions,
425 ) -> crate::ParseResult<Self> {
426 let just_type =
427 reader.conditional_next(|t| matches!(t, TSXToken::Keyword(TSXKeyword::Type))).is_some();
428
429 if U::PREFIX {
430 let (alias, position) = ImportExportName::from_reader(reader, state, options)?;
431 if reader.conditional_next(|t| matches!(t, TSXToken::Keyword(TSXKeyword::As))).is_some()
432 {
433 let name = crate::VariableIdentifier::from_reader(reader, state, options)?;
434 let position = position.union(name.get_position());
435 Ok(Self {
436 just_type,
437 name,
438 alias: Some(alias),
439 position,
440 _marker: Default::default(),
441 })
442 } else if let ImportExportName::Reference(name) = alias {
443 let name = crate::VariableIdentifier::Standard(name, position);
444 Ok(Self { just_type, name, alias: None, position, _marker: Default::default() })
445 } else {
446 crate::throw_unexpected_token(reader, &[TSXToken::Keyword(TSXKeyword::As)])
447 }
448 } else {
449 let name = crate::VariableIdentifier::from_reader(reader, state, options)?;
450 let mut position = name.get_position();
451 let alias = if reader
452 .conditional_next(|t| matches!(t, TSXToken::Keyword(TSXKeyword::As)))
453 .is_some()
454 {
455 let (alias, end) = ImportExportName::from_reader(reader, state, options)?;
456 position = position.union(end);
457 Some(alias)
458 } else {
459 None
460 };
461 Ok(Self { just_type, name, alias, position, _marker: Default::default() })
462 }
463 }
464
465 fn to_string_from_buffer<T: source_map::ToString>(
466 &self,
467 buf: &mut T,
468 options: &crate::ToStringOptions,
469 local: crate::LocalToStringInformation,
470 ) {
471 if self.just_type && options.include_type_annotations {
472 buf.push_str("type ");
473 }
474 if let Some(ref alias) = self.alias {
475 if U::PREFIX {
476 alias.to_string_from_buffer(buf, options, local);
477 buf.push_str(" as ");
478 self.name.to_string_from_buffer(buf, options, local);
479 } else {
480 self.name.to_string_from_buffer(buf, options, local);
481 buf.push_str(" as ");
482 alias.to_string_from_buffer(buf, options, local);
483 }
484 } else {
485 self.name.to_string_from_buffer(buf, options, local);
486 }
487 }
488}
489
490fn import_export_parts_to_string_from_buffer<T: source_map::ToString, U: ImportOrExport>(
492 parts: &[ImportExportPart<U>],
493 buf: &mut T,
494 options: &crate::ToStringOptions,
495 local: crate::LocalToStringInformation,
496) {
497 use super::ASTNode;
498 use iterator_endiate::EndiateIteratorExt;
499
500 buf.push('{');
501 options.push_gap_optionally(buf);
502 if options.pretty {
503 let mut parts: Vec<&ImportExportPart<U>> = parts.iter().collect();
504 parts.sort_unstable_by_key(|part| part.name.as_option_str().unwrap_or_default());
505 for (at_end, part) in parts.iter().endiate() {
506 part.to_string_from_buffer(buf, options, local);
507 if !at_end {
508 buf.push(',');
509 options.push_gap_optionally(buf);
510 }
511 }
512 } else {
513 for (at_end, part) in parts.iter().endiate() {
514 part.to_string_from_buffer(buf, options, local);
515 if !at_end {
516 buf.push(',');
517 options.push_gap_optionally(buf);
518 }
519 }
520 }
521 options.push_gap_optionally(buf);
522 buf.push('}');
523}
524
525#[cfg(feature = "self-rust-tokenize")]
526impl<U: ImportOrExport> self_rust_tokenize::SelfRustTokenize for ImportExportPart<U> {
527 fn append_to_token_stream(
528 &self,
529 _token_stream: &mut self_rust_tokenize::proc_macro2::TokenStream,
530 ) {
531 todo!("")
532 }
533}
534
535#[derive(Debug, Clone, PartialEq)]
537#[apply(derive_ASTNode)]
538pub enum ImportExportName {
539 Reference(String),
540 Quoted(String, Quoted),
541 #[cfg_attr(feature = "self-rust-tokenize", self_tokenize_field(0))]
543 Marker(
544 #[cfg_attr(target_family = "wasm", tsify(type = "Marker<ImportExportName>"))] Marker<Self>,
545 ),
546}
547
548impl ImportExportName {
549 pub(crate) fn from_reader(
550 reader: &mut impl crate::TokenReader<TSXToken, crate::TokenStart>,
551 state: &mut crate::ParsingState,
552 options: &ParseOptions,
553 ) -> crate::ParseResult<(Self, Span)> {
554 if let Some(Token(TSXToken::Comma, pos)) = reader.peek() {
555 let marker = state.new_partial_point_marker(*pos);
556 return Ok((ImportExportName::Marker(marker), pos.union(source_map::End(pos.0))));
557 }
558 let token = reader.next().unwrap();
559 if let Token(TSXToken::StringLiteral(alias, quoted), start) = token {
560 let with_length = start.with_length(alias.len() + 1);
561 state.constant_imports.push(alias.clone());
562 Ok((ImportExportName::Quoted(alias, quoted), with_length))
563 } else {
564 let (ident, pos) = crate::tokens::token_as_identifier(token, "import alias")?;
565 if options.interpolation_points && ident == crate::marker::MARKER {
566 Ok((ImportExportName::Marker(state.new_partial_point_marker(pos.get_start())), pos))
567 } else {
568 Ok((ImportExportName::Reference(ident), pos))
569 }
570 }
571 }
572
573 pub(crate) fn to_string_from_buffer<T: source_map::ToString>(
574 &self,
575 buf: &mut T,
576 _options: &crate::ToStringOptions,
577 _local: crate::LocalToStringInformation,
578 ) {
579 match self {
580 ImportExportName::Reference(alias) => buf.push_str(alias),
581 ImportExportName::Quoted(alias, q) => {
582 buf.push(q.as_char());
583 buf.push_str(alias);
584 buf.push(q.as_char());
585 }
586 ImportExportName::Marker(_) => {}
587 }
588 }
589}