mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
feat(transformer): support importSource option in react_jsx (#1115)
This commit is contained in:
parent
b16c298b35
commit
f0e452a599
6 changed files with 41 additions and 14 deletions
|
|
@ -79,7 +79,6 @@ impl<'a> Transformer<'a> {
|
||||||
Self {
|
Self {
|
||||||
// TODO: pass verbatim_module_syntax from user config
|
// TODO: pass verbatim_module_syntax from user config
|
||||||
typescript: source_type.is_typescript().then(|| TypeScript::new(Rc::clone(&ast), ctx.clone(), false)),
|
typescript: source_type.is_typescript().then(|| TypeScript::new(Rc::clone(&ast), ctx.clone(), false)),
|
||||||
react_jsx: options.react_jsx.map(|options| ReactJsx::new(Rc::clone(&ast), &ctx, options)),
|
|
||||||
regexp_flags: RegexpFlags::new(Rc::clone(&ast), &options),
|
regexp_flags: RegexpFlags::new(Rc::clone(&ast), &options),
|
||||||
es2022_class_static_block: es2022::ClassStaticBlock::new(Rc::clone(&ast), &options),
|
es2022_class_static_block: es2022::ClassStaticBlock::new(Rc::clone(&ast), &options),
|
||||||
es2021_logical_assignment_operators: LogicalAssignmentOperators::new(Rc::clone(&ast), ctx.clone(), &options),
|
es2021_logical_assignment_operators: LogicalAssignmentOperators::new(Rc::clone(&ast), ctx.clone(), &options),
|
||||||
|
|
@ -88,6 +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)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use oxc_syntax::assumptions::CompilerAssumptions;
|
||||||
|
|
||||||
use crate::{es2020::NullishCoalescingOperatorOptions, react_jsx::ReactJsxOptions};
|
use crate::{es2020::NullishCoalescingOperatorOptions, react_jsx::ReactJsxOptions};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct TransformOptions {
|
pub struct TransformOptions {
|
||||||
pub target: TransformTarget,
|
pub target: TransformTarget,
|
||||||
pub assumptions: CompilerAssumptions,
|
pub assumptions: CompilerAssumptions,
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ pub struct ReactJsx<'a> {
|
||||||
import_jsxs: bool,
|
import_jsxs: bool,
|
||||||
import_fragment: bool,
|
import_fragment: bool,
|
||||||
import_create_element: bool,
|
import_create_element: bool,
|
||||||
|
// Will be store jsx runtime importer, like `react/jsx-runtime`
|
||||||
|
jsx_runtime_importer: Atom,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum JSXElementOrFragment<'a, 'b> {
|
enum JSXElementOrFragment<'a, 'b> {
|
||||||
|
|
@ -75,10 +77,19 @@ impl<'a> ReactJsx<'a> {
|
||||||
) -> Self {
|
) -> 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());
|
||||||
|
|
||||||
|
let jsx_runtime_importer =
|
||||||
|
if options.import_source == "react" || options.runtime.is_classic() {
|
||||||
|
Atom::new_inline("react/jsx-runtime")
|
||||||
|
} else {
|
||||||
|
Atom::from(format!("{}/jsx-runtime", options.import_source))
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
ast,
|
ast,
|
||||||
options,
|
options,
|
||||||
imports,
|
imports,
|
||||||
|
jsx_runtime_importer,
|
||||||
import_jsx: false,
|
import_jsx: false,
|
||||||
import_jsxs: false,
|
import_jsxs: false,
|
||||||
import_fragment: false,
|
import_fragment: false,
|
||||||
|
|
@ -111,6 +122,10 @@ impl<'a> ReactJsx<'a> {
|
||||||
program.body.splice(index..index, imports);
|
program.body.splice(index..index, imports);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_string_literal(name: &str) -> StringLiteral {
|
||||||
|
StringLiteral::new(SPAN, name.into())
|
||||||
|
}
|
||||||
|
|
||||||
fn add_import<'b>(
|
fn add_import<'b>(
|
||||||
&mut self,
|
&mut self,
|
||||||
e: &JSXElementOrFragment<'a, 'b>,
|
e: &JSXElementOrFragment<'a, 'b>,
|
||||||
|
|
@ -133,21 +148,24 @@ impl<'a> ReactJsx<'a> {
|
||||||
fn add_import_jsx(&mut self) {
|
fn add_import_jsx(&mut self) {
|
||||||
if !self.import_jsx {
|
if !self.import_jsx {
|
||||||
self.import_jsx = true;
|
self.import_jsx = true;
|
||||||
self.add_import_statement("jsx", "_jsx", "react/jsx-runtime");
|
let source = Self::new_string_literal(self.jsx_runtime_importer.as_str());
|
||||||
|
self.add_import_statement("jsx", "_jsx", source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_import_jsxs(&mut self) {
|
fn add_import_jsxs(&mut self) {
|
||||||
if !self.import_jsxs {
|
if !self.import_jsxs {
|
||||||
self.import_jsxs = true;
|
self.import_jsxs = true;
|
||||||
self.add_import_statement("jsxs", "_jsxs", "react/jsx-runtime");
|
let source = Self::new_string_literal(self.jsx_runtime_importer.as_str());
|
||||||
|
self.add_import_statement("jsxs", "_jsxs", source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_import_fragment(&mut self) {
|
fn add_import_fragment(&mut self) {
|
||||||
if !self.import_fragment {
|
if !self.import_fragment {
|
||||||
self.import_fragment = true;
|
self.import_fragment = true;
|
||||||
self.add_import_statement("Fragment", "_Fragment", "react/jsx-runtime");
|
let source = Self::new_string_literal(self.jsx_runtime_importer.as_str());
|
||||||
|
self.add_import_statement("Fragment", "_Fragment", source);
|
||||||
self.add_import_jsx();
|
self.add_import_jsx();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -155,11 +173,12 @@ impl<'a> ReactJsx<'a> {
|
||||||
fn add_import_create_element(&mut self) {
|
fn add_import_create_element(&mut self) {
|
||||||
if !self.import_create_element {
|
if !self.import_create_element {
|
||||||
self.import_create_element = true;
|
self.import_create_element = true;
|
||||||
self.add_import_statement("createElement", "_createElement", "react");
|
let source = Self::new_string_literal(self.options.import_source.as_ref());
|
||||||
|
self.add_import_statement("createElement", "_createElement", source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_import_statement(&mut self, imported: &str, local: &str, source: &str) {
|
fn add_import_statement(&mut self, imported: &str, local: &str, source: StringLiteral) {
|
||||||
let mut specifiers = self.ast.new_vec_with_capacity(1);
|
let mut specifiers = self.ast.new_vec_with_capacity(1);
|
||||||
specifiers.push(ImportDeclarationSpecifier::ImportSpecifier(ImportSpecifier {
|
specifiers.push(ImportDeclarationSpecifier::ImportSpecifier(ImportSpecifier {
|
||||||
span: SPAN,
|
span: SPAN,
|
||||||
|
|
@ -167,7 +186,6 @@ impl<'a> ReactJsx<'a> {
|
||||||
local: BindingIdentifier::new(SPAN, local.into()),
|
local: BindingIdentifier::new(SPAN, local.into()),
|
||||||
import_kind: ImportOrExportKind::Value,
|
import_kind: ImportOrExportKind::Value,
|
||||||
}));
|
}));
|
||||||
let source = StringLiteral::new(SPAN, source.into());
|
|
||||||
let import_statement = self.ast.import_declaration(
|
let import_statement = self.ast.import_declaration(
|
||||||
SPAN,
|
SPAN,
|
||||||
Some(specifiers),
|
Some(specifiers),
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,23 @@
|
||||||
use serde::Deserialize;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use oxc_semantic::Semantic;
|
use oxc_semantic::Semantic;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, Deserialize)]
|
#[derive(Debug, Default, Clone, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ReactJsxOptions {
|
pub struct ReactJsxOptions {
|
||||||
/// Decides which runtime to use.
|
/// Decides which runtime to use.
|
||||||
pub runtime: ReactJsxRuntime,
|
pub runtime: ReactJsxRuntime,
|
||||||
/// Toggles whether or not to throw an error if an XML namespaced tag name is used. e.g. `<f:image />`
|
/// Toggles whether or not to throw an error if an XML namespaced tag name is used. e.g. `<f:image />`
|
||||||
/// Though the JSX spec allows this, it is disabled by default since React's JSX does not currently have support for it.
|
/// Though the JSX spec allows this, it is disabled by default since React's JSX does not currently have support for it.
|
||||||
pub throw_if_namespace: Option<bool>,
|
pub throw_if_namespace: Option<bool>,
|
||||||
|
/// Replaces the import source when importing functions. default to `react`
|
||||||
|
#[serde(default = "default_import_source")]
|
||||||
|
pub import_source: Cow<'static, str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_import_source() -> Cow<'static, str> {
|
||||||
|
Cow::Borrowed("react")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, Deserialize)]
|
#[derive(Debug, Default, Clone, Copy, Deserialize)]
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,8 @@ impl Tester {
|
||||||
let program = Parser::new(&self.allocator, source_text, self.source_type).parse().program;
|
let program = Parser::new(&self.allocator, source_text, self.source_type).parse().program;
|
||||||
let semantic = SemanticBuilder::new(source_text, self.source_type).build(&program).semantic;
|
let semantic = SemanticBuilder::new(source_text, self.source_type).build(&program).semantic;
|
||||||
let program = self.allocator.alloc(program);
|
let program = self.allocator.alloc(program);
|
||||||
Transformer::new(&self.allocator, self.source_type, semantic, self.options).build(program);
|
Transformer::new(&self.allocator, self.source_type, semantic, self.options.clone())
|
||||||
|
.build(program);
|
||||||
Codegen::<false>::new(source_text.len(), CodegenOptions).build(program)
|
Codegen::<false>::new(source_text.len(), CodegenOptions).build(program)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
Passed: 255/1113
|
Passed: 256/1113
|
||||||
|
|
||||||
# All Passed:
|
# All Passed:
|
||||||
* babel-plugin-transform-numeric-separator
|
* babel-plugin-transform-numeric-separator
|
||||||
|
|
@ -825,14 +825,13 @@ Passed: 255/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 (93/170)
|
# babel-plugin-transform-react-jsx (94/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
|
||||||
* autoImport/import-source/input.js
|
|
||||||
* autoImport/import-source-pragma/input.js
|
* autoImport/import-source-pragma/input.js
|
||||||
* pure/false-default-pragma-automatic-runtime/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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue