mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
feat(transformer/react): read comment pragma @jsxRuntime classic / automatic (#1133)
closes #1120
This commit is contained in:
parent
c7fcb31812
commit
203cf37695
9 changed files with 62 additions and 20 deletions
|
|
@ -74,10 +74,7 @@ impl TriviasMap {
|
||||||
self.comments.insert(span.start, comment);
|
self.comments.insert(span.start, comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn comments_spans(&self) -> Vec<(Comment, Span)> {
|
pub fn comments_spans(&self) -> impl Iterator<Item = (Comment, Span)> + '_ {
|
||||||
self.comments()
|
self.comments().iter().map(|(start, comment)| (*comment, Span::new(*start, comment.end)))
|
||||||
.iter()
|
|
||||||
.map(|(start, comment)| (*comment, Span::new(*start, comment.end)))
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ impl<'a> JSDocBuilder<'a> {
|
||||||
comment_text.is_some()
|
comment_text.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the jsdoc doc in frontend this span, a.k.a leading comment
|
/// Find the jsdoc doc in front of this span, a.k.a leading comment
|
||||||
fn find_jsdoc_comment(&self, span: Span) -> Option<&'a str> {
|
fn find_jsdoc_comment(&self, span: Span) -> Option<&'a str> {
|
||||||
let (start, comment) = self.trivias.comments().range(..span.start).next()?;
|
let (start, comment) = self.trivias.comments().range(..span.start).next()?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,10 @@ fn main() {
|
||||||
println!("Original:\n");
|
println!("Original:\n");
|
||||||
println!("{printed}\n");
|
println!("{printed}\n");
|
||||||
|
|
||||||
let semantic = SemanticBuilder::new(&source_text, source_type).build(&ret.program).semantic;
|
let semantic = SemanticBuilder::new(&source_text, source_type)
|
||||||
|
.with_trivias(ret.trivias)
|
||||||
|
.build(&ret.program)
|
||||||
|
.semantic;
|
||||||
|
|
||||||
let program = allocator.alloc(ret.program);
|
let program = allocator.alloc(ret.program);
|
||||||
let transform_options = TransformOptions {
|
let transform_options = TransformOptions {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,10 @@ impl<'a> TransformerCtx<'a> {
|
||||||
Self { ast, semantic }
|
Self { ast, semantic }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn semantic(&self) -> Ref<'_, Semantic<'a>> {
|
||||||
|
self.semantic.borrow()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn symbols(&self) -> Ref<'_, SymbolTable> {
|
pub fn symbols(&self) -> Ref<'_, SymbolTable> {
|
||||||
Ref::map(self.semantic.borrow(), |semantic| semantic.symbols())
|
Ref::map(self.semantic.borrow(), |semantic| semantic.symbols())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ 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), options)),
|
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),
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use oxc_syntax::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use self::options::{ReactJsxOptions, ReactJsxRuntime};
|
pub use self::options::{ReactJsxOptions, ReactJsxRuntime};
|
||||||
|
use crate::context::TransformerCtx;
|
||||||
|
|
||||||
/// Transform React JSX
|
/// Transform React JSX
|
||||||
///
|
///
|
||||||
|
|
@ -67,8 +68,13 @@ impl<'a, 'b> JSXElementOrFragment<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ReactJsx<'a> {
|
impl<'a> ReactJsx<'a> {
|
||||||
pub fn new(ast: Rc<AstBuilder<'a>>, options: ReactJsxOptions) -> Self {
|
pub fn new(
|
||||||
|
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());
|
||||||
Self {
|
Self {
|
||||||
ast,
|
ast,
|
||||||
options,
|
options,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use oxc_semantic::Semantic;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, Deserialize)]
|
#[derive(Debug, Default, Clone, Copy, Deserialize)]
|
||||||
pub struct ReactJsxOptions {
|
pub struct ReactJsxOptions {
|
||||||
/// Decides which runtime to use.
|
/// Decides which runtime to use.
|
||||||
|
|
@ -28,3 +30,30 @@ impl ReactJsxRuntime {
|
||||||
matches!(self, Self::Automatic)
|
matches!(self, Self::Automatic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ReactJsxOptions {
|
||||||
|
/// Scan through all comments and find the following pragmas
|
||||||
|
///
|
||||||
|
/// * @jsxRuntime classic / automatic
|
||||||
|
///
|
||||||
|
/// The comment does not need to be a jsdoc,
|
||||||
|
/// otherwise `JSDoc` could be used instead.
|
||||||
|
///
|
||||||
|
/// This behavior is aligned with babel.
|
||||||
|
pub(crate) fn with_comments(mut self, semantic: &Semantic) -> Self {
|
||||||
|
for (_, span) in semantic.trivias().comments_spans() {
|
||||||
|
let comment = span.source_text(semantic.source_text());
|
||||||
|
// strip leading jsdoc comment `*` and then whitespaces
|
||||||
|
let comment = comment.strip_prefix('*').unwrap_or(comment).trim_start();
|
||||||
|
// strip leading `@`
|
||||||
|
let Some(comment) = comment.strip_prefix('@') else { continue };
|
||||||
|
// read jsxRuntime
|
||||||
|
match comment.strip_prefix("jsxRuntime").map(str::trim) {
|
||||||
|
Some("classic") => self.runtime = ReactJsxRuntime::Classic,
|
||||||
|
Some("automatic") => self.runtime = ReactJsxRuntime::Automatic,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
Passed: 241/1080
|
Passed: 242/1080
|
||||||
|
|
||||||
# All Passed:
|
# All Passed:
|
||||||
* babel-plugin-transform-numeric-separator
|
* babel-plugin-transform-numeric-separator
|
||||||
|
|
@ -803,7 +803,7 @@ Passed: 241/1080
|
||||||
* 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 (92/170)
|
# babel-plugin-transform-react-jsx (93/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
|
||||||
|
|
@ -878,7 +878,6 @@ Passed: 241/1080
|
||||||
* removed-options/invalid-use-spread-true/input.js
|
* removed-options/invalid-use-spread-true/input.js
|
||||||
* runtime/defaults-to-automatic/input.js
|
* runtime/defaults-to-automatic/input.js
|
||||||
* runtime/invalid-runtime/input.js
|
* runtime/invalid-runtime/input.js
|
||||||
* runtime/pragma-runtime-classsic/input.js
|
|
||||||
* runtime/runtime-automatic/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
|
||||||
|
|
|
||||||
|
|
@ -137,12 +137,13 @@ pub trait TestCase {
|
||||||
let allocator = Allocator::default();
|
let allocator = Allocator::default();
|
||||||
let source_text = fs::read_to_string(path).unwrap();
|
let source_text = fs::read_to_string(path).unwrap();
|
||||||
let source_type = SourceType::from_path(path).unwrap();
|
let source_type = SourceType::from_path(path).unwrap();
|
||||||
let transformed_program =
|
let ret = Parser::new(&allocator, &source_text, source_type).parse();
|
||||||
Parser::new(&allocator, &source_text, source_type).parse().program;
|
|
||||||
|
|
||||||
let semantic =
|
let semantic = SemanticBuilder::new(&source_text, source_type)
|
||||||
SemanticBuilder::new(&source_text, source_type).build(&transformed_program).semantic;
|
.with_trivias(ret.trivias)
|
||||||
let transformed_program = allocator.alloc(transformed_program);
|
.build(&ret.program)
|
||||||
|
.semantic;
|
||||||
|
let transformed_program = allocator.alloc(ret.program);
|
||||||
|
|
||||||
Transformer::new(&allocator, source_type, semantic, self.transform_options())
|
Transformer::new(&allocator, source_type, semantic, self.transform_options())
|
||||||
.build(transformed_program);
|
.build(transformed_program);
|
||||||
|
|
@ -190,9 +191,12 @@ impl TestCase for ConformanceTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform input.js
|
// Transform input.js
|
||||||
let program = Parser::new(&allocator, &input, source_type).parse().program;
|
let ret = Parser::new(&allocator, &input, source_type).parse();
|
||||||
let semantic = SemanticBuilder::new(&input, source_type).build(&program).semantic;
|
let semantic = SemanticBuilder::new(&input, source_type)
|
||||||
let program = allocator.alloc(program);
|
.with_trivias(ret.trivias)
|
||||||
|
.build(&ret.program)
|
||||||
|
.semantic;
|
||||||
|
let program = allocator.alloc(ret.program);
|
||||||
Transformer::new(&allocator, source_type, semantic, self.transform_options())
|
Transformer::new(&allocator, source_type, semantic, self.transform_options())
|
||||||
.build(program);
|
.build(program);
|
||||||
let transformed_code = Codegen::<false>::new(input.len(), CodegenOptions).build(program);
|
let transformed_code = Codegen::<false>::new(input.len(), CodegenOptions).build(program);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue