Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/swc/tests/fixture/issues-8xxx/8210/output/1.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const Component = ()=>{
fileName: "$DIR/tests/fixture/issues-8xxx/8210/input/1.js",
lineNumber: 2,
columnNumber: 23
}, void 0),
}, this),
children: "Hello"
}, void 0, false, {
fileName: "$DIR/tests/fixture/issues-8xxx/8210/input/1.js",
Expand Down
120 changes: 69 additions & 51 deletions crates/swc_ecma_transforms_react/src/jsx/automatic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ use std::iter::once;

use swc_atoms::{atom, Atom};
use swc_common::{
comments::Comments, errors::HANDLER, sync::Lrc, util::take::Take, BytePos, Mark, Spanned,
SyntaxContext, DUMMY_SP,
comments::Comments, errors::HANDLER, sync::Lrc, util::take::Take, BytePos, Mark, SourceMap,
Spanned, SyntaxContext, DUMMY_SP,
};
use swc_ecma_ast::*;
use swc_ecma_utils::{prepend_stmt, private_ident, quote_ident, ExprFactory, StmtLike};
use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};

use crate::{
jsx::should_use_create_element, jsx_name, jsx_text_to_str, transform_jsx_attr_str,
AutomaticConfig, CommonConfig,
jsx::{should_use_create_element, DevelopmentContext, JsxDev},
jsx_name, jsx_text_to_str, transform_jsx_attr_str, visit_mut_development, AutomaticConfig,
CommonConfig,
};

/// Automatic runtime JSX transformer
Expand All @@ -27,6 +28,7 @@ pub fn automatic<C>(
common: CommonConfig,
unresolved_mark: Mark,
comments: Option<C>,
cm: Lrc<SourceMap>,
) -> impl Pass + VisitMut
where
C: Comments + 'static,
Expand All @@ -46,9 +48,13 @@ where
import_fragment: None,
import_create_element: None,

development: common.development.into_bool(),
throw_if_namespace: common.throw_if_namespace.into_bool(),

development: common.development.into_bool(),
development_ctx: DevelopmentContext::default(),

add_pure_comment,
cm,
})
}

Expand All @@ -61,10 +67,13 @@ struct Automatic {
import_create_element: Option<Ident>,
import_fragment: Option<Ident>,

development: bool,
throw_if_namespace: bool,

development: bool,
development_ctx: DevelopmentContext,

add_pure_comment: Lrc<dyn Fn(BytePos)>,
cm: Lrc<SourceMap>,
}

impl Automatic {
Expand Down Expand Up @@ -241,8 +250,6 @@ impl Automatic {
};

let mut key = None;
let mut source_props = None;
let mut self_props = None;

for attr in el.opening.attrs {
match attr {
Expand Down Expand Up @@ -270,36 +277,6 @@ impl Automatic {
continue;
}

if !use_create_element && *i.sym == *"__source" && self.development {
if source_props.is_some() {
panic!("Duplicate __source is found");
}
source_props = attr
.value
.and_then(jsx_attr_value_to_expr)
.map(|expr| expr.as_arg());
assert_ne!(
source_props, None,
"value of property '__source' should not be empty"
);
continue;
}

if !use_create_element && *i.sym == *"__self" && self.development {
if self_props.is_some() {
panic!("Duplicate __self is found");
}
self_props = attr
.value
.and_then(jsx_attr_value_to_expr)
.map(|expr| expr.as_arg());
assert_ne!(
self_props, None,
"value of property '__self' should not be empty"
);
continue;
}

let value = match attr.value {
Some(v) => {
jsx_attr_value_to_expr(v).expect("empty expression container?")
Expand Down Expand Up @@ -407,6 +384,23 @@ impl Automatic {
}
}

if use_create_element && self.development {
props_obj.props.push(
Prop::KeyValue(KeyValueProp {
key: quote_ident!("__source").into(),
value: self.source_props(el.span.lo).into(),
})
.into(),
);
props_obj.props.push(
Prop::KeyValue(KeyValueProp {
key: quote_ident!("__self").into(),
value: self.self_props().into(),
})
.into(),
);
}

let args = once(name.as_arg()).chain(once(props_obj.as_arg()));
let args = if use_create_element {
args.chain(children.into_iter().flatten()).collect()
Expand All @@ -417,21 +411,10 @@ impl Automatic {
None => Expr::undefined(DUMMY_SP).as_arg(),
};

// set undefined literal to __source if __source is None
let source_props = match source_props {
Some(source_props) => source_props,
None => Expr::undefined(DUMMY_SP).as_arg(),
};

// set undefined literal to __self if __self is None
let self_props = match self_props {
Some(self_props) => self_props,
None => Expr::undefined(DUMMY_SP).as_arg(),
};
args.chain(once(key))
.chain(once(use_jsxs.as_arg()))
.chain(once(source_props))
.chain(once(self_props))
.chain(once(self.source_props(el.span.lo).as_arg()))
.chain(once(self.self_props().as_arg()))
.collect()
} else {
args.chain(key).collect()
Expand Down Expand Up @@ -488,9 +471,44 @@ impl Automatic {
}
}

impl Automatic {
fn source_props(&self, pos: BytePos) -> ObjectLit {
let loc = self.cm.lookup_char_pos(pos);

ObjectLit {
props: vec![
Prop::KeyValue(KeyValueProp {
key: quote_ident!("fileName").into(),
value: loc.file.name.to_string().into(),
})
.into(),
Prop::KeyValue(KeyValueProp {
key: quote_ident!("lineNumber").into(),
value: loc.line.into(),
})
.into(),
Prop::KeyValue(KeyValueProp {
key: quote_ident!("columnNumber").into(),
value: (loc.col.0 + 1).into(),
})
.into(),
],
..Default::default()
}
}
}

impl JsxDev for Automatic {
fn development_ctxt(&mut self) -> &mut DevelopmentContext {
&mut self.development_ctx
}
}

impl VisitMut for Automatic {
noop_visit_mut_type!();

visit_mut_development!();

fn visit_mut_expr(&mut self, expr: &mut Expr) {
if let Expr::JSXElement(el) = expr {
// <div></div> => React.createElement('div', null);
Expand Down
94 changes: 89 additions & 5 deletions crates/swc_ecma_transforms_react/src/jsx/classic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ use swc_common::{
};
use swc_ecma_ast::*;
use swc_ecma_parser::{parse_file_as_expr, Syntax};
use swc_ecma_utils::{drop_span, ExprFactory};
use swc_ecma_utils::{drop_span, quote_ident, ExprFactory};
use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};

use crate::{jsx_name, jsx_text_to_str, transform_jsx_attr_str, ClassicConfig, CommonConfig};
use crate::{
jsx::{DevelopmentContext, JsxDev},
jsx_name, jsx_text_to_str, transform_jsx_attr_str, visit_mut_development, ClassicConfig,
CommonConfig,
};

/// Parse `src` to use as a `pragma` or `pragmaFrag` in jsx.
pub fn parse_expr_for_jsx(
Expand Down Expand Up @@ -107,20 +111,29 @@ where

visit_mut_pass(Classic {
pragma,

pragma_frag,

throw_if_namespace: common.throw_if_namespace.into_bool(),

development: common.development.into_bool(),
development_ctx: DevelopmentContext::default(),

add_pure_comment,
cm,
})
}

struct Classic {
pragma: Lrc<Box<Expr>>,

pragma_frag: Lrc<Box<Expr>>,

throw_if_namespace: bool,

development: bool,
development_ctx: DevelopmentContext,

add_pure_comment: Lrc<dyn Fn(BytePos)>,
cm: Lrc<SourceMap>,
}

#[cfg(feature = "concurrent")]
Expand Down Expand Up @@ -317,10 +330,20 @@ impl Classic {
}
}

impl JsxDev for Classic {
fn development_ctxt(&mut self) -> &mut DevelopmentContext {
&mut self.development_ctx
}
}

impl VisitMut for Classic {
noop_visit_mut_type!();

visit_mut_development!();

fn visit_mut_expr(&mut self, expr: &mut Expr) {
expr.visit_mut_children_with(self);

if let Expr::JSXElement(el) = expr {
// <div></div> => React.createElement('div', null);
*expr = self.jsx_elem_to_expr(*el.take());
Expand All @@ -340,8 +363,69 @@ impl VisitMut for Classic {
*expr = self.jsx_frag_to_expr(frag.take());
}
}
}

expr.visit_mut_children_with(self);
fn visit_mut_jsx_opening_element(&mut self, e: &mut JSXOpeningElement) {
e.visit_mut_children_with(self);

if !self.development {
return;
}

let loc = self.cm.lookup_char_pos(e.span.lo);
let file_name = loc.file.name.to_string();

e.attrs.push(
JSXAttr {
span: DUMMY_SP,
name: quote_ident!("__source").into(),
value: Some(
JSXExprContainer {
span: DUMMY_SP,
expr: JSXExpr::Expr(
ObjectLit {
span: DUMMY_SP,
props: vec![
Prop::KeyValue(KeyValueProp {
key: quote_ident!("fileName").into(),
value: file_name.into(),
})
.into(),
Prop::KeyValue(KeyValueProp {
key: quote_ident!("lineNumber").into(),
value: loc.line.into(),
})
.into(),
Prop::KeyValue(KeyValueProp {
key: quote_ident!("columnNumber").into(),
value: (loc.col.0 + 1).into(),
})
.into(),
],
}
.into(),
),
}
.into(),
),
}
.into(),
);

e.attrs.push(
JSXAttr {
span: DUMMY_SP,
name: quote_ident!("__self").into(),
value: Some(
JSXExprContainer {
span: DUMMY_SP,
expr: JSXExpr::Expr(self.self_props().into()),
}
.into(),
),
}
.into(),
);
}
}

Expand Down
Loading
Loading