Philipp Spiess 88b762b539
Vite: Remove module-graph scanner (#16631)
Alternative to #16425

Fixes #16585
Fixes #16389
Fixes #16252
Fixes #15794
Fixes #16646
Fixes #16358

This PR changes the Vite plugin to use the file-system to discover
potential class names instead of relying on the module-graph. This comes
after a lot of testing and various issue reports where builds that span
different Vite instances were missing class names.

Because we now scan for candidates using the file-system, we can also
remove a lot of the bookkeeping necessary to make production builds and
development builds work as we no longer have to change the resulting
stylesheet based on the `transform` callbacks of other files that might
happen later.

This change comes at a small performance penalty that is noticeable
especially on very large projects with many files to scan. However, we
offset that change by fixing an issue that I found in the current Vite
integration that did a needless rebuild of the whole Tailwind root
whenever any source file changed. Because of how impactful this change
is, I expect many normal to medium sized projects to actually see a
performance improvement after these changes. Furthermore we do plan to
continue to use the module-graph to further improve the performance in
dev mode.

## Test plan

- Added new integration tests with cases found across the issues above.
- Manual testing by adding a local version of the Vite plugin to repos
from the issue list above and the [tailwindcss
playgrounds](https://github.com/philipp-spiess/tailwindcss-playgrounds).
2025-02-20 15:23:44 +01:00

122 lines
2.7 KiB
TypeScript

import { candidate, css, html, json, test, ts } from '../utils'
const WORKSPACE = {
'index.html': html`
<body>
<div id="app"></div>
<script type="module" src="./src/index.ts"></script>
</body>
`,
'src/index.css': css`@import 'tailwindcss';`,
'src/index.ts': ts`
import './index.css'
document.querySelector('#app').innerHTML = \`
<div class="underline m-2">Hello, world!</div>
\`
`,
'server.ts': ts`
import css from './src/index.css?url'
document.querySelector('#app').innerHTML = \`
<link rel="stylesheet" href="\${css}">
<div class="overline m-3">Hello, world!</div>
\`
`,
}
test(
'Vite 5',
{
fs: {
'package.json': json`
{
"type": "module",
"dependencies": {
"@tailwindcss/vite": "workspace:^",
"tailwindcss": "workspace:^"
},
"_comment": "This test uses Vite 5.3 on purpose. Do not upgrade it to Vite 6.",
"devDependencies": {
"vite": "^5.3"
}
}
`,
'vite.config.ts': ts`
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'vite'
export default defineConfig({
build: {
cssMinify: false,
ssrEmitAssets: true,
},
plugins: [tailwindcss()],
})
`,
...WORKSPACE,
},
},
async ({ fs, exec, expect }) => {
await exec('pnpm vite build --ssr server.ts')
let files = await fs.glob('dist/**/*.css')
expect(files).toHaveLength(1)
let [filename] = files[0]
await fs.expectFileToContain(filename, [
candidate`underline`,
candidate`m-2`,
candidate`overline`,
candidate`m-3`,
])
},
)
test(
`Vite 6`,
{
fs: {
'package.json': json`
{
"type": "module",
"dependencies": {
"@tailwindcss/vite": "workspace:^",
"tailwindcss": "workspace:^"
},
"devDependencies": {
"vite": "^6.0"
}
}
`,
'vite.config.ts': ts`
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'vite'
export default defineConfig({
build: {
cssMinify: false,
ssrEmitAssets: true,
},
plugins: [tailwindcss()],
})
`,
...WORKSPACE,
},
},
async ({ fs, exec, expect }) => {
await exec('pnpm vite build --ssr server.ts')
let files = await fs.glob('dist/**/*.css')
expect(files).toHaveLength(1)
let [filename] = files[0]
await fs.expectFileToContain(filename, [
candidate`underline`,
candidate`m-2`,
candidate`overline`,
candidate`m-3`,
])
},
)