Skip to content

Commit 3f85b74

Browse files
Add integration tests for multi-root builds (#14564)
When your Vite or postcss project has multiple Tailwind CSS roots with different configs, they should not influence each other (with the exception of the same candidates being used).
1 parent 3813f03 commit 3f85b74

File tree

2 files changed

+214
-0
lines changed

2 files changed

+214
-0
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { candidate, css, html, js, json, test } from '../utils'
2+
3+
test(
4+
'production build',
5+
{
6+
fs: {
7+
'package.json': json`
8+
{
9+
"dependencies": {
10+
"postcss": "^8",
11+
"postcss-cli": "^10",
12+
"tailwindcss": "workspace:^",
13+
"@tailwindcss/postcss": "workspace:^"
14+
}
15+
}
16+
`,
17+
'postcss.config.js': js`
18+
module.exports = {
19+
plugins: {
20+
'@tailwindcss/postcss': {},
21+
},
22+
}
23+
`,
24+
'index.html': html`
25+
<div class="one:underline two:underline"></div>
26+
`,
27+
'src/shared.css': css`
28+
@import 'tailwindcss/theme' theme(reference);
29+
@import 'tailwindcss/utilities';
30+
`,
31+
'src/root1.css': css`
32+
@import './shared.css';
33+
@variant one (&:is([data-root='1']));
34+
`,
35+
'src/root2.css': css`
36+
@import './shared.css';
37+
@variant two (&:is([data-root='2']));
38+
`,
39+
},
40+
},
41+
async ({ fs, exec }) => {
42+
await exec('pnpm postcss src/*.css -d dist')
43+
44+
await fs.expectFileToContain('dist/root1.css', [candidate`one:underline`])
45+
await fs.expectFileNotToContain('dist/root1.css', [candidate`two:underline`])
46+
47+
await fs.expectFileNotToContain('dist/root2.css', [candidate`one:underline`])
48+
await fs.expectFileToContain('dist/root2.css', [candidate`two:underline`])
49+
},
50+
)

integrations/vite/multi-root.test.ts

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import { expect } from 'vitest'
2+
import { candidate, css, fetchStyles, html, json, retryAssertion, test, ts } from '../utils'
3+
4+
test(
5+
`production build`,
6+
{
7+
fs: {
8+
'package.json': json`
9+
{
10+
"type": "module",
11+
"dependencies": {
12+
"@tailwindcss/vite": "workspace:^",
13+
"tailwindcss": "workspace:^"
14+
},
15+
"devDependencies": {
16+
"vite": "^5.3.5"
17+
}
18+
}
19+
`,
20+
'vite.config.ts': ts`
21+
import tailwindcss from '@tailwindcss/vite'
22+
import path from 'node:path'
23+
import { defineConfig } from 'vite'
24+
25+
export default defineConfig({
26+
build: {
27+
cssMinify: false,
28+
rollupOptions: {
29+
input: {
30+
root1: path.resolve(__dirname, 'root1.html'),
31+
root2: path.resolve(__dirname, 'root2.html'),
32+
},
33+
},
34+
},
35+
plugins: [tailwindcss()],
36+
})
37+
`,
38+
'root1.html': html`
39+
<head>
40+
<link rel="stylesheet" href="./src/root1.css" />
41+
</head>
42+
<body>
43+
<div class="one:underline two:underline">Hello, world!</div>
44+
</body>
45+
`,
46+
'src/shared.css': css`
47+
@import 'tailwindcss/theme' theme(reference);
48+
@import 'tailwindcss/utilities';
49+
`,
50+
'src/root1.css': css`
51+
@import './shared.css';
52+
@variant one (&:is([data-root='1']));
53+
`,
54+
'root2.html': html`
55+
<head>
56+
<link rel="stylesheet" href="./src/root2.css" />
57+
</head>
58+
<body>
59+
<div class="one:underline two:underline">Hello, world!</div>
60+
</body>
61+
`,
62+
'src/root2.css': css`
63+
@import './shared.css';
64+
@variant two (&:is([data-root='2']));
65+
`,
66+
},
67+
},
68+
async ({ fs, exec }) => {
69+
await exec('pnpm vite build')
70+
71+
let files = await fs.glob('dist/**/*.css')
72+
expect(files).toHaveLength(2)
73+
74+
let root1 = files.find(([filename]) => filename.includes('root1'))
75+
let root2 = files.find(([filename]) => filename.includes('root2'))
76+
77+
expect(root1).toBeDefined()
78+
expect(root2).toBeDefined()
79+
80+
expect(root1![1]).toContain(candidate`one:underline`)
81+
expect(root1![1]).not.toContain(candidate`two:underline`)
82+
83+
expect(root2![1]).not.toContain(candidate`one:underline`)
84+
expect(root2![1]).toContain(candidate`two:underline`)
85+
},
86+
)
87+
88+
test(
89+
`dev mode`,
90+
{
91+
fs: {
92+
'package.json': json`
93+
{
94+
"type": "module",
95+
"dependencies": {
96+
"@tailwindcss/vite": "workspace:^",
97+
"tailwindcss": "workspace:^"
98+
},
99+
"devDependencies": {
100+
"vite": "^5.3.5"
101+
}
102+
}
103+
`,
104+
'vite.config.ts': ts`
105+
import tailwindcss from '@tailwindcss/vite'
106+
import path from 'node:path'
107+
import { defineConfig } from 'vite'
108+
109+
export default defineConfig({
110+
build: { cssMinify: false },
111+
plugins: [tailwindcss()],
112+
})
113+
`,
114+
'root1.html': html`
115+
<head>
116+
<link rel="stylesheet" href="./src/root1.css" />
117+
</head>
118+
<body>
119+
<div class="one:underline two:underline">Hello, world!</div>
120+
</body>
121+
`,
122+
'src/shared.css': css`
123+
@import 'tailwindcss/theme' theme(reference);
124+
@import 'tailwindcss/utilities';
125+
`,
126+
'src/root1.css': css`
127+
@import './shared.css';
128+
@variant one (&:is([data-root='1']));
129+
`,
130+
'root2.html': html`
131+
<head>
132+
<link rel="stylesheet" href="./src/root2.css" />
133+
</head>
134+
<body>
135+
<div class="one:underline two:underline">Hello, world!</div>
136+
</body>
137+
`,
138+
'src/root2.css': css`
139+
@import './shared.css';
140+
@variant two (&:is([data-root='2']));
141+
`,
142+
},
143+
},
144+
async ({ root, spawn, getFreePort, fs }) => {
145+
let port = await getFreePort()
146+
await spawn(`pnpm vite dev --port ${port}`)
147+
148+
// Candidates are resolved lazily, so the first visit of index.html
149+
// will only have candidates from this file.
150+
await retryAssertion(async () => {
151+
let styles = await fetchStyles(port, '/root1.html')
152+
expect(styles).toContain(candidate`one:underline`)
153+
expect(styles).not.toContain(candidate`two:underline`)
154+
})
155+
156+
// Going to about.html will extend the candidate list to include
157+
// candidates from about.html.
158+
await retryAssertion(async () => {
159+
let styles = await fetchStyles(port, '/root2.html')
160+
expect(styles).not.toContain(candidate`one:underline`)
161+
expect(styles).toContain(candidate`two:underline`)
162+
})
163+
},
164+
)

0 commit comments

Comments
 (0)