From c550a62be4838f4aef1f7a3354ee9ec1db3648cf Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 8 Mar 2024 18:36:07 +0100 Subject: [PATCH] [v4] Make CSS optimization and minification configurable (#13130) * only run Lightning CSS when passing `--minify` to the CLI * only optimize the CSS when creating a production build * add `optimize` option to PostCSS plugin - The optimize option can be set to `true`, which will optimize (unnesting, adding browser prefixes, lowering values) and minify - The optimize option can also be set to `{ minify: false }`, which will optimize but not minify. * default `optimize` option to the true for `NODE_ENV=production` * add `--optimize` flag to CLI This will only optimize the CSS output without minification. * update `--minify` description * update changelog --- CHANGELOG.md | 1 + .../src/commands/build/index.ts | 32 +++++++++++++------ .../@tailwindcss-postcss/src/index.test.ts | 14 +++++--- packages/@tailwindcss-postcss/src/index.ts | 12 ++++++- packages/@tailwindcss-vite/src/index.ts | 8 +++-- 5 files changed, 51 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a0e54c0ce80..21fa52d30eec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add Android builds ([#13115](https://github.com/tailwindlabs/tailwindcss/pull/13115)) +- Make CSS optimization and minification configurable ([#13130](https://github.com/tailwindlabs/tailwindcss/pull/13130)) ## [4.0.0-alpha.6] - 2024-03-07 diff --git a/packages/@tailwindcss-cli/src/commands/build/index.ts b/packages/@tailwindcss-cli/src/commands/build/index.ts index baf980892346..f3627ff9144e 100644 --- a/packages/@tailwindcss-cli/src/commands/build/index.ts +++ b/packages/@tailwindcss-cli/src/commands/build/index.ts @@ -46,9 +46,13 @@ export function options() { }, '--minify': { type: 'boolean', - description: 'Minify the output', + description: 'Optimize and minify the output', alias: '-m', }, + '--optimize': { + type: 'boolean', + description: 'Optimize the output without minifying', + }, '--cwd': { type: 'string', description: 'The current working directory', @@ -96,10 +100,15 @@ export async function handle(args: Result>) { ) // Compile the input - let result = optimizeCss(compile(input, candidates), { - file: args['--input'] ?? 'input.css', - minify: args['--minify'], - }) + let result = compile(input, candidates) + + // Optimize the output + if (args['--minify'] || args['--optimize']) { + result = optimizeCss(result, { + file: args['--input'] ?? 'input.css', + minify: args['--minify'] ?? false, + }) + } // Write the output if (args['--output']) { @@ -184,10 +193,15 @@ export async function handle(args: Result>) { } // Compile the input - let result = optimizeCss(compile(input, candidates), { - file: args['--input'] ?? 'input.css', - minify: args['--minify'], - }) + let result = compile(input, candidates) + + // Optimize the output + if (args['--minify'] || args['--optimize']) { + result = optimizeCss(result, { + file: args['--input'] ?? 'input.css', + minify: args['--minify'] ?? false, + }) + } // Write the output if (args['--output']) { diff --git a/packages/@tailwindcss-postcss/src/index.test.ts b/packages/@tailwindcss-postcss/src/index.test.ts index 799b96dac6f8..41bb8fddbd4e 100644 --- a/packages/@tailwindcss-postcss/src/index.test.ts +++ b/packages/@tailwindcss-postcss/src/index.test.ts @@ -18,7 +18,9 @@ beforeEach(async () => { }) test("`@import 'tailwindcss'` is replaced with the generated CSS", async () => { - let processor = postcss([tailwindcss({ base: `${__dirname}/fixtures/example-project` })]) + let processor = postcss([ + tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }), + ]) let result = await processor.process(`@import 'tailwindcss'`, { from: INPUT_CSS_PATH }) @@ -47,7 +49,9 @@ test("`@import 'tailwindcss'` is replaced with the generated CSS", async () => { }) test('output is optimized by Lightning CSS', async () => { - let processor = postcss([tailwindcss({ base: `${__dirname}/fixtures/example-project` })]) + let processor = postcss([ + tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }), + ]) // `@apply` is used because Lightning is skipped if neither `@tailwind` nor // `@apply` is used. @@ -82,7 +86,9 @@ test('output is optimized by Lightning CSS', async () => { }) test('@apply can be used without emitting the theme in the CSS file', async () => { - let processor = postcss([tailwindcss({ base: `${__dirname}/fixtures/example-project` })]) + let processor = postcss([ + tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }), + ]) // `@apply` is used because Lightning is skipped if neither `@tailwind` nor // `@apply` is used. @@ -112,7 +118,7 @@ describe('processing without specifying a base path', () => { afterEach(() => unlink(filepath)) test('the current working directory is used by default', async () => { - let processor = postcss([tailwindcss()]) + let processor = postcss([tailwindcss({ optimize: { minify: false } })]) let result = await processor.process(`@import "tailwindcss"`, { from: INPUT_CSS_PATH }) diff --git a/packages/@tailwindcss-postcss/src/index.ts b/packages/@tailwindcss-postcss/src/index.ts index 95f2f940a204..adab5cc565de 100644 --- a/packages/@tailwindcss-postcss/src/index.ts +++ b/packages/@tailwindcss-postcss/src/index.ts @@ -6,10 +6,14 @@ import { compile, optimizeCss } from 'tailwindcss' type PluginOptions = { // The base directory to scan for class candidates. base?: string + + // Optimize the output CSS. + optimize?: boolean | { minify?: boolean } } function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin { let base = opts.base ?? process.cwd() + let optimize = opts.optimize ?? process.env.NODE_ENV === 'production' return { postcssPlugin: 'tailwindcss-v4', @@ -38,7 +42,13 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin { function replaceCss(css: string) { root.removeAll() - root.append(postcss.parse(optimizeCss(css), result.opts)) + let output = css + if (optimize) { + output = optimizeCss(output, { + minify: typeof optimize === 'object' ? optimize.minify : false, + }) + } + root.append(postcss.parse(output, result.opts)) } // No `@tailwind` means we don't have to look for candidates diff --git a/packages/@tailwindcss-vite/src/index.ts b/packages/@tailwindcss-vite/src/index.ts index 166b39a52245..a4f66530c9f5 100644 --- a/packages/@tailwindcss-vite/src/index.ts +++ b/packages/@tailwindcss-vite/src/index.ts @@ -61,7 +61,11 @@ export default function tailwindcss(): Plugin[] { } function generateCss(css: string) { - return optimizeCss(compile(css, Array.from(candidates)), { minify }) + return compile(css, Array.from(candidates)) + } + + function generateOptimizedCss(css: string) { + return optimizeCss(generateCss(css), { minify }) } // In dev mode, there isn't a hook to signal that we've seen all files. We use @@ -168,7 +172,7 @@ export default function tailwindcss(): Plugin[] { rawSource instanceof Uint8Array ? new TextDecoder().decode(rawSource) : rawSource if (source.includes('@tailwind')) { - item.source = generateCss(source) + item.source = generateOptimizedCss(source) } } },