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);
|
||||
}
|
||||
|
||||
pub fn comments_spans(&self) -> Vec<(Comment, Span)> {
|
||||
self.comments()
|
||||
.iter()
|
||||
.map(|(start, comment)| (*comment, Span::new(*start, comment.end)))
|
||||
.collect()
|
||||
pub fn comments_spans(&self) -> impl Iterator<Item = (Comment, Span)> + '_ {
|
||||
self.comments().iter().map(|(start, comment)| (*comment, Span::new(*start, comment.end)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ impl<'a> JSDocBuilder<'a> {
|
|||
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> {
|
||||
let (start, comment) = self.trivias.comments().range(..span.start).next()?;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,10 @@ fn main() {
|
|||
println!("Original:\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 transform_options = TransformOptions {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ impl<'a> TransformerCtx<'a> {
|
|||
Self { ast, semantic }
|
||||
}
|
||||
|
||||
pub fn semantic(&self) -> Ref<'_, Semantic<'a>> {
|
||||
self.semantic.borrow()
|
||||
}
|
||||
|
||||
pub fn symbols(&self) -> Ref<'_, SymbolTable> {
|
||||
Ref::map(self.semantic.borrow(), |semantic| semantic.symbols())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ impl<'a> Transformer<'a> {
|
|||
Self {
|
||||
// TODO: pass verbatim_module_syntax from user config
|
||||
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),
|
||||
es2022_class_static_block: es2022::ClassStaticBlock::new(Rc::clone(&ast), &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};
|
||||
use crate::context::TransformerCtx;
|
||||
|
||||
/// Transform React JSX
|
||||
///
|
||||
|
|
@ -67,8 +68,13 @@ impl<'a, 'b> JSXElementOrFragment<'a, 'b> {
|
|||
}
|
||||
|
||||
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 options = options.with_comments(&ctx.semantic());
|
||||
Self {
|
||||
ast,
|
||||
options,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use serde::Deserialize;
|
||||
|
||||
use oxc_semantic::Semantic;
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Deserialize)]
|
||||
pub struct ReactJsxOptions {
|
||||
/// Decides which runtime to use.
|
||||
|
|
@ -28,3 +30,30 @@ impl ReactJsxRuntime {
|
|||
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:
|
||||
* babel-plugin-transform-numeric-separator
|
||||
|
|
@ -803,7 +803,7 @@ Passed: 241/1080
|
|||
* regression/11061/input.mjs
|
||||
* 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-script-not-supported/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
|
||||
* runtime/defaults-to-automatic/input.js
|
||||
* runtime/invalid-runtime/input.js
|
||||
* runtime/pragma-runtime-classsic/input.js
|
||||
* runtime/runtime-automatic/input.js
|
||||
* spread-transform/transform-to-babel-extend/input.js
|
||||
* spread-transform/transform-to-object-assign/input.js
|
||||
|
|
|
|||
|
|
@ -137,12 +137,13 @@ pub trait TestCase {
|
|||
let allocator = Allocator::default();
|
||||
let source_text = fs::read_to_string(path).unwrap();
|
||||
let source_type = SourceType::from_path(path).unwrap();
|
||||
let transformed_program =
|
||||
Parser::new(&allocator, &source_text, source_type).parse().program;
|
||||
let ret = Parser::new(&allocator, &source_text, source_type).parse();
|
||||
|
||||
let semantic =
|
||||
SemanticBuilder::new(&source_text, source_type).build(&transformed_program).semantic;
|
||||
let transformed_program = allocator.alloc(transformed_program);
|
||||
let semantic = SemanticBuilder::new(&source_text, source_type)
|
||||
.with_trivias(ret.trivias)
|
||||
.build(&ret.program)
|
||||
.semantic;
|
||||
let transformed_program = allocator.alloc(ret.program);
|
||||
|
||||
Transformer::new(&allocator, source_type, semantic, self.transform_options())
|
||||
.build(transformed_program);
|
||||
|
|
@ -190,9 +191,12 @@ impl TestCase for ConformanceTestCase {
|
|||
}
|
||||
|
||||
// Transform input.js
|
||||
let program = Parser::new(&allocator, &input, source_type).parse().program;
|
||||
let semantic = SemanticBuilder::new(&input, source_type).build(&program).semantic;
|
||||
let program = allocator.alloc(program);
|
||||
let ret = Parser::new(&allocator, &input, source_type).parse();
|
||||
let semantic = SemanticBuilder::new(&input, source_type)
|
||||
.with_trivias(ret.trivias)
|
||||
.build(&ret.program)
|
||||
.semantic;
|
||||
let program = allocator.alloc(ret.program);
|
||||
Transformer::new(&allocator, source_type, semantic, self.transform_options())
|
||||
.build(program);
|
||||
let transformed_code = Codegen::<false>::new(input.len(), CodegenOptions).build(program);
|
||||
|
|
|
|||
Loading…
Reference in a new issue