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        #[cfg(swc_ast_unknown)]
336        _ => unreachable!(),
337    }
338}
339
340fn parse_export<'a, P: Parser<'a>>(
341    p: &mut P,
342    mut decorators: Vec<Decorator>,
343) -> PResult<ModuleDecl> {
344    if !p.ctx().contains(Context::Module) && p.ctx().contains(Context::TopLevel) {
345        // Switch to module mode
346        let ctx = p.ctx() | Context::Module | Context::Strict;
347        p.set_ctx(ctx);
348    }
349
350    let start = p.cur_pos();
351    p.assert_and_bump(&P::Token::EXPORT);
352
353    let cur = p.input().cur();
354    if cur.is_eof() {
355        return Err(eof_error(p));
356    }
357
358    let after_export_start = p.cur_pos();
359
360    // "export declare" is equivalent to just "export".
361    let declare = p.input().syntax().typescript() && p.input_mut().eat(&P::Token::DECLARE);
362
363    if declare {
364        // TODO: Remove
365        if let Some(decl) = try_parse_ts_declare(p, after_export_start, decorators.clone())? {
366            return Ok(ExportDecl {
367                span: p.span(start),
368                decl,
369            }
370            .into());
371        }
372    }
373
374    if p.input().syntax().typescript() {
375        let cur = p.input().cur();
376        if cur.is_word() {
377            let sym = cur.clone().take_word(p.input()).unwrap();
378            // TODO: remove clone
379            if let Some(decl) = try_parse_ts_export_decl(p, decorators.clone(), sym) {
380                return Ok(ExportDecl {
381                    span: p.span(start),
382                    decl,
383                }
384                .into());
385            }
386        }
387
388        if p.input_mut().eat(&P::Token::IMPORT) {
389            let is_type_only =
390                p.input().is(&P::Token::TYPE) && peek!(p).is_some_and(|p| p.is_word());
391
392            if is_type_only {
393                p.assert_and_bump(&P::Token::TYPE);
394            }
395
396            let id = parse_ident_name(p)?;
397
398            // export import A = B
399            return parse_ts_import_equals_decl(
400                p,
401                start,
402                id.into(),
403                /* is_export */ true,
404                is_type_only,
405            )
406            .map(From::from);
407        }
408
409        if p.input_mut().eat(&P::Token::EQUAL) {
410            // `export = x;`
411            let expr = p.parse_expr()?;
412            p.expect_general_semi()?;
413            return Ok(TsExportAssignment {
414                span: p.span(start),
415                expr,
416            }
417            .into());
418        }
419
420        if p.input_mut().eat(&P::Token::AS) {
421            // `export as namespace A;`
422            // See `parseNamespaceExportDeclaration` in TypeScript's own parser
423            expect!(p, &P::Token::NAMESPACE);
424            let id = parse_ident(p, false, false)?;
425            p.expect_general_semi()?;
426            return Ok(TsNamespaceExportDecl {
427                span: p.span(start),
428                id,
429            }
430            .into());
431        }
432    }
433
434    let ns_export_specifier_start = p.cur_pos();
435
436    let type_only = p.input().syntax().typescript() && p.input_mut().eat(&P::Token::TYPE);
437
438    // Some("default") if default is exported from 'src'
439    let mut export_default = None;
440
441    if !type_only && p.input_mut().eat(&P::Token::DEFAULT) {
442        if p.input().is(&P::Token::AT) {
443            let start = p.cur_pos();
444            let after_decorators = parse_decorators(p, false)?;
445
446            if !decorators.is_empty() {
447                syntax_error!(p, p.span(start), SyntaxError::TS8038);
448            }
449
450            decorators = after_decorators;
451        }
452
453        if p.input().syntax().typescript() {
454            if p.input().is(&P::Token::ABSTRACT)
455                && peek!(p).is_some_and(|cur| cur.is_class())
456                && !p.input_mut().has_linebreak_between_cur_and_peeked()
457            {
458                let class_start = p.cur_pos();
459                p.assert_and_bump(&P::Token::ABSTRACT);
460                let cur = p.input().cur();
461                if cur.is_error() {
462                    let err = p.input_mut().expect_error_token_and_bump();
463                    return Err(err);
464                }
465
466                return parse_default_class(p, start, class_start, decorators, true)
467                    .map(ModuleDecl::ExportDefaultDecl);
468            }
469            if p.input().is(&P::Token::ABSTRACT) && peek!(p).is_some_and(|cur| cur.is_interface()) {
470                p.emit_err(p.input().cur_span(), SyntaxError::TS1242);
471                p.assert_and_bump(&P::Token::ABSTRACT);
472            }
473
474            if p.input().is(&P::Token::INTERFACE) {
475                let interface_start = p.cur_pos();
476                p.assert_and_bump(&P::Token::INTERFACE);
477                let decl = parse_ts_interface_decl(p, interface_start).map(DefaultDecl::from)?;
478                return Ok(ExportDefaultDecl {
479                    span: p.span(start),
480                    decl,
481                }
482                .into());
483            }
484        }
485
486        if p.input().is(&P::Token::CLASS) {
487            let class_start = p.cur_pos();
488            let decl = parse_default_class(p, start, class_start, decorators, false)?;
489            return Ok(decl.into());
490        } else if p.input().is(&P::Token::ASYNC)
491            && peek!(p).is_some_and(|cur| cur.is_function())
492            && !p.input_mut().has_linebreak_between_cur_and_peeked()
493        {
494            let decl = parse_default_async_fn(p, start, decorators)?;
495            return Ok(decl.into());
496        } else if p.input().is(&P::Token::FUNCTION) {
497            let decl = parse_default_fn(p, start, decorators)?;
498            return Ok(decl.into());
499        } else if p.input().syntax().export_default_from()
500            && ((p.input().is(&P::Token::FROM) && peek!(p).is_some_and(|peek| peek.is_str()))
501                || (p.input().is(&P::Token::COMMA)
502                    && (peek!(p).is_some_and(|peek| peek.is_star() || peek.is_lbrace()))))
503        {
504            export_default = Some(Ident::new_no_ctxt(atom!("default"), p.input().prev_span()))
505        } else {
506            let expr = p.allow_in_expr(parse_assignment_expr)?;
507            p.expect_general_semi()?;
508            return Ok(ExportDefaultExpr {
509                span: p.span(start),
510                expr,
511            }
512            .into());
513        }
514    }
515
516    if p.input().is(&P::Token::AT) {
517        let start = p.cur_pos();
518        let after_decorators = parse_decorators(p, false)?;
519
520        if !decorators.is_empty() {
521            syntax_error!(p, p.span(start), SyntaxError::TS8038);
522        }
523
524        decorators = after_decorators;
525    }
526
527    let decl = if !type_only && p.input().is(&P::Token::CLASS) {
528        let class_start = p.cur_pos();
529        parse_class_decl(p, start, class_start, decorators, false)?
530    } else if !type_only
531        && p.input().is(&P::Token::ASYNC)
532        && peek!(p).is_some_and(|cur| cur.is_function())
533        && !p.input_mut().has_linebreak_between_cur_and_peeked()
534    {
535        parse_async_fn_decl(p, decorators)?
536    } else if !type_only && p.input().is(&P::Token::FUNCTION) {
537        parse_fn_decl(p, decorators)?
538    } else if !type_only
539        && p.input().syntax().typescript()
540        && p.input().is(&P::Token::CONST)
541        && peek!(p).is_some_and(|cur| cur.is_enum())
542    {
543        let enum_start = p.cur_pos();
544        p.assert_and_bump(&P::Token::CONST);
545        p.assert_and_bump(&P::Token::ENUM);
546        return parse_ts_enum_decl(p, enum_start, /* is_const */ true)
547            .map(Decl::from)
548            .map(|decl| {
549                ExportDecl {
550                    span: p.span(start),
551                    decl,
552                }
553                .into()
554            });
555    } else if !type_only
556        && (p.input().is(&P::Token::VAR)
557            || p.input().is(&P::Token::CONST)
558            || (p.input().is(&P::Token::LET))
559                && peek!(p).map(|t| t.follows_keyword_let()).unwrap_or(false))
560    {
561        parse_var_stmt(p, false).map(Decl::Var)?
562    } else {
563        // ```javascript
564        // export foo, * as bar, { baz } from "mod"; // *
565        // export      * as bar, { baz } from "mod"; // *
566        // export foo,           { baz } from "mod"; // *
567        // export foo, * as bar          from "mod"; // *
568        // export foo                    from "mod"; // *
569        // export      * as bar          from "mod"; //
570        // export                { baz } from "mod"; //
571        // export                { baz }           ; //
572        // export      *                 from "mod"; //
573        // ```
574
575        // export default
576        // export foo
577        let default = match export_default {
578            Some(default) => Some(default),
579            None => {
580                if p.input().syntax().export_default_from() && p.input().cur().is_word() {
581                    Some(parse_ident(p, false, false)?)
582                } else {
583                    None
584                }
585            }
586        };
587
588        if default.is_none()
589            && p.input().is(&P::Token::MUL)
590            && !peek!(p).is_some_and(|cur| cur.is_as())
591        {
592            p.assert_and_bump(&P::Token::MUL);
593
594            // improve error message for `export * from foo`
595            let (src, with) = parse_from_clause_and_semi(p)?;
596            return Ok(ExportAll {
597                span: p.span(start),
598                src,
599                type_only,
600                with,
601            }
602            .into());
603        }
604
605        let mut specifiers = Vec::new();
606
607        let mut has_default = false;
608        let mut has_ns = false;
609
610        if let Some(default) = default {
611            has_default = true;
612            specifiers.push(ExportSpecifier::Default(ExportDefaultSpecifier {
613                exported: default,
614            }))
615        }
616
617        // export foo, * as bar
618        //           ^
619        if !specifiers.is_empty()
620            && p.input().is(&P::Token::COMMA)
621            && peek!(p).is_some_and(|cur| cur.is_star())
622        {
623            p.assert_and_bump(&P::Token::COMMA);
624
625            has_ns = true;
626        }
627        // export     * as bar
628        //            ^
629        else if specifiers.is_empty() && p.input().is(&P::Token::MUL) {
630            has_ns = true;
631        }
632
633        if has_ns {
634            p.assert_and_bump(&P::Token::MUL);
635            expect!(p, &P::Token::AS);
636            let name = parse_module_export_name(p)?;
637            specifiers.push(ExportSpecifier::Namespace(ExportNamespaceSpecifier {
638                span: p.span(ns_export_specifier_start),
639                name,
640            }));
641        }
642
643        if has_default || has_ns {
644            if p.input().is(&P::Token::FROM) {
645                let (src, with) = parse_from_clause_and_semi(p)?;
646                return Ok(NamedExport {
647                    span: p.span(start),
648                    specifiers,
649                    src: Some(src),
650                    type_only,
651                    with,
652                }
653                .into());
654            } else if !p.input().syntax().export_default_from() {
655                // emit error
656                expect!(p, &P::Token::FROM);
657            }
658
659            expect!(p, &P::Token::COMMA);
660        }
661
662        expect!(p, &P::Token::LBRACE);
663
664        while !p.input().is(&P::Token::RBRACE) {
665            let specifier = parse_named_export_specifier(p, type_only)?;
666            specifiers.push(ExportSpecifier::Named(specifier));
667
668            if p.input().is(&P::Token::RBRACE) {
669                break;
670            } else {
671                expect!(p, &P::Token::COMMA);
672            }
673        }
674        expect!(p, &P::Token::RBRACE);
675
676        let opt = if p.input().is(&P::Token::FROM) {
677            Some(parse_from_clause_and_semi(p)?)
678        } else {
679            for s in &specifiers {
680                match s {
681                    ExportSpecifier::Default(default) => {
682                        p.emit_err(
683                            default.exported.span,
684                            SyntaxError::ExportExpectFrom(default.exported.sym.clone()),
685                        );
686                    }
687                    ExportSpecifier::Namespace(namespace) => {
688                        let export_name = match &namespace.name {
689                            ModuleExportName::Ident(i) => i.sym.clone(),
690                            ModuleExportName::Str(s) => s.value.clone(),
691                            #[cfg(swc_ast_unknown)]
692                            _ => unreachable!(),
693                        };
694                        p.emit_err(namespace.span, SyntaxError::ExportExpectFrom(export_name));
695                    }
696                    ExportSpecifier::Named(named) => match &named.orig {
697                        ModuleExportName::Ident(id) if id.is_reserved() => {
698                            p.emit_err(id.span, SyntaxError::ExportExpectFrom(id.sym.clone()));
699                        }
700                        ModuleExportName::Str(s) => {
701                            p.emit_err(s.span, SyntaxError::ExportBindingIsString);
702                        }
703                        _ => {}
704                    },
705                    #[cfg(swc_ast_unknown)]
706                    _ => (),
707                }
708            }
709
710            p.eat_general_semi();
711
712            None
713        };
714        let (src, with) = match opt {
715            Some(v) => (Some(v.0), v.1),
716            None => (None, None),
717        };
718        return Ok(NamedExport {
719            span: p.span(start),
720            specifiers,
721            src,
722            type_only,
723            with,
724        }
725        .into());
726    };
727
728    Ok(ExportDecl {
729        span: p.span(start),
730        decl,
731    }
732    .into())
733}
734
735fn parse_import<'a, P: Parser<'a>>(p: &mut P) -> PResult<ModuleItem> {
736    let start = p.cur_pos();
737
738    if peek!(p).is_some_and(|cur| cur.is_dot()) {
739        let expr = p.parse_expr()?;
740
741        p.eat_general_semi();
742
743        return Ok(ExprStmt {
744            span: p.span(start),
745            expr,
746        }
747        .into());
748    }
749
750    if peek!(p).is_some_and(|cur| cur.is_lparen()) {
751        let expr = p.parse_expr()?;
752
753        p.eat_general_semi();
754
755        return Ok(ExprStmt {
756            span: p.span(start),
757            expr,
758        }
759        .into());
760    }
761
762    // It's now import statement
763
764    if !p.ctx().contains(Context::Module) {
765        // Switch to module mode
766        let ctx = p.ctx() | Context::Module | Context::Strict;
767        p.set_ctx(ctx);
768    }
769
770    expect!(p, &P::Token::IMPORT);
771
772    // Handle import 'mod.js'
773    if p.input().cur().is_str() {
774        let src = Box::new(parse_str_lit(p));
775        let with = if p.input().syntax().import_attributes()
776            && !p.input().had_line_break_before_cur()
777            && (p.input_mut().eat(&P::Token::ASSERT) || p.input_mut().eat(&P::Token::WITH))
778        {
779            match parse_object_expr(p)? {
780                Expr::Object(v) => Some(Box::new(v)),
781                _ => unreachable!(),
782            }
783        } else {
784            None
785        };
786        p.eat_general_semi();
787        return Ok(ImportDecl {
788            span: p.span(start),
789            src,
790            specifiers: Vec::new(),
791            type_only: false,
792            with,
793            phase: Default::default(),
794        }
795        .into());
796    }
797
798    let mut type_only = false;
799    let mut phase = ImportPhase::Evaluation;
800    let mut specifiers = Vec::with_capacity(4);
801
802    'import_maybe_ident: {
803        if p.is_ident_ref() {
804            let mut local = parse_imported_default_binding(p)?;
805
806            if p.input().syntax().typescript() && local.sym == "type" {
807                let cur = p.input().cur();
808                if cur.is_lbrace() || cur.is_star() {
809                    type_only = true;
810                    break 'import_maybe_ident;
811                }
812
813                if p.is_ident_ref() {
814                    if !p.input().is(&P::Token::FROM) || peek!(p).is_some_and(|cur| cur.is_from()) {
815                        type_only = true;
816                        local = parse_imported_default_binding(p)?;
817                    } else if peek!(p).is_some_and(|cur| cur.is_equal()) {
818                        type_only = true;
819                        local = parse_ident_name(p).map(From::from)?;
820                    }
821                }
822            }
823
824            if p.input().syntax().typescript() && p.input().is(&P::Token::EQUAL) {
825                return parse_ts_import_equals_decl(p, start, local, false, type_only)
826                    .map(ModuleDecl::from)
827                    .map(ModuleItem::from);
828            }
829
830            if matches!(&*local.sym, "source" | "defer") {
831                let new_phase = match &*local.sym {
832                    "source" => ImportPhase::Source,
833                    "defer" => ImportPhase::Defer,
834                    _ => unreachable!(),
835                };
836
837                let cur = p.input().cur();
838                if cur.is_lbrace() || cur.is_star() {
839                    phase = new_phase;
840                    break 'import_maybe_ident;
841                }
842
843                if p.is_ident_ref() && !p.input().is(&P::Token::FROM)
844                    || peek!(p).is_some_and(|cur| cur.is_from())
845                {
846                    // For defer phase, we expect only namespace imports, so break here
847                    // and let the subsequent code handle validation
848                    if new_phase == ImportPhase::Defer {
849                        break 'import_maybe_ident;
850                    }
851                    phase = new_phase;
852                    local = parse_imported_default_binding(p)?;
853                }
854            }
855
856            //TODO: Better error reporting
857            if !p.input().is(&P::Token::FROM) {
858                expect!(p, &P::Token::COMMA);
859            }
860            specifiers.push(ImportSpecifier::Default(ImportDefaultSpecifier {
861                span: local.span,
862                local,
863            }));
864        }
865    }
866
867    {
868        let import_spec_start = p.cur_pos();
869        // Namespace imports are not allowed in source phase.
870        if phase != ImportPhase::Source && p.input_mut().eat(&P::Token::MUL) {
871            expect!(p, &P::Token::AS);
872            let local = parse_imported_binding(p)?;
873            specifiers.push(ImportSpecifier::Namespace(ImportStarAsSpecifier {
874                span: p.span(import_spec_start),
875                local,
876            }));
877            // Named imports are only allowed in evaluation phase.
878        } else if phase == ImportPhase::Evaluation && p.input_mut().eat(&P::Token::LBRACE) {
879            while !p.input().is(&P::Token::RBRACE) {
880                specifiers.push(parse_import_specifier(p, type_only)?);
881
882                if p.input().is(&P::Token::RBRACE) {
883                    break;
884                } else {
885                    expect!(p, &P::Token::COMMA);
886                }
887            }
888            expect!(p, &P::Token::RBRACE);
889        }
890    }
891
892    let src = {
893        expect!(p, &P::Token::FROM);
894        if p.input().cur().is_str() {
895            Box::new(parse_str_lit(p))
896        } else {
897            unexpected!(p, "a string literal")
898        }
899    };
900
901    let with = if p.input().syntax().import_attributes()
902        && !p.input().had_line_break_before_cur()
903        && (p.input_mut().eat(&P::Token::ASSERT) || p.input_mut().eat(&P::Token::WITH))
904    {
905        match parse_object_expr(p)? {
906            Expr::Object(v) => Some(Box::new(v)),
907            _ => unreachable!(),
908        }
909    } else {
910        None
911    };
912
913    p.expect_general_semi()?;
914
915    Ok(ImportDecl {
916        span: p.span(start),
917        specifiers,
918        src,
919        type_only,
920        with,
921        phase,
922    }
923    .into())
924}
925
926pub fn parse_module_item<'a>(p: &mut impl Parser<'a>) -> PResult<ModuleItem> {
927    p.do_inside_of_context(Context::TopLevel, |p| {
928        parse_stmt_like(p, true, handle_import_export)
929    })
930}