feat(transformer): support importSource option in react_jsx (#1115)

This commit is contained in:
Dunqing 2023-11-07 16:42:08 +08:00 committed by GitHub
parent b16c298b35
commit f0e452a599
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 41 additions and 14 deletions

View file

@ -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)),
} }
} }

View file

@ -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,

View file

@ -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),

View file

@ -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)]

View file

@ -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)
} }

View file

@ -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