diff --git a/CHANGELOG.md b/CHANGELOG.md index 22868c5c4863..eb128f4d539e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - _Experimental_: Add `user-valid` and `user-invalid` variants ([#12370](https://github.com/tailwindlabs/tailwindcss/pull/12370)) +- Vite: Add a new `scanner` option to disable module-graph based scanning ([#16425](https://github.com/tailwindlabs/tailwindcss/pull/16425)) ### Fixed @@ -22,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Export backwards compatible config and plugin types from `tailwindcss/plugin` ([#16505](https://github.com/tailwindlabs/tailwindcss/pull/16505)) - Upgrade: Report errors when updating dependencies ([#16504](https://github.com/tailwindlabs/tailwindcss/pull/16504)) - Upgrade: Ensure a `darkMode` JS config setting with block syntax converts to use `@slot` ([#16507](https://github.com/tailwindlabs/tailwindcss/pull/16507)) +- Vite: Ensure that Astro builds with client-only components are always scanned ([#16425](https://github.com/tailwindlabs/tailwindcss/pull/16425)) ## [4.0.6] - 2025-02-10 diff --git a/integrations/vite/astro.test.ts b/integrations/vite/astro.test.ts index 5b7407e4714b..81db3e82c6d5 100644 --- a/integrations/vite/astro.test.ts +++ b/integrations/vite/astro.test.ts @@ -1,4 +1,4 @@ -import { candidate, fetchStyles, html, json, retryAssertion, test, ts } from '../utils' +import { candidate, fetchStyles, html, js, json, retryAssertion, test, ts } from '../utils' test( 'dev mode', @@ -19,11 +19,7 @@ test( import { defineConfig } from 'astro/config' // https://astro.build/config - export default defineConfig({ - vite: { - plugins: [tailwindcss()], - }, - }) + export default defineConfig({ vite: { plugins: [tailwindcss()] } }) `, 'src/pages/index.astro': html`
`, 'project-a/tailwind.config.js': js` - export default { - content: ['../project-b/src/**/*.js'], - } + export default { content: ['../project-b/src/**/*.js'] } `, 'project-a/src/index.css': css` @import 'tailwindcss/theme' theme(reference); @@ -122,7 +125,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { export default defineConfig({ css: ${transformer === 'postcss' ? '{}' : "{ transformer: 'lightningcss' }"}, build: { cssMinify: false }, - plugins: [tailwindcss()], + plugins: [tailwindcss({ scanner: '${scanner}' })], }) `, 'project-a/index.html': html` @@ -142,9 +145,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { `, 'project-a/tailwind.config.js': js` - export default { - content: ['../project-b/src/**/*.js'], - } + export default { content: ['../project-b/src/**/*.js'] } `, 'project-a/src/index.css': css` @import 'tailwindcss/theme' theme(reference); @@ -162,9 +163,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { }, }, async ({ root, spawn, fs, expect }) => { - let process = await spawn('pnpm vite dev', { - cwd: path.join(root, 'project-a'), - }) + let process = await spawn('pnpm vite dev', { cwd: path.join(root, 'project-a') }) await process.onStdout((m) => m.includes('ready in')) let url = '' @@ -174,17 +173,19 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { return Boolean(url) }) - // Candidates are resolved lazily, so the first visit of index.html - // will only have candidates from this file. + // Candidates are resolved lazily in module-graph mode, so the first visit of index.html will + // only have candidates from this file. await retryAssertion(async () => { let styles = await fetchStyles(url, '/index.html') expect(styles).toContain(candidate`underline`) expect(styles).toContain(candidate`flex`) - expect(styles).not.toContain(candidate`font-bold`) + + if (scanner === 'module-graph') { + expect(styles).not.toContain(candidate`font-bold`) + } }) - // Going to about.html will extend the candidate list to include - // candidates from about.html. + // Going to about.html will extend the candidate list to include candidates from about.html. await retryAssertion(async () => { let styles = await fetchStyles(url, '/about.html') expect(styles).toContain(candidate`underline`) @@ -232,8 +233,8 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { }) await retryAssertion(async () => { - // After updates to the CSS file, all previous candidates should still be in - // the generated CSS + // After updates to the CSS file, all previous candidates should still be in the generated + // stylesheet. await fs.write( 'project-a/src/index.css', css` @@ -283,10 +284,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' - export default defineConfig({ - build: { cssMinify: false }, - plugins: [tailwindcss()], - }) + export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()] }) `, 'project-a/index.html': html`
@@ -297,9 +295,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { `, 'project-a/tailwind.config.js': js` - export default { - content: ['../project-b/src/**/*.js'], - } + export default { content: ['../project-b/src/**/*.js'] } `, 'project-a/src/index.css': css` @import 'tailwindcss/theme' theme(reference); @@ -324,9 +320,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { }, }, async ({ root, spawn, fs, expect }) => { - let process = await spawn('pnpm vite build --watch', { - cwd: path.join(root, 'project-a'), - }) + let process = await spawn('pnpm vite build --watch', { cwd: path.join(root, 'project-a') }) await process.onStdout((m) => m.includes('built in')) let filename = '' @@ -465,7 +459,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { export default defineConfig({ css: ${transformer === 'postcss' ? '{}' : "{ transformer: 'lightningcss' }"}, build: { cssMinify: false }, - plugins: [tailwindcss()], + plugins: [tailwindcss({ scanner: '${scanner}' })], }) `, 'project-a/index.html': html` @@ -551,7 +545,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { export default defineConfig({ css: ${transformer === 'postcss' ? '{}' : "{ transformer: 'lightningcss' }"}, build: { cssMinify: false }, - plugins: [tailwindcss()], + plugins: [tailwindcss({ scanner: '${scanner}' })], }) `, 'project-a/index.html': html` @@ -653,7 +647,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { export default defineConfig({ css: ${transformer === 'postcss' ? '{}' : "{ transformer: 'lightningcss' }"}, build: { cssMinify: false }, - plugins: [tailwindcss()], + plugins: [tailwindcss({ scanner: '${scanner}' })], }) `, 'project-a/index.html': html` @@ -702,23 +696,15 @@ test( 'package.json': json` { "type": "module", - "dependencies": { - "@tailwindcss/vite": "workspace:^", - "tailwindcss": "workspace:^" - }, - "devDependencies": { - "vite": "^6" - } + "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, + "devDependencies": { "vite": "^6" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' - export default defineConfig({ - build: { cssMinify: false }, - plugins: [tailwindcss()], - }) + export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()] }) `, 'index.html': html`
@@ -784,23 +770,15 @@ test( 'package.json': json` { "type": "module", - "dependencies": { - "@tailwindcss/vite": "workspace:^", - "tailwindcss": "workspace:^" - }, - "devDependencies": { - "vite": "^6" - } + "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, + "devDependencies": { "vite": "^6" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' - export default defineConfig({ - build: { cssMinify: false }, - plugins: [tailwindcss()], - }) + export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()] }) `, 'index.html': html`
@@ -860,10 +838,7 @@ test( import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' - export default defineConfig({ - build: { cssMinify: false }, - plugins: [tailwindcss()], - }) + export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()] }) `, 'index.html': html`
diff --git a/integrations/vite/react-router.test.ts b/integrations/vite/react-router.test.ts new file mode 100644 index 000000000000..436947680099 --- /dev/null +++ b/integrations/vite/react-router.test.ts @@ -0,0 +1,178 @@ +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
+ } + `, + '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 ( + +
+
+
+
+
+