/
githubmirror
/
tailwindcss
Обзор
Документация
Войти
/
githubmirror
/
tailwindcss
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
integrations/vite/react-router.test.ts
266 строк
8 KB
Robin Malfait
Cleanup inconsistencies related to (regex) escapes (#19804)
16 мар 2026, 00:04
Не верифицирован
16 мар 2026, 00:04
faa5e88
Код
Авторство
О чём код?
import { candidate, css, fetchStyles, json, retryAssertion, test, ts, txt } from '../utils' const WORKSPACE = { 'package.json': json` { "type": "module", "dependencies": { "@react-router/dev": "^7", "@react-router/node": "^7", "@react-router/serve": "^7", "@tailwindcss/vite": "workspace:^", "@types/node": "^20", "@types/react-dom": "^19", "@types/react": "^19", "isbot": "^5", "react-dom": "^19", "react-router": "^7", "react": "^19", "tailwindcss": "workspace:^", "vite": "^5" } } `, 'react-router.config.ts': ts` import type { Config } from '@react-router/dev/config' export default { ssr: true } satisfies Config `, 'vite.config.ts': ts` import { defineConfig } from 'vite' import { reactRouter } from '@react-router/dev/vite' import tailwindcss from '@tailwindcss/vite' export default defineConfig({ plugins: [tailwindcss(), reactRouter()], }) `, 'app/routes/home.tsx': ts` export default function Home() { return <h1 className="font-bold">Welcome to React Router</h1> } `, 'app/app.css': css`@import 'tailwindcss';`, 'app/routes.ts': ts` import { type RouteConfig, index } from '@react-router/dev/routes' export default [index('routes/home.tsx')] satisfies RouteConfig `, 'app/root.tsx': ts` import { Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router' import './app.css' export function Layout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <head> <meta charSet="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <Meta /> <Links /> </head> <body> {children} <ScrollRestoration /> <Scripts /> </body> </html> ) } export default function App() { return <Outlet /> } `, } test('dev mode', { fs: WORKSPACE }, async ({ fs, spawn, expect }) => { let process = await spawn('pnpm react-router dev') let url = '' await process.onStdout((m) => { let match = /Local:\s*(http.*)\//.exec(m) if (match) url = match[1] return Boolean(url) }) await retryAssertion(async () => { let css = await fetchStyles(url) expect(css).toContain(candidate`font-bold`) }) await retryAssertion(async () => { await fs.write( 'app/routes/home.tsx', ts` export default function Home() { return <h1 className="font-bold underline">Welcome to React Router</h1> } `, ) let css = await fetchStyles(url) expect(css).toContain(candidate`underline`) expect(css).toContain(candidate`font-bold`) }) }) test( // cf. https://github.com/remix-run/react-router/blob/00cb4d7b310663b2e84152700c05d3b503005e83/integration/vite-hmr-hdr-test.ts#L311-L318 'dev mode, editing a server-only loader dependency triggers HDR instead of a full reload', { fs: { ...WORKSPACE, 'package.json': json` { "type": "module", "dependencies": { "@react-router/dev": "^7", "@react-router/node": "^7", "@react-router/serve": "^7", "@tailwindcss/vite": "workspace:^", "@types/node": "^20", "@types/react-dom": "^19", "@types/react": "^19", "isbot": "^5", "react-dom": "^19", "react-router": "^7", "react": "^19", "tailwindcss": "workspace:^", "vite": "^7" } } `, 'app/routes/home.tsx': ts` import type { Route } from './+types/home' import { direct } from '../direct-hdr-dep' export async function loader() { return { message: direct } } export default function Home({ loaderData }: Route.ComponentProps) { return ( <div> <h1 className="font-bold">{loaderData.message}</h1> <input data-testinput /> </div> ) } `, 'app/direct-hdr-dep.ts': ts` export const direct = 'HDR: 0' `, }, }, async ({ fs, spawn, expect }) => { let process = await spawn('pnpm react-router dev') let url = '' await process.onStdout((m) => { let match = /Local:\s*(http.*)\//.exec(m) if (match) url = match[1] return Boolean(url) }) // check initial state await retryAssertion(async () => { let html = await (await fetch(url)).text() expect(html).toContain('HDR: 0') let css = await fetchStyles(url) expect(css).toContain(candidate`font-bold`) }) // Flush stdout so we only see messages triggered by the edit below. process.flush() // Edit the server-only module. The client environment watches this file // but it only exists in the server module graph. Without the fix, the // Tailwind CSS plugin would trigger a full page reload on the client // instead of letting react-router handle HDR. await fs.write('app/direct-hdr-dep.ts', ts` export const direct = 'HDR: 1' `) // check update await retryAssertion(async () => { let html = await (await fetch(url)).text() expect(html).toContain('HDR: 1') let css = await fetchStyles(url) expect(css).toContain(candidate`font-bold`) }) // Assert the client receives an HMR update (not a full page reload). await process.onStdout((m) => m.includes('(client) hmr update')) }, ) test('build mode', { fs: WORKSPACE }, async ({ spawn, exec, expect }) => { await exec('pnpm react-router build') let process = await spawn('pnpm react-router-serve ./build/server/index.js') let url = '' await process.onStdout((m) => { let match = /\[react-router-serve\]\s*(http.*) \/?/.exec(m) if (match) url = match[1] return url != '' }) await retryAssertion(async () => { let css = await fetchStyles(url) expect(css).toContain(candidate`font-bold`) }) }) test( 'build mode using ?url stylesheet imports should only build one stylesheet (requires `file-system` scanner)', { fs: { ...WORKSPACE, 'app/root.tsx': ts` import { Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router' import styles from './app.css?url' export const links: Route.LinksFunction = () => [{ rel: 'stylesheet', href: styles }] export function Layout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <head> <meta charSet="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <Meta /> <Links /> </head> <body class="dark"> {children} <ScrollRestoration /> <Scripts /> </body> </html> ) } export default function App() { return <Outlet /> } `, 'vite.config.ts': ts` import { defineConfig } from 'vite' import { reactRouter } from '@react-router/dev/vite' import tailwindcss from '@tailwindcss/vite' export default defineConfig({ plugins: [tailwindcss(), reactRouter()], }) `, '.gitignore': txt` node_modules/ build/ `, }, }, async ({ fs, exec, expect }) => { await exec('pnpm react-router build') let files = await fs.glob('build/client/assets/**/*.css') expect(files).toHaveLength(1) let [filename] = files[0] await fs.expectFileToContain(filename, [candidate`font-bold`]) }, )