Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(es/ast): Support import phase #8279

Merged
merged 2 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions crates/swc_bundler/src/bundler/import/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ where
src: Box::new(src.clone()),
type_only: false,
with: None,
phase: Default::default(),
};

if self.top_level {
Expand Down Expand Up @@ -645,6 +646,7 @@ where
src: Box::new(src),
type_only: false,
with: None,
phase: Default::default(),
};

// if self.top_level {
Expand Down
1 change: 1 addition & 0 deletions crates/swc_bundler/src/bundler/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ where
src: Box::new(src),
type_only: false,
with: None,
phase: Default::default(),
},
true,
false,
Expand Down
8 changes: 6 additions & 2 deletions crates/swc_ecma_ast/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::{
TsAsExpr, TsConstAssertion, TsInstantiation, TsNonNullExpr, TsSatisfiesExpr, TsTypeAnn,
TsTypeAssertion, TsTypeParamDecl, TsTypeParamInstantiation,
},
ComputedPropName, Id, Invalid, KeyValueProp, PropName, Str,
ComputedPropName, Id, ImportPhase, Invalid, KeyValueProp, PropName, Str,
};

#[ast_node(no_clone)]
Expand Down Expand Up @@ -1207,11 +1207,15 @@ impl Take for Super {
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Import {
pub span: Span,
pub phase: ImportPhase,
}

impl Take for Import {
fn dummy() -> Self {
Import { span: DUMMY_SP }
Import {
span: DUMMY_SP,
phase: ImportPhase::default(),
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/swc_ecma_ast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub use self::{
module_decl::{
DefaultDecl, ExportAll, ExportDecl, ExportDefaultDecl, ExportDefaultExpr,
ExportDefaultSpecifier, ExportNamedSpecifier, ExportNamespaceSpecifier, ExportSpecifier,
ImportDecl, ImportDefaultSpecifier, ImportNamedSpecifier, ImportSpecifier,
ImportDecl, ImportDefaultSpecifier, ImportNamedSpecifier, ImportPhase, ImportSpecifier,
ImportStarAsSpecifier, ModuleDecl, ModuleExportName, NamedExport,
},
operators::{AssignOp, BinaryOp, UnaryOp, UpdateOp},
Expand Down
23 changes: 23 additions & 0 deletions crates/swc_ecma_ast/src/module_decl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,28 @@ pub struct ImportDecl {

#[cfg_attr(feature = "serde-impl", serde(default))]
pub with: Option<Box<ObjectLit>>,

#[cfg_attr(feature = "serde-impl", serde(default))]
pub phase: ImportPhase,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default, EqIgnoreSpan)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[cfg_attr(
any(feature = "rkyv-impl"),
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(u32)))]
#[cfg_attr(feature = "serde-impl", derive(serde::Serialize, serde::Deserialize))]
pub enum ImportPhase {
#[default]
#[cfg_attr(feature = "serde-impl", serde(rename = "evaluation"))]
Evaluation,
#[cfg_attr(feature = "serde-impl", serde(rename = "source"))]
Source,
#[cfg_attr(feature = "serde-impl", serde(rename = "defer"))]
Defer,
}

impl Take for ImportDecl {
Expand All @@ -95,6 +117,7 @@ impl Take for ImportDecl {
src: Take::dummy(),
type_only: Default::default(),
with: Take::dummy(),
phase: Default::default(),
}
}
}
Expand Down
23 changes: 23 additions & 0 deletions crates/swc_ecma_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,18 @@ where
keyword!("type");
}

match n.phase {
ImportPhase::Evaluation => {}
ImportPhase::Source => {
space!();
keyword!("source");
}
ImportPhase::Defer => {
space!();
keyword!("defer");
}
}

let starts_with_ident = !n.specifiers.is_empty()
&& match &n.specifiers[0] {
ImportSpecifier::Default(_) => true,
Expand Down Expand Up @@ -820,6 +832,17 @@ where
#[emitter]
fn emit_import_callee(&mut self, node: &Import) -> Result {
keyword!(node.span, "import");
match node.phase {
ImportPhase::Source => {
punct!(".");
keyword!("source")
}
ImportPhase::Defer => {
punct!(".");
keyword!("defer")
}
_ => {}
}
}

#[emitter]
Expand Down
1 change: 1 addition & 0 deletions crates/swc_ecma_compat_es2020/src/export_namespace_from.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ impl VisitMut for ExportNamespaceFrom {
src: src.clone(),
type_only: false,
with: with.clone(),
phase: Default::default(),
})));

stmts.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
Expand Down
69 changes: 47 additions & 22 deletions crates/swc_ecma_parser/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,19 +259,17 @@ impl<I: Tokens> Parser<I> {

tok!("import") => {
let import = self.parse_ident_name()?;

if is!(self, '.') {
self.state.found_module_item = true;
if !self.ctx().can_be_module {
let span = span!(self, start);
self.emit_err(span, SyntaxError::ImportMetaInScript);
}
return self
.parse_import_meta_prop(Import { span: import.span })
.map(Expr::MetaProp)
.map(Box::new);
return self.parse_import_meta_prop(start, import.span);
}

return self.parse_dynamic_import(start, import.span);
return self.parse_dynamic_import(start, import.span, ImportPhase::Evaluation);
}

tok!("async") => {
Expand Down Expand Up @@ -541,19 +539,27 @@ impl<I: Tokens> Parser<I> {
}

/// `parseImportMetaProperty`
pub(super) fn parse_import_meta_prop(&mut self, import: Import) -> PResult<MetaPropExpr> {
pub(super) fn parse_import_meta_prop(
&mut self,
start: BytePos,
import_span: Span,
) -> PResult<Box<Expr>> {
expect!(self, '.');

let _ = if is!(self, "meta") {
self.parse_ident_name()?
} else {
unexpected!(self, "meta");
};
let ident = self.parse_ident_name()?;

Ok(MetaPropExpr {
span: span!(self, import.span.lo()),
kind: MetaPropKind::ImportMeta,
})
match &*ident.sym {
"meta" => Ok(MetaPropExpr {
span: span!(self, import_span.lo()),
kind: MetaPropKind::ImportMeta,
}
.into()),
"source" => self.parse_dynamic_import(start, import_span, ImportPhase::Source),
// TODO: The proposal doesn't mention import.defer yet because it was
// pending on a decision for import.source. Wait to enable it until it's
// included in the proposal.
_ => unexpected!(self, "meta"),
}
}

/// `is_new_expr`: true iff we are parsing production 'NewExpression'.
Expand Down Expand Up @@ -676,6 +682,7 @@ impl<I: Tokens> Parser<I> {
if eat!(self, "import") {
let base = Callee::Import(Import {
span: span!(self, start),
phase: Default::default(),
});
return self.parse_subscripts(base, true, false);
}
Expand Down Expand Up @@ -1446,16 +1453,28 @@ impl<I: Tokens> Parser<I> {

return Ok((
Box::new(match obj {
Callee::Import(_) => match prop {
MemberProp::Ident(Ident { sym: meta, .. }) if &*meta == "meta" => {
callee @ Callee::Import(_) => match prop {
MemberProp::Ident(Ident { sym, .. }) => {
if !self.ctx().can_be_module {
let span = span!(self, start);
self.emit_err(span, SyntaxError::ImportMetaInScript);
}
Expr::MetaProp(MetaPropExpr {
span,
kind: MetaPropKind::ImportMeta,
})
match &*sym {
"meta" => Expr::MetaProp(MetaPropExpr {
span,
kind: MetaPropKind::ImportMeta,
}),
_ => {
let args = self.parse_args(true)?;

Expr::Call(CallExpr {
span,
callee,
args,
type_args: None,
})
}
}
}
_ => {
unexpected!(self, "meta");
Expand Down Expand Up @@ -1606,6 +1625,7 @@ impl<I: Tokens> Parser<I> {
if eat!(self, "import") {
let obj = Callee::Import(Import {
span: span!(self, start),
phase: Default::default(),
});
return self.parse_subscripts(obj, false, false);
}
Expand Down Expand Up @@ -1650,6 +1670,7 @@ impl<I: Tokens> Parser<I> {
_ if callee.is_ident_ref_to("import") => (
Callee::Import(Import {
span: callee.span(),
phase: Default::default(),
}),
true,
),
Expand Down Expand Up @@ -2075,11 +2096,15 @@ impl<I: Tokens> Parser<I> {
&mut self,
start: BytePos,
import_span: Span,
phase: ImportPhase,
) -> PResult<Box<Expr>> {
let args = self.parse_args(true)?;
let import = Box::new(Expr::Call(CallExpr {
span: span!(self, start),
callee: Callee::Import(Import { span: import_span }),
callee: Callee::Import(Import {
span: import_span,
phase,
}),
args,
type_args: Default::default(),
}));
Expand Down
5 changes: 4 additions & 1 deletion crates/swc_ecma_parser/src/parser/expr/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,10 @@ fn issue_328() {
span,
expr: Box::new(Expr::Call(CallExpr {
span,
callee: Callee::Import(Import { span }),
callee: Callee::Import(Import {
span,
phase: Default::default()
}),
args: vec![ExprOrSpread {
spread: None,
expr: Box::new(Expr::Lit(Lit::Str(Str {
Expand Down
21 changes: 21 additions & 0 deletions crates/swc_ecma_parser/src/parser/stmt/module_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,12 @@ impl<I: Tokens> Parser<I> {
specifiers: vec![],
type_only: false,
with,
phase: Default::default(),
})));
}

let mut type_only = false;
let mut phase = ImportPhase::Evaluation;
let mut specifiers = vec![];

'import_maybe_ident: {
Expand Down Expand Up @@ -106,6 +108,24 @@ impl<I: Tokens> Parser<I> {
.map(ModuleItem::from);
}

if matches!(&*local.sym, "source" | "defer") {
let new_phase = match &*local.sym {
"source" => ImportPhase::Source,
"defer" => ImportPhase::Defer,
_ => unreachable!(),
};

if is_one_of!(self, '*', '{') {
phase = new_phase;
break 'import_maybe_ident;
}

if is!(self, BindingIdent) && !is!(self, "from") || peeked_is!(self, "from") {
phase = new_phase;
local = self.parse_imported_default_binding()?;
}
}

//TODO: Better error reporting
if !is!(self, "from") {
expect!(self, ',');
Expand Down Expand Up @@ -178,6 +198,7 @@ impl<I: Tokens> Parser<I> {
src,
type_only,
with,
phase,
})))
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import.defer("x", { with: { attr: "val" } });
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import.defer("foo");
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import.defer("foo");
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import defer * as ns from "x" with { attr: "val" };
Loading
Loading