mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
feat(transformer/react-jsx): support the sourceType is a script (#1192)
This commit is contained in:
parent
ed48809b1a
commit
7d85492a03
4 changed files with 98 additions and 21 deletions
|
|
@ -87,7 +87,7 @@ impl<'a> Transformer<'a> {
|
||||||
es2016_exponentiation_operator: ExponentiationOperator::new(Rc::clone(&ast), ctx.clone(), &options),
|
es2016_exponentiation_operator: ExponentiationOperator::new(Rc::clone(&ast), ctx.clone(), &options),
|
||||||
es2015_shorthand_properties: ShorthandProperties::new(Rc::clone(&ast), &options),
|
es2015_shorthand_properties: ShorthandProperties::new(Rc::clone(&ast), &options),
|
||||||
es2015_template_literals: TemplateLiterals::new(Rc::clone(&ast), &options),
|
es2015_template_literals: TemplateLiterals::new(Rc::clone(&ast), &options),
|
||||||
react_jsx: options.react_jsx.map(|options| ReactJsx::new(Rc::clone(&ast), &ctx, options)),
|
react_jsx: options.react_jsx.map(|options| ReactJsx::new(Rc::clone(&ast), ctx.clone(), options)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ use crate::context::TransformerCtx;
|
||||||
/// * <https://github.com/babel/babel/tree/main/packages/babel-helper-builder-react-jsx>
|
/// * <https://github.com/babel/babel/tree/main/packages/babel-helper-builder-react-jsx>
|
||||||
pub struct ReactJsx<'a> {
|
pub struct ReactJsx<'a> {
|
||||||
ast: Rc<AstBuilder<'a>>,
|
ast: Rc<AstBuilder<'a>>,
|
||||||
|
ctx: Rc<TransformerCtx<'a>>,
|
||||||
options: ReactJsxOptions,
|
options: ReactJsxOptions,
|
||||||
|
|
||||||
imports: Vec<'a, Statement<'a>>,
|
imports: Vec<'a, Statement<'a>>,
|
||||||
|
|
@ -27,6 +28,7 @@ pub struct ReactJsx<'a> {
|
||||||
import_jsxs: bool,
|
import_jsxs: bool,
|
||||||
import_fragment: bool,
|
import_fragment: bool,
|
||||||
import_create_element: bool,
|
import_create_element: bool,
|
||||||
|
require_jsx_runtime: bool,
|
||||||
// Will be store jsx runtime importer, like `react/jsx-runtime`
|
// Will be store jsx runtime importer, like `react/jsx-runtime`
|
||||||
jsx_runtime_importer: Atom,
|
jsx_runtime_importer: Atom,
|
||||||
}
|
}
|
||||||
|
|
@ -70,11 +72,7 @@ impl<'a, 'b> JSXElementOrFragment<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ReactJsx<'a> {
|
impl<'a> ReactJsx<'a> {
|
||||||
pub fn new(
|
pub fn new(ast: Rc<AstBuilder<'a>>, ctx: TransformerCtx<'a>, options: ReactJsxOptions) -> Self {
|
||||||
ast: Rc<AstBuilder<'a>>,
|
|
||||||
ctx: &TransformerCtx<'a>,
|
|
||||||
options: ReactJsxOptions,
|
|
||||||
) -> Self {
|
|
||||||
let imports = ast.new_vec();
|
let imports = ast.new_vec();
|
||||||
let options = options.with_comments(&ctx.semantic());
|
let options = options.with_comments(&ctx.semantic());
|
||||||
|
|
||||||
|
|
@ -85,11 +83,14 @@ impl<'a> ReactJsx<'a> {
|
||||||
Atom::from(format!("{}/jsx-runtime", options.import_source))
|
Atom::from(format!("{}/jsx-runtime", options.import_source))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let ctx = Rc::new(ctx);
|
||||||
Self {
|
Self {
|
||||||
ast,
|
ast,
|
||||||
|
ctx,
|
||||||
options,
|
options,
|
||||||
imports,
|
imports,
|
||||||
jsx_runtime_importer,
|
jsx_runtime_importer,
|
||||||
|
require_jsx_runtime: false,
|
||||||
import_jsx: false,
|
import_jsx: false,
|
||||||
import_jsxs: false,
|
import_jsxs: false,
|
||||||
import_fragment: false,
|
import_fragment: false,
|
||||||
|
|
@ -145,16 +146,30 @@ impl<'a> ReactJsx<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_require_jsx_runtime(&mut self) {
|
||||||
|
if !self.require_jsx_runtime {
|
||||||
|
self.require_jsx_runtime = true;
|
||||||
|
self.require_jsx_runtime(Self::new_string_literal(self.jsx_runtime_importer.as_str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn add_import_jsx(&mut self) {
|
fn add_import_jsx(&mut self) {
|
||||||
if !self.import_jsx {
|
if self.ctx.semantic().source_type().is_script() {
|
||||||
|
self.add_require_jsx_runtime();
|
||||||
|
} else if !self.import_jsx {
|
||||||
self.import_jsx = true;
|
self.import_jsx = true;
|
||||||
let source = Self::new_string_literal(self.jsx_runtime_importer.as_str());
|
self.add_import_statement(
|
||||||
self.add_import_statement("jsx", "_jsx", source);
|
"jsx",
|
||||||
|
"_jsx",
|
||||||
|
Self::new_string_literal(self.jsx_runtime_importer.as_str()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_import_jsxs(&mut self) {
|
fn add_import_jsxs(&mut self) {
|
||||||
if !self.import_jsxs {
|
if self.ctx.semantic().source_type().is_script() {
|
||||||
|
self.add_require_jsx_runtime();
|
||||||
|
} else if !self.import_jsxs {
|
||||||
self.import_jsxs = true;
|
self.import_jsxs = true;
|
||||||
let source = Self::new_string_literal(self.jsx_runtime_importer.as_str());
|
let source = Self::new_string_literal(self.jsx_runtime_importer.as_str());
|
||||||
self.add_import_statement("jsxs", "_jsxs", source);
|
self.add_import_statement("jsxs", "_jsxs", source);
|
||||||
|
|
@ -162,7 +177,9 @@ impl<'a> ReactJsx<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_import_fragment(&mut self) {
|
fn add_import_fragment(&mut self) {
|
||||||
if !self.import_fragment {
|
if self.ctx.semantic().source_type().is_script() {
|
||||||
|
self.add_require_jsx_runtime();
|
||||||
|
} else if !self.import_fragment {
|
||||||
self.import_fragment = true;
|
self.import_fragment = true;
|
||||||
let source = Self::new_string_literal(self.jsx_runtime_importer.as_str());
|
let source = Self::new_string_literal(self.jsx_runtime_importer.as_str());
|
||||||
self.add_import_statement("Fragment", "_Fragment", source);
|
self.add_import_statement("Fragment", "_Fragment", source);
|
||||||
|
|
@ -198,6 +215,41 @@ impl<'a> ReactJsx<'a> {
|
||||||
self.imports.push(decl);
|
self.imports.push(decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn require_jsx_runtime(&mut self, source: StringLiteral) {
|
||||||
|
let callee = self
|
||||||
|
.ast
|
||||||
|
.identifier_reference_expression(IdentifierReference::new(SPAN, "require".into()));
|
||||||
|
let arguments = self
|
||||||
|
.ast
|
||||||
|
.new_vec_single(Argument::Expression(self.ast.literal_string_expression(source)));
|
||||||
|
let init = self.ast.call_expression(SPAN, callee, arguments, false, None);
|
||||||
|
let id = self.ast.binding_pattern(
|
||||||
|
self.ast.binding_pattern_identifier(BindingIdentifier::new(
|
||||||
|
SPAN,
|
||||||
|
"_reactJsxRuntime".into(),
|
||||||
|
)),
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
let decl = self.ast.new_vec_single(self.ast.variable_declarator(
|
||||||
|
SPAN,
|
||||||
|
VariableDeclarationKind::Var,
|
||||||
|
id,
|
||||||
|
Some(init),
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
|
||||||
|
let variable_declaration = self.ast.variable_declaration(
|
||||||
|
SPAN,
|
||||||
|
VariableDeclarationKind::Var,
|
||||||
|
decl,
|
||||||
|
Modifiers::empty(),
|
||||||
|
);
|
||||||
|
let stmt = Statement::Declaration(Declaration::VariableDeclaration(variable_declaration));
|
||||||
|
|
||||||
|
self.imports.push(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
fn transform_jsx<'b>(&mut self, e: &JSXElementOrFragment<'a, 'b>) -> Expression<'a> {
|
fn transform_jsx<'b>(&mut self, e: &JSXElementOrFragment<'a, 'b>) -> Expression<'a> {
|
||||||
let is_classic = self.options.runtime.is_classic();
|
let is_classic = self.options.runtime.is_classic();
|
||||||
let is_automatic = self.options.runtime.is_automatic();
|
let is_automatic = self.options.runtime.is_automatic();
|
||||||
|
|
@ -287,6 +339,8 @@ impl<'a> ReactJsx<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.add_import(e, has_key_after_props_spread, need_jsxs);
|
||||||
|
|
||||||
if !properties.is_empty() || is_automatic {
|
if !properties.is_empty() || is_automatic {
|
||||||
let object_expression = self.ast.object_expression(SPAN, properties, None);
|
let object_expression = self.ast.object_expression(SPAN, properties, None);
|
||||||
arguments.push(Argument::Expression(object_expression));
|
arguments.push(Argument::Expression(object_expression));
|
||||||
|
|
@ -306,7 +360,6 @@ impl<'a> ReactJsx<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let callee = self.get_create_element(has_key_after_props_spread, need_jsxs);
|
let callee = self.get_create_element(has_key_after_props_spread, need_jsxs);
|
||||||
self.add_import(e, has_key_after_props_spread, need_jsxs);
|
|
||||||
self.ast.call_expression(SPAN, callee, arguments, false, None)
|
self.ast.call_expression(SPAN, callee, arguments, false, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -352,13 +405,30 @@ impl<'a> ReactJsx<'a> {
|
||||||
ReactJsxRuntime::Automatic => {
|
ReactJsxRuntime::Automatic => {
|
||||||
let name = if has_key_after_props_spread {
|
let name = if has_key_after_props_spread {
|
||||||
"_createElement"
|
"_createElement"
|
||||||
|
} else if self.require_jsx_runtime {
|
||||||
|
if jsxs {
|
||||||
|
"jsxs"
|
||||||
|
} else {
|
||||||
|
"jsx"
|
||||||
|
}
|
||||||
} else if jsxs {
|
} else if jsxs {
|
||||||
"_jsxs"
|
"_jsxs"
|
||||||
} else {
|
} else {
|
||||||
"_jsx"
|
"_jsx"
|
||||||
};
|
};
|
||||||
let ident = IdentifierReference::new(SPAN, name.into());
|
|
||||||
self.ast.identifier_reference_expression(ident)
|
if has_key_after_props_spread || !self.require_jsx_runtime {
|
||||||
|
let ident = IdentifierReference::new(SPAN, name.into());
|
||||||
|
self.ast.identifier_reference_expression(ident)
|
||||||
|
} else {
|
||||||
|
let ident = IdentifierReference::new(SPAN, "_reactJsxRuntime".into());
|
||||||
|
self.ast.static_member_expression(
|
||||||
|
SPAN,
|
||||||
|
self.ast.identifier_reference_expression(ident),
|
||||||
|
IdentifierName::new(SPAN, name.into()),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
Passed: 272/1113
|
Passed: 276/1113
|
||||||
|
|
||||||
# All Passed:
|
# All Passed:
|
||||||
* babel-plugin-transform-numeric-separator
|
* babel-plugin-transform-numeric-separator
|
||||||
|
|
@ -825,20 +825,17 @@ Passed: 272/1113
|
||||||
* regression/11061/input.mjs
|
* regression/11061/input.mjs
|
||||||
* variable-declaration/non-null-in-optional-chain/input.ts
|
* variable-declaration/non-null-in-optional-chain/input.ts
|
||||||
|
|
||||||
# babel-plugin-transform-react-jsx (110/170)
|
# babel-plugin-transform-react-jsx (114/170)
|
||||||
* autoImport/after-polyfills-compiled-to-cjs/input.mjs
|
* autoImport/after-polyfills-compiled-to-cjs/input.mjs
|
||||||
* autoImport/after-polyfills-script-not-supported/input.js
|
* autoImport/after-polyfills-script-not-supported/input.js
|
||||||
* autoImport/auto-import-react-source-type-module/input.js
|
* autoImport/auto-import-react-source-type-module/input.js
|
||||||
* autoImport/auto-import-react-source-type-script/input.js
|
* autoImport/auto-import-react-source-type-script/input.js
|
||||||
* autoImport/complicated-scope-module/input.js
|
* autoImport/complicated-scope-module/input.js
|
||||||
* autoImport/complicated-scope-script/input.js
|
* autoImport/complicated-scope-script/input.js
|
||||||
* pure/false-default-pragma-automatic-runtime/input.js
|
|
||||||
* pure/false-pragma-comment-automatic-runtime/input.js
|
* pure/false-pragma-comment-automatic-runtime/input.js
|
||||||
* pure/false-pragma-option-automatic-runtime/input.js
|
* pure/false-pragma-option-automatic-runtime/input.js
|
||||||
* pure/true-default-pragma-automatic-runtime/input.js
|
|
||||||
* pure/true-pragma-comment-automatic-runtime/input.js
|
* pure/true-pragma-comment-automatic-runtime/input.js
|
||||||
* pure/true-pragma-option-automatic-runtime/input.js
|
* pure/true-pragma-option-automatic-runtime/input.js
|
||||||
* pure/unset-default-pragma-automatic-runtime/input.js
|
|
||||||
* pure/unset-pragma-comment-automatic-runtime/input.js
|
* pure/unset-pragma-comment-automatic-runtime/input.js
|
||||||
* pure/unset-pragma-option-automatic-runtime/input.js
|
* pure/unset-pragma-option-automatic-runtime/input.js
|
||||||
* react/adds-appropriate-newlines-when-using-spread-attribute-babel-7/input.js
|
* react/adds-appropriate-newlines-when-using-spread-attribute-babel-7/input.js
|
||||||
|
|
@ -883,7 +880,6 @@ Passed: 272/1113
|
||||||
* runtime/defaults-to-automatic/input.js
|
* runtime/defaults-to-automatic/input.js
|
||||||
* runtime/defaults-to-classis-babel-7/input.js
|
* runtime/defaults-to-classis-babel-7/input.js
|
||||||
* runtime/invalid-runtime/input.js
|
* runtime/invalid-runtime/input.js
|
||||||
* runtime/runtime-automatic/input.js
|
|
||||||
* spread-transform/transform-to-babel-extend/input.js
|
* spread-transform/transform-to-babel-extend/input.js
|
||||||
* spread-transform/transform-to-object-assign/input.js
|
* spread-transform/transform-to-object-assign/input.js
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,18 @@ impl TestCase for ConformanceTestCase {
|
||||||
|
|
||||||
let allocator = Allocator::default();
|
let allocator = Allocator::default();
|
||||||
let input = fs::read_to_string(&self.path).unwrap();
|
let input = fs::read_to_string(&self.path).unwrap();
|
||||||
let source_type = SourceType::from_path(&self.path).unwrap();
|
let input_is_js = self.path.extension().and_then(std::ffi::OsStr::to_str) == Some("js");
|
||||||
|
let output_is_js = output_path
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|path| path.extension().and_then(std::ffi::OsStr::to_str) == Some("js"));
|
||||||
|
|
||||||
|
let source_type = SourceType::from_path(&self.path).unwrap().with_script(
|
||||||
|
if self.options.source_type.is_some() {
|
||||||
|
!self.options.is_module()
|
||||||
|
} else {
|
||||||
|
input_is_js && output_is_js
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if filtered {
|
if filtered {
|
||||||
println!("input_path: {:?}", &self.path);
|
println!("input_path: {:?}", &self.path);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue