swc_ecma_lexer/common/parser/
module_item.rs

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