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

View File

@ -344,6 +344,9 @@ macro_rules! tok {
("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 {

View File

@ -55,10 +55,13 @@ impl<'a, I: Tokens> Parser<'a, I> {
span: span!(start),
src,
specifiers: vec![],
type_only: false,
}))
.map(ModuleItem::from);
}
let type_only = self.syntax().typescript() && eat!("type");
let mut specifiers = vec![];
if is!(BindingIdent) {
@ -103,6 +106,7 @@ impl<'a, I: Tokens> Parser<'a, I> {
span: span!(start),
specifiers,
src,
type_only,
}))
.map(ModuleItem::from)
}
@ -181,7 +185,7 @@ impl<'a, I: Tokens> Parser<'a, I> {
_ => unreachable!(),
};
// 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 {
span: span!(start),
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'
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 is!("abstract") && peeked_is!("class") {
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!();
self.parse_class_decl(start, class_start, decorators)?
} else if is!("async")
} else if !type_only
&& is!("async")
&& peeked_is!("function")
&& !self.input.has_linebreak_between_cur_and_peeked()
{
self.parse_async_fn_decl(decorators)?
} else if is!("function") {
} else if !type_only && is!("function") {
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!();
assert_and_bump!("const");
let _ = cur!(true);
@ -335,15 +346,16 @@ impl<'a, I: Tokens> Parser<'a, I> {
decl,
})
});
} else if is!("var")
|| is!("const")
|| (is!("let")
&& peek!()
.map(|t| {
// module code is always in strict mode.
t.follows_keyword_let(true)
})
.unwrap_or(false))
} else if !type_only
&& (is!("var")
|| is!("const")
|| (is!("let"))
&& peek!()
.map(|t| {
// module code is always in strict mode.
t.follows_keyword_let(true)
})
.unwrap_or(false))
{
self.parse_var_stmt(false).map(Decl::Var)?
} else {
@ -357,6 +369,7 @@ impl<'a, I: Tokens> Parser<'a, I> {
span: Span::new(start, src.span.hi(), Default::default()),
specifiers: vec![s],
src: Some(src),
type_only,
}));
}
}
@ -381,6 +394,7 @@ impl<'a, I: Tokens> Parser<'a, I> {
exported: default,
})],
src: Some(src),
type_only,
}));
}
}
@ -438,6 +452,7 @@ impl<'a, I: Tokens> Parser<'a, I> {
span: span!(start),
specifiers,
src,
type_only,
}));
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -43,7 +43,8 @@
},
"value": "react",
"hasEscape": false
}
},
"typeOnly": false
},
{
"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",
"hasEscape": false
}
},
"typeOnly": false
}
],
"interpreter": null

View File

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

View File

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

View File

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

View File

@ -365,6 +365,7 @@ impl Fold<ModuleDecl> for RestFolder {
span,
specifiers,
src: None,
type_only: false,
};
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"),
})],
src: quote_str!("@swc/helpers"),
type_only: false,
}))]
} else {
vec![]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -210,7 +210,14 @@ impl Fold<Vec<ModuleItem>> for Strip {
for item in items {
self.was_side_effect_import = false;
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)) => {
let i = i.fold_with(self);

View File

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