diff --git a/integrations/vite/other-transforms.test.ts b/integrations/vite/other-transforms.test.ts
new file mode 100644
index 000000000000..f897b4171358
--- /dev/null
+++ b/integrations/vite/other-transforms.test.ts
@@ -0,0 +1,153 @@
+import { describe, expect } from 'vitest'
+import { css, fetchStyles, html, retryAssertion, test, ts, txt } from '../utils'
+
+function createSetup(transformer: 'postcss' | 'lightningcss') {
+ return {
+ fs: {
+ 'package.json': txt`
+ {
+ "type": "module",
+ "dependencies": {
+ "@tailwindcss/vite": "workspace:^",
+ "tailwindcss": "workspace:^"
+ },
+ "devDependencies": {
+ ${transformer === 'lightningcss' ? `"lightningcss": "^1.26.0",` : ''}
+ "vite": "^5.3.5"
+ }
+ }
+ `,
+ 'vite.config.ts': ts`
+ import tailwindcss from '@tailwindcss/vite'
+ import { defineConfig } from 'vite'
+
+ export default defineConfig({
+ css: ${transformer === 'postcss' ? '{}' : "{ transformer: 'lightningcss' }"},
+ build: { cssMinify: false },
+ plugins: [
+ tailwindcss(),
+ {
+ name: 'recolor',
+ transform(code, id) {
+ if (id.includes('.css')) {
+ return code.replace(/red/g, 'blue')
+ }
+ },
+ },
+ ],
+ })
+ `,
+ 'index.html': html`
+
+
+
+
+ Hello, world!
+
+ `,
+ 'src/index.css': css`
+ @import 'tailwindcss/theme' theme(reference);
+ @import 'tailwindcss/utilities';
+
+ .foo {
+ color: red;
+ }
+ `,
+ },
+ }
+}
+
+for (let transformer of ['postcss', 'lightningcss'] as const) {
+ describe(transformer, () => {
+ test(`production build`, createSetup(transformer), async ({ fs, exec }) => {
+ await exec('pnpm vite build')
+
+ let files = await fs.glob('dist/**/*.css')
+ expect(files).toHaveLength(1)
+ let [filename] = files[0]
+
+ await fs.expectFileToContain(filename, [
+ css`
+ .foo {
+ color: blue;
+ }
+ `,
+ ])
+ })
+
+ test(`dev mode`, createSetup(transformer), async ({ spawn, getFreePort, fs }) => {
+ let port = await getFreePort()
+ await spawn(`pnpm vite dev --port ${port}`)
+
+ await retryAssertion(async () => {
+ let styles = await fetchStyles(port, '/index.html')
+ expect(styles).toContain(css`
+ .foo {
+ color: blue;
+ }
+ `)
+ })
+
+ await retryAssertion(async () => {
+ await fs.write(
+ 'src/index.css',
+ css`
+ @import 'tailwindcss/theme' theme(reference);
+ @import 'tailwindcss/utilities';
+
+ .foo {
+ background-color: red;
+ }
+ `,
+ )
+
+ let styles = await fetchStyles(port)
+ expect(styles).toContain(css`
+ .foo {
+ background-color: blue;
+ }
+ `)
+ })
+ })
+
+ test('watch mode', createSetup(transformer), async ({ spawn, fs }) => {
+ await spawn(`pnpm vite build --watch`)
+
+ await retryAssertion(async () => {
+ let files = await fs.glob('dist/**/*.css')
+ expect(files).toHaveLength(1)
+ let [, styles] = files[0]
+
+ expect(styles).toContain(css`
+ .foo {
+ color: blue;
+ }
+ `)
+ })
+
+ await retryAssertion(async () => {
+ await fs.write(
+ 'src/index.css',
+ css`
+ @import 'tailwindcss/theme' theme(reference);
+ @import 'tailwindcss/utilities';
+
+ .foo {
+ background-color: red;
+ }
+ `,
+ )
+
+ let files = await fs.glob('dist/**/*.css')
+ expect(files).toHaveLength(1)
+ let [, styles] = files[0]
+
+ expect(styles).toContain(css`
+ .foo {
+ background-color: blue;
+ }
+ `)
+ })
+ })
+ })
+}
diff --git a/packages/@tailwindcss-vite/src/index.ts b/packages/@tailwindcss-vite/src/index.ts
index 6442288912a5..d61071c6cede 100644
--- a/packages/@tailwindcss-vite/src/index.ts
+++ b/packages/@tailwindcss-vite/src/index.ts
@@ -15,11 +15,6 @@ export default function tailwindcss(): Plugin[] {
let isSSR = false
let minify = false
- // A list of css plugins defined in the Vite config. We need to retain these
- // so that we can rerun the right transformations in build mode where we have
- // to manually rebuild the css file after the compilation is done.
- let cssPlugins: readonly Plugin[] = []
-
// The Vite extension has two types of sources for candidates:
//
// 1. The module graph: These are all modules that vite transforms and we want
@@ -109,8 +104,26 @@ export default function tailwindcss(): Plugin[] {
},
}
- for (let plugin of cssPlugins) {
+ for (let plugin of config!.plugins) {
if (!plugin.transform) continue
+
+ if (plugin.name.startsWith('@tailwindcss/')) {
+ // We do not run any Tailwind transforms anymore
+ continue
+ } else if (
+ plugin.name.startsWith('vite:') &&
+ // Apply the vite:css plugin to generated CSS for transformations like
+ // URL path rewriting and image inlining.
+ plugin.name !== 'vite:css' &&
+ // In build mode, since `renderStart` runs after all transformations, we
+ // need to also apply vite:css-post.
+ plugin.name !== 'vite:css-post' &&
+ // The vite:vue plugin handles CSS specific post-processing for Vue
+ plugin.name !== 'vite:vue'
+ ) {
+ continue
+ }
+
let transformHandler =
'handler' in plugin.transform! ? plugin.transform.handler : plugin.transform!
@@ -147,20 +160,6 @@ export default function tailwindcss(): Plugin[] {
config = _config
minify = config.build.cssMinify !== false
isSSR = config.build.ssr !== false && config.build.ssr !== undefined
-
- let allowedPlugins = [
- // Apply the vite:css plugin to generated CSS for transformations like
- // URL path rewriting and image inlining.
- 'vite:css',
-
- // In build mode, since renderChunk runs after all transformations, we
- // need to also apply vite:css-post.
- ...(config.command === 'build' ? ['vite:css-post'] : []),
- ]
-
- cssPlugins = config.plugins.filter((plugin) => {
- return allowedPlugins.includes(plugin.name)
- })
},
// Scan all non-CSS files for candidates
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1fccffb9d90d..dd06432003d2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1087,7 +1087,6 @@ packages:
'@parcel/watcher-darwin-arm64@2.4.2-alpha.0':
resolution: {integrity: sha512-2xH4Ve7OKjIh+4YRfTN3HGJa2W8KTPLOALHZj5fxcbTPwaVxdpIRItDrcikUx2u3AzGAFme7F+AZZXHnf0F15Q==}
engines: {node: '>= 10.0.0'}
- cpu: [arm64]
os: [darwin]
'@parcel/watcher-darwin-x64@2.4.1':
@@ -1099,7 +1098,6 @@ packages:
'@parcel/watcher-darwin-x64@2.4.2-alpha.0':
resolution: {integrity: sha512-xtjmXUH4YZVah5+7Q0nb+fpRP5qZn9cFfuPuZ4k77UfUGVwhacgZyIRQgIOwMP3GkgW4TsrKQaw1KIe7L1ZqcQ==}
engines: {node: '>= 10.0.0'}
- cpu: [x64]
os: [darwin]
'@parcel/watcher-freebsd-x64@2.4.1':
@@ -1123,7 +1121,6 @@ packages:
'@parcel/watcher-linux-arm64-glibc@2.4.2-alpha.0':
resolution: {integrity: sha512-vIIOcZf+fgsRReIK3Fw0WINvGo9UwiXfisnqYRzfpNByRZvkEPkGTIVe8iiDp72NhPTVmwIvBqM6yKDzIaw8GQ==}
engines: {node: '>= 10.0.0'}
- cpu: [arm64]
os: [linux]
'@parcel/watcher-linux-arm64-musl@2.4.1':
@@ -1135,7 +1132,6 @@ packages:
'@parcel/watcher-linux-arm64-musl@2.4.2-alpha.0':
resolution: {integrity: sha512-gXqEAoLG9bBCbQNUgqjSOxHcjpmCZmYT9M8UvrdTMgMYgXgiWcR8igKlPRd40mCIRZSkMpN2ScSy2WjQ0bQZnQ==}
engines: {node: '>= 10.0.0'}
- cpu: [arm64]
os: [linux]
'@parcel/watcher-linux-x64-glibc@2.4.1':
@@ -1147,7 +1143,6 @@ packages:
'@parcel/watcher-linux-x64-glibc@2.4.2-alpha.0':
resolution: {integrity: sha512-/WJJ3Y46ubwQW+Z+mzpzK3pvqn/AT7MA63NB0+k9GTLNxJQZNREensMtpJ/FJ+LVIiraEHTY22KQrsx9+DeNbw==}
engines: {node: '>= 10.0.0'}
- cpu: [x64]
os: [linux]
'@parcel/watcher-linux-x64-musl@2.4.1':
@@ -1159,7 +1154,6 @@ packages:
'@parcel/watcher-linux-x64-musl@2.4.2-alpha.0':
resolution: {integrity: sha512-1dz4fTM5HaANk3RSRmdhALT+bNqTHawVDL1D77HwV/FuF/kSjlM3rGrJuFaCKwQ5E8CInHCcobqMN8Jh8LYaRg==}
engines: {node: '>= 10.0.0'}
- cpu: [x64]
os: [linux]
'@parcel/watcher-win32-arm64@2.4.1':
@@ -1183,7 +1177,6 @@ packages:
'@parcel/watcher-win32-x64@2.4.2-alpha.0':
resolution: {integrity: sha512-U2abMKF7JUiIxQkos19AvTLFcnl2Xn8yIW1kzu+7B0Lux4Gkuu/BUDBroaM1s6+hwgK63NOLq9itX2Y3GwUThg==}
engines: {node: '>= 10.0.0'}
- cpu: [x64]
os: [win32]
'@parcel/watcher@2.4.1':
@@ -1615,7 +1608,6 @@ packages:
bun@1.1.29:
resolution: {integrity: sha512-SKhpyKNZtgxrVel9ec9xon3LDv8mgpiuFhARgcJo1YIbggY2PBrKHRNiwQ6Qlb+x3ivmRurfuwWgwGexjpgBRg==}
- cpu: [arm64, x64]
os: [darwin, linux, win32]
hasBin: true
@@ -2505,13 +2497,11 @@ packages:
lightningcss-darwin-arm64@1.26.0:
resolution: {integrity: sha512-n4TIvHO1NY1ondKFYpL2ZX0bcC2y6yjXMD6JfyizgR8BCFNEeArINDzEaeqlfX9bXz73Bpz/Ow0nu+1qiDrBKg==}
engines: {node: '>= 12.0.0'}
- cpu: [arm64]
os: [darwin]
lightningcss-darwin-x64@1.26.0:
resolution: {integrity: sha512-Rf9HuHIDi1R6/zgBkJh25SiJHF+dm9axUZW/0UoYCW1/8HV0gMI0blARhH4z+REmWiU1yYT/KyNF3h7tHyRXUg==}
engines: {node: '>= 12.0.0'}
- cpu: [x64]
os: [darwin]
lightningcss-freebsd-x64@1.26.0:
@@ -2529,25 +2519,21 @@ packages:
lightningcss-linux-arm64-gnu@1.26.0:
resolution: {integrity: sha512-iJmZM7fUyVjH+POtdiCtExG+67TtPUTer7K/5A8DIfmPfrmeGvzfRyBltGhQz13Wi15K1lf2cPYoRaRh6vcwNA==}
engines: {node: '>= 12.0.0'}
- cpu: [arm64]
os: [linux]
lightningcss-linux-arm64-musl@1.26.0:
resolution: {integrity: sha512-XxoEL++tTkyuvu+wq/QS8bwyTXZv2y5XYCMcWL45b8XwkiS8eEEEej9BkMGSRwxa5J4K+LDeIhLrS23CpQyfig==}
engines: {node: '>= 12.0.0'}
- cpu: [arm64]
os: [linux]
lightningcss-linux-x64-gnu@1.26.0:
resolution: {integrity: sha512-1dkTfZQAYLj8MUSkd6L/+TWTG8V6Kfrzfa0T1fSlXCXQHrt1HC1/UepXHtKHDt/9yFwyoeayivxXAsApVxn6zA==}
engines: {node: '>= 12.0.0'}
- cpu: [x64]
os: [linux]
lightningcss-linux-x64-musl@1.26.0:
resolution: {integrity: sha512-yX3Rk9m00JGCUzuUhFEojY+jf/6zHs3XU8S8Vk+FRbnr4St7cjyMXdNjuA2LjiT8e7j8xHRCH8hyZ4H/btRE4A==}
engines: {node: '>= 12.0.0'}
- cpu: [x64]
os: [linux]
lightningcss-win32-arm64-msvc@1.26.0:
@@ -2559,7 +2545,6 @@ packages:
lightningcss-win32-x64-msvc@1.26.0:
resolution: {integrity: sha512-pYS3EyGP3JRhfqEFYmfFDiZ9/pVNfy8jVIYtrx9TVNusVyDK3gpW1w/rbvroQ4bDJi7grdUtyrYU6V2xkY/bBw==}
engines: {node: '>= 12.0.0'}
- cpu: [x64]
os: [win32]
lightningcss@1.26.0:
@@ -4952,7 +4937,7 @@ snapshots:
debug: 4.3.7
enhanced-resolve: 5.17.1
eslint: 9.11.1(jiti@2.3.3)
- eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@9.11.1(jiti@2.3.3))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.11.1(jiti@2.3.3))
+ eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@9.11.1(jiti@2.3.3))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@9.11.1(jiti@2.3.3))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@9.11.1(jiti@2.3.3)))(eslint@9.11.1(jiti@2.3.3)))(eslint@9.11.1(jiti@2.3.3))
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@9.11.1(jiti@2.3.3))(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@9.11.1(jiti@2.3.3))
fast-glob: 3.3.2
get-tsconfig: 4.7.6
@@ -4970,7 +4955,7 @@ snapshots:
debug: 4.3.7
enhanced-resolve: 5.17.1
eslint: 9.13.0(jiti@2.3.3)
- eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0(jiti@2.3.3))
+ eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.13.0(jiti@2.3.3)))(eslint@9.13.0(jiti@2.3.3))
fast-glob: 3.3.2
get-tsconfig: 4.8.1
is-bun-module: 1.2.1
@@ -4983,7 +4968,7 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
- eslint-module-utils@2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0(jiti@2.3.3)):
+ eslint-module-utils@2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.13.0(jiti@2.3.3)))(eslint@9.13.0(jiti@2.3.3)):
dependencies:
debug: 3.2.7
optionalDependencies:
@@ -4994,7 +4979,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@9.11.1(jiti@2.3.3))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.11.1(jiti@2.3.3)):
+ eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@9.11.1(jiti@2.3.3))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@9.11.1(jiti@2.3.3))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@9.11.1(jiti@2.3.3)))(eslint@9.11.1(jiti@2.3.3)))(eslint@9.11.1(jiti@2.3.3)):
dependencies:
debug: 3.2.7
optionalDependencies:
@@ -5015,7 +5000,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.11.1(jiti@2.3.3)
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@9.11.1(jiti@2.3.3))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.11.1(jiti@2.3.3))
+ eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@9.11.1(jiti@2.3.3))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@9.11.1(jiti@2.3.3))(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@9.11.1(jiti@2.3.3)))(eslint@9.11.1(jiti@2.3.3)))(eslint@9.11.1(jiti@2.3.3))
hasown: 2.0.2
is-core-module: 2.15.0
is-glob: 4.0.3
@@ -5043,7 +5028,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.13.0(jiti@2.3.3)
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.13.0(jiti@2.3.3))
+ eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.11.0(eslint@9.13.0(jiti@2.3.3))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.13.0(jiti@2.3.3)))(eslint@9.13.0(jiti@2.3.3))
hasown: 2.0.2
is-core-module: 2.15.1
is-glob: 4.0.3