type-only import, exports (#662)

This commit is contained in:
강동윤 2020-02-13 22:56:13 +09:00 committed by GitHub
parent f344caa4fa
commit 1fc09caa2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 356 additions and 28 deletions

View File

@ -62,6 +62,9 @@ pub struct ImportDecl {
#[serde(rename = "source")] #[serde(rename = "source")]
pub src: Str, pub src: Str,
#[serde(rename = "typeOnly")]
pub type_only: bool,
} }
/// `export * from 'mod'` /// `export * from 'mod'`
@ -83,6 +86,9 @@ pub struct NamedExport {
#[serde(rename = "source")] #[serde(rename = "source")]
pub src: Option<Str>, pub src: Option<Str>,
#[serde(rename = "typeOnly")]
pub type_only: bool,
} }
#[ast_node("ExportDefaultDeclaration")] #[ast_node("ExportDefaultDeclaration")]

View File

@ -344,6 +344,9 @@ macro_rules! tok {
("meta") => { ("meta") => {
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("meta"))) crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("meta")))
}; };
("type") => {
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("type")))
};
} }
macro_rules! token_including_semi { macro_rules! token_including_semi {

View File

@ -55,10 +55,13 @@ impl<'a, I: Tokens> Parser<'a, I> {
span: span!(start), span: span!(start),
src, src,
specifiers: vec![], specifiers: vec![],
type_only: false,
})) }))
.map(ModuleItem::from); .map(ModuleItem::from);
} }
let type_only = self.syntax().typescript() && eat!("type");
let mut specifiers = vec![]; let mut specifiers = vec![];
if is!(BindingIdent) { if is!(BindingIdent) {
@ -103,6 +106,7 @@ impl<'a, I: Tokens> Parser<'a, I> {
span: span!(start), span: span!(start),
specifiers, specifiers,
src, src,
type_only,
})) }))
.map(ModuleItem::from) .map(ModuleItem::from)
} }
@ -181,7 +185,7 @@ impl<'a, I: Tokens> Parser<'a, I> {
_ => unreachable!(), _ => unreachable!(),
}; };
// TODO: remove clone // TODO: remove clone
if let Some(decl) = self.try_parse_ts_export_decl(decorators.clone(), sym)? { if let Some(decl) = self.try_parse_ts_export_decl(decorators.clone(), sym) {
return Ok(ModuleDecl::ExportDecl(ExportDecl { return Ok(ModuleDecl::ExportDecl(ExportDecl {
span: span!(start), span: span!(start),
decl, decl,
@ -249,10 +253,12 @@ impl<'a, I: Tokens> Parser<'a, I> {
} }
} }
let type_only = self.input.syntax().typescript() && eat!("type");
// Some("default") if default is exported from 'src' // Some("default") if default is exported from 'src'
let mut export_default = None; let mut export_default = None;
if export_ns.is_none() && eat!("default") { if !type_only && export_ns.is_none() && eat!("default") {
if self.input.syntax().typescript() { if self.input.syntax().typescript() {
if is!("abstract") && peeked_is!("class") { if is!("abstract") && peeked_is!("class") {
let class_start = cur_pos!(); let class_start = cur_pos!();
@ -311,17 +317,22 @@ impl<'a, I: Tokens> Parser<'a, I> {
} }
} }
let decl = if is!("class") { let decl = if !type_only && is!("class") {
let class_start = cur_pos!(); let class_start = cur_pos!();
self.parse_class_decl(start, class_start, decorators)? self.parse_class_decl(start, class_start, decorators)?
} else if is!("async") } else if !type_only
&& is!("async")
&& peeked_is!("function") && peeked_is!("function")
&& !self.input.has_linebreak_between_cur_and_peeked() && !self.input.has_linebreak_between_cur_and_peeked()
{ {
self.parse_async_fn_decl(decorators)? self.parse_async_fn_decl(decorators)?
} else if is!("function") { } else if !type_only && is!("function") {
self.parse_fn_decl(decorators)? self.parse_fn_decl(decorators)?
} else if self.input.syntax().typescript() && is!("const") && peeked_is!("enum") { } else if !type_only
&& self.input.syntax().typescript()
&& is!("const")
&& peeked_is!("enum")
{
let start = cur_pos!(); let start = cur_pos!();
assert_and_bump!("const"); assert_and_bump!("const");
let _ = cur!(true); let _ = cur!(true);
@ -335,9 +346,10 @@ impl<'a, I: Tokens> Parser<'a, I> {
decl, decl,
}) })
}); });
} else if is!("var") } else if !type_only
&& (is!("var")
|| is!("const") || is!("const")
|| (is!("let") || (is!("let"))
&& peek!() && peek!()
.map(|t| { .map(|t| {
// module code is always in strict mode. // module code is always in strict mode.
@ -357,6 +369,7 @@ impl<'a, I: Tokens> Parser<'a, I> {
span: Span::new(start, src.span.hi(), Default::default()), span: Span::new(start, src.span.hi(), Default::default()),
specifiers: vec![s], specifiers: vec![s],
src: Some(src), src: Some(src),
type_only,
})); }));
} }
} }
@ -381,6 +394,7 @@ impl<'a, I: Tokens> Parser<'a, I> {
exported: default, exported: default,
})], })],
src: Some(src), src: Some(src),
type_only,
})); }));
} }
} }
@ -438,6 +452,7 @@ impl<'a, I: Tokens> Parser<'a, I> {
span: span!(start), span: span!(start),
specifiers, specifiers,
src, src,
type_only,
})); }));
}; };

View File

@ -2014,10 +2014,15 @@ impl<'a, I: Tokens> Parser<'a, I> {
&mut self, &mut self,
decorators: Vec<Decorator>, decorators: Vec<Decorator>,
value: JsWord, value: JsWord,
) -> PResult<'a, Option<Decl>> { ) -> Option<Decl> {
self.try_parse_ts(|p| {
let start = cur_pos!(); let start = cur_pos!();
let opt = p.parse_ts_decl(start, decorators, value, true)?;
self.parse_ts_decl(start, decorators, value, true) Ok(match opt {
Some(v) => Some(v),
None => None,
})
})
} }
/// Common to tsTryParseDeclare, tsTryParseExportDeclaration, and /// Common to tsTryParseDeclare, tsTryParseExportDeclaration, and

View File

@ -43,7 +43,8 @@
}, },
"value": "react", "value": "react",
"hasEscape": false "hasEscape": false
} },
"typeOnly": false
}, },
{ {
"type": "FunctionDeclaration", "type": "FunctionDeclaration",

View File

@ -43,7 +43,8 @@
}, },
"value": "react", "value": "react",
"hasEscape": false "hasEscape": false
} },
"typeOnly": false
}, },
{ {
"type": "FunctionDeclaration", "type": "FunctionDeclaration",

View File

@ -150,7 +150,8 @@
"exported": null "exported": null
} }
], ],
"source": null "source": null,
"typeOnly": false
} }
], ],
"interpreter": null "interpreter": null

View File

@ -41,7 +41,8 @@
}, },
"value": "test", "value": "test",
"hasEscape": false "hasEscape": false
} },
"typeOnly": false
}, },
{ {
"type": "ImportDeclaration", "type": "ImportDeclaration",
@ -80,7 +81,8 @@
}, },
"value": "test", "value": "test",
"hasEscape": false "hasEscape": false
} },
"typeOnly": false
} }
], ],
"interpreter": null "interpreter": null

View File

@ -43,7 +43,8 @@
}, },
"value": "react", "value": "react",
"hasEscape": false "hasEscape": false
} },
"typeOnly": false
}, },
{ {
"type": "FunctionDeclaration", "type": "FunctionDeclaration",

View File

@ -0,0 +1 @@
export type { Foo as Bar };

View File

@ -0,0 +1,53 @@
{
"type": "Module",
"span": {
"start": 0,
"end": 27,
"ctxt": 0
},
"body": [
{
"type": "ExportNamedDeclaration",
"span": {
"start": 0,
"end": 27,
"ctxt": 0
},
"specifiers": [
{
"type": "ExportSpecifier",
"span": {
"start": 14,
"end": 24,
"ctxt": 0
},
"orig": {
"type": "Identifier",
"span": {
"start": 14,
"end": 17,
"ctxt": 0
},
"value": "Foo",
"typeAnnotation": null,
"optional": false
},
"exported": {
"type": "Identifier",
"span": {
"start": 21,
"end": 24,
"ctxt": 0
},
"value": "Bar",
"typeAnnotation": null,
"optional": false
}
}
],
"source": null,
"typeOnly": true
}
],
"interpreter": null
}

View File

@ -0,0 +1 @@
export type { Foo };

View File

@ -0,0 +1,43 @@
{
"type": "Module",
"span": {
"start": 0,
"end": 20,
"ctxt": 0
},
"body": [
{
"type": "ExportNamedDeclaration",
"span": {
"start": 0,
"end": 20,
"ctxt": 0
},
"specifiers": [
{
"type": "ExportSpecifier",
"span": {
"start": 14,
"end": 17,
"ctxt": 0
},
"orig": {
"type": "Identifier",
"span": {
"start": 14,
"end": 17,
"ctxt": 0
},
"value": "Foo",
"typeAnnotation": null,
"optional": false
},
"exported": null
}
],
"source": null,
"typeOnly": true
}
],
"interpreter": null
}

View File

@ -0,0 +1 @@
import type { Foo as Bar } from 'foo';

View File

@ -0,0 +1,62 @@
{
"type": "Module",
"span": {
"start": 0,
"end": 38,
"ctxt": 0
},
"body": [
{
"type": "ImportDeclaration",
"span": {
"start": 0,
"end": 38,
"ctxt": 0
},
"specifiers": [
{
"type": "ImportSpecifier",
"span": {
"start": 14,
"end": 24,
"ctxt": 0
},
"local": {
"type": "Identifier",
"span": {
"start": 21,
"end": 24,
"ctxt": 0
},
"value": "Bar",
"typeAnnotation": null,
"optional": false
},
"imported": {
"type": "Identifier",
"span": {
"start": 14,
"end": 17,
"ctxt": 0
},
"value": "Foo",
"typeAnnotation": null,
"optional": false
}
}
],
"source": {
"type": "StringLiteral",
"span": {
"start": 32,
"end": 37,
"ctxt": 0
},
"value": "foo",
"hasEscape": false
},
"typeOnly": true
}
],
"interpreter": null
}

View File

@ -0,0 +1 @@
import type Foo from 'foo';

View File

@ -0,0 +1,51 @@
{
"type": "Module",
"span": {
"start": 0,
"end": 27,
"ctxt": 0
},
"body": [
{
"type": "ImportDeclaration",
"span": {
"start": 0,
"end": 27,
"ctxt": 0
},
"specifiers": [
{
"type": "ImportDefaultSpecifier",
"span": {
"start": 12,
"end": 15,
"ctxt": 0
},
"local": {
"type": "Identifier",
"span": {
"start": 12,
"end": 15,
"ctxt": 0
},
"value": "Foo",
"typeAnnotation": null,
"optional": false
}
}
],
"source": {
"type": "StringLiteral",
"span": {
"start": 21,
"end": 26,
"ctxt": 0
},
"value": "foo",
"hasEscape": false
},
"typeOnly": true
}
],
"interpreter": null
}

View File

@ -0,0 +1 @@
import type { Foo } from 'foo';

View File

@ -0,0 +1,52 @@
{
"type": "Module",
"span": {
"start": 0,
"end": 31,
"ctxt": 0
},
"body": [
{
"type": "ImportDeclaration",
"span": {
"start": 0,
"end": 31,
"ctxt": 0
},
"specifiers": [
{
"type": "ImportSpecifier",
"span": {
"start": 14,
"end": 17,
"ctxt": 0
},
"local": {
"type": "Identifier",
"span": {
"start": 14,
"end": 17,
"ctxt": 0
},
"value": "Foo",
"typeAnnotation": null,
"optional": false
},
"imported": null
}
],
"source": {
"type": "StringLiteral",
"span": {
"start": 25,
"end": 30,
"ctxt": 0
},
"value": "foo",
"hasEscape": false
},
"typeOnly": true
}
],
"interpreter": null
}

View File

@ -43,7 +43,8 @@
}, },
"value": "package", "value": "package",
"hasEscape": false "hasEscape": false
} },
"typeOnly": false
} }
], ],
"interpreter": null "interpreter": null

View File

@ -70,7 +70,8 @@
}, },
"value": "a", "value": "a",
"hasEscape": false "hasEscape": false
} },
"typeOnly": false
} }
] ]
} }

View File

@ -289,6 +289,7 @@ impl Fold<Module> for Polyfills {
value: src, value: src,
has_escape: false, has_escape: false,
}, },
type_only: false,
})) }))
}), }),
); );
@ -304,6 +305,7 @@ impl Fold<Module> for Polyfills {
value: src, value: src,
has_escape: false, has_escape: false,
}, },
type_only: false,
})) }))
}), }),
); );

View File

@ -105,6 +105,7 @@ where
} }
.into()], .into()],
src: None, src: None,
type_only: false,
}, },
)) { )) {
Ok(t) => t, Ok(t) => t,

View File

@ -365,6 +365,7 @@ impl Fold<ModuleDecl> for RestFolder {
span, span,
specifiers, specifiers,
src: None, src: None,
type_only: false,
}; };
let mut var_decl = var_decl.fold_with(self); let mut var_decl = var_decl.fold_with(self);

View File

@ -239,6 +239,7 @@ impl InjectHelpers {
local: quote_ident!(DUMMY_SP.apply_mark(mark), "swcHelpers"), local: quote_ident!(DUMMY_SP.apply_mark(mark), "swcHelpers"),
})], })],
src: quote_str!("@swc/helpers"), src: quote_str!("@swc/helpers"),
type_only: false,
}))] }))]
} else { } else {
vec![] vec![]

View File

@ -32,6 +32,7 @@ impl<'a> Fold<Vec<ModuleItem>> for Operator<'a> {
exported: Some($orig), exported: Some($orig),
})], })],
src: None, src: None,
type_only: false,
}, },
))); )));
}; };
@ -133,6 +134,7 @@ impl<'a> Fold<Vec<ModuleItem>> for Operator<'a> {
span, span,
specifiers: renamed, specifiers: renamed,
src: None, src: None,
type_only: false,
}, },
))); )));
} }

View File

@ -82,6 +82,7 @@ where
} }
.into()], .into()],
src: None, src: None,
type_only: false,
}, },
)) { )) {
Ok(t) => t, Ok(t) => t,

View File

@ -136,6 +136,7 @@ impl Fold<Vec<ModuleItem>> for Decorators {
} }
.into()], .into()],
src: None, src: None,
type_only: false,
}, },
))); )));
} }

View File

@ -39,6 +39,7 @@ impl Fold<Module> for Legacy {
span: DUMMY_SP, span: DUMMY_SP,
specifiers: replace(&mut self.exports, Default::default()), specifiers: replace(&mut self.exports, Default::default()),
src: None, src: None,
type_only: false,
}); });
m.body.push(decl.into()); m.body.push(decl.into());

View File

@ -46,6 +46,7 @@ impl Fold<Vec<ModuleItem>> for ExportDefaultFrom {
.src .src
.clone() .clone()
.expect("`export default from` requires source"), .expect("`export default from` requires source"),
type_only: false,
}))); })));
extra_stmts.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed( extra_stmts.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
NamedExport { NamedExport {
@ -58,6 +59,7 @@ impl Fold<Vec<ModuleItem>> for ExportDefaultFrom {
}, },
)], )],
src: None, src: None,
type_only: false,
}, },
))); )));
} }
@ -74,6 +76,7 @@ impl Fold<Vec<ModuleItem>> for ExportDefaultFrom {
.src .src
.clone() .clone()
.expect("`export default from` requires source"), .expect("`export default from` requires source"),
type_only: false,
}))); })));
extra_stmts.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed( extra_stmts.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
NamedExport { NamedExport {
@ -86,6 +89,7 @@ impl Fold<Vec<ModuleItem>> for ExportDefaultFrom {
}, },
)], )],
src: None, src: None,
type_only: false,
}, },
))); )));
} }

View File

@ -210,7 +210,14 @@ impl Fold<Vec<ModuleItem>> for Strip {
for item in items { for item in items {
self.was_side_effect_import = false; self.was_side_effect_import = false;
match item { match item {
ModuleItem::Stmt(Stmt::Empty(..)) => continue, ModuleItem::Stmt(Stmt::Empty(..))
| ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl {
type_only: true, ..
}))
| ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(NamedExport {
type_only: true,
..
})) => continue,
ModuleItem::ModuleDecl(ModuleDecl::Import(i)) => { ModuleItem::ModuleDecl(ModuleDecl::Import(i)) => {
let i = i.fold_with(self); let i = i.fold_with(self);

View File

@ -330,3 +330,7 @@ test!(
"export const x = { text: 'hello' };", "export const x = { text: 'hello' };",
ok_if_code_eq ok_if_code_eq
); );
to!(import_type, "import type foo from 'foo'", "");
to!(export_type, "export type { foo }", "");