Skip to content

Commit a458e5d

Browse files
authored
Add incremental rebuilds to @tailwindcss/cli (tailwindlabs#13169)
* ensure the root CSS file part of the `cssImportPaths` * add incremental rebuilds to `@tailwindcss/cli` * update changelog
1 parent d230f2e commit a458e5d

File tree

2 files changed

+54
-54
lines changed

2 files changed

+54
-54
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717

1818
- Replace `--radius-none` and `--radius-full` theme values with static `rounded-none` and `rounded-full` utilities ([#13186](https://github.com/tailwindlabs/tailwindcss/pull/13186))
1919

20+
### Added
21+
22+
- Improve performance of incremental rebuilds for `@tailwindcss/cli` ([#13169](https://github.com/tailwindlabs/tailwindcss/pull/13169))
23+
2024
## [4.0.0-alpha.7] - 2024-03-08
2125

2226
### Added

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

+50-54
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
import watcher from '@parcel/watcher'
2-
import {
3-
IO,
4-
Parsing,
5-
clearCache,
6-
scanDir,
7-
scanFiles,
8-
type ChangedContent,
9-
} from '@tailwindcss/oxide'
2+
import { IO, Parsing, scanDir, scanFiles, type ChangedContent } from '@tailwindcss/oxide'
103
import { existsSync } from 'node:fs'
114
import fs from 'node:fs/promises'
125
import path from 'node:path'
@@ -99,25 +92,42 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
9992
args['--input'] ?? base,
10093
)
10194

102-
// Compile the input
103-
let { build } = compile(input)
104-
let result = build(candidates)
105-
106-
// Optimize the output
107-
if (args['--minify'] || args['--optimize']) {
108-
result = optimizeCss(result, {
109-
file: args['--input'] ?? 'input.css',
110-
minify: args['--minify'] ?? false,
111-
})
95+
let previous = {
96+
css: '',
97+
optimizedCss: '',
11298
}
11399

114-
// Write the output
115-
if (args['--output']) {
116-
await outputFile(args['--output'], result)
117-
} else {
118-
println(result)
100+
async function write(css: string, args: Result<ReturnType<typeof options>>) {
101+
let output = css
102+
103+
// Optimize the output
104+
if (args['--minify'] || args['--optimize']) {
105+
if (css !== previous.css) {
106+
let optimizedCss = optimizeCss(css, {
107+
file: args['--input'] ?? 'input.css',
108+
minify: args['--minify'] ?? false,
109+
})
110+
previous.css = css
111+
previous.optimizedCss = optimizedCss
112+
output = optimizedCss
113+
} else {
114+
output = previous.optimizedCss
115+
}
116+
}
117+
118+
// Write the output
119+
if (args['--output']) {
120+
await outputFile(args['--output'], output)
121+
} else {
122+
println(output)
123+
}
119124
}
120125

126+
// Compile the input
127+
let { build } = compile(input)
128+
129+
await write(build(candidates), args)
130+
121131
let end = process.hrtime.bigint()
122132
eprintln(header())
123133
eprintln()
@@ -162,26 +172,14 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
162172
// Re-compile the input
163173
let start = process.hrtime.bigint()
164174

175+
// Track the compiled CSS
176+
let compiledCss = ''
177+
165178
// Scan the entire `base` directory for full rebuilds.
166179
if (rebuildStrategy === 'full') {
167-
// Clear the cache because we need to re-scan the entire directory.
168-
clearCache()
169-
170180
// Re-scan the directory to get the new `candidates`.
171181
candidates = scanDir({ base }).candidates
172-
}
173-
174-
// Scan changed files only for incremental rebuilds.
175-
else if (rebuildStrategy === 'incremental') {
176-
let uniqueCandidates = new Set(candidates)
177-
for (let candidate of scanFiles(changedFiles, IO.Sequential | Parsing.Sequential)) {
178-
uniqueCandidates.add(candidate)
179-
}
180-
candidates = Array.from(uniqueCandidates)
181-
}
182182

183-
// Resolve the input
184-
if (rebuildStrategy === 'full') {
185183
// Collect the new `input` and `cssImportPaths`.
186184
;[input, cssImportPaths] = await handleImports(
187185
args['--input']
@@ -191,25 +189,19 @@ export async function handle(args: Result<ReturnType<typeof options>>) {
191189
`,
192190
args['--input'] ?? base,
193191
)
192+
193+
build = compile(input).build
194+
compiledCss = build(candidates)
194195
}
195196

196-
// Compile the input
197-
result = compile(input).build(candidates)
197+
// Scan changed files only for incremental rebuilds.
198+
else if (rebuildStrategy === 'incremental') {
199+
let newCandidates = scanFiles(changedFiles, IO.Sequential | Parsing.Sequential)
198200

199-
// Optimize the output
200-
if (args['--minify'] || args['--optimize']) {
201-
result = optimizeCss(result, {
202-
file: args['--input'] ?? 'input.css',
203-
minify: args['--minify'] ?? false,
204-
})
201+
compiledCss = build(newCandidates)
205202
}
206203

207-
// Write the output
208-
if (args['--output']) {
209-
await outputFile(args['--output'], result)
210-
} else {
211-
println(result)
212-
}
204+
await write(compiledCss, args)
213205

214206
let end = process.hrtime.bigint()
215207
eprintln(`Done in ${formatDuration(end - start)}`)
@@ -244,7 +236,9 @@ function handleImports(
244236
// Relevant specification:
245237
// - CSS Import Resolve: https://csstools.github.io/css-import-resolve/
246238

247-
if (!input.includes('@import')) return [input, []]
239+
if (!input.includes('@import')) {
240+
return [input, [file]]
241+
}
248242

249243
return postcss()
250244
.use(atImport())
@@ -254,6 +248,8 @@ function handleImports(
254248

255249
// Use `result.messages` to get the imported files. This also includes the
256250
// current file itself.
257-
result.messages.filter((msg) => msg.type === 'postcss-import').map((msg) => msg.file),
251+
[file].concat(
252+
result.messages.filter((msg) => msg.type === 'dependency').map((msg) => msg.file),
253+
),
258254
])
259255
}

0 commit comments

Comments
 (0)