diff --git a/crates/oxc_transformer/src/jsx/comments.rs b/crates/oxc_transformer/src/jsx/comments.rs index 0959005f83d20..ad2f0f94fc399 100644 --- a/crates/oxc_transformer/src/jsx/comments.rs +++ b/crates/oxc_transformer/src/jsx/comments.rs @@ -103,6 +103,23 @@ fn find_jsx_pragma(mut comment_str: &str) -> Option<(PragmaType, &str, &str)> { // to find `@` characters, and then checking if `@` is followed by `jsx` separately. let at_sign_index = memchr(b'@', comment_str.as_bytes())?; + // `@` must be preceded by whitespace or `*` (or be at start of comment) to count + // as a pragma. This avoids matching inside inline code spans like `` `@jsxImportSource foo` ``. + // Note: esbuild does no preceding-character check at all (matches `@jsx` anywhere). + // We are intentionally stricter here — only checking the immediately preceding byte, + // which is sufficient for the backtick case without being as strict as Babel's + // full start-of-line regex. + // + if at_sign_index > 0 { + let prev_byte = comment_str.as_bytes()[at_sign_index - 1]; + if !matches!(prev_byte, b' ' | b'\t' | b'\r' | b'\n' | b'*') { + // SAFETY: Byte at `at_sign_index` is `@`, so `at_sign_index + 1` is either within + // string or end of string, and on a UTF-8 char boundary. + comment_str = unsafe { comment_str.get_unchecked(at_sign_index + 1..) }; + continue; + } + } + // Check `@` is start of `@jsx`. // Note: Checking 4 bytes including leading `@` is faster than checking the 3 bytes after `@`, // because 4 bytes is a `u32`. @@ -240,6 +257,15 @@ mod tests { ("@jsx @jsx h", &[(PragmaType::Jsx, "@jsx")]), // Multiple `@` signs ("@@@@@jsx h", &[(PragmaType::Jsx, "h")]), + // Pragma inside backticks (inline code span) should not be recognized + ("`@jsxImportSource custom/source`", &[]), + ("`@jsx h`", &[]), + ("This mentions `@jsxImportSource custom/source` in docs", &[]), + // But valid pragma before backtick-wrapped text should still work + ( + "@jsxImportSource react\n * This mentions `@jsxImportSource custom/source` in docs", + &[(PragmaType::JsxImportSource, "react")], + ), ]; let prefixes = ["", " ", "\n\n", "*\n* "]; diff --git a/tasks/transform_conformance/snapshots/oxc.snap.md b/tasks/transform_conformance/snapshots/oxc.snap.md index f90c37200f6f5..81caf736bec83 100644 --- a/tasks/transform_conformance/snapshots/oxc.snap.md +++ b/tasks/transform_conformance/snapshots/oxc.snap.md @@ -1,6 +1,6 @@ commit: 0124e7c7 -Passed: 208/344 +Passed: 209/345 # All Passed: * babel-plugin-transform-class-static-block @@ -540,7 +540,7 @@ after transform: [ReferenceId(0), ReferenceId(1), ReferenceId(4), ReferenceId(9) rebuilt : [ReferenceId(5)] -# babel-plugin-transform-react-jsx (47/50) +# babel-plugin-transform-react-jsx (48/51) * refresh/import-after-component/input.js Missing ScopeId Missing ReferenceId: "useFoo" diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/issues/issue-20669/input.jsx b/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/issues/issue-20669/input.jsx new file mode 100644 index 0000000000000..5a478d0bcde99 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/issues/issue-20669/input.jsx @@ -0,0 +1,8 @@ +/** @jsxImportSource react */ + +/** + * This comment mentions `@jsxImportSource custom/source` in docs. + */ +function App() { + return
hello
; +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/issues/issue-20669/options.json b/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/issues/issue-20669/options.json new file mode 100644 index 0000000000000..1c3260292f20a --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/issues/issue-20669/options.json @@ -0,0 +1,4 @@ +{ + "plugins": [["transform-react-jsx", { "runtime": "automatic" }]], + "sourceType": "module" +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/issues/issue-20669/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/issues/issue-20669/output.js new file mode 100644 index 0000000000000..1fcd5ac416df3 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-react-jsx/test/fixtures/issues/issue-20669/output.js @@ -0,0 +1,7 @@ +import { jsx as _jsx } from "react/jsx-runtime"; +/** + * This comment mentions `@jsxImportSource custom/source` in docs. + */ +function App() { + return _jsx("div", { children: "hello" }); +}