mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
Expose timing information in debug mode (#14553)
This PR exposes when using the the `DEBUG` environment variable. This follows the `DEBUG` conventions where: - `DEBUG=1` - `DEBUG=true` - `DEBUG=*` - `DEBUG=tailwindcss` Will enable the debug information, but when using: - `DEBUG=0` - `DEBUG=false` - `DEBUG=-tailwindcss` It will not. This currently only exposes some timings related to: 1. Scanning for candidates 2. Building the CSS 3. Optimizing the CSS We can implement a more advanced version of this where we also expose more fine grained information such as the files we scanned, the amount of candidates we found and so on. But I believe that this will be enough to start triaging performance related issues.
This commit is contained in:
parent
4f8ca556cf
commit
ab82efab7d
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Added
|
||||
|
||||
- Add support for prefixes ([#14501](https://github.com/tailwindlabs/tailwindcss/pull/14501))
|
||||
- Expose timing information in debug mode ([#14553](https://github.com/tailwindlabs/tailwindcss/pull/14553))
|
||||
- _Experimental_: Add template codemods for migrating `bg-gradient-*` utilities to `bg-linear-*` ([#14537](https://github.com/tailwindlabs/tailwindcss/pull/14537]))
|
||||
- _Experimental_: Migrate `@import "tailwindcss/tailwind.css"` to `@import "tailwindcss"` ([#14514](https://github.com/tailwindlabs/tailwindcss/pull/14514))
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import watcher from '@parcel/watcher'
|
||||
import { compile } from '@tailwindcss/node'
|
||||
import { compile, env } from '@tailwindcss/node'
|
||||
import { clearRequireCache } from '@tailwindcss/node/require-cache'
|
||||
import { Scanner, type ChangedContent } from '@tailwindcss/oxide'
|
||||
import { Features, transform } from 'lightningcss'
|
||||
@ -98,10 +98,12 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
|
||||
// Optimize the output
|
||||
if (args['--minify'] || args['--optimize']) {
|
||||
if (css !== previous.css) {
|
||||
env.DEBUG && console.time('[@tailwindcss/cli] Optimize CSS')
|
||||
let optimizedCss = optimizeCss(css, {
|
||||
file: args['--input'] ?? 'input.css',
|
||||
minify: args['--minify'] ?? false,
|
||||
})
|
||||
env.DEBUG && console.timeEnd('[@tailwindcss/cli] Optimize CSS')
|
||||
previous.css = css
|
||||
previous.optimizedCss = optimizedCss
|
||||
output = optimizedCss
|
||||
@ -111,11 +113,13 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
|
||||
}
|
||||
|
||||
// Write the output
|
||||
env.DEBUG && console.time('[@tailwindcss/cli] Write output')
|
||||
if (args['--output']) {
|
||||
await outputFile(args['--output'], output)
|
||||
} else {
|
||||
println(output)
|
||||
}
|
||||
env.DEBUG && console.timeEnd('[@tailwindcss/cli] Write output')
|
||||
}
|
||||
|
||||
let inputBasePath =
|
||||
@ -206,18 +210,24 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
|
||||
})
|
||||
|
||||
// Scan the directory for candidates
|
||||
env.DEBUG && console.time('[@tailwindcss/cli] Scan for candidates')
|
||||
let candidates = scanner.scan()
|
||||
env.DEBUG && console.timeEnd('[@tailwindcss/cli] Scan for candidates')
|
||||
|
||||
// Setup new watchers
|
||||
cleanupWatchers = await createWatchers(watchDirectories(base, scanner), handle)
|
||||
|
||||
// Re-compile the CSS
|
||||
env.DEBUG && console.time('[@tailwindcss/cli] Build CSS')
|
||||
compiledCss = compiler.build(candidates)
|
||||
env.DEBUG && console.timeEnd('[@tailwindcss/cli] Build CSS')
|
||||
}
|
||||
|
||||
// Scan changed files only for incremental rebuilds.
|
||||
else if (rebuildStrategy === 'incremental') {
|
||||
env.DEBUG && console.time('[@tailwindcss/cli] Scan for candidates')
|
||||
let newCandidates = scanner.scanFiles(changedFiles)
|
||||
env.DEBUG && console.timeEnd('[@tailwindcss/cli] Scan for candidates')
|
||||
|
||||
// No new candidates found which means we don't need to write to
|
||||
// disk, and can return early.
|
||||
@ -227,7 +237,9 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
|
||||
return
|
||||
}
|
||||
|
||||
env.DEBUG && console.time('[@tailwindcss/cli] Build CSS')
|
||||
compiledCss = compiler.build(newCandidates)
|
||||
env.DEBUG && console.timeEnd('[@tailwindcss/cli] Build CSS')
|
||||
}
|
||||
|
||||
await write(compiledCss, args)
|
||||
@ -257,7 +269,13 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
|
||||
process.stdin.resume()
|
||||
}
|
||||
|
||||
await write(compiler.build(scanner.scan()), args)
|
||||
env.DEBUG && console.time('[@tailwindcss/cli] Scan for candidates')
|
||||
let candidates = scanner.scan()
|
||||
env.DEBUG && console.timeEnd('[@tailwindcss/cli] Scan for candidates')
|
||||
env.DEBUG && console.time('[@tailwindcss/cli] Build CSS')
|
||||
let output = compiler.build(candidates)
|
||||
env.DEBUG && console.timeEnd('[@tailwindcss/cli] Build CSS')
|
||||
await write(output, args)
|
||||
|
||||
let end = process.hrtime.bigint()
|
||||
eprintln(header())
|
||||
|
||||
40
packages/@tailwindcss-node/src/env.ts
Normal file
40
packages/@tailwindcss-node/src/env.ts
Normal file
@ -0,0 +1,40 @@
|
||||
export const DEBUG = resolveDebug(process.env.DEBUG)
|
||||
|
||||
function resolveDebug(debug: typeof process.env.DEBUG) {
|
||||
if (debug === undefined) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Environment variables are strings, so convert to boolean
|
||||
if (debug === 'true' || debug === '1') {
|
||||
return true
|
||||
}
|
||||
|
||||
if (debug === 'false' || debug === '0') {
|
||||
return false
|
||||
}
|
||||
|
||||
// Keep the debug convention into account:
|
||||
// DEBUG=* -> This enables all debug modes
|
||||
// DEBUG=projectA,projectB,projectC -> This enables debug for projectA, projectB and projectC
|
||||
// DEBUG=projectA:* -> This enables all debug modes for projectA (if you have sub-types)
|
||||
// DEBUG=projectA,-projectB -> This enables debug for projectA and explicitly disables it for projectB
|
||||
|
||||
if (debug === '*') {
|
||||
return true
|
||||
}
|
||||
|
||||
let debuggers = debug.split(',').map((d) => d.split(':')[0])
|
||||
|
||||
// Ignoring tailwindcss
|
||||
if (debuggers.includes('-tailwindcss')) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Including tailwindcss
|
||||
if (debuggers.includes('tailwindcss')) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@ -1,7 +1,9 @@
|
||||
import * as Module from 'node:module'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import * as env from './env'
|
||||
export * from './compile'
|
||||
export * from './normalize-path'
|
||||
export { env }
|
||||
|
||||
// In Bun, ESM modules will also populate `require.cache`, so the module hook is
|
||||
// not necessary.
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import * as Module from 'node:module'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import * as env from './env'
|
||||
export * from './compile'
|
||||
export * from './normalize-path'
|
||||
export { env }
|
||||
|
||||
// In Bun, ESM modules will also populate `require.cache`, so the module hook is
|
||||
// not necessary.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { compile } from '@tailwindcss/node'
|
||||
import { compile, env } from '@tailwindcss/node'
|
||||
import { clearRequireCache } from '@tailwindcss/node/require-cache'
|
||||
import { Scanner } from '@tailwindcss/oxide'
|
||||
import fs from 'fs'
|
||||
@ -61,21 +61,26 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
|
||||
{
|
||||
postcssPlugin: 'tailwindcss',
|
||||
async OnceExit(root, { result }) {
|
||||
env.DEBUG && console.time('[@tailwindcss/postcss] Total time in @tailwindcss/postcss')
|
||||
let inputFile = result.opts.from ?? ''
|
||||
let context = cache.get(inputFile)
|
||||
let inputBasePath = path.dirname(path.resolve(inputFile))
|
||||
|
||||
async function createCompiler() {
|
||||
env.DEBUG && console.time('[@tailwindcss/postcss] Setup compiler')
|
||||
clearRequireCache(context.fullRebuildPaths)
|
||||
|
||||
context.fullRebuildPaths = []
|
||||
|
||||
return compile(root.toString(), {
|
||||
let compiler = compile(root.toString(), {
|
||||
base: inputBasePath,
|
||||
onDependency: (path) => {
|
||||
context.fullRebuildPaths.push(path)
|
||||
},
|
||||
})
|
||||
|
||||
env.DEBUG && console.timeEnd('[@tailwindcss/postcss] Setup compiler')
|
||||
return compiler
|
||||
}
|
||||
|
||||
// Setup the compiler if it doesn't exist yet. This way we can
|
||||
@ -126,7 +131,9 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
|
||||
sources: context.compiler.globs,
|
||||
})
|
||||
|
||||
env.DEBUG && console.time('[@tailwindcss/postcss] Scan for candidates')
|
||||
let candidates = scanner.scan()
|
||||
env.DEBUG && console.timeEnd('[@tailwindcss/postcss] Scan for candidates')
|
||||
|
||||
// Add all found files as direct dependencies
|
||||
for (let file of scanner.files) {
|
||||
@ -154,17 +161,26 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
|
||||
if (rebuildStrategy === 'full') {
|
||||
context.compiler = await createCompiler()
|
||||
}
|
||||
|
||||
env.DEBUG && console.time('[@tailwindcss/postcss] Build CSS')
|
||||
css = context.compiler.build(candidates)
|
||||
env.DEBUG && console.timeEnd('[@tailwindcss/postcss] Build CSS')
|
||||
|
||||
// Replace CSS
|
||||
if (css !== context.css && optimize) {
|
||||
env.DEBUG && console.time('[@tailwindcss/postcss] Optimize CSS')
|
||||
context.optimizedCss = optimizeCss(css, {
|
||||
minify: typeof optimize === 'object' ? optimize.minify : true,
|
||||
})
|
||||
env.DEBUG && console.timeEnd('[@tailwindcss/postcss] Optimize CSS')
|
||||
}
|
||||
context.css = css
|
||||
|
||||
env.DEBUG && console.time('[@tailwindcss/postcss] Update PostCSS AST')
|
||||
root.removeAll()
|
||||
root.append(postcss.parse(optimize ? context.optimizedCss : context.css, result.opts))
|
||||
env.DEBUG && console.timeEnd('[@tailwindcss/postcss] Update PostCSS AST')
|
||||
env.DEBUG && console.timeEnd('[@tailwindcss/postcss] Total time in @tailwindcss/postcss')
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { compile, normalizePath } from '@tailwindcss/node'
|
||||
import { compile, env, normalizePath } from '@tailwindcss/node'
|
||||
import { clearRequireCache } from '@tailwindcss/node/require-cache'
|
||||
|
||||
import { Scanner } from '@tailwindcss/oxide'
|
||||
import { Features, transform } from 'lightningcss'
|
||||
import path from 'path'
|
||||
@ -92,7 +91,10 @@ export default function tailwindcss(): Plugin[] {
|
||||
if (generated === false) {
|
||||
return
|
||||
}
|
||||
return optimizeCss(generated, { minify })
|
||||
env.DEBUG && console.time('[@tailwindcss/vite] Optimize CSS')
|
||||
let result = optimizeCss(generated, { minify })
|
||||
env.DEBUG && console.timeEnd('[@tailwindcss/vite] Optimize CSS')
|
||||
return result
|
||||
}
|
||||
|
||||
// Manually run the transform functions of non-Tailwind plugins on the given CSS
|
||||
@ -378,9 +380,11 @@ class Root {
|
||||
// This should not be here, but right now the Vite plugin is setup where we
|
||||
// setup a new scanner and compiler every time we request the CSS file
|
||||
// (regardless whether it actually changed or not).
|
||||
env.DEBUG && console.time('[@tailwindcss/vite] Scan for candidates')
|
||||
for (let candidate of this.scanner.scan()) {
|
||||
this.candidates.add(candidate)
|
||||
}
|
||||
env.DEBUG && console.timeEnd('[@tailwindcss/vite] Scan for candidates')
|
||||
|
||||
// Watch individual files found via custom `@source` paths
|
||||
for (let file of this.scanner.files) {
|
||||
@ -404,6 +408,10 @@ class Root {
|
||||
|
||||
this.requiresRebuild = true
|
||||
|
||||
return this.compiler.build([...this.getSharedCandidates(), ...this.candidates])
|
||||
env.DEBUG && console.time('[@tailwindcss/vite] Build CSS')
|
||||
let result = this.compiler.build([...this.getSharedCandidates(), ...this.candidates])
|
||||
env.DEBUG && console.timeEnd('[@tailwindcss/vite] Build CSS')
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user