tailwindcss/packages/@tailwindcss-webpack/src/index.ts

288 lines
8.6 KiB
TypeScript
Raw Normal View History

Add @tailwindcss/webpack loader for Tailwind CSS v4 (#19610) ## Summary This PR adds a new `@tailwindcss/webpack` package that provides a dedicated webpack loader for Tailwind CSS v4. This loader works with both standard webpack and Turbopack's webpack loader compatibility layer. ### Why a dedicated loader? The current webpack integration uses `postcss-loader` + `@tailwindcss/postcss`. While this works, a dedicated loader: - **Eliminates PostCSS as a middleman** - works directly with CSS strings (no AST conversions) - **Simpler and more efficient** - follows the same pattern as `@tailwindcss/vite` - **Better for Turbopack** - gives direct control over dependency reporting via webpack's loader API ### How it works The loader mirrors the Vite plugin's approach: 1. Uses `compile()` from `@tailwindcss/node` to parse CSS and resolve `@apply` directives 2. Uses `Scanner` from `@tailwindcss/oxide` to scan content files for utility candidates 3. Reports dependencies via `this.addDependency()` and `this.addContextDependency()` 4. Optionally optimizes output with Lightning CSS ### Usage ```javascript // webpack.config.js module.exports = { module: { rules: [ { test: /.css$/i, use: [ MiniCssExtractPlugin.loader, 'css-loader', '@tailwindcss/webpack', // No PostCSS needed! ], }, ], }, } ``` ### Options - `base` - The base directory to scan for class candidates (defaults to `process.cwd()`) - `optimize` - Whether to optimize/minify the output CSS (defaults to `true` in production) ### Files added - `packages/@tailwindcss-webpack/` - New package - `src/index.ts` - Main loader implementation - `src/index.cts` - CommonJS entry point for webpack compatibility - `package.json`, `tsconfig.json`, `tsup.config.ts`, `README.md` - `integrations/webpack/loader.test.ts` - Integration tests - `integrations/utils.ts` - Added webpack override for transitive dependencies ### Test plan - [x] Build test - verifies basic compilation - [x] Watch test - verifies HMR when adding new Tailwind classes - [x] `@apply` test - verifies `@apply` directives work correctly - [x] Optimization test - verifies minification works --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-01-29 15:16:31 +01:00
import QuickLRU from '@alloc/quick-lru'
import {
compile,
env,
Features,
Instrumentation,
normalizePath,
optimize,
Polyfills,
} from '@tailwindcss/node'
import { clearRequireCache } from '@tailwindcss/node/require-cache'
import { Scanner } from '@tailwindcss/oxide'
import fs from 'node:fs'
import path from 'node:path'
import type { LoaderContext } from 'webpack'
const DEBUG = env.DEBUG
export interface LoaderOptions {
/**
* The base directory to scan for class candidates.
*
* Defaults to the current working directory.
*/
base?: string
/**
* Optimize and minify the output CSS.
*/
optimize?: boolean | { minify?: boolean }
}
interface CacheEntry {
mtimes: Map<string, number>
compiler: null | Awaited<ReturnType<typeof compile>>
scanner: null | Scanner
candidates: Set<string>
fullRebuildPaths: string[]
}
const cache = new QuickLRU<string, CacheEntry>({ maxSize: 50 })
Fix webpack loader cache key for resource queries (#19723) <!-- 👋 Hey, thanks for your interest in contributing to Tailwind! **Please ask first before starting work on any significant new features.** It's never a fun experience to have your pull request declined after investing a lot of time and effort into a new feature. To avoid this from happening, we request that contributors create a discussion to first discuss any significant new features. For more info, check out the contributing guide: https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md --> ## Summary <!-- Provide a summary of the issue and the changes you're making. How does your change solve the problem? --> `@tailwindcss/webpack` currently uses `this.resourcePath` as the cache key, which ignores the resource query. When the same CSS file is imported multiple times with different `resourceQuery` values, all of those imports share a single `CacheEntry`. That means the utilities discovered for one entry can leak into the CSS output for another entry. This PR changes the cache key to use `this.resource` (path + query) instead, while still using `this.resourcePath` for all filesystem work. ## Test plan <!-- Explain how you tested your changes. Include the exact commands that you used to verify the change works and include screenshots/screen recordings of the update behavior in the browser if applicable. --> - `pnpm test:integrations -- webpack/loader.test.ts` - Confirms all existing webpack loader integration tests pass. - Confirms the new `@tailwindcss/webpack loader isolates cache by resource including query` test passes, verifying that two entries importing the same CSS file with different queries produce isolated outputs (`dist/a.css` only contains `only-a` / `--color-red-500`, and `dist/b.css` only contains `only-b` / `--color-blue-500`). --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-03-21 00:34:11 +08:00
function getCacheKey(resourceId: string, opts: LoaderOptions): string {
return `${resourceId}:${opts.base ?? ''}:${JSON.stringify(opts.optimize)}`
}
function getContextFromCache(resourceId: string, opts: LoaderOptions): CacheEntry {
let key = getCacheKey(resourceId, opts)
Add @tailwindcss/webpack loader for Tailwind CSS v4 (#19610) ## Summary This PR adds a new `@tailwindcss/webpack` package that provides a dedicated webpack loader for Tailwind CSS v4. This loader works with both standard webpack and Turbopack's webpack loader compatibility layer. ### Why a dedicated loader? The current webpack integration uses `postcss-loader` + `@tailwindcss/postcss`. While this works, a dedicated loader: - **Eliminates PostCSS as a middleman** - works directly with CSS strings (no AST conversions) - **Simpler and more efficient** - follows the same pattern as `@tailwindcss/vite` - **Better for Turbopack** - gives direct control over dependency reporting via webpack's loader API ### How it works The loader mirrors the Vite plugin's approach: 1. Uses `compile()` from `@tailwindcss/node` to parse CSS and resolve `@apply` directives 2. Uses `Scanner` from `@tailwindcss/oxide` to scan content files for utility candidates 3. Reports dependencies via `this.addDependency()` and `this.addContextDependency()` 4. Optionally optimizes output with Lightning CSS ### Usage ```javascript // webpack.config.js module.exports = { module: { rules: [ { test: /.css$/i, use: [ MiniCssExtractPlugin.loader, 'css-loader', '@tailwindcss/webpack', // No PostCSS needed! ], }, ], }, } ``` ### Options - `base` - The base directory to scan for class candidates (defaults to `process.cwd()`) - `optimize` - Whether to optimize/minify the output CSS (defaults to `true` in production) ### Files added - `packages/@tailwindcss-webpack/` - New package - `src/index.ts` - Main loader implementation - `src/index.cts` - CommonJS entry point for webpack compatibility - `package.json`, `tsconfig.json`, `tsup.config.ts`, `README.md` - `integrations/webpack/loader.test.ts` - Integration tests - `integrations/utils.ts` - Added webpack override for transitive dependencies ### Test plan - [x] Build test - verifies basic compilation - [x] Watch test - verifies HMR when adding new Tailwind classes - [x] `@apply` test - verifies `@apply` directives work correctly - [x] Optimization test - verifies minification works --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-01-29 15:16:31 +01:00
if (cache.has(key)) return cache.get(key)!
let entry: CacheEntry = {
mtimes: new Map<string, number>(),
compiler: null,
scanner: null,
candidates: new Set<string>(),
fullRebuildPaths: [],
}
cache.set(key, entry)
return entry
}
export default async function tailwindLoader(
this: LoaderContext<LoaderOptions>,
source: string,
): Promise<void> {
let callback = this.async()
let options = this.getOptions() ?? {}
let inputFile = this.resourcePath
Fix webpack loader cache key for resource queries (#19723) <!-- 👋 Hey, thanks for your interest in contributing to Tailwind! **Please ask first before starting work on any significant new features.** It's never a fun experience to have your pull request declined after investing a lot of time and effort into a new feature. To avoid this from happening, we request that contributors create a discussion to first discuss any significant new features. For more info, check out the contributing guide: https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md --> ## Summary <!-- Provide a summary of the issue and the changes you're making. How does your change solve the problem? --> `@tailwindcss/webpack` currently uses `this.resourcePath` as the cache key, which ignores the resource query. When the same CSS file is imported multiple times with different `resourceQuery` values, all of those imports share a single `CacheEntry`. That means the utilities discovered for one entry can leak into the CSS output for another entry. This PR changes the cache key to use `this.resource` (path + query) instead, while still using `this.resourcePath` for all filesystem work. ## Test plan <!-- Explain how you tested your changes. Include the exact commands that you used to verify the change works and include screenshots/screen recordings of the update behavior in the browser if applicable. --> - `pnpm test:integrations -- webpack/loader.test.ts` - Confirms all existing webpack loader integration tests pass. - Confirms the new `@tailwindcss/webpack loader isolates cache by resource including query` test passes, verifying that two entries importing the same CSS file with different queries produce isolated outputs (`dist/a.css` only contains `only-a` / `--color-red-500`, and `dist/b.css` only contains `only-b` / `--color-blue-500`). --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-03-21 00:34:11 +08:00
let resourceId = this.resource
Add @tailwindcss/webpack loader for Tailwind CSS v4 (#19610) ## Summary This PR adds a new `@tailwindcss/webpack` package that provides a dedicated webpack loader for Tailwind CSS v4. This loader works with both standard webpack and Turbopack's webpack loader compatibility layer. ### Why a dedicated loader? The current webpack integration uses `postcss-loader` + `@tailwindcss/postcss`. While this works, a dedicated loader: - **Eliminates PostCSS as a middleman** - works directly with CSS strings (no AST conversions) - **Simpler and more efficient** - follows the same pattern as `@tailwindcss/vite` - **Better for Turbopack** - gives direct control over dependency reporting via webpack's loader API ### How it works The loader mirrors the Vite plugin's approach: 1. Uses `compile()` from `@tailwindcss/node` to parse CSS and resolve `@apply` directives 2. Uses `Scanner` from `@tailwindcss/oxide` to scan content files for utility candidates 3. Reports dependencies via `this.addDependency()` and `this.addContextDependency()` 4. Optionally optimizes output with Lightning CSS ### Usage ```javascript // webpack.config.js module.exports = { module: { rules: [ { test: /.css$/i, use: [ MiniCssExtractPlugin.loader, 'css-loader', '@tailwindcss/webpack', // No PostCSS needed! ], }, ], }, } ``` ### Options - `base` - The base directory to scan for class candidates (defaults to `process.cwd()`) - `optimize` - Whether to optimize/minify the output CSS (defaults to `true` in production) ### Files added - `packages/@tailwindcss-webpack/` - New package - `src/index.ts` - Main loader implementation - `src/index.cts` - CommonJS entry point for webpack compatibility - `package.json`, `tsconfig.json`, `tsup.config.ts`, `README.md` - `integrations/webpack/loader.test.ts` - Integration tests - `integrations/utils.ts` - Added webpack override for transitive dependencies ### Test plan - [x] Build test - verifies basic compilation - [x] Watch test - verifies HMR when adding new Tailwind classes - [x] `@apply` test - verifies `@apply` directives work correctly - [x] Optimization test - verifies minification works --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-01-29 15:16:31 +01:00
let base = options.base ?? process.cwd()
let shouldOptimize = options.optimize ?? process.env.NODE_ENV === 'production'
let isCSSModuleFile = inputFile.endsWith('.module.css')
using I = new Instrumentation()
DEBUG && I.start(`[@tailwindcss/webpack] ${path.relative(base, inputFile)}`)
// Bail out early if this is guaranteed to be a non-Tailwind CSS file.
{
DEBUG && I.start('Quick bail check')
let canBail = !/@(import|reference|theme|variant|config|plugin|apply|tailwind)\b/.test(source)
if (canBail) {
DEBUG && I.end('Quick bail check')
DEBUG && I.end(`[@tailwindcss/webpack] ${path.relative(base, inputFile)}`)
callback(null, source)
return
}
DEBUG && I.end('Quick bail check')
}
try {
Fix webpack loader cache key for resource queries (#19723) <!-- 👋 Hey, thanks for your interest in contributing to Tailwind! **Please ask first before starting work on any significant new features.** It's never a fun experience to have your pull request declined after investing a lot of time and effort into a new feature. To avoid this from happening, we request that contributors create a discussion to first discuss any significant new features. For more info, check out the contributing guide: https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md --> ## Summary <!-- Provide a summary of the issue and the changes you're making. How does your change solve the problem? --> `@tailwindcss/webpack` currently uses `this.resourcePath` as the cache key, which ignores the resource query. When the same CSS file is imported multiple times with different `resourceQuery` values, all of those imports share a single `CacheEntry`. That means the utilities discovered for one entry can leak into the CSS output for another entry. This PR changes the cache key to use `this.resource` (path + query) instead, while still using `this.resourcePath` for all filesystem work. ## Test plan <!-- Explain how you tested your changes. Include the exact commands that you used to verify the change works and include screenshots/screen recordings of the update behavior in the browser if applicable. --> - `pnpm test:integrations -- webpack/loader.test.ts` - Confirms all existing webpack loader integration tests pass. - Confirms the new `@tailwindcss/webpack loader isolates cache by resource including query` test passes, verifying that two entries importing the same CSS file with different queries produce isolated outputs (`dist/a.css` only contains `only-a` / `--color-red-500`, and `dist/b.css` only contains `only-b` / `--color-blue-500`). --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-03-21 00:34:11 +08:00
let context = getContextFromCache(resourceId, options)
Add @tailwindcss/webpack loader for Tailwind CSS v4 (#19610) ## Summary This PR adds a new `@tailwindcss/webpack` package that provides a dedicated webpack loader for Tailwind CSS v4. This loader works with both standard webpack and Turbopack's webpack loader compatibility layer. ### Why a dedicated loader? The current webpack integration uses `postcss-loader` + `@tailwindcss/postcss`. While this works, a dedicated loader: - **Eliminates PostCSS as a middleman** - works directly with CSS strings (no AST conversions) - **Simpler and more efficient** - follows the same pattern as `@tailwindcss/vite` - **Better for Turbopack** - gives direct control over dependency reporting via webpack's loader API ### How it works The loader mirrors the Vite plugin's approach: 1. Uses `compile()` from `@tailwindcss/node` to parse CSS and resolve `@apply` directives 2. Uses `Scanner` from `@tailwindcss/oxide` to scan content files for utility candidates 3. Reports dependencies via `this.addDependency()` and `this.addContextDependency()` 4. Optionally optimizes output with Lightning CSS ### Usage ```javascript // webpack.config.js module.exports = { module: { rules: [ { test: /.css$/i, use: [ MiniCssExtractPlugin.loader, 'css-loader', '@tailwindcss/webpack', // No PostCSS needed! ], }, ], }, } ``` ### Options - `base` - The base directory to scan for class candidates (defaults to `process.cwd()`) - `optimize` - Whether to optimize/minify the output CSS (defaults to `true` in production) ### Files added - `packages/@tailwindcss-webpack/` - New package - `src/index.ts` - Main loader implementation - `src/index.cts` - CommonJS entry point for webpack compatibility - `package.json`, `tsconfig.json`, `tsup.config.ts`, `README.md` - `integrations/webpack/loader.test.ts` - Integration tests - `integrations/utils.ts` - Added webpack override for transitive dependencies ### Test plan - [x] Build test - verifies basic compilation - [x] Watch test - verifies HMR when adding new Tailwind classes - [x] `@apply` test - verifies `@apply` directives work correctly - [x] Optimization test - verifies minification works --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-01-29 15:16:31 +01:00
let inputBasePath = path.dirname(path.resolve(inputFile))
// Whether this is the first build or not
let isInitialBuild = context.compiler === null
async function createCompiler() {
DEBUG && I.start('Setup compiler')
if (context.fullRebuildPaths.length > 0 && !isInitialBuild) {
clearRequireCache(context.fullRebuildPaths)
}
context.fullRebuildPaths = []
DEBUG && I.start('Create compiler')
let compiler = await compile(source, {
from: inputFile,
base: inputBasePath,
shouldRewriteUrls: true,
onDependency: (depPath) => context.fullRebuildPaths.push(depPath),
// In CSS Module files, we have to disable the `@property` polyfill since these will
// emit global `*` rules which are considered to be non-pure and will cause builds
// to fail.
polyfills: isCSSModuleFile ? Polyfills.All ^ Polyfills.AtProperty : Polyfills.All,
})
DEBUG && I.end('Create compiler')
DEBUG && I.end('Setup compiler')
return compiler
}
// Setup the compiler if it doesn't exist yet
context.compiler ??= await createCompiler()
// Early exit if no Tailwind features are used
if (context.compiler.features === Features.None) {
DEBUG && I.end(`[@tailwindcss/webpack] ${path.relative(base, inputFile)}`)
callback(null, source)
return
}
let rebuildStrategy: 'full' | 'incremental' = 'incremental'
// Track file modification times to CSS files
DEBUG && I.start('Register full rebuild paths')
{
// Report dependencies for config files, plugins, etc.
for (let file of context.fullRebuildPaths) {
this.addDependency(path.resolve(file))
}
let files = [...context.fullRebuildPaths, inputFile]
for (let file of files) {
let changedTime: number | null = null
try {
changedTime = fs.statSync(file)?.mtimeMs ?? null
} catch {
// File might not exist
}
if (changedTime === null) {
if (file === inputFile) {
rebuildStrategy = 'full'
}
continue
}
let prevTime = context.mtimes.get(file)
if (prevTime === changedTime) continue
rebuildStrategy = 'full'
context.mtimes.set(file, changedTime)
}
}
DEBUG && I.end('Register full rebuild paths')
if (rebuildStrategy === 'full' && !isInitialBuild) {
context.compiler = await createCompiler()
}
let compiler = context.compiler
// Check if we need to process this file at all
if (
!(
compiler.features &
(Features.AtApply | Features.JsPluginCompat | Features.ThemeFunction | Features.Utilities)
)
) {
DEBUG && I.end(`[@tailwindcss/webpack] ${path.relative(base, inputFile)}`)
callback(null, source)
return
}
// Setup or update scanner if needed
if (context.scanner === null || rebuildStrategy === 'full') {
DEBUG && I.start('Setup scanner')
let sources = (() => {
// Disable auto source detection
if (compiler.root === 'none') {
return []
}
// No root specified, use the base directory
if (compiler.root === null) {
return [{ base, pattern: '**/*', negated: false }]
}
// Use the specified root
return [{ ...compiler.root, negated: false }]
})().concat(compiler.sources)
context.scanner = new Scanner({ sources })
DEBUG && I.end('Setup scanner')
}
// Scan for candidates if utilities are used
if (compiler.features & Features.Utilities) {
DEBUG && I.start('Scan for candidates')
for (let candidate of context.scanner.scan()) {
context.candidates.add(candidate)
}
DEBUG && I.end('Scan for candidates')
DEBUG && I.start('Register dependency messages')
// Add all found files as direct dependencies
let resolvedInputFile = path.resolve(base, inputFile)
for (let file of context.scanner.files) {
let absolutePath = path.resolve(file)
// The CSS file cannot be a dependency of itself
if (absolutePath === resolvedInputFile) {
continue
}
this.addDependency(absolutePath)
}
// Register context dependencies for glob patterns
for (let glob of context.scanner.globs) {
// Skip negated patterns
if (glob.pattern[0] === '!') continue
// Avoid adding a dependency on the base directory itself
if (glob.pattern === '*' && base === glob.base) {
continue
}
this.addContextDependency(path.resolve(glob.base))
}
// Validate that source(...) paths are directories
let root = compiler.root
if (root !== 'none' && root !== null) {
let basePath = normalizePath(path.resolve(root.base, root.pattern))
try {
let stats = fs.statSync(basePath)
if (!stats.isDirectory()) {
throw new Error(
`The path given to \`source(…)\` must be a directory but got \`source(${basePath})\` instead.`,
)
}
} catch (err) {
if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {
throw err
}
// Directory doesn't exist yet, which is fine
}
}
DEBUG && I.end('Register dependency messages')
}
DEBUG && I.start('Build utilities')
let css = compiler.build([...context.candidates])
DEBUG && I.end('Build utilities')
// Optionally optimize the output
let result = css
if (shouldOptimize) {
DEBUG && I.start('Optimization')
let optimized = optimize(css, {
minify: typeof shouldOptimize === 'object' ? shouldOptimize.minify : true,
})
result = optimized.code
DEBUG && I.end('Optimization')
}
DEBUG && I.end(`[@tailwindcss/webpack] ${path.relative(base, inputFile)}`)
callback(null, result)
} catch (error) {
// Clear the cache entry on error to force a full rebuild next time
Fix webpack loader cache key for resource queries (#19723) <!-- 👋 Hey, thanks for your interest in contributing to Tailwind! **Please ask first before starting work on any significant new features.** It's never a fun experience to have your pull request declined after investing a lot of time and effort into a new feature. To avoid this from happening, we request that contributors create a discussion to first discuss any significant new features. For more info, check out the contributing guide: https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md --> ## Summary <!-- Provide a summary of the issue and the changes you're making. How does your change solve the problem? --> `@tailwindcss/webpack` currently uses `this.resourcePath` as the cache key, which ignores the resource query. When the same CSS file is imported multiple times with different `resourceQuery` values, all of those imports share a single `CacheEntry`. That means the utilities discovered for one entry can leak into the CSS output for another entry. This PR changes the cache key to use `this.resource` (path + query) instead, while still using `this.resourcePath` for all filesystem work. ## Test plan <!-- Explain how you tested your changes. Include the exact commands that you used to verify the change works and include screenshots/screen recordings of the update behavior in the browser if applicable. --> - `pnpm test:integrations -- webpack/loader.test.ts` - Confirms all existing webpack loader integration tests pass. - Confirms the new `@tailwindcss/webpack loader isolates cache by resource including query` test passes, verifying that two entries importing the same CSS file with different queries produce isolated outputs (`dist/a.css` only contains `only-a` / `--color-red-500`, and `dist/b.css` only contains `only-b` / `--color-blue-500`). --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-03-21 00:34:11 +08:00
let key = getCacheKey(resourceId, options)
Add @tailwindcss/webpack loader for Tailwind CSS v4 (#19610) ## Summary This PR adds a new `@tailwindcss/webpack` package that provides a dedicated webpack loader for Tailwind CSS v4. This loader works with both standard webpack and Turbopack's webpack loader compatibility layer. ### Why a dedicated loader? The current webpack integration uses `postcss-loader` + `@tailwindcss/postcss`. While this works, a dedicated loader: - **Eliminates PostCSS as a middleman** - works directly with CSS strings (no AST conversions) - **Simpler and more efficient** - follows the same pattern as `@tailwindcss/vite` - **Better for Turbopack** - gives direct control over dependency reporting via webpack's loader API ### How it works The loader mirrors the Vite plugin's approach: 1. Uses `compile()` from `@tailwindcss/node` to parse CSS and resolve `@apply` directives 2. Uses `Scanner` from `@tailwindcss/oxide` to scan content files for utility candidates 3. Reports dependencies via `this.addDependency()` and `this.addContextDependency()` 4. Optionally optimizes output with Lightning CSS ### Usage ```javascript // webpack.config.js module.exports = { module: { rules: [ { test: /.css$/i, use: [ MiniCssExtractPlugin.loader, 'css-loader', '@tailwindcss/webpack', // No PostCSS needed! ], }, ], }, } ``` ### Options - `base` - The base directory to scan for class candidates (defaults to `process.cwd()`) - `optimize` - Whether to optimize/minify the output CSS (defaults to `true` in production) ### Files added - `packages/@tailwindcss-webpack/` - New package - `src/index.ts` - Main loader implementation - `src/index.cts` - CommonJS entry point for webpack compatibility - `package.json`, `tsconfig.json`, `tsup.config.ts`, `README.md` - `integrations/webpack/loader.test.ts` - Integration tests - `integrations/utils.ts` - Added webpack override for transitive dependencies ### Test plan - [x] Build test - verifies basic compilation - [x] Watch test - verifies HMR when adding new Tailwind classes - [x] `@apply` test - verifies `@apply` directives work correctly - [x] Optimization test - verifies minification works --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-01-29 15:16:31 +01:00
cache.delete(key)
DEBUG && I.end(`[@tailwindcss/webpack] ${path.relative(base, inputFile)}`)
callback(error as Error)
}
}