From bd100219fc25fc7ecdc3d2820c66e4860f027089 Mon Sep 17 00:00:00 2001 From: Dunqing <29533304+Dunqing@users.noreply.github.com> Date: Tue, 24 Mar 2026 09:22:51 +0000 Subject: [PATCH] perf(transformer): only scan comments before first statement for JSX pragmas (#20675) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Restrict JSX pragma scanning to only comments appearing before the first statement, since pragmas are file-level directives - Pass `first_statement_start` position to `update_options_with_comments` and skip comments at or after that position ## Motivation Previously all comments in a file were scanned for JSX pragmas, which meant pragmas in doc comments on functions could override file-level pragmas. This aligns Oxc with TypeScript and SWC, which both restrict pragma scanning to leading comments. | Tool | Pragma scan scope | |------|-------------------| | TypeScript | Leading comments before first token | | SWC | Leading block comments, stops at first pragma-bearing statement | | Babel | All comments (last wins) | | esbuild | All comments (last wins) | | **Oxc (after)** | Comments before first statement | ## Test plan - [x] All transformer tests pass - [x] Conformance tests pass (including `issue-20669` fixture from parent PR) - [x] Clippy clean 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- crates/oxc_transformer/src/jsx/comments.rs | 8 ++++++-- crates/oxc_transformer/src/lib.rs | 12 +++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/crates/oxc_transformer/src/jsx/comments.rs b/crates/oxc_transformer/src/jsx/comments.rs index ad2f0f94fc399..769ebac99b013 100644 --- a/crates/oxc_transformer/src/jsx/comments.rs +++ b/crates/oxc_transformer/src/jsx/comments.rs @@ -6,17 +6,21 @@ use oxc_ast::Comment; use crate::{JsxOptions, JsxRuntime, TransformCtx, TypeScriptOptions}; -/// Scan through all comments and find the following pragmas: +/// Scan through leading comments and find the following pragmas: /// /// * @jsx Preact.h /// * @jsxRuntime classic / automatic /// * @jsxImportSource custom-jsx-library /// * @jsxFrag Preact.Fragment /// +/// The caller should only pass comments before the first statement, +/// since pragmas are file-level directives. This is aligned with TypeScript and SWC. +/// +/// /// The comment does not need to be a JSDoc comment, /// otherwise `JSDoc` could be used instead. /// -/// This behavior is aligned with ESBuild. +/// Multiple pragmas in a single comment are accepted (aligned with esbuild). /// Babel is less liberal - it doesn't accept multiple pragmas in a single line /// e.g. `/** @jsx h @jsxRuntime classic */` /// diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 4c052cef99313..e872fc5268466 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -11,7 +11,7 @@ use oxc_allocator::{Allocator, TakeIn, Vec as ArenaVec}; use oxc_ast::{AstBuilder, ast::*}; use oxc_diagnostics::OxcDiagnostic; use oxc_semantic::Scoping; -use oxc_span::SPAN; +use oxc_span::{GetSpan, SPAN}; use oxc_traverse::{ReusableTraverseCtx, Traverse, traverse_mut_with_ctx}; // Core @@ -139,9 +139,15 @@ impl<'a> Transformer<'a> { self.state.source_type = program.source_type; self.state.source_text = program.source_text; - if program.source_type.is_jsx() { + if program.source_type.is_jsx() + && let Some(first_statement) = program.body.first() + { + // Only scan comments before the first statement for pragmas, + // since pragmas are file-level directives (aligned with TypeScript and SWC). + let leading_comments_end = + program.comments.partition_point(|c| c.span.start < first_statement.span().start); jsx::update_options_with_comments( - &program.comments, + &program.comments[..leading_comments_end], &mut self.typescript, &mut self.jsx, &self.state,