Skip to content

Commit 6f3e372

Browse files
committed
feat: add jsxImportSource option (fixes #25)
1 parent f30e0ab commit 6f3e372

18 files changed

+634
-21
lines changed

CHANGELOG.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
## Unreleased
44

5-
- fix: commonjs default export (fixes [#14](https://github.com/vitejs/vite-plugin-react-swc/issues/14))
6-
- fix: support Vite base option (fixes [#18](https://github.com/vitejs/vite-plugin-react-swc/issues/18))
7-
- fix: compatibility with react-refresh runtime in @vitejs/plugin-react ([#20](https://github.com/vitejs/vite-plugin-react-swc/pull/20))
5+
- Support Emotion via the new `jsxImportSource` option (fixes [#25](https://github.com/vitejs/vite-plugin-react-swc/issues/25))
6+
- Fix HMR when using Vite `base` option (fixes [#18](https://github.com/vitejs/vite-plugin-react-swc/issues/18))
7+
- Fix usage with `Vite Ruby` and `Laravel Vite` ([#20](https://github.com/vitejs/vite-plugin-react-swc/pull/20))
8+
- Fix plugin default export when using commonjs (fixes [#14](https://github.com/vitejs/vite-plugin-react-swc/issues/14))
89

910
## 3.0.0
1011

README.md

+13
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ This plugin is only used in development and aims to be kept simple to enable goo
3434
- JS files are not transformed
3535
- tsconfig is not resolved, so properties other than the ones listed above behaves like TS defaults
3636

37+
## Changing the JSX import source
38+
39+
You can use the jsxImportSource option like this:
40+
41+
```ts
42+
import { defineConfig } from "vite";
43+
import react from "@vitejs/plugin-react-swc";
44+
45+
export default defineConfig({
46+
plugins: [react({ jsxImportSource: "@emotion/react" })],
47+
});
48+
```
49+
3750
## Consistent components exports
3851

3952
For React refresh to work correctly, your file should only export React components. The best explanation I've read is the one from the [Gatsby docs](https://www.gatsbyjs.com/docs/reference/local-development/fast-refresh/#how-it-works).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { expect, test } from "@playwright/test";
2+
import {
3+
setupDevServer,
4+
setupBuildAndPreview,
5+
setupWaitForLogs,
6+
rgbToHex,
7+
} from "../../utils";
8+
9+
test("Emotion build", async ({ page }) => {
10+
const { testUrl, server } = await setupBuildAndPreview("emotion");
11+
await page.goto(testUrl);
12+
13+
const button = page.locator("button");
14+
await button.hover();
15+
await expect(
16+
rgbToHex(await button.evaluate((el) => getComputedStyle(el).color)),
17+
).toBe("#646cff");
18+
19+
await button.click();
20+
await expect(button).toHaveText("count is 1");
21+
22+
await server.httpServer.close();
23+
});
24+
25+
test("Emotion HMR", async ({ page }) => {
26+
const { testUrl, server, editFile } = await setupDevServer("emotion");
27+
const waitForLogs = await setupWaitForLogs(page);
28+
await page.goto(testUrl);
29+
await waitForLogs("[vite] connected.");
30+
31+
const button = page.locator("button");
32+
await button.hover();
33+
await expect(
34+
rgbToHex(await button.evaluate((el) => getComputedStyle(el).color)),
35+
).toBe("#646cff");
36+
37+
await button.click();
38+
await expect(button).toHaveText("count is 1");
39+
40+
editFile("src/Button.tsx", [
41+
"background-color: #d26ac2;",
42+
"background-color: #646cff;",
43+
]);
44+
await waitForLogs("[vite] hot updated: /src/Button.tsx");
45+
await expect(button).toHaveText("count is 1");
46+
await expect(
47+
rgbToHex(
48+
await button.evaluate((el) => getComputedStyle(el).backgroundColor),
49+
),
50+
).toBe("#646cff");
51+
52+
editFile("src/App.tsx", ['color="#646cff"', 'color="#d26ac2"']);
53+
await waitForLogs("[vite] hot updated: /src/App.tsx");
54+
await expect(button).toHaveText("count is 1");
55+
await expect(
56+
rgbToHex(await button.evaluate((el) => getComputedStyle(el).color)),
57+
).toBe("#d26ac2");
58+
59+
await server.close();
60+
});

playground/emotion/index.html

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Vite + React + TS + Emotion</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
<script type="module" src="/src/index.tsx"></script>
12+
</body>
13+
</html>

playground/emotion/package.json

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "playground-emotion",
3+
"private": true,
4+
"scripts": {
5+
"dev": "vite",
6+
"build": "vite build"
7+
},
8+
"dependencies": {
9+
"react": "^18.2.0",
10+
"react-dom": "^18.2.0",
11+
"@emotion/react": "^11.10.5"
12+
},
13+
"devDependencies": {
14+
"@types/react-dom": "^18.0.9",
15+
"@types/react": "^18.0.26",
16+
"@vitejs/plugin-react-swc": "../../dist"
17+
}
18+
}

playground/emotion/public/vite.svg

+1
Loading

playground/emotion/src/App.css

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#root {
2+
max-width: 1280px;
3+
margin: 0 auto;
4+
padding: 2rem;
5+
text-align: center;
6+
}
7+
8+
.logo {
9+
height: 6em;
10+
padding: 1.5em;
11+
will-change: filter;
12+
}
13+
.logo:hover {
14+
filter: drop-shadow(0 0 2em #646cffaa);
15+
}
16+
.logo.emotion:hover {
17+
filter: drop-shadow(0 0 2em #d26ac2aa);
18+
}
19+
20+
.card {
21+
padding: 2em;
22+
}
23+
24+
.read-the-docs {
25+
color: #888;
26+
}

playground/emotion/src/App.tsx

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import "./App.css";
2+
import { Button } from "./Button";
3+
4+
export const App = () => (
5+
<div>
6+
<div>
7+
<a href="https://vitejs.dev" target="_blank" rel="noreferrer">
8+
<img src="/vite.svg" className="logo" alt="Vite logo" />
9+
</a>
10+
<a href="https://emotion.sh/" target="_blank" rel="noreferrer">
11+
<img
12+
src="https://emotion.sh/logo-96x96.png"
13+
className="logo emotion"
14+
alt="Emotion logo"
15+
/>
16+
</a>
17+
</div>
18+
<div className="card">
19+
<Button color="#646cff" />
20+
<p>
21+
Edit <code>src/Button.tsx</code> and save to test HMR
22+
</p>
23+
</div>
24+
<p className="read-the-docs">
25+
Click on the Vite and Emotion logos to learn more
26+
</p>
27+
</div>
28+
);

playground/emotion/src/Button.tsx

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { css } from "@emotion/react";
2+
import { useState } from "react";
3+
4+
export const Button = ({ color }: { color: string }) => {
5+
const [count, setCount] = useState(0);
6+
7+
return (
8+
<button
9+
css={css`
10+
padding: 10px 16px;
11+
background-color: #d26ac2;
12+
font-size: 20px;
13+
border-radius: 4px;
14+
border: 0px;
15+
&:hover {
16+
color: ${color};
17+
}
18+
`}
19+
onClick={() => setCount(count + 1)}
20+
>
21+
count is {count}
22+
</button>
23+
);
24+
};

playground/emotion/src/index.css

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
:root {
2+
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
3+
font-size: 16px;
4+
line-height: 24px;
5+
font-weight: 400;
6+
7+
color-scheme: light dark;
8+
color: rgba(255, 255, 255, 0.87);
9+
background-color: #242424;
10+
11+
font-synthesis: none;
12+
text-rendering: optimizeLegibility;
13+
-webkit-font-smoothing: antialiased;
14+
-moz-osx-font-smoothing: grayscale;
15+
-webkit-text-size-adjust: 100%;
16+
}
17+
18+
body {
19+
margin: 0;
20+
display: flex;
21+
place-items: center;
22+
min-width: 320px;
23+
min-height: 100vh;
24+
}
25+
26+
h1 {
27+
font-size: 3.2em;
28+
line-height: 1.1;
29+
}

playground/emotion/src/index.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { StrictMode } from "react";
2+
import { createRoot } from "react-dom/client";
3+
import { App } from "./App";
4+
import "./index.css";
5+
6+
createRoot(document.getElementById("root")!).render(
7+
<StrictMode>
8+
<App />
9+
</StrictMode>,
10+
);

playground/emotion/tsconfig.json

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"include": ["src", "vite.config.ts"],
3+
"compilerOptions": {
4+
"module": "ESNext",
5+
"lib": ["ESNext", "DOM", "DOM.Iterable"],
6+
"target": "ESNext",
7+
"jsx": "react-jsx",
8+
"jsxImportSource": "@emotion/react",
9+
"types": ["vite/client", "@emotion/react"],
10+
"noEmit": true,
11+
"isolatedModules": true,
12+
"skipLibCheck": true,
13+
14+
/* Imports */
15+
"moduleResolution": "node", // Allow `index` imports
16+
"resolveJsonModule": true, // Allow json import
17+
"forceConsistentCasingInFileNames": true, // Avoid difference in case between file name and import
18+
"esModuleInterop": false,
19+
20+
/* Linting */
21+
"strict": true,
22+
"noUnusedLocals": true,
23+
"noUnusedParameters": true,
24+
"noFallthroughCasesInSwitch": true,
25+
"useUnknownInCatchVariables": true
26+
}
27+
}

playground/emotion/vite.config.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { defineConfig } from "vite";
2+
import react from "@vitejs/plugin-react-swc";
3+
4+
export default defineConfig({
5+
plugins: [react({ jsxImportSource: "@emotion/react" })],
6+
});

playground/utils.ts

+15
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,18 @@ export const setupBuildAndPreview = async (name: string) => {
8080
server,
8181
};
8282
};
83+
84+
export const rgbToHex = (rgb: string): string => {
85+
const [_, rs, gs, bs] = rgb.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/)!;
86+
return (
87+
"#" +
88+
componentToHex(parseInt(rs, 10)) +
89+
componentToHex(parseInt(gs, 10)) +
90+
componentToHex(parseInt(bs, 10))
91+
);
92+
};
93+
94+
const componentToHex = (c: number): string => {
95+
const hex = c.toString(16);
96+
return hex.length === 1 ? "0" + hex : hex;
97+
};

0 commit comments

Comments
 (0)