mirror of
https://github.com/swc-project/swc.git
synced 2024-12-24 22:22:34 +03:00
Shebang & small improvement for object rest spread (#184)
swc_ecma_ast: - handle shebang swc_ecma_codegen: - handle shebang swc_ecma_parser: - handle shebang swc_ecma_transforms: - fix arrow expression used with object rest spread
This commit is contained in:
parent
f797019b79
commit
2d062432a8
@ -33,7 +33,7 @@ pub use self::{
|
||||
JSXOpeningFragment, JSXSpreadChild, JSXText,
|
||||
},
|
||||
lit::{Bool, Lit, Null, Number, Regex, RegexFlags, Str},
|
||||
module::{Module, ModuleItem},
|
||||
module::{Module, ModuleItem, Script},
|
||||
module_decl::{
|
||||
ExportAll, ExportDefaultDecl, ExportSpecifier, ImportDecl, ImportDefault, ImportSpecific,
|
||||
ImportSpecifier, ImportStarAs, ModuleDecl, NamedExport, NamedExportSpecifier,
|
||||
|
@ -1,10 +1,19 @@
|
||||
use crate::{module_decl::ModuleDecl, stmt::Stmt};
|
||||
use swc_atoms::JsWord;
|
||||
use swc_common::{ast_node, Span};
|
||||
|
||||
#[ast_node]
|
||||
pub struct Module {
|
||||
pub span: Span,
|
||||
pub body: Vec<ModuleItem>,
|
||||
pub shebang: Option<JsWord>,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub struct Script {
|
||||
pub span: Span,
|
||||
pub body: Vec<Stmt>,
|
||||
pub shebang: Option<JsWord>,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
|
@ -72,7 +72,7 @@ pub struct Emitter<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Emitter<'a> {
|
||||
pub fn emit_script(&mut self, stmts: &[Stmt]) -> Result {
|
||||
pub fn emit_stmts(&mut self, stmts: &[Stmt]) -> Result {
|
||||
let span = if stmts.is_empty() {
|
||||
DUMMY_SP
|
||||
} else {
|
||||
@ -91,6 +91,21 @@ impl<'a> Emitter<'a> {
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_module(&mut self, node: &Module) -> Result {
|
||||
if let Some(ref shebang) = node.shebang {
|
||||
punct!("#!");
|
||||
self.wr.write_lit(DUMMY_SP, &*shebang)?;
|
||||
}
|
||||
for stmt in &node.body {
|
||||
emit!(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_script(&mut self, node: &Script) -> Result {
|
||||
if let Some(ref shebang) = node.shebang {
|
||||
punct!("#!");
|
||||
self.wr.write_lit(DUMMY_SP, &*shebang)?;
|
||||
}
|
||||
for stmt in &node.body {
|
||||
emit!(stmt);
|
||||
}
|
||||
@ -1462,14 +1477,6 @@ impl<'a> Emitter<'a> {
|
||||
|
||||
/// Statements
|
||||
impl<'a> Emitter<'a> {
|
||||
pub fn emit_stmts(&mut self, stmts: &[Stmt]) -> Result {
|
||||
for stmt in stmts {
|
||||
self.emit_stmt(stmt)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_stmt(&mut self, node: &Stmt) -> Result {
|
||||
match *node {
|
||||
|
@ -758,6 +758,16 @@ impl<'a, I: Input> Lexer<'a, I> {
|
||||
))
|
||||
}
|
||||
|
||||
fn read_shebang(&mut self) -> LexResult<Option<JsWord>> {
|
||||
if self.input.cur() != Some('#') || self.input.peek() != Some('!') {
|
||||
return Ok(None);
|
||||
}
|
||||
self.input.bump();
|
||||
self.input.bump();
|
||||
let s = self.input.uncons_while(|c| !c.is_line_break());
|
||||
Ok(Some(s.into()))
|
||||
}
|
||||
|
||||
fn read_tmpl_token(&mut self, start_of_tpl: BytePos) -> LexResult<Token> {
|
||||
let start = self.cur_pos();
|
||||
|
||||
|
@ -99,43 +99,41 @@ impl<'a, I: Input> Lexer<'a, I> {
|
||||
impl<'a, I: Input> Iterator for Lexer<'a, I> {
|
||||
type Item = TokenAndSpan;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.state.had_line_break = self.state.is_first;
|
||||
self.state.is_first = false;
|
||||
|
||||
// skip spaces before getting next character, if we are allowed to.
|
||||
if self.state.can_skip_space() {
|
||||
let start = self.cur_pos();
|
||||
|
||||
match self.skip_space() {
|
||||
Err(err) => {
|
||||
return Some(Token::Error(err)).map(|token| {
|
||||
// Attatch span to token.
|
||||
TokenAndSpan {
|
||||
token,
|
||||
had_line_break: self.had_line_break_before_last(),
|
||||
span: self.span(start),
|
||||
}
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
|
||||
let c = match self.input.cur() {
|
||||
Some(c) => c,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
// println!(
|
||||
// "\tContext: ({:?}) {:?}",
|
||||
// self.input.cur().unwrap(),
|
||||
// self.state.context.0
|
||||
// );
|
||||
|
||||
let start = self.cur_pos();
|
||||
self.state.start = start;
|
||||
|
||||
let res = (|| -> Result<Option<_>, _> {
|
||||
if self.state.is_first {
|
||||
if let Some(shebang) = self.read_shebang()? {
|
||||
return Ok(Some(Token::Shebang(shebang)));
|
||||
}
|
||||
}
|
||||
|
||||
self.state.had_line_break = self.state.is_first;
|
||||
self.state.is_first = false;
|
||||
|
||||
// skip spaces before getting next character, if we are allowed to.
|
||||
if self.state.can_skip_space() {
|
||||
match self.skip_space() {
|
||||
Err(err) => {
|
||||
return Err(err);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
|
||||
let c = match self.input.cur() {
|
||||
Some(c) => c,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
// println!(
|
||||
// "\tContext: ({:?}) {:?}",
|
||||
// self.input.cur().unwrap(),
|
||||
// self.state.context.0
|
||||
// );
|
||||
|
||||
self.state.start = start;
|
||||
|
||||
if self.syntax.typescript() && self.ctx.in_type {
|
||||
if c == '<' {
|
||||
self.input.bump();
|
||||
|
@ -896,6 +896,14 @@ fn max_integer() {
|
||||
lex_tokens(::Syntax::default(), "1.7976931348623157e+308");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shebang() {
|
||||
assert_eq!(
|
||||
lex_tokens(::Syntax::default(), "#!/usr/bin/env node",),
|
||||
vec![Token::Shebang("/usr/bin/env node".into())]
|
||||
);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn lex_colors_js(b: &mut Bencher) {
|
||||
b.bytes = include_str!("../../colors.js").len() as _;
|
||||
|
@ -58,17 +58,28 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
self.input.take_comments()
|
||||
}
|
||||
|
||||
pub fn parse_script(&mut self) -> PResult<'a, (Vec<Stmt>)> {
|
||||
pub fn parse_script(&mut self) -> PResult<'a, Script> {
|
||||
let start = cur_pos!();
|
||||
|
||||
let shebang = self.parse_shebang()?;
|
||||
|
||||
let ctx = Context {
|
||||
module: false,
|
||||
..self.ctx()
|
||||
};
|
||||
self.set_ctx(ctx);
|
||||
|
||||
self.parse_block_body(true, true, None)
|
||||
self.parse_block_body(true, true, None).map(|body| Script {
|
||||
span: span!(start),
|
||||
body,
|
||||
shebang,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_module(&mut self) -> PResult<'a, Module> {
|
||||
let start = cur_pos!();
|
||||
|
||||
let shebang = self.parse_shebang()?;
|
||||
//TODO: parse() -> PResult<'a, Program>
|
||||
let ctx = Context {
|
||||
module: true,
|
||||
@ -78,13 +89,23 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
// Module code is always in strict mode
|
||||
self.set_ctx(ctx);
|
||||
|
||||
let start = cur_pos!();
|
||||
self.parse_block_body(true, true, None).map(|body| Module {
|
||||
span: span!(start),
|
||||
body,
|
||||
shebang,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_shebang(&mut self) -> PResult<'a, Option<JsWord>> {
|
||||
match *cur!(false)? {
|
||||
Token::Shebang(..) => match bump!() {
|
||||
Token::Shebang(v) => Ok(Some(v)),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn ctx(&self) -> Context {
|
||||
self.input.get_ctx()
|
||||
}
|
||||
|
@ -1061,4 +1061,21 @@ export default App"#;
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shebang() {
|
||||
let src = "#!/usr/bin/env node";
|
||||
test_parser(
|
||||
src,
|
||||
Syntax::Es(EsConfig {
|
||||
..Default::default()
|
||||
}),
|
||||
|p| {
|
||||
p.parse_module().map_err(|mut e| {
|
||||
e.emit();
|
||||
()
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -131,6 +131,7 @@ pub(crate) enum Token {
|
||||
JSXTagStart,
|
||||
JSXTagEnd,
|
||||
|
||||
Shebang(JsWord),
|
||||
Error(#[cfg_attr(feature = "fold", fold(ignore))] Error),
|
||||
}
|
||||
|
||||
|
@ -262,7 +262,7 @@ fn identity_tests(tests: &mut Vec<TestDescAndFn>) -> Result<(), io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_script(file_name: &Path) -> Result<Vec<Stmt>, NormalizedOutput> {
|
||||
fn parse_script(file_name: &Path) -> Result<Script, NormalizedOutput> {
|
||||
with_parser(file_name, |p| p.parse_script())
|
||||
}
|
||||
fn parse_module<'a>(file_name: &Path) -> Result<Module, NormalizedOutput> {
|
||||
|
@ -72,8 +72,7 @@ macro_rules! impl_for_for_stmt {
|
||||
left
|
||||
}
|
||||
VarDeclOrPat::Pat(pat) => {
|
||||
let var_ident =
|
||||
quote_ident!(DUMMY_SP.apply_mark(Mark::fresh(Mark::root())), "_ref");
|
||||
let var_ident = private_ident!("_ref");
|
||||
let index = self.vars.len();
|
||||
let pat =
|
||||
self.fold_rest(pat, box Expr::Ident(var_ident.clone()), false, true);
|
||||
@ -161,7 +160,10 @@ impl Fold<Vec<VarDeclarator>> for RestFolder {
|
||||
|
||||
let var_ident = match decl.init {
|
||||
Some(box Expr::Ident(ref ident)) => ident.clone(),
|
||||
_ => quote_ident!(DUMMY_SP.apply_mark(Mark::fresh(Mark::root())), "_ref"),
|
||||
_ => match decl.name {
|
||||
Pat::Ident(ref i) => i.clone(),
|
||||
_ => private_ident!("_ref"),
|
||||
},
|
||||
};
|
||||
|
||||
let has_init = decl.init.is_some();
|
||||
@ -410,7 +412,10 @@ impl Fold<ArrowExpr> for RestFolder {
|
||||
f.params,
|
||||
match f.body {
|
||||
BlockStmtOrExpr::BlockStmt(block) => block.stmts,
|
||||
BlockStmtOrExpr::Expr(expr) => vec![Stmt::Expr(expr)],
|
||||
BlockStmtOrExpr::Expr(expr) => vec![Stmt::Return(ReturnStmt {
|
||||
span: DUMMY_SP,
|
||||
arg: Some(expr),
|
||||
})],
|
||||
},
|
||||
);
|
||||
ArrowExpr {
|
||||
@ -480,8 +485,19 @@ impl Fold<CatchClause> for RestFolder {
|
||||
}
|
||||
|
||||
impl RestFolder {
|
||||
#[inline(always)]
|
||||
fn insert_var_if_not_empty(&mut self, idx: usize, decl: VarDeclarator) {
|
||||
match decl.init {
|
||||
Some(box Expr::Ident(ref i1)) => match decl.name {
|
||||
Pat::Ident(ref i2) => {
|
||||
if *i1 == *i2 {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match decl.name {
|
||||
Pat::Object(ObjectPat { ref props, .. }) => {
|
||||
if props.len() == 0 {
|
||||
@ -493,8 +509,19 @@ impl RestFolder {
|
||||
self.vars.insert(idx, decl)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn push_var_if_not_empty(&mut self, decl: VarDeclarator) {
|
||||
match decl.init {
|
||||
Some(box Expr::Ident(ref i1)) => match decl.name {
|
||||
Pat::Ident(ref i2) => {
|
||||
if *i1 == *i2 {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match decl.name {
|
||||
Pat::Object(ObjectPat { ref props, .. }) => {
|
||||
if props.len() == 0 {
|
||||
|
@ -7,6 +7,21 @@ fn tr() -> impl Fold<Module> {
|
||||
object_rest_spread()
|
||||
}
|
||||
|
||||
test!(
|
||||
::swc_ecma_parser::Syntax::default(),
|
||||
|_| tr(),
|
||||
issue_181,
|
||||
r#"
|
||||
const fn = ({ a, ...otherProps }) => otherProps;
|
||||
"#,
|
||||
r#"
|
||||
const fn = (_param)=>{
|
||||
var { a } = _param, otherProps = _objectWithoutProperties(_param, ['a']);
|
||||
return otherProps;
|
||||
};
|
||||
"#
|
||||
);
|
||||
|
||||
test!(
|
||||
::swc_ecma_parser::Syntax::default(),
|
||||
|_| tr(),
|
||||
|
@ -43,7 +43,7 @@ macro_rules! add_to {
|
||||
None,
|
||||
)
|
||||
.parse_script()
|
||||
.map(|stmts| stmts.fold_with(&mut DropSpan))
|
||||
.map(|script| script.body.fold_with(&mut DropSpan))
|
||||
.map_err(|mut e| {
|
||||
e.emit();
|
||||
()
|
||||
|
@ -57,6 +57,7 @@ where
|
||||
Ok(Module {
|
||||
span: DUMMY_SP,
|
||||
body: op(tester)?.into_iter().map(ModuleItem::Stmt).collect(),
|
||||
shebang: None,
|
||||
})
|
||||
},
|
||||
expected,
|
||||
|
@ -467,7 +467,6 @@ impl Fold<Module> for Amd {
|
||||
// ====================
|
||||
|
||||
Module {
|
||||
span: module.span,
|
||||
body: vec![ModuleItem::Stmt(Stmt::Expr(box Expr::Call(CallExpr {
|
||||
span: DUMMY_SP,
|
||||
callee: quote_ident!("define").as_callee(),
|
||||
@ -500,6 +499,7 @@ impl Fold<Module> for Amd {
|
||||
.collect(),
|
||||
type_args: Default::default(),
|
||||
})))],
|
||||
..module
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -442,12 +442,16 @@ macro_rules! var_noop {
|
||||
fn visit(&mut self, _: &$T) {}
|
||||
}
|
||||
};
|
||||
|
||||
($T:path, $($rest:tt)*) => {
|
||||
var_noop!($T);
|
||||
var_noop!($($rest)*);
|
||||
};
|
||||
}
|
||||
|
||||
var_noop!(Expr);
|
||||
var_noop!(ArrowExpr);
|
||||
var_noop!(Function);
|
||||
var_noop!(Constructor);
|
||||
var_noop!(Expr, ArrowExpr, Function, Constructor);
|
||||
|
||||
var_noop!(TsType, TsTypeAnn, TsTypeParam);
|
||||
|
||||
/// Private `_exports` ident.
|
||||
pub(super) struct Exports(pub Ident);
|
||||
|
@ -71,10 +71,12 @@ impl<'a> Tester<'a> {
|
||||
|
||||
pub fn parse_stmts(&mut self, file_name: &str, src: &str) -> Result<Vec<Stmt>, ()> {
|
||||
let stmts = self.with_parser(file_name, Syntax::default(), src, |p| {
|
||||
p.parse_script().map_err(|mut e| {
|
||||
e.emit();
|
||||
()
|
||||
})
|
||||
p.parse_script()
|
||||
.map_err(|mut e| {
|
||||
e.emit();
|
||||
()
|
||||
})
|
||||
.map(|script| script.body)
|
||||
})?;
|
||||
|
||||
Ok(stmts)
|
||||
|
Loading…
Reference in New Issue
Block a user