swc_ecma_parser/parser/
module_item.rs

1use swc_atoms::atom;
2use swc_common::Span;
3use swc_ecma_ast::*;
4
5use crate::{error::SyntaxError, input::Tokens, lexer::Token, Context, PResult, Parser};
6
7impl<I: Tokens> Parser<I> {
8    pub fn parse_module_item(&mut self) -> PResult<ModuleItem> {
9        self.do_inside_of_context(Context::TopLevel, |p| {
10            p.parse_stmt_like(true, handle_import_export)
11        })
12    }
13
14    pub(crate) fn parse_module_item_block_body(
15        &mut self,
16        allow_directives: bool,
17        end: Option<Token>,
18    ) -> PResult<Vec<ModuleItem>> {
19        self.parse_block_body(allow_directives, end, handle_import_export)
20    }
21
22    /// Parses `from 'foo.js' with {};` or `from 'foo.js' assert {};`
23    fn parse_from_clause_and_semi(&mut self) -> PResult<(Box<Str>, Option<Box<ObjectLit>>)> {
24        expect!(self, Token::From);
25
26        let cur = self.input().cur();
27        let src = if cur == Token::Str {
28            Box::new(self.parse_str_lit())
29        } else {
30            unexpected!(self, "a string literal")
31        };
32        let with = if self.input().syntax().import_attributes()
33            && !self.input().had_line_break_before_cur()
34            && (self.input_mut().eat(Token::Assert) || self.input_mut().eat(Token::With))
35        {
36            match self.parse_object_expr()? {
37                Expr::Object(v) => Some(Box::new(v)),
38                _ => unreachable!(),
39            }
40        } else {
41            None
42        };
43        self.expect_general_semi()?;
44        Ok((src, with))
45    }
46
47    fn parse_named_export_specifier(&mut self, type_only: bool) -> PResult<ExportNamedSpecifier> {
48        let start = self.cur_pos();
49
50        let mut is_type_only = false;
51
52        let orig = match self.parse_module_export_name()? {
53            ModuleExportName::Ident(orig_ident) => {
54                // Handle:
55                // `export { type xx }`
56                // `export { type xx as yy }`
57                // `export { type as }`
58                // `export { type as as }`
59                // `export { type as as as }`
60                if self.syntax().typescript()
61                    && orig_ident.sym == "type"
62                    && self.input().cur().is_word()
63                {
64                    let possibly_orig = self.parse_ident_name().map(Ident::from)?;
65                    if possibly_orig.sym == "as" {
66                        // `export { type as }`
67                        if !self.input().cur().is_word() {
68                            if type_only {
69                                self.emit_err(orig_ident.span, SyntaxError::TS2207);
70                            }
71
72                            return Ok(ExportNamedSpecifier {
73                                span: self.span(start),
74                                orig: ModuleExportName::Ident(possibly_orig),
75                                exported: None,
76                                is_type_only: true,
77                            });
78                        }
79
80                        let maybe_as = self.parse_ident_name().map(Ident::from)?;
81                        if maybe_as.sym == "as" {
82                            if self.input().cur().is_word() {
83                                // `export { type as as as }`
84                                // `export { type as as foo }`
85                                let exported = self.parse_ident_name().map(Ident::from)?;
86
87                                if type_only {
88                                    self.emit_err(orig_ident.span, SyntaxError::TS2207);
89                                }
90
91                                debug_assert!(start <= orig_ident.span.hi());
92                                return Ok(ExportNamedSpecifier {
93                                    span: Span::new_with_checked(start, orig_ident.span.hi()),
94                                    orig: ModuleExportName::Ident(possibly_orig),
95                                    exported: Some(ModuleExportName::Ident(exported)),
96                                    is_type_only: true,
97                                });
98                            } else {
99                                // `export { type as as }`
100                                return Ok(ExportNamedSpecifier {
101                                    span: Span::new_with_checked(start, orig_ident.span.hi()),
102                                    orig: ModuleExportName::Ident(orig_ident),
103                                    exported: Some(ModuleExportName::Ident(maybe_as)),
104                                    is_type_only: false,
105                                });
106                            }
107                        } else {
108                            // `export { type as xxx }`
109                            return Ok(ExportNamedSpecifier {
110                                span: Span::new_with_checked(start, orig_ident.span.hi()),
111                                orig: ModuleExportName::Ident(orig_ident),
112                                exported: Some(ModuleExportName::Ident(maybe_as)),
113                                is_type_only: false,
114                            });
115                        }
116                    } else {
117                        // `export { type xx }`
118                        // `export { type xx as yy }`
119                        if type_only {
120                            self.emit_err(orig_ident.span, SyntaxError::TS2207);
121                        }
122
123                        is_type_only = true;
124                        ModuleExportName::Ident(possibly_orig)
125                    }
126                } else {
127                    ModuleExportName::Ident(orig_ident)
128                }
129            }
130            module_export_name => module_export_name,
131        };
132
133        let exported = if self.input_mut().eat(Token::As) {
134            Some(self.parse_module_export_name()?)
135        } else {
136            None
137        };
138
139        Ok(ExportNamedSpecifier {
140            span: self.span(start),
141            orig,
142            exported,
143            is_type_only,
144        })
145    }
146
147    fn parse_imported_binding(&mut self) -> PResult<Ident> {
148        Ok(self
149            .do_outside_of_context(Context::InAsync.union(Context::InGenerator), |p| {
150                p.parse_binding_ident(false)
151            })?
152            .into())
153    }
154
155    fn parse_imported_default_binding(&mut self) -> PResult<Ident> {
156        self.parse_imported_binding()
157    }
158
159    /// Parse `foo`, `foo2 as bar` in `import { foo, foo2 as bar }`
160    fn parse_import_specifier(&mut self, type_only: bool) -> PResult<ImportSpecifier> {
161        let start = self.cur_pos();
162        match self.parse_module_export_name()? {
163            ModuleExportName::Ident(mut orig_name) => {
164                let mut is_type_only = false;
165                // Handle:
166                // `import { type xx } from 'mod'`
167                // `import { type xx as yy } from 'mod'`
168                // `import { type as } from 'mod'`
169                // `import { type as as } from 'mod'`
170                // `import { type as as as } from 'mod'`
171                if self.syntax().typescript()
172                    && orig_name.sym == "type"
173                    && self.input().cur().is_word()
174                {
175                    let possibly_orig_name = self.parse_ident_name().map(Ident::from)?;
176                    if possibly_orig_name.sym == "as" {
177                        // `import { type as } from 'mod'`
178                        if !self.input().cur().is_word() {
179                            if self.ctx().is_reserved_word(&possibly_orig_name.sym) {
180                                syntax_error!(
181                                    self,
182                                    possibly_orig_name.span,
183                                    SyntaxError::ReservedWordInImport
184                                )
185                            }
186
187                            if type_only {
188                                self.emit_err(orig_name.span, SyntaxError::TS2206);
189                            }
190
191                            return Ok(ImportSpecifier::Named(ImportNamedSpecifier {
192                                span: self.span(start),
193                                local: possibly_orig_name,
194                                imported: None,
195                                is_type_only: true,
196                            }));
197                        }
198
199                        let maybe_as: Ident = self.parse_binding_ident(false)?.into();
200                        if maybe_as.sym == "as" {
201                            if self.input().cur().is_word() {
202                                // `import { type as as as } from 'mod'`
203                                // `import { type as as foo } from 'mod'`
204                                let local: Ident = self.parse_binding_ident(false)?.into();
205
206                                if type_only {
207                                    self.emit_err(orig_name.span, SyntaxError::TS2206);
208                                }
209
210                                return Ok(ImportSpecifier::Named(ImportNamedSpecifier {
211                                    span: Span::new_with_checked(start, orig_name.span.hi()),
212                                    local,
213                                    imported: Some(ModuleExportName::Ident(possibly_orig_name)),
214                                    is_type_only: true,
215                                }));
216                            } else {
217                                // `import { type as as } from 'mod'`
218                                return Ok(ImportSpecifier::Named(ImportNamedSpecifier {
219                                    span: Span::new_with_checked(start, maybe_as.span.hi()),
220                                    local: maybe_as,
221                                    imported: Some(ModuleExportName::Ident(orig_name)),
222                                    is_type_only: false,
223                                }));
224                            }
225                        } else {
226                            // `import { type as xxx } from 'mod'`
227                            return Ok(ImportSpecifier::Named(ImportNamedSpecifier {
228                                span: Span::new_with_checked(start, orig_name.span.hi()),
229                                local: maybe_as,
230                                imported: Some(ModuleExportName::Ident(orig_name)),
231                                is_type_only: false,
232                            }));
233                        }
234                    } else {
235                        // `import { type xx } from 'mod'`
236                        // `import { type xx as yy } from 'mod'`
237                        if type_only {
238                            self.emit_err(orig_name.span, SyntaxError::TS2206);
239                        }
240
241                        orig_name = possibly_orig_name;
242                        is_type_only = true;
243                    }
244                }
245
246                if self.input_mut().eat(Token::As) {
247                    let local: Ident = self.parse_binding_ident(false)?.into();
248                    return Ok(ImportSpecifier::Named(ImportNamedSpecifier {
249                        span: Span::new_with_checked(start, local.span.hi()),
250                        local,
251                        imported: Some(ModuleExportName::Ident(orig_name)),
252                        is_type_only,
253                    }));
254                }
255
256                // Handle difference between
257                //
258                // 'ImportedBinding'
259                // 'IdentifierName' as 'ImportedBinding'
260                if self.ctx().is_reserved_word(&orig_name.sym) {
261                    syntax_error!(self, orig_name.span, SyntaxError::ReservedWordInImport)
262                }
263
264                let local = orig_name;
265                Ok(ImportSpecifier::Named(ImportNamedSpecifier {
266                    span: self.span(start),
267                    local,
268                    imported: None,
269                    is_type_only,
270                }))
271            }
272            ModuleExportName::Str(orig_str) => {
273                if self.input_mut().eat(Token::As) {
274                    let local: Ident = self.parse_binding_ident(false)?.into();
275                    Ok(ImportSpecifier::Named(ImportNamedSpecifier {
276                        span: Span::new_with_checked(start, local.span.hi()),
277                        local,
278                        imported: Some(ModuleExportName::Str(orig_str)),
279                        is_type_only: false,
280                    }))
281                } else {
282                    syntax_error!(
283                        self,
284                        orig_str.span,
285                        SyntaxError::ImportBindingIsString(orig_str.value.to_string_lossy().into())
286                    )
287                }
288            }
289            #[cfg(swc_ast_unknown)]
290            _ => unreachable!(),
291        }
292    }
293
294    pub(crate) fn parse_export(&mut self, mut decorators: Vec<Decorator>) -> PResult<ModuleDecl> {
295        if !self.ctx().contains(Context::Module) && self.ctx().contains(Context::TopLevel) {
296            // Switch to module mode
297            let ctx = self.ctx() | Context::Module | Context::Strict;
298            self.set_ctx(ctx);
299        }
300
301        let start = self.cur_pos();
302        self.assert_and_bump(Token::Export);
303
304        let cur = self.input().cur();
305        if cur == Token::Eof {
306            return Err(self.eof_error());
307        }
308
309        let after_export_start = self.cur_pos();
310
311        // "export declare" is equivalent to just "export".
312        let declare = self.input().syntax().typescript() && self.input_mut().eat(Token::Declare);
313
314        if declare {
315            // TODO: Remove
316            if let Some(decl) = self.try_parse_ts_declare(after_export_start, decorators.clone())? {
317                return Ok(ExportDecl {
318                    span: self.span(start),
319                    decl,
320                }
321                .into());
322            }
323        }
324
325        if self.input().syntax().typescript() {
326            let cur = self.input().cur();
327            if cur.is_word() {
328                let sym = cur.take_word(self.input()).unwrap();
329                // TODO: remove clone
330                if let Some(decl) = self.try_parse_ts_export_decl(decorators.clone(), sym) {
331                    return Ok(ExportDecl {
332                        span: self.span(start),
333                        decl,
334                    }
335                    .into());
336                }
337            }
338
339            if self.input_mut().eat(Token::Import) {
340                let is_type_only =
341                    self.input().is(Token::Type) && peek!(self).is_some_and(|p| p.is_word());
342
343                if is_type_only {
344                    self.assert_and_bump(Token::Type);
345                }
346
347                let id = self.parse_ident_name()?;
348
349                // export import A = B
350                return self
351                    .parse_ts_import_equals_decl(
352                        start,
353                        id.into(),
354                        /* is_export */ true,
355                        is_type_only,
356                    )
357                    .map(From::from);
358            }
359
360            if self.input_mut().eat(Token::Eq) {
361                // `export = x;`
362                let expr = self.parse_expr()?;
363                self.expect_general_semi()?;
364                return Ok(TsExportAssignment {
365                    span: self.span(start),
366                    expr,
367                }
368                .into());
369            }
370
371            if self.input_mut().eat(Token::As) {
372                // `export as namespace A;`
373                // See `parseNamespaceExportDeclaration` in TypeScript's own parser
374                expect!(self, Token::Namespace);
375                let id = self.parse_ident(false, false)?;
376                self.expect_general_semi()?;
377                return Ok(TsNamespaceExportDecl {
378                    span: self.span(start),
379                    id,
380                }
381                .into());
382            }
383        }
384
385        let ns_export_specifier_start = self.cur_pos();
386
387        let type_only = self.input().syntax().typescript() && self.input_mut().eat(Token::Type);
388
389        // Some("default") if default is exported from 'src'
390        let mut export_default = None;
391
392        if !type_only && self.input_mut().eat(Token::Default) {
393            if self.input().is(Token::At) {
394                let start = self.cur_pos();
395                let after_decorators = self.parse_decorators(false)?;
396
397                if !decorators.is_empty() {
398                    syntax_error!(self, self.span(start), SyntaxError::TS8038);
399                }
400
401                decorators = after_decorators;
402            }
403
404            if self.input().syntax().typescript() {
405                if self.input().is(Token::Abstract)
406                    && peek!(self).is_some_and(|cur| cur == Token::Class)
407                    && !self.input_mut().has_linebreak_between_cur_and_peeked()
408                {
409                    let class_start = self.cur_pos();
410                    self.assert_and_bump(Token::Abstract);
411                    let cur = self.input().cur();
412                    if cur == Token::Error {
413                        let err = self.input_mut().expect_error_token_and_bump();
414                        return Err(err);
415                    }
416
417                    return self
418                        .parse_default_class(start, class_start, decorators, true)
419                        .map(ModuleDecl::ExportDefaultDecl);
420                }
421                if self.input().is(Token::Abstract)
422                    && peek!(self).is_some_and(|cur| cur == Token::Interface)
423                {
424                    self.emit_err(self.input().cur_span(), SyntaxError::TS1242);
425                    self.assert_and_bump(Token::Abstract);
426                }
427
428                if self.input().is(Token::Interface) {
429                    let interface_start = self.cur_pos();
430                    self.assert_and_bump(Token::Interface);
431                    let decl = self
432                        .parse_ts_interface_decl(interface_start)
433                        .map(DefaultDecl::from)?;
434                    return Ok(ExportDefaultDecl {
435                        span: self.span(start),
436                        decl,
437                    }
438                    .into());
439                }
440            }
441
442            if self.input().is(Token::Class) {
443                let class_start = self.cur_pos();
444                let decl = self.parse_default_class(start, class_start, decorators, false)?;
445                return Ok(decl.into());
446            } else if self.input().is(Token::Async)
447                && peek!(self).is_some_and(|cur| cur == Token::Function)
448                && !self.input_mut().has_linebreak_between_cur_and_peeked()
449            {
450                let decl = self.parse_default_async_fn(start, decorators)?;
451                return Ok(decl.into());
452            } else if self.input().is(Token::Function) {
453                let decl = self.parse_default_fn(start, decorators)?;
454                return Ok(decl.into());
455            } else if self.input().syntax().export_default_from()
456                && ((self.input().is(Token::From)
457                    && peek!(self).is_some_and(|peek| peek == Token::Str))
458                    || (self.input().is(Token::Comma)
459                        && (peek!(self)
460                            .is_some_and(|peek| matches!(peek, Token::Asterisk | Token::LBrace)))))
461            {
462                export_default = Some(Ident::new_no_ctxt(
463                    atom!("default"),
464                    self.input().prev_span(),
465                ))
466            } else {
467                let expr = self.allow_in_expr(Self::parse_assignment_expr)?;
468                self.expect_general_semi()?;
469                return Ok(ExportDefaultExpr {
470                    span: self.span(start),
471                    expr,
472                }
473                .into());
474            }
475        }
476
477        if self.input().is(Token::At) {
478            let start = self.cur_pos();
479            let after_decorators = self.parse_decorators(false)?;
480
481            if !decorators.is_empty() {
482                syntax_error!(self, self.span(start), SyntaxError::TS8038);
483            }
484
485            decorators = after_decorators;
486        }
487
488        let decl = if !type_only && self.input().is(Token::Class) {
489            let class_start = self.cur_pos();
490            self.parse_class_decl(start, class_start, decorators, false)?
491        } else if !type_only
492            && self.input().is(Token::Async)
493            && peek!(self).is_some_and(|cur| cur == Token::Function)
494            && !self.input_mut().has_linebreak_between_cur_and_peeked()
495        {
496            self.parse_async_fn_decl(decorators)?
497        } else if !type_only && self.input().is(Token::Function) {
498            self.parse_fn_decl(decorators)?
499        } else if !type_only
500            && self.input().syntax().typescript()
501            && self.input().is(Token::Const)
502            && peek!(self).is_some_and(|cur| cur == Token::Enum)
503        {
504            let enum_start = self.cur_pos();
505            self.assert_and_bump(Token::Const);
506            self.assert_and_bump(Token::Enum);
507            return self
508                .parse_ts_enum_decl(enum_start, /* is_const */ true)
509                .map(Decl::from)
510                .map(|decl| {
511                    ExportDecl {
512                        span: self.span(start),
513                        decl,
514                    }
515                    .into()
516                });
517        } else if !type_only
518            && (self.input().is(Token::Var)
519                || self.input().is(Token::Const)
520                || (self.input().is(Token::Let))
521                    && peek!(self)
522                        .map(|t| t.follows_keyword_let())
523                        .unwrap_or(false))
524        {
525            self.parse_var_stmt(false).map(Decl::Var)?
526        } else {
527            // ```javascript
528            // export foo, * as bar, { baz } from "mod"; // *
529            // export      * as bar, { baz } from "mod"; // *
530            // export foo,           { baz } from "mod"; // *
531            // export foo, * as bar          from "mod"; // *
532            // export foo                    from "mod"; // *
533            // export      * as bar          from "mod"; //
534            // export                { baz } from "mod"; //
535            // export                { baz }           ; //
536            // export      *                 from "mod"; //
537            // ```
538
539            // export default
540            // export foo
541            let default = match export_default {
542                Some(default) => Some(default),
543                None => {
544                    if self.input().syntax().export_default_from() && self.input().cur().is_word() {
545                        Some(self.parse_ident(false, false)?)
546                    } else {
547                        None
548                    }
549                }
550            };
551
552            if default.is_none()
553                && self.input().is(Token::Asterisk)
554                && !peek!(self).is_some_and(|cur| cur == Token::As)
555            {
556                self.assert_and_bump(Token::Asterisk);
557
558                // improve error message for `export * from foo`
559                let (src, with) = self.parse_from_clause_and_semi()?;
560                return Ok(ExportAll {
561                    span: self.span(start),
562                    src,
563                    type_only,
564                    with,
565                }
566                .into());
567            }
568
569            let mut specifiers = Vec::new();
570
571            let mut has_default = false;
572            let mut has_ns = false;
573
574            if let Some(default) = default {
575                has_default = true;
576                specifiers.push(ExportSpecifier::Default(ExportDefaultSpecifier {
577                    exported: default,
578                }))
579            }
580
581            // export foo, * as bar
582            //           ^
583            if !specifiers.is_empty()
584                && self.input().is(Token::Comma)
585                && peek!(self).is_some_and(|cur| cur == Token::Asterisk)
586            {
587                self.assert_and_bump(Token::Comma);
588
589                has_ns = true;
590            }
591            // export     * as bar
592            //            ^
593            else if specifiers.is_empty() && self.input().is(Token::Asterisk) {
594                has_ns = true;
595            }
596
597            if has_ns {
598                self.assert_and_bump(Token::Asterisk);
599                expect!(self, Token::As);
600                let name = self.parse_module_export_name()?;
601                specifiers.push(ExportSpecifier::Namespace(ExportNamespaceSpecifier {
602                    span: self.span(ns_export_specifier_start),
603                    name,
604                }));
605            }
606
607            if has_default || has_ns {
608                if self.input().is(Token::From) {
609                    let (src, with) = self.parse_from_clause_and_semi()?;
610                    return Ok(NamedExport {
611                        span: self.span(start),
612                        specifiers,
613                        src: Some(src),
614                        type_only,
615                        with,
616                    }
617                    .into());
618                } else if !self.input().syntax().export_default_from() {
619                    // emit error
620                    expect!(self, Token::From);
621                }
622
623                expect!(self, Token::Comma);
624            }
625
626            expect!(self, Token::LBrace);
627
628            while !self.input().is(Token::RBrace) {
629                let specifier = self.parse_named_export_specifier(type_only)?;
630                specifiers.push(ExportSpecifier::Named(specifier));
631
632                if self.input().is(Token::RBrace) {
633                    break;
634                } else {
635                    expect!(self, Token::Comma);
636                }
637            }
638            expect!(self, Token::RBrace);
639
640            let opt = if self.input().is(Token::From) {
641                Some(self.parse_from_clause_and_semi()?)
642            } else {
643                for s in &specifiers {
644                    match s {
645                        ExportSpecifier::Default(default) => {
646                            self.emit_err(
647                                default.exported.span,
648                                SyntaxError::ExportExpectFrom(default.exported.sym.clone()),
649                            );
650                        }
651                        ExportSpecifier::Namespace(namespace) => {
652                            let export_name = match &namespace.name {
653                                ModuleExportName::Ident(i) => i.sym.clone(),
654                                ModuleExportName::Str(s) => s.value.to_string_lossy().into(),
655                                #[cfg(swc_ast_unknown)]
656                                _ => unreachable!(),
657                            };
658                            self.emit_err(
659                                namespace.span,
660                                SyntaxError::ExportExpectFrom(export_name),
661                            );
662                        }
663                        ExportSpecifier::Named(named) => match &named.orig {
664                            ModuleExportName::Ident(id) if id.is_reserved() => {
665                                self.emit_err(
666                                    id.span,
667                                    SyntaxError::ExportExpectFrom(id.sym.clone()),
668                                );
669                            }
670                            ModuleExportName::Str(s) => {
671                                self.emit_err(s.span, SyntaxError::ExportBindingIsString);
672                            }
673                            _ => {}
674                        },
675                        #[cfg(swc_ast_unknown)]
676                        _ => unreachable!(),
677                    }
678                }
679
680                self.eat_general_semi();
681
682                None
683            };
684            let (src, with) = match opt {
685                Some(v) => (Some(v.0), v.1),
686                None => (None, None),
687            };
688            return Ok(NamedExport {
689                span: self.span(start),
690                specifiers,
691                src,
692                type_only,
693                with,
694            }
695            .into());
696        };
697
698        Ok(ExportDecl {
699            span: self.span(start),
700            decl,
701        }
702        .into())
703    }
704
705    pub(crate) fn parse_import(&mut self) -> PResult<ModuleItem> {
706        let start = self.cur_pos();
707
708        if peek!(self).is_some_and(|cur| cur == Token::Dot) {
709            let expr = self.parse_expr()?;
710
711            self.eat_general_semi();
712
713            return Ok(ExprStmt {
714                span: self.span(start),
715                expr,
716            }
717            .into());
718        }
719
720        if peek!(self).is_some_and(|cur| cur == Token::LParen) {
721            let expr = self.parse_expr()?;
722
723            self.eat_general_semi();
724
725            return Ok(ExprStmt {
726                span: self.span(start),
727                expr,
728            }
729            .into());
730        }
731
732        // It's now import statement
733
734        if !self.ctx().contains(Context::Module) {
735            // Switch to module mode
736            let ctx = self.ctx() | Context::Module | Context::Strict;
737            self.set_ctx(ctx);
738        }
739
740        expect!(self, Token::Import);
741
742        // Handle import 'mod.js'
743        if self.input().cur() == Token::Str {
744            let src = Box::new(self.parse_str_lit());
745            let with = if self.input().syntax().import_attributes()
746                && !self.input().had_line_break_before_cur()
747                && (self.input_mut().eat(Token::Assert) || self.input_mut().eat(Token::With))
748            {
749                match self.parse_object_expr()? {
750                    Expr::Object(v) => Some(Box::new(v)),
751                    _ => unreachable!(),
752                }
753            } else {
754                None
755            };
756            self.eat_general_semi();
757            return Ok(ImportDecl {
758                span: self.span(start),
759                src,
760                specifiers: Vec::new(),
761                type_only: false,
762                with,
763                phase: Default::default(),
764            }
765            .into());
766        }
767
768        let mut type_only = false;
769        let mut phase = ImportPhase::Evaluation;
770        let mut specifiers = Vec::with_capacity(4);
771
772        'import_maybe_ident: {
773            if self.is_ident_ref() {
774                let mut local = self.parse_imported_default_binding()?;
775
776                if self.input().syntax().typescript() && local.sym == "type" {
777                    let cur = self.input().cur();
778                    if cur == Token::LBrace || cur == Token::Asterisk {
779                        type_only = true;
780                        break 'import_maybe_ident;
781                    }
782
783                    if self.is_ident_ref() {
784                        if !self.input().is(Token::From)
785                            || peek!(self).is_some_and(|cur| cur == Token::From)
786                        {
787                            type_only = true;
788                            local = self.parse_imported_default_binding()?;
789                        } else if peek!(self).is_some_and(|cur| cur == Token::Eq) {
790                            type_only = true;
791                            local = self.parse_ident_name().map(From::from)?;
792                        }
793                    }
794                }
795
796                if self.input().syntax().typescript() && self.input().is(Token::Eq) {
797                    return self
798                        .parse_ts_import_equals_decl(start, local, false, type_only)
799                        .map(ModuleDecl::from)
800                        .map(ModuleItem::from);
801                }
802
803                if matches!(&*local.sym, "source" | "defer") {
804                    let new_phase = match &*local.sym {
805                        "source" => ImportPhase::Source,
806                        "defer" => ImportPhase::Defer,
807                        _ => unreachable!(),
808                    };
809
810                    let cur = self.input().cur();
811                    if cur == Token::LBrace || cur == Token::Asterisk {
812                        phase = new_phase;
813                        break 'import_maybe_ident;
814                    }
815
816                    if self.is_ident_ref() && !self.input().is(Token::From)
817                        || peek!(self).is_some_and(|cur| cur == Token::From)
818                    {
819                        // For defer phase, we expect only namespace imports, so break here
820                        // and let the subsequent code handle validation
821                        if new_phase == ImportPhase::Defer {
822                            break 'import_maybe_ident;
823                        }
824                        phase = new_phase;
825                        local = self.parse_imported_default_binding()?;
826                    }
827                }
828
829                //TODO: Better error reporting
830                if !self.input().is(Token::From) {
831                    expect!(self, Token::Comma);
832                }
833                specifiers.push(ImportSpecifier::Default(ImportDefaultSpecifier {
834                    span: local.span,
835                    local,
836                }));
837            }
838        }
839
840        {
841            let import_spec_start = self.cur_pos();
842            // Namespace imports are not allowed in source phase.
843            if phase != ImportPhase::Source && self.input_mut().eat(Token::Asterisk) {
844                expect!(self, Token::As);
845                let local = self.parse_imported_binding()?;
846                specifiers.push(ImportSpecifier::Namespace(ImportStarAsSpecifier {
847                    span: self.span(import_spec_start),
848                    local,
849                }));
850                // Named imports are only allowed in evaluation phase.
851            } else if phase == ImportPhase::Evaluation && self.input_mut().eat(Token::LBrace) {
852                while !self.input().is(Token::RBrace) {
853                    specifiers.push(self.parse_import_specifier(type_only)?);
854
855                    if self.input().is(Token::RBrace) {
856                        break;
857                    } else {
858                        expect!(self, Token::Comma);
859                    }
860                }
861                expect!(self, Token::RBrace);
862            }
863        }
864
865        let src = {
866            expect!(self, Token::From);
867            if self.input().cur() == Token::Str {
868                Box::new(self.parse_str_lit())
869            } else {
870                unexpected!(self, "a string literal")
871            }
872        };
873
874        let with = if self.input().syntax().import_attributes()
875            && !self.input().had_line_break_before_cur()
876            && (self.input_mut().eat(Token::Assert) || self.input_mut().eat(Token::With))
877        {
878            match self.parse_object_expr()? {
879                Expr::Object(v) => Some(Box::new(v)),
880                _ => unreachable!(),
881            }
882        } else {
883            None
884        };
885
886        self.expect_general_semi()?;
887
888        Ok(ImportDecl {
889            span: self.span(start),
890            specifiers,
891            src,
892            type_only,
893            with,
894            phase,
895        }
896        .into())
897    }
898}
899
900fn handle_import_export<I: Tokens>(
901    p: &mut Parser<I>,
902    decorators: Vec<Decorator>,
903) -> PResult<ModuleItem> {
904    if !p
905        .ctx()
906        .intersects(Context::TopLevel.union(Context::TsModuleBlock))
907    {
908        syntax_error!(p, SyntaxError::NonTopLevelImportExport);
909    }
910
911    let decl = if p.input().is(Token::Import) {
912        p.parse_import()?
913    } else if p.input().is(Token::Export) {
914        p.parse_export(decorators).map(ModuleItem::from)?
915    } else {
916        unreachable!(
917            "handle_import_export should not be called if current token isn't import nor export"
918        )
919    };
920
921    Ok(decl)
922}
923
924#[cfg(test)]
925mod tests {
926    use crate::{EsSyntax, Syntax};
927
928    #[test]
929    fn test_legacy_decorator() {
930        crate::test_parser(
931            "@foo
932export default class Foo {
933  bar() {
934    class Baz {}
935  }
936}",
937            Syntax::Es(EsSyntax {
938                decorators: true,
939                decorators_before_export: true,
940                ..Default::default()
941            }),
942            |p| p.parse_module(),
943        );
944    }
945}