Skip to content

Bun.Transpiler: default autoImportJSX to true for the automatic JSX runtime - #35582

Closed
robobun wants to merge 1 commit into
mainfrom
farm/4502aa88/transpiler-auto-import-jsx
Closed

Bun.Transpiler: default autoImportJSX to true for the automatic JSX runtime#35582
robobun wants to merge 1 commit into
mainfrom
farm/4502aa88/transpiler-auto-import-jsx

Conversation

@robobun

@robobun robobun commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Fixes #7499.

Problem

Bun.Transpiler with the automatic JSX runtime (the default) rewrites <div /> into a call against a generated symbol like jsxDEV_7x81h0kn("div", ...) whose hash suffix guards against shadowing by user locals. The import { jsxDEV as jsxDEV_7x81h0kn } from "react/jsx-dev-runtime" statement that defines that symbol is only emitted when autoImportJSX is true, and the option defaulted to false.

new Bun.Transpiler({ loader: "tsx" }).transformSync("export default <div />");
// before: export default jsxDEV_7x81h0kn("div", {}, undefined, false, undefined, this);

That output references an identifier that nothing defines. Running it fails with ReferenceError: Can't find variable: jsxDEV_7x81h0kn, and feeding it back into Bun.build from a plugin (the #7499 repro) fails the same way.

Fix

Default autoImportJSX to true in Bun.Transpiler, matching the bundler, the module loader, and what the automatic runtime is defined to do. The classic runtime (jsx: "react") has no auto-import and is unaffected. autoImportJSX: false still opts out.

new Bun.Transpiler({ loader: "tsx" }).transformSync("export default <div />");
// after:
// import { jsxDEV as jsxDEV_7x81h0kn } from "react/jsx-dev-runtime";
// export default jsxDEV_7x81h0kn("div", {}, undefined, false, undefined, this);

Verification

New test in test/bundler/transpiler/transpiler.test.js fails on main (Expected to start with: "import {") and passes with this change. The three existing JSX-mechanics tests that assert on exact output now pass autoImportJSX: false to keep their expectations focused on the transform body.

…untime

With the automatic JSX runtime (the default), Bun.Transpiler rewrites
<div /> to a call against a generated symbol (jsxDEV_7x81h0kn, etc.)
but only emits the matching import when autoImportJSX is true. The
option previously defaulted to false, so the default transpiler output
referenced an undefined identifier and could not be run or re-bundled.

This flips the Bun.Transpiler default to true. The bundler and the
module loader already enable it, so this only changes the standalone
API. The classic runtime and an explicit autoImportJSX: false are
unchanged.

Fixes #7499.
@robobun
robobun requested a review from alii as a code owner July 25, 2026 07:41
@robobun

robobun commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 12:41 AM PT - Jul 25th, 2026

@robobun, your commit 18b6960 is building: #80312

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changes

Automatic JSX runtime

Layer / File(s) Summary
Enable and document the default
packages/bun-types/bun.d.ts, src/runtime/api/JSTranspiler.rs
Documents autoImportJSX and explicitly enables it in the transpiler’s default runtime configuration.
Validate JSX runtime selection
test/bundler/transpiler/transpiler.test.js
Tests automatic imports, jsxImportSource, classic runtime behavior, opt-out handling, and stable existing JSX outputs.

Possibly related PRs

  • oven-sh/bun#34422: Covers tsconfig-driven JSX runtime selection and corresponding runtime module paths.
  • oven-sh/bun#35442: Overlaps on autoImportJSX and jsxImportSource behavior.

Suggested reviewers: alii, jarred-sumner

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main change: defaulting autoImportJSX to true for the automatic JSX runtime.
Description check ✅ Passed The description covers the problem, fix, and verification, which satisfies the repository template in substance.
Linked Issues check ✅ Passed The change matches #7499 by auto-importing the JSX runtime by default and preserving the explicit opt-out path.
Out of Scope Changes check ✅ Passed The diff stays focused on the JSX auto-import default and related tests/docs, with no obvious unrelated changes.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/bun-types/bun.d.ts`:
- Around line 2465-2472: Update the documentation comment for the automatic JSX
runtime option to mention that development transforms import from
<jsxImportSource>/jsx-dev-runtime, using react/jsx-dev-runtime as an example,
while retaining the existing /jsx-runtime path for other automatic transforms.

In `@test/bundler/transpiler/transpiler.test.js`:
- Around line 2158-2161: In the regression test around the issue 7499 URL,
remove the explanatory comments describing automatic JSX runtime behavior and
retain only the issue URL comment. Apply the same cleanup to the corresponding
repeated regression-test comment locations, preserving the test code unchanged.
- Around line 2173-2189: Update the JSX runtime tests around the preact,
classic, and autoImportJSX opt-out cases to run for both “jsx” and “tsx”
loaders, following the repository’s existing matrix-test convention. Preserve
each case’s current assertion while parameterizing only the loader so both
transformation paths are covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: facbe5d1-88ed-4b01-b57a-3877ab20cc9c

📥 Commits

Reviewing files that changed from the base of the PR and between df6c7ee and 18b6960.

📒 Files selected for processing (3)
  • packages/bun-types/bun.d.ts
  • src/runtime/api/JSTranspiler.rs
  • test/bundler/transpiler/transpiler.test.js

Comment on lines +2465 to +2472
/**
* When the automatic JSX runtime is active, prepend the
* `import { jsx, ... } from "<jsxImportSource>/jsx-runtime"` statement that
* binds the generated `jsx`/`jsxs`/`jsxDEV`/`Fragment` calls.
*
* Has no effect when the classic runtime (`jsx: "react"`) is in use.
*
* @default true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Document the development runtime import path.

Automatic development transforms import <jsxImportSource>/jsx-dev-runtime—for example, the test expects react/jsx-dev-runtime on Line 2166—while this documentation only names /jsx-runtime despite mentioning jsxDEV.

Proposed documentation fix
-     * `import { jsx, ... } from "<jsxImportSource>/jsx-runtime"` statement that
+     * `import { jsx, ... } from "<jsxImportSource>/jsx-runtime"` (or
+     * `"<jsxImportSource>/jsx-dev-runtime"` in development) that
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* When the automatic JSX runtime is active, prepend the
* `import { jsx, ... } from "<jsxImportSource>/jsx-runtime"` statement that
* binds the generated `jsx`/`jsxs`/`jsxDEV`/`Fragment` calls.
*
* Has no effect when the classic runtime (`jsx: "react"`) is in use.
*
* @default true
/**
* When the automatic JSX runtime is active, prepend the
* `import { jsx, ... } from "<jsxImportSource>/jsx-runtime"` (or
* `"<jsxImportSource>/jsx-dev-runtime"` in development) statement that
* binds the generated `jsx`/`jsxs`/`jsxDEV`/`Fragment` calls.
*
* Has no effect when the classic runtime (`jsx: "react"`) is in use.
*
* `@default` true
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/bun-types/bun.d.ts` around lines 2465 - 2472, Update the
documentation comment for the automatic JSX runtime option to mention that
development transforms import from <jsxImportSource>/jsx-dev-runtime, using
react/jsx-dev-runtime as an example, while retaining the existing /jsx-runtime
path for other automatic transforms.

Comment on lines +2158 to +2161
// https://github.com/oven-sh/bun/issues/7499
// The automatic JSX runtime emits calls to generated symbols (jsxDEV_7x81h0kn
// etc.). Bun.Transpiler previously defaulted autoImportJSX to false, so those
// calls were left undefined in the output.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Keep regression-test comments to the issue URL only.

The repository guideline requires regression tests to contain exactly the issue URL comment. Keep the URL at Line 2158 and remove the explanatory comments in this test.

Proposed cleanup
 // https://github.com/oven-sh/bun/issues/7499
-// The automatic JSX runtime emits calls to generated symbols ...
-// ...
-// Every generated JSX symbol that is called/used must be bound by the import.
-// tsconfig jsxImportSource is honored by the default-on auto-import.
-// The classic runtime has no auto-import and must stay import-free.
-// autoImportJSX: false opts out of the import.

Also applies to: 2167-2167, 2173-2173, 2180-2180, 2187-2187

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/bundler/transpiler/transpiler.test.js` around lines 2158 - 2161, In the
regression test around the issue 7499 URL, remove the explanatory comments
describing automatic JSX runtime behavior and retain only the issue URL comment.
Apply the same cleanup to the corresponding repeated regression-test comment
locations, preserving the test code unchanged.

Source: Coding guidelines

Comment on lines +2173 to +2189
// tsconfig jsxImportSource is honored by the default-on auto-import.
const preact = new Bun.Transpiler({
loader: "tsx",
tsconfig: { compilerOptions: { jsx: "react-jsx", jsxImportSource: "preact" } },
}).transformSync("export default <div />");
expect(preact).toContain('from "preact/jsx-runtime"');

// The classic runtime has no auto-import and must stay import-free.
const classic = new Bun.Transpiler({
loader: "tsx",
tsconfig: { compilerOptions: { jsx: "react" } },
}).transformSync("export default <div />");
expect(classic).not.toContain("import ");

// autoImportJSX: false opts out of the import.
const optOut = new Bun.Transpiler({ loader: "tsx", autoImportJSX: false }).transformSync("export default <div />");
expect(optOut).not.toContain("import ");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Cover both jsx and tsx for every changed behavior.

The default-import assertion iterates over both loaders, but the jsxImportSource, classic-runtime, and autoImportJSX: false cases only exercise tsx. A regression in the corresponding jsx path could therefore pass unnoticed. Parameterize these cases across both loaders, preferably using the repository’s matrix-test convention.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/bundler/transpiler/transpiler.test.js` around lines 2173 - 2189, Update
the JSX runtime tests around the preact, classic, and autoImportJSX opt-out
cases to run for both “jsx” and “tsx” loaders, following the repository’s
existing matrix-test convention. Preserve each case’s current assertion while
parameterizing only the loader so both transformation paths are covered.

Source: Coding guidelines

@github-actions

Copy link
Copy Markdown
Contributor

Found 1 issue this PR may fix:

  1. Bun.transpiler JSX empty result #14789 - Bun.Transpiler JSX empty result: with autoImportJSX defaulting to false, the JSX import was missing, causing the transpiled JSX element to become dead code that gets eliminated, producing an empty output

If this is helpful, copy the block below into the PR description to auto-close this issue on merge.

Fixes #14789

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. Bun.Transpiler: default autoImportJSX to true for the automatic runtime #35557 - Also defaults autoImportJSX to true for the automatic JSX runtime in Bun.Transpiler

🤖 Generated with Claude Code

@robobun

robobun commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator Author

Duplicate of #35557, which applies the same default change and already has green CI. Closing in favor of that one.

@robobun robobun closed this Jul 25, 2026
@robobun
robobun deleted the farm/4502aa88/transpiler-auto-import-jsx branch July 25, 2026 07:50
log: bun_ast::Log::default(), // overwritten at construction
runtime: Runtime::Features {
top_level_await: true,
auto_import_jsx: true,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The .transformSync() example output in docs/runtime/transpiler.mdx (lines 36-43) still shows the bare jsxDEV_7x81h0kn(...) call with no defining import — i.e. the pre-PR output. Now that autoImportJSX defaults to true, the real output for that exact snippet begins with import { jsxDEV as jsxDEV_7x81h0kn } from "react/jsx-dev-runtime";. Worth refreshing the doc block in this PR since it's the one place in the docs that shows what that mangled identifier is, and leaving it undefined-looking perpetuates the #7499 confusion.

Extended reasoning...

What's stale

docs/runtime/transpiler.mdx documents .transformSync() with this exact call shape:

const transpiler = new Bun.Transpiler({ loader: 'tsx' });
const code = `
import * as whatever from "./whatever.ts"
export function Home(props: {title: string}){
  return <p>{props.title}</p>;
}`;
const result = transpiler.transformSync(code);

and shows the output (lines 36-43) as:

import * as whatever from "./whatever.ts";
export function Home(props) {
  return jsxDEV_7x81h0kn("p", {
    children: props.title
  }, undefined, false, undefined, this);
}

That output block references jsxDEV_7x81h0kn with nothing defining it — which is precisely the "where does this identifier come from?" confusion that #7499 reports and this PR fixes.

Why it drifts after this PR

This PR flips Config::default().runtime.auto_import_jsx to true in src/runtime/api/JSTranspiler.rs:100. With no explicit autoImportJSX in the options, new Bun.Transpiler({ loader: 'tsx' }).transformSync(code) on JSX now prepends the runtime import. Running the doc's snippet against a build with this change produces:

import { jsxDEV as jsxDEV_7x81h0kn } from "react/jsx-dev-runtime";
import * as whatever from "./whatever.ts";
export function Home(props) {
  return jsxDEV_7x81h0kn("p", {
    children: props.title
  }, undefined, false, undefined, this);
}

The documented output no longer matches what Bun emits.

Step-by-step

  1. User reads docs/runtime/transpiler.mdx and copies the transformSync example verbatim.
  2. Config::default() now sets auto_import_jsx: true (JSTranspiler.rs:100); no autoImportJSX option is passed, so it stays true.
  3. get_parse_resulttranspiler.parse runs with auto_import_jsx = true, injecting the react/jsx-dev-runtime import statement.
  4. The printed output has an extra leading import { jsxDEV as jsxDEV_7x81h0kn } from "react/jsx-dev-runtime"; line that the doc's output block doesn't show.
  5. A user diffing their result against the doc sees a mismatch; a user reading only the doc still sees an apparently-undefined jsxDEV_7x81h0kn — the exact symptom this PR eliminates in the runtime.

Why nothing already covers this

The PR updates packages/bun-types/bun.d.ts to document the new default and updates the three existing tests in transpiler.test.js that asserted on import-free output by adding autoImportJSX: false. But docs/runtime/transpiler.mdx is not touched, and the doc example uses the default (no autoImportJSX key), so it now documents stale behavior. Per REVIEW.md, "When changing output/defaults/messages, grep the suite for assertions on the old behavior and update them in the same PR" — the .mdx output block is effectively one such assertion.

Fix

Add the auto-injected import as the first line of the output block in docs/runtime/transpiler.mdx:

 ```ts output
+import { jsxDEV as jsxDEV_7x81h0kn } from "react/jsx-dev-runtime";
 import * as whatever from "./whatever.ts";
 export function Home(props) {

Severity

Nit — doc drift only, no runtime impact. But it's worth doing in this PR because (a) the doc snippet is the exact call shape whose default this PR changes, and (b) the stale block is the only place in the docs that shows the mangled JSX identifier, so leaving it without the defining import keeps the very confusion #7499 filed alive in the documentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto import jsx-runtime to fix: Can't find variable: jsxDEV

1 participant