Skip to content

Commit

Permalink
fix(es/transforms/react): Allow non-first jsx directives (swc-project…
Browse files Browse the repository at this point in the history
…#2377)

swc_ecma_transforsm_react:
 - Allow using jsx directives like `jsxRuntime` at any statement.
  • Loading branch information
kdy1 committed Oct 9, 2021
1 parent 945bc9c commit 8bf520f
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 127 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion ecmascript/transforms/react/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
283 changes: 158 additions & 125 deletions ecmascript/transforms/react/src/jsx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -729,107 +729,182 @@ where
}
}

impl<C> VisitMut for Jsx<C>
impl<C> Jsx<C>
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<C> VisitMut for Jsx<C>
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;
// <div></div> => 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;
}
}

Expand Down Expand Up @@ -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;
// <div></div> => 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<C> Jsx<C>
Expand Down

0 comments on commit 8bf520f

Please sign in to comment.