diff --git a/integrations/postcss/multi-root.test.ts b/integrations/postcss/multi-root.test.ts new file mode 100644 index 000000000..bfd2340e5 --- /dev/null +++ b/integrations/postcss/multi-root.test.ts @@ -0,0 +1,50 @@ +import { candidate, css, html, js, json, test } from '../utils' + +test( + 'production build', + { + fs: { + 'package.json': json` + { + "dependencies": { + "postcss": "^8", + "postcss-cli": "^10", + "tailwindcss": "workspace:^", + "@tailwindcss/postcss": "workspace:^" + } + } + `, + 'postcss.config.js': js` + module.exports = { + plugins: { + '@tailwindcss/postcss': {}, + }, + } + `, + 'index.html': html` +
+ `, + 'src/shared.css': css` + @import 'tailwindcss/theme' theme(reference); + @import 'tailwindcss/utilities'; + `, + 'src/root1.css': css` + @import './shared.css'; + @variant one (&:is([data-root='1'])); + `, + 'src/root2.css': css` + @import './shared.css'; + @variant two (&:is([data-root='2'])); + `, + }, + }, + async ({ fs, exec }) => { + await exec('pnpm postcss src/*.css -d dist') + + await fs.expectFileToContain('dist/root1.css', [candidate`one:underline`]) + await fs.expectFileNotToContain('dist/root1.css', [candidate`two:underline`]) + + await fs.expectFileNotToContain('dist/root2.css', [candidate`one:underline`]) + await fs.expectFileToContain('dist/root2.css', [candidate`two:underline`]) + }, +) diff --git a/integrations/vite/multi-root.test.ts b/integrations/vite/multi-root.test.ts new file mode 100644 index 000000000..76b189d9c --- /dev/null +++ b/integrations/vite/multi-root.test.ts @@ -0,0 +1,164 @@ +import { expect } from 'vitest' +import { candidate, css, fetchStyles, html, json, retryAssertion, test, ts } from '../utils' + +test( + `production build`, + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^5.3.5" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import path from 'node:path' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { + cssMinify: false, + rollupOptions: { + input: { + root1: path.resolve(__dirname, 'root1.html'), + root2: path.resolve(__dirname, 'root2.html'), + }, + }, + }, + plugins: [tailwindcss()], + }) + `, + 'root1.html': html` + + + + +
Hello, world!
+ + `, + 'src/shared.css': css` + @import 'tailwindcss/theme' theme(reference); + @import 'tailwindcss/utilities'; + `, + 'src/root1.css': css` + @import './shared.css'; + @variant one (&:is([data-root='1'])); + `, + 'root2.html': html` + + + + +
Hello, world!
+ + `, + 'src/root2.css': css` + @import './shared.css'; + @variant two (&:is([data-root='2'])); + `, + }, + }, + async ({ fs, exec }) => { + await exec('pnpm vite build') + + let files = await fs.glob('dist/**/*.css') + expect(files).toHaveLength(2) + + let root1 = files.find(([filename]) => filename.includes('root1')) + let root2 = files.find(([filename]) => filename.includes('root2')) + + expect(root1).toBeDefined() + expect(root2).toBeDefined() + + expect(root1![1]).toContain(candidate`one:underline`) + expect(root1![1]).not.toContain(candidate`two:underline`) + + expect(root2![1]).not.toContain(candidate`one:underline`) + expect(root2![1]).toContain(candidate`two:underline`) + }, +) + +test( + `dev mode`, + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^5.3.5" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import path from 'node:path' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss()], + }) + `, + 'root1.html': html` + + + + +
Hello, world!
+ + `, + 'src/shared.css': css` + @import 'tailwindcss/theme' theme(reference); + @import 'tailwindcss/utilities'; + `, + 'src/root1.css': css` + @import './shared.css'; + @variant one (&:is([data-root='1'])); + `, + 'root2.html': html` + + + + +
Hello, world!
+ + `, + 'src/root2.css': css` + @import './shared.css'; + @variant two (&:is([data-root='2'])); + `, + }, + }, + async ({ root, spawn, getFreePort, fs }) => { + let port = await getFreePort() + await spawn(`pnpm vite dev --port ${port}`) + + // Candidates are resolved lazily, so the first visit of index.html + // will only have candidates from this file. + await retryAssertion(async () => { + let styles = await fetchStyles(port, '/root1.html') + expect(styles).toContain(candidate`one:underline`) + expect(styles).not.toContain(candidate`two:underline`) + }) + + // Going to about.html will extend the candidate list to include + // candidates from about.html. + await retryAssertion(async () => { + let styles = await fetchStyles(port, '/root2.html') + expect(styles).not.toContain(candidate`one:underline`) + expect(styles).toContain(candidate`two:underline`) + }) + }, +)