diff --git a/.changeset/clever-months-swim.md b/.changeset/clever-months-swim.md new file mode 100644 index 00000000000..b2891178dde --- /dev/null +++ b/.changeset/clever-months-swim.md @@ -0,0 +1,5 @@ +--- +"@remix-run/dev": patch +--- + +Support JSX usage in `.jsx` files without manual `React` import in Vite diff --git a/contributors.yml b/contributors.yml index 0eded58d49e..e563fb427d7 100644 --- a/contributors.yml +++ b/contributors.yml @@ -195,6 +195,7 @@ - harmony7 - helderburato - HenryVogt +- hi-ogawa - hicksy - himorishige - Hirochon diff --git a/integration/vite-dev-test.ts b/integration/vite-dev-test.ts index 2a847db5d35..c948106e949 100644 --- a/integration/vite-dev-test.ts +++ b/integration/vite-dev-test.ts @@ -93,7 +93,7 @@ test.describe("Vite dev", () => { export const loader: LoaderFunction = () => { const headers = new Headers(); - + headers.append( "Set-Cookie", "first=one; Domain=localhost; Path=/; SameSite=Lax" @@ -110,12 +110,12 @@ test.describe("Vite dev", () => { ); headers.set("location", "http://localhost:${devPort}/get-cookies"); - + const response = new Response(null, { headers, status: 302, }); - + return response; }; `, @@ -136,6 +136,15 @@ test.describe("Vite dev", () => { ); } `, + "app/routes/jsx.jsx": js` + export default function JsxRoute() { + return ( +
+

HMR updated: no

+
+ ); + } + `, }, }); @@ -228,6 +237,33 @@ test.describe("Vite dev", () => { "first=one; second=two; third=three" ); }); + + test("handles JSX in .jsx file without React import", async ({ page }) => { + let pageErrors: unknown[] = []; + page.on("pageerror", (error) => pageErrors.push(error)); + + await page.goto(`http://localhost:${devPort}/jsx`, { + waitUntil: "networkidle", + }); + expect(pageErrors).toEqual([]); + + let hmrStatus = page.locator("#jsx [data-hmr]"); + await expect(hmrStatus).toHaveText("HMR updated: no"); + + let indexRouteContents = await fs.readFile( + path.join(projectDir, "app/routes/jsx.jsx"), + "utf8" + ); + await fs.writeFile( + path.join(projectDir, "app/routes/jsx.jsx"), + indexRouteContents.replace("HMR updated: no", "HMR updated: yes"), + "utf8" + ); + await page.waitForLoadState("networkidle"); + await expect(hmrStatus).toHaveText("HMR updated: yes"); + + expect(pageErrors).toEqual([]); + }); }); let bufferize = (stream: Readable): (() => string) => { diff --git a/packages/remix-dev/vite/plugin.ts b/packages/remix-dev/vite/plugin.ts index 51224a2f4a0..e580c2660d9 100644 --- a/packages/remix-dev/vite/plugin.ts +++ b/packages/remix-dev/vite/plugin.ts @@ -432,6 +432,10 @@ export const remixVitePlugin: RemixVitePlugin = (options = {}) => { "react-dom/client", ], }, + esbuild: { + jsx: "automatic", + jsxDev: viteCommand !== "build", + }, resolve: { // https://react.dev/warnings/invalid-hook-call-warning#duplicate-react dedupe: ["react", "react-dom"],