Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions crates/oxc_transformer/src/jsx/comments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
// <https://github.com/oxc-project/oxc/issues/20669>
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`.
Expand Down Expand Up @@ -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* "];
Expand Down
4 changes: 2 additions & 2 deletions tasks/transform_conformance/snapshots/oxc.snap.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
commit: 0124e7c7

Passed: 208/344
Passed: 209/345

# All Passed:
* babel-plugin-transform-class-static-block
Expand Down Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/** @jsxImportSource react */

/**
* This comment mentions `@jsxImportSource custom/source` in docs.
*/
function App() {
return <div>hello</div>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"plugins": [["transform-react-jsx", { "runtime": "automatic" }]],
"sourceType": "module"
}
Original file line number Diff line number Diff line change
@@ -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" });
}
Loading