From 8bf520f4574fdc0e51ae6c8c35e455f4c7af89b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 8 Oct 2021 15:49:14 +0900 Subject: [PATCH] fix(es/transforms/react): Allow non-first jsx directives (#2377) swc_ecma_transforsm_react: - Allow using jsx directives like `jsxRuntime` at any statement. --- Cargo.lock | 2 +- ecmascript/transforms/react/Cargo.toml | 2 +- ecmascript/transforms/react/src/jsx/mod.rs | 283 ++++++++++++--------- 3 files changed, 160 insertions(+), 127 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ff42bbb251ad..0220c67f9c42c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3696,7 +3696,7 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_react" -version = "0.46.0" +version = "0.46.1" dependencies = [ "base64 0.13.0", "dashmap", diff --git a/ecmascript/transforms/react/Cargo.toml b/ecmascript/transforms/react/Cargo.toml index 7adbce30c4809..5698f699f11dd 100644 --- a/ecmascript/transforms/react/Cargo.toml +++ b/ecmascript/transforms/react/Cargo.toml @@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs"] license = "Apache-2.0/MIT" name = "swc_ecma_transforms_react" repository = "https://github.com/swc-project/swc.git" -version = "0.46.0" +version = "0.46.1" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/ecmascript/transforms/react/src/jsx/mod.rs b/ecmascript/transforms/react/src/jsx/mod.rs index 9b48c1f99d8cf..f654ba42c9f6c 100644 --- a/ecmascript/transforms/react/src/jsx/mod.rs +++ b/ecmascript/transforms/react/src/jsx/mod.rs @@ -8,11 +8,11 @@ use std::{iter, iter::once, mem}; use string_enum::StringEnum; use swc_atoms::{js_word, JsWord}; use swc_common::{ - comments::{CommentKind, Comments}, + comments::{Comment, CommentKind, Comments}, iter::IdentifyLast, sync::Lrc, util::take::Take, - FileName, Mark, SourceMap, Spanned, DUMMY_SP, + FileName, Mark, SourceMap, Span, Spanned, DUMMY_SP, }; use swc_ecma_ast::*; use swc_ecma_parser::{Parser, StringInput, Syntax}; @@ -729,107 +729,182 @@ where } } -impl VisitMut for Jsx +impl Jsx where C: Comments, { - noop_visit_mut_type!(); + fn parse_directives(&mut self, span: Span) -> bool { + let mut found = false; - fn visit_mut_module(&mut self, module: &mut Module) { let leading = if let Some(comments) = &self.comments { - let leading = comments.take_leading(module.span.lo); + let leading = comments.take_leading(span.lo); if let Some(leading) = &leading { - for leading in &**leading { - if leading.kind != CommentKind::Block { - continue; - } + found |= self.parse_comment_contents(span, &leading); + } - for line in leading.text.lines() { - let mut line = line.trim(); - if line.starts_with('*') { - line = line[1..].trim(); - } + leading + } else { + None + }; - if !line.starts_with("@jsx") { - continue; - } + if let Some(leading) = leading { + if let Some(comments) = &self.comments { + comments.add_leading_comments(span.lo, leading); + } + } - if line.starts_with("@jsxRuntime ") { - let src = line.replace("@jsxRuntime ", "").trim().to_string(); - if src == "classic" { - self.runtime = Runtime::Classic; - } else if src == "automatic" { - self.runtime = Runtime::Automatic; - } else { - todo!("proper error reporting for wrong `@jsxRuntime`") - } - continue; - } + found + } - if line.starts_with("@jsxImportSource") { - let src = line.replace("@jsxImportSource", "").trim().to_string(); - self.runtime = Runtime::Automatic; - self.import_source = src.into(); - } + /// If we found required jsx directives, we returns true. + fn parse_comment_contents(&mut self, span: Span, leading: &[Comment]) -> bool { + let mut found = false; - if line.starts_with("@jsxFrag") { - if self.runtime == Runtime::Automatic { - HANDLER.with(|handler| { - handler - .struct_span_err( - module.span, - "pragma and pragmaFrag cannot be set when runtime is \ - automatic", - ) - .emit() - }); - } + for cmt in leading { + if cmt.kind != CommentKind::Block { + continue; + } - let src = line.replace("@jsxFrag", "").trim().to_string(); - self.pragma_frag = ExprOrSpread { - expr: parse_classic_option( - &self.cm, - "module-jsx-pragma-frag", - src, - self.top_level_mark, - ), - spread: None, - }; - } else if line.starts_with("@jsx ") { - if self.runtime == Runtime::Automatic { - HANDLER.with(|handler| { - handler - .struct_span_err( - module.span, - "pragma and pragmaFrag cannot be set when runtime is \ - automatic", - ) - .emit() - }); - } + for line in cmt.text.lines() { + let mut line = line.trim(); + if line.starts_with('*') { + line = line[1..].trim(); + } - let src = line.replace("@jsx", "").trim().to_string(); + if !line.starts_with("@jsx") { + continue; + } - self.pragma = ExprOrSuper::Expr(parse_classic_option( - &self.cm, - "module-jsx-pragma", - src, - self.top_level_mark, - )); - } + if line.starts_with("@jsxRuntime ") { + let src = line.replace("@jsxRuntime ", "").trim().to_string(); + found = true; + if src == "classic" { + self.runtime = Runtime::Classic; + } else if src == "automatic" { + self.runtime = Runtime::Automatic; + } else { + todo!("proper error reporting for wrong `@jsxRuntime`") } + continue; + } + + if line.starts_with("@jsxImportSource") { + let src = line.replace("@jsxImportSource", "").trim().to_string(); + self.runtime = Runtime::Automatic; + self.import_source = src.into(); + found = true; + } + + if line.starts_with("@jsxFrag") { + found = true; + + if self.runtime == Runtime::Automatic { + HANDLER.with(|handler| { + handler + .struct_span_err( + span, + "pragma and pragmaFrag cannot be set when runtime is automatic", + ) + .emit() + }); + } + + let src = line.replace("@jsxFrag", "").trim().to_string(); + self.pragma_frag = ExprOrSpread { + expr: parse_classic_option( + &self.cm, + "module-jsx-pragma-frag", + src, + self.top_level_mark, + ), + spread: None, + }; + } else if line.starts_with("@jsx ") { + found = true; + + if self.runtime == Runtime::Automatic { + HANDLER.with(|handler| { + handler + .struct_span_err( + span, + "pragma and pragmaFrag cannot be set when runtime is automatic", + ) + .emit() + }); + } + + let src = line.replace("@jsx", "").trim().to_string(); + + self.pragma = ExprOrSuper::Expr(parse_classic_option( + &self.cm, + "module-jsx-pragma", + src, + self.top_level_mark, + )); } } + } - leading - } else { - None - }; + found + } +} - if let Some(leading) = leading { - if let Some(comments) = &self.comments { - comments.add_leading_comments(module.span.lo, leading); +impl VisitMut for Jsx +where + C: Comments, +{ + noop_visit_mut_type!(); + + fn visit_mut_expr(&mut self, expr: &mut Expr) { + let top_level_node = self.top_level_node; + let mut did_work = false; + + if let Expr::JSXElement(el) = expr { + did_work = true; + //
=> React.createElement('div', null); + *expr = self.jsx_elem_to_expr(*el.take()); + } else if let Expr::JSXFragment(frag) = expr { + // <> => React.createElement(React.Fragment, null); + did_work = true; + *expr = self.jsx_frag_to_expr(frag.take()); + } else if let Expr::Paren(ParenExpr { + expr: inner_expr, .. + }) = expr + { + if let Expr::JSXElement(el) = &mut **inner_expr { + did_work = true; + *expr = self.jsx_elem_to_expr(*el.take()); + } else if let Expr::JSXFragment(frag) = &mut **inner_expr { + // <> => React.createElement(React.Fragment, null); + did_work = true; + *expr = self.jsx_frag_to_expr(frag.take()); + } + } + + if did_work { + self.top_level_node = false; + } + + expr.visit_mut_children_with(self); + + self.top_level_node = top_level_node; + } + + fn visit_mut_member_expr(&mut self, e: &mut MemberExpr) { + e.obj.visit_mut_with(self); + if e.computed { + e.prop.visit_mut_with(self); + } + } + + fn visit_mut_module(&mut self, module: &mut Module) { + self.parse_directives(module.span); + + for item in &module.body { + let span = item.span(); + if self.parse_directives(span) { + break; } } @@ -908,48 +983,6 @@ where } } } - - fn visit_mut_expr(&mut self, expr: &mut Expr) { - let top_level_node = self.top_level_node; - let mut did_work = false; - - if let Expr::JSXElement(el) = expr { - did_work = true; - //
=> React.createElement('div', null); - *expr = self.jsx_elem_to_expr(*el.take()); - } else if let Expr::JSXFragment(frag) = expr { - // <> => React.createElement(React.Fragment, null); - did_work = true; - *expr = self.jsx_frag_to_expr(frag.take()); - } else if let Expr::Paren(ParenExpr { - expr: inner_expr, .. - }) = expr - { - if let Expr::JSXElement(el) = &mut **inner_expr { - did_work = true; - *expr = self.jsx_elem_to_expr(*el.take()); - } else if let Expr::JSXFragment(frag) = &mut **inner_expr { - // <> => React.createElement(React.Fragment, null); - did_work = true; - *expr = self.jsx_frag_to_expr(frag.take()); - } - } - - if did_work { - self.top_level_node = false; - } - - expr.visit_mut_children_with(self); - - self.top_level_node = top_level_node; - } - - fn visit_mut_member_expr(&mut self, e: &mut MemberExpr) { - e.obj.visit_mut_with(self); - if e.computed { - e.prop.visit_mut_with(self); - } - } } impl Jsx