/
githubmirror
/
tailwindcss
Обзор
Документация
Войти
/
githubmirror
/
tailwindcss
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
integrations/vite/resolvers.test.ts
804 строки
21 KB
Robin Malfait
Handle CSS nesting natively (#20124)
07 июл 2026, 19:28
Не верифицирован
07 июл 2026, 19:28
5835691
Код
Авторство
О чём код?
import { describe } from 'vitest' import { candidate, css, fetchStyles, html, js, json, retryAssertion, test, ts, txt, yaml, } from '../utils' test( 'resolves tsconfig paths in production build', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^8" } } `, 'tsconfig.json': json` { "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["./src/*"] } } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], resolve: { tsconfigPaths: true, }, }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css" /> </head> <body> <div class="underline custom-underline">Hello, world!</div> </body> `, 'src/index.css': css` @import '@/styles/base.css'; @plugin '@/plugin.js'; `, 'src/styles/base.css': css` @reference 'tailwindcss/theme'; @import 'tailwindcss/utilities'; `, 'src/plugin.js': js` export default function ({ addUtilities }) { addUtilities({ '.custom-underline': { 'border-bottom': '1px solid green' } }) } `, }, }, async ({ fs, exec, expect }) => { await exec('pnpm vite build') let files = await fs.glob('dist/**/*.css') expect(files).toHaveLength(1) let [filename] = files[0] await fs.expectFileToContain(filename, [candidate`underline`, candidate`custom-underline`]) }, ) test( 'resolves tsconfig paths in dev mode', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^8" } } `, 'tsconfig.json': json` { "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["./src/*"] } } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], resolve: { tsconfigPaths: true, }, }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css" /> </head> <body> <div class="underline custom-underline">Hello, world!</div> </body> `, 'src/index.css': css` @import '@/styles/base.css'; @plugin '@/plugin.js'; `, 'src/styles/base.css': css` @reference 'tailwindcss/theme'; @import 'tailwindcss/utilities'; `, 'src/plugin.js': js` export default function ({ addUtilities }) { addUtilities({ '.custom-underline': { 'border-bottom': '1px solid green' } }) } `, }, }, async ({ spawn, expect }) => { let process = await spawn('pnpm vite dev') await process.onStdout((m) => m.includes('ready in')) 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 styles = await fetchStyles(url, '/index.html') expect(styles).toContain(candidate`underline`) expect(styles).toContain(candidate`custom-underline`) }) }, ) test( 'resolves at-sign aliases in production build', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^8" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' import { fileURLToPath } from 'node:url' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], resolve: { alias: [{ find: '@', replacement: fileURLToPath(new URL('.', import.meta.url)) }], }, }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css" /> </head> <body> <div class="underline custom-underline">Hello, world!</div> </body> `, 'src/index.css': css` @import '@/src/styles/base.css'; @plugin '@/src/plugin.js'; `, 'src/styles/base.css': css` @reference 'tailwindcss/theme'; @import 'tailwindcss/utilities'; `, 'src/plugin.js': js` export default function ({ addUtilities }) { addUtilities({ '.custom-underline': { 'border-bottom': '1px solid green' } }) } `, }, }, async ({ fs, exec, expect }) => { await exec('pnpm vite build') let files = await fs.glob('dist/**/*.css') expect(files).toHaveLength(1) let [filename] = files[0] await fs.expectFileToContain(filename, [candidate`underline`, candidate`custom-underline`]) }, ) test( 'resolves package plugins in production build with at-sign aliases', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "tailwind-scrollbar": "^4.0.2", "tailwindcss-motion": "^1.1.1", "vite": "^8" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' import { fileURLToPath } from 'node:url' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], resolve: { alias: [{ find: '@', replacement: fileURLToPath(new URL('.', import.meta.url)) }], }, }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css" /> </head> <body> <div class="scrollbar scrollbar-thumb-red-500 motion-preset-fade">Hello, world!</div> </body> `, 'src/index.css': css` @import 'tailwindcss'; @plugin 'tailwind-scrollbar'; @plugin 'tailwindcss-motion'; `, }, }, async ({ fs, exec, expect }) => { await exec('pnpm vite build') let files = await fs.glob('dist/**/*.css') expect(files).toHaveLength(1) let [filename] = files[0] await fs.expectFileToContain(filename, [ candidate`scrollbar`, candidate`scrollbar-thumb-red-500`, candidate`motion-preset-fade`, ]) }, ) test( 'resolves package plugins to JS entries in production build when browser points to CSS', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "plugin-browser-css": "workspace:*", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^8" } } `, 'pnpm-workspace.yaml': yaml` # packages: - packages/* `, 'packages/plugin-browser-css/package.json': json` { "name": "plugin-browser-css", "version": "1.0.0", "type": "module", "main": "./index.js", "module": "./index.js", "browser": "./browser.css" } `, 'packages/plugin-browser-css/index.js': js` export default function ({ addUtilities }) { addUtilities({ '.browser-css-plugin': { 'border-bottom': '1px solid green' } }) } `, 'packages/plugin-browser-css/browser.css': css` .should-not-be-loaded-as-a-plugin { display: none; } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <html> <head> <link rel="stylesheet" href="./src/index.css" /> </head> <body> <div class="browser-css-plugin">Hello, world!</div> </body> </html> `, 'src/index.css': css` @import 'tailwindcss'; @plugin 'plugin-browser-css'; `, }, }, async ({ fs, exec, expect }) => { await exec('pnpm vite build') let files = await fs.glob('dist/**/*.css') expect(files).toHaveLength(1) let [filename] = files[0] await fs.expectFileToContain(filename, [candidate`browser-css-plugin`]) }, ) test( 'resolves package plugins to JS entries in dev mode when browser points to CSS', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "plugin-browser-css": "workspace:*", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^8" } } `, 'pnpm-workspace.yaml': yaml` # packages: - packages/* `, 'packages/plugin-browser-css/package.json': json` { "name": "plugin-browser-css", "version": "1.0.0", "type": "module", "main": "./index.js", "module": "./index.js", "browser": "./browser.css" } `, 'packages/plugin-browser-css/index.js': js` export default function ({ addUtilities }) { addUtilities({ '.browser-css-plugin': { 'border-bottom': '1px solid green' } }) } `, 'packages/plugin-browser-css/browser.css': css` .should-not-be-loaded-as-a-plugin { display: none; } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <html> <head> <link rel="stylesheet" href="./src/index.css" /> </head> <body> <div class="browser-css-plugin">Hello, world!</div> </body> </html> `, 'src/index.css': css` @import 'tailwindcss'; @plugin 'plugin-browser-css'; `, }, }, async ({ spawn, expect }) => { let process = await spawn('pnpm vite dev') await process.onStdout((m) => m.includes('ready in')) 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 styles = await fetchStyles(url, '/index.html') expect(styles).toContain(candidate`browser-css-plugin`) }) }, ) test( 'resolves package plugins in dev mode when package exports CSS files', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "daisyui": "^5", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^8" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <html> <head> <link rel="stylesheet" href="./src/index.css" /> </head> <body> <button class="btn btn-primary">Hello, world!</button> </body> </html> `, 'src/index.css': css` @import 'tailwindcss'; @plugin 'daisyui'; `, }, }, async ({ spawn, expect }) => { let process = await spawn('pnpm vite dev') await process.onStdout((m) => m.includes('ready in')) 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 styles = await fetchStyles(url, '/index.html') expect(styles).toContain('.btn') expect(styles).toContain('.btn-primary') }) }, ) test( 'resolve relative CSS files correctly', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^8" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <html> <head> <link rel="stylesheet" href="./src/index.css" /> </head> <body></body> </html> `, 'src/index.css': css` @reference 'tailwindcss/theme'; @import 'tailwindcss/utilities'; @import './themes/glow.css'; `, // References a file in the current folder, which names happens to match a // file in the parent folder as well. 'src/themes/glow.css': css`@import './entry.css';`, 'src/themes/entry.css': css` .do-include-me { color: green; } `, // Never rerefenced, so should not be included 'src/entry.css': css` .do-not-include-me { color: red; } `, }, }, async ({ exec, fs, expect }) => { await exec('pnpm vite build') expect( (await fs.dumpFiles('./dist/**/*.css')).replace(/-([-_a-zA-Z0-9]*?)\.css/g, '-<hash>.css'), ).toMatchInlineSnapshot(` " --- ./dist/assets/index-<hash>.css --- .do-include-me { color: green; } " `) }, ) test( 'resolve relative JS files correctly', { fs: { 'package.json': json` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { "vite": "^8" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' export default defineConfig({ build: { cssMinify: false }, plugins: [tailwindcss()], }) `, 'index.html': html` <html> <head> <link rel="stylesheet" href="./src/index.css" /> </head> <body></body> </html> `, 'src/index.css': css` @reference 'tailwindcss/theme'; @import 'tailwindcss/utilities'; @import './themes/glow.css'; `, // References a file in the current folder, which names happens to match a // file in the parent folder as well. 'src/themes/glow.css': css`@plugin "./my-plugin.js";`, 'src/themes/my-plugin.js': ts` export default function ({ addBase }) { addBase({ '.do-include-me': { color: 'green' } }) } `, // Never rerefenced, so should not be included 'src/my-plugin.js': css` export default function ({ addBase }) { addBase({ '.do-not-include-me': { 'color': 'red' } }) } `, }, }, async ({ exec, fs, expect }) => { await exec('pnpm vite build') expect( (await fs.dumpFiles('./dist/**/*.css')).replace(/-([-_a-zA-Z0-9]*?)\.css/g, '-<hash>.css'), ).toMatchInlineSnapshot(` " --- ./dist/assets/index-<hash>.css --- @layer base { .do-include-me { color: green; } } " `) }, ) describe.each(['postcss', 'lightningcss'])('%s', (transformer) => { test( 'resolves aliases in production build', { fs: { 'package.json': txt` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { ${transformer === 'lightningcss' ? `"lightningcss": "^1",` : ''} "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' import { fileURLToPath } from 'node:url' export default defineConfig({ css: ${transformer === 'postcss' ? '{}' : "{ transformer: 'lightningcss' }"}, build: { cssMinify: false }, plugins: [tailwindcss()], resolve: { alias: { '#css-alias': fileURLToPath(new URL('./src/alias.css', import.meta.url)), '#js-alias': fileURLToPath(new URL('./src/plugin.js', import.meta.url)), }, }, }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css" /> </head> <body> <div class="underline custom-underline">Hello, world!</div> </body> `, 'src/index.css': css` @import '#css-alias'; @plugin '#js-alias'; `, 'src/alias.css': css` @reference 'tailwindcss/theme'; @import 'tailwindcss/utilities'; `, 'src/plugin.js': js` export default function ({ addUtilities }) { addUtilities({ '.custom-underline': { 'border-bottom': '1px solid green' } }) } `, }, }, async ({ fs, exec, expect }) => { await exec('pnpm vite build') let files = await fs.glob('dist/**/*.css') expect(files).toHaveLength(1) let [filename] = files[0] await fs.expectFileToContain(filename, [candidate`underline`, candidate`custom-underline`]) }, ) test( 'resolves aliases in dev mode', { fs: { 'package.json': txt` { "type": "module", "dependencies": { "@tailwindcss/vite": "workspace:^", "tailwindcss": "workspace:^" }, "devDependencies": { ${transformer === 'lightningcss' ? `"lightningcss": "^1",` : ''} "vite": "^5.3.5" } } `, 'vite.config.ts': ts` import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' import { fileURLToPath } from 'node:url' export default defineConfig({ css: ${transformer === 'postcss' ? '{}' : "{ transformer: 'lightningcss' }"}, build: { cssMinify: false }, plugins: [tailwindcss()], resolve: { alias: { '#css-alias': fileURLToPath(new URL('./src/alias.css', import.meta.url)), '#js-alias': fileURLToPath(new URL('./src/plugin.js', import.meta.url)), }, }, }) `, 'index.html': html` <head> <link rel="stylesheet" href="./src/index.css" /> </head> <body> <div class="underline custom-underline">Hello, world!</div> </body> `, 'src/index.css': css` @import '#css-alias'; @plugin '#js-alias'; `, 'src/alias.css': css` @reference 'tailwindcss/theme'; @import 'tailwindcss/utilities'; `, 'src/plugin.js': js` export default function ({ addUtilities }) { addUtilities({ '.custom-underline': { 'border-bottom': '1px solid green' } }) } `, }, }, async ({ spawn, expect }) => { let process = await spawn('pnpm vite dev') await process.onStdout((m) => m.includes('ready in')) 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 styles = await fetchStyles(url, '/index.html') expect(styles).toContain(candidate`underline`) expect(styles).toContain(candidate`custom-underline`) }) }, ) })