Skip to content

Commit ab82efa

Browse files
authored
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.
1 parent 4f8ca55 commit ab82efa

File tree

7 files changed

+95
-8
lines changed

7 files changed

+95
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- Add support for prefixes ([#14501](https://github.com/tailwindlabs/tailwindcss/pull/14501))
13+
- Expose timing information in debug mode ([#14553](https://github.com/tailwindlabs/tailwindcss/pull/14553))
1314
- _Experimental_: Add template codemods for migrating `bg-gradient-*` utilities to `bg-linear-*` ([#14537](https://github.com/tailwindlabs/tailwindcss/pull/14537]))
1415
- _Experimental_: Migrate `@import "tailwindcss/tailwind.css"` to `@import "tailwindcss"` ([#14514](https://github.com/tailwindlabs/tailwindcss/pull/14514))
1516

packages/@tailwindcss-cli/src/commands/build/index.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import watcher from '@parcel/watcher'
2-
import { compile } from '@tailwindcss/node'
2+
import { compile, env } from '@tailwindcss/node'
33
import { clearRequireCache } from '@tailwindcss/node/require-cache'
44
import { Scanner, type ChangedContent } from '@tailwindcss/oxide'
55
import { Features, transform } from 'lightningcss'
@@ -98,10 +98,12 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
9898
// Optimize the output
9999
if (args['--minify'] || args['--optimize']) {
100100
if (css !== previous.css) {
101+
env.DEBUG && console.time('[@tailwindcss/cli] Optimize CSS')
101102
let optimizedCss = optimizeCss(css, {
102103
file: args['--input'] ?? 'input.css',
103104
minify: args['--minify'] ?? false,
104105
})
106+
env.DEBUG && console.timeEnd('[@tailwindcss/cli] Optimize CSS')
105107
previous.css = css
106108
previous.optimizedCss = optimizedCss
107109
output = optimizedCss
@@ -111,11 +113,13 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
111113
}
112114

113115
// Write the output
116+
env.DEBUG && console.time('[@tailwindcss/cli] Write output')
114117
if (args['--output']) {
115118
await outputFile(args['--output'], output)
116119
} else {
117120
println(output)
118121
}
122+
env.DEBUG && console.timeEnd('[@tailwindcss/cli] Write output')
119123
}
120124

121125
let inputBasePath =
@@ -206,18 +210,24 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
206210
})
207211

208212
// Scan the directory for candidates
213+
env.DEBUG && console.time('[@tailwindcss/cli] Scan for candidates')
209214
let candidates = scanner.scan()
215+
env.DEBUG && console.timeEnd('[@tailwindcss/cli] Scan for candidates')
210216

211217
// Setup new watchers
212218
cleanupWatchers = await createWatchers(watchDirectories(base, scanner), handle)
213219

214220
// Re-compile the CSS
221+
env.DEBUG && console.time('[@tailwindcss/cli] Build CSS')
215222
compiledCss = compiler.build(candidates)
223+
env.DEBUG && console.timeEnd('[@tailwindcss/cli] Build CSS')
216224
}
217225

218226
// Scan changed files only for incremental rebuilds.
219227
else if (rebuildStrategy === 'incremental') {
228+
env.DEBUG && console.time('[@tailwindcss/cli] Scan for candidates')
220229
let newCandidates = scanner.scanFiles(changedFiles)
230+
env.DEBUG && console.timeEnd('[@tailwindcss/cli] Scan for candidates')
221231

222232
// No new candidates found which means we don't need to write to
223233
// disk, and can return early.
@@ -227,7 +237,9 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
227237
return
228238
}
229239

240+
env.DEBUG && console.time('[@tailwindcss/cli] Build CSS')
230241
compiledCss = compiler.build(newCandidates)
242+
env.DEBUG && console.timeEnd('[@tailwindcss/cli] Build CSS')
231243
}
232244

233245
await write(compiledCss, args)
@@ -257,7 +269,13 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
257269
process.stdin.resume()
258270
}
259271

260-
await write(compiler.build(scanner.scan()), args)
272+
env.DEBUG && console.time('[@tailwindcss/cli] Scan for candidates')
273+
let candidates = scanner.scan()
274+
env.DEBUG && console.timeEnd('[@tailwindcss/cli] Scan for candidates')
275+
env.DEBUG && console.time('[@tailwindcss/cli] Build CSS')
276+
let output = compiler.build(candidates)
277+
env.DEBUG && console.timeEnd('[@tailwindcss/cli] Build CSS')
278+
await write(output, args)
261279

262280
let end = process.hrtime.bigint()
263281
eprintln(header())

packages/@tailwindcss-node/src/env.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
export const DEBUG = resolveDebug(process.env.DEBUG)
2+
3+
function resolveDebug(debug: typeof process.env.DEBUG) {
4+
if (debug === undefined) {
5+
return false
6+
}
7+
8+
// Environment variables are strings, so convert to boolean
9+
if (debug === 'true' || debug === '1') {
10+
return true
11+
}
12+
13+
if (debug === 'false' || debug === '0') {
14+
return false
15+
}
16+
17+
// Keep the debug convention into account:
18+
// DEBUG=* -> This enables all debug modes
19+
// DEBUG=projectA,projectB,projectC -> This enables debug for projectA, projectB and projectC
20+
// DEBUG=projectA:* -> This enables all debug modes for projectA (if you have sub-types)
21+
// DEBUG=projectA,-projectB -> This enables debug for projectA and explicitly disables it for projectB
22+
23+
if (debug === '*') {
24+
return true
25+
}
26+
27+
let debuggers = debug.split(',').map((d) => d.split(':')[0])
28+
29+
// Ignoring tailwindcss
30+
if (debuggers.includes('-tailwindcss')) {
31+
return false
32+
}
33+
34+
// Including tailwindcss
35+
if (debuggers.includes('tailwindcss')) {
36+
return true
37+
}
38+
39+
return false
40+
}

packages/@tailwindcss-node/src/index.cts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import * as Module from 'node:module'
22
import { pathToFileURL } from 'node:url'
3+
import * as env from './env'
34
export * from './compile'
45
export * from './normalize-path'
6+
export { env }
57

68
// In Bun, ESM modules will also populate `require.cache`, so the module hook is
79
// not necessary.

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import * as Module from 'node:module'
22
import { pathToFileURL } from 'node:url'
3+
import * as env from './env'
34
export * from './compile'
45
export * from './normalize-path'
6+
export { env }
57

68
// In Bun, ESM modules will also populate `require.cache`, so the module hook is
79
// not necessary.

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { compile } from '@tailwindcss/node'
1+
import { compile, env } from '@tailwindcss/node'
22
import { clearRequireCache } from '@tailwindcss/node/require-cache'
33
import { Scanner } from '@tailwindcss/oxide'
44
import fs from 'fs'
@@ -61,21 +61,26 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
6161
{
6262
postcssPlugin: 'tailwindcss',
6363
async OnceExit(root, { result }) {
64+
env.DEBUG && console.time('[@tailwindcss/postcss] Total time in @tailwindcss/postcss')
6465
let inputFile = result.opts.from ?? ''
6566
let context = cache.get(inputFile)
6667
let inputBasePath = path.dirname(path.resolve(inputFile))
6768

6869
async function createCompiler() {
70+
env.DEBUG && console.time('[@tailwindcss/postcss] Setup compiler')
6971
clearRequireCache(context.fullRebuildPaths)
7072

7173
context.fullRebuildPaths = []
7274

73-
return compile(root.toString(), {
75+
let compiler = compile(root.toString(), {
7476
base: inputBasePath,
7577
onDependency: (path) => {
7678
context.fullRebuildPaths.push(path)
7779
},
7880
})
81+
82+
env.DEBUG && console.timeEnd('[@tailwindcss/postcss] Setup compiler')
83+
return compiler
7984
}
8085

8186
// Setup the compiler if it doesn't exist yet. This way we can
@@ -126,7 +131,9 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
126131
sources: context.compiler.globs,
127132
})
128133

134+
env.DEBUG && console.time('[@tailwindcss/postcss] Scan for candidates')
129135
let candidates = scanner.scan()
136+
env.DEBUG && console.timeEnd('[@tailwindcss/postcss] Scan for candidates')
130137

131138
// Add all found files as direct dependencies
132139
for (let file of scanner.files) {
@@ -154,17 +161,26 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
154161
if (rebuildStrategy === 'full') {
155162
context.compiler = await createCompiler()
156163
}
164+
165+
env.DEBUG && console.time('[@tailwindcss/postcss] Build CSS')
157166
css = context.compiler.build(candidates)
167+
env.DEBUG && console.timeEnd('[@tailwindcss/postcss] Build CSS')
158168

159169
// Replace CSS
160170
if (css !== context.css && optimize) {
171+
env.DEBUG && console.time('[@tailwindcss/postcss] Optimize CSS')
161172
context.optimizedCss = optimizeCss(css, {
162173
minify: typeof optimize === 'object' ? optimize.minify : true,
163174
})
175+
env.DEBUG && console.timeEnd('[@tailwindcss/postcss] Optimize CSS')
164176
}
165177
context.css = css
178+
179+
env.DEBUG && console.time('[@tailwindcss/postcss] Update PostCSS AST')
166180
root.removeAll()
167181
root.append(postcss.parse(optimize ? context.optimizedCss : context.css, result.opts))
182+
env.DEBUG && console.timeEnd('[@tailwindcss/postcss] Update PostCSS AST')
183+
env.DEBUG && console.timeEnd('[@tailwindcss/postcss] Total time in @tailwindcss/postcss')
168184
},
169185
},
170186
],

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { compile, normalizePath } from '@tailwindcss/node'
1+
import { compile, env, normalizePath } from '@tailwindcss/node'
22
import { clearRequireCache } from '@tailwindcss/node/require-cache'
3-
43
import { Scanner } from '@tailwindcss/oxide'
54
import { Features, transform } from 'lightningcss'
65
import path from 'path'
@@ -92,7 +91,10 @@ export default function tailwindcss(): Plugin[] {
9291
if (generated === false) {
9392
return
9493
}
95-
return optimizeCss(generated, { minify })
94+
env.DEBUG && console.time('[@tailwindcss/vite] Optimize CSS')
95+
let result = optimizeCss(generated, { minify })
96+
env.DEBUG && console.timeEnd('[@tailwindcss/vite] Optimize CSS')
97+
return result
9698
}
9799

98100
// Manually run the transform functions of non-Tailwind plugins on the given CSS
@@ -378,9 +380,11 @@ class Root {
378380
// This should not be here, but right now the Vite plugin is setup where we
379381
// setup a new scanner and compiler every time we request the CSS file
380382
// (regardless whether it actually changed or not).
383+
env.DEBUG && console.time('[@tailwindcss/vite] Scan for candidates')
381384
for (let candidate of this.scanner.scan()) {
382385
this.candidates.add(candidate)
383386
}
387+
env.DEBUG && console.timeEnd('[@tailwindcss/vite] Scan for candidates')
384388

385389
// Watch individual files found via custom `@source` paths
386390
for (let file of this.scanner.files) {
@@ -404,6 +408,10 @@ class Root {
404408

405409
this.requiresRebuild = true
406410

407-
return this.compiler.build([...this.getSharedCandidates(), ...this.candidates])
411+
env.DEBUG && console.time('[@tailwindcss/vite] Build CSS')
412+
let result = this.compiler.build([...this.getSharedCandidates(), ...this.candidates])
413+
env.DEBUG && console.timeEnd('[@tailwindcss/vite] Build CSS')
414+
415+
return result
408416
}
409417
}

0 commit comments

Comments
 (0)