Add --poll option to @tailwindcss/cli#20297
Conversation
732f595 to
e1bd806
Compare
Confidence Score: 4/5This is close, but one polling rebuild case should be fixed before merging.
crates/oxide/src/scanner/mod.rs Reviews (3): Last reviewed commit: "update changelog" | Re-trigger Greptile |
WalkthroughThis change adds polling-based watch mode to the Tailwind CLI and records the set of files selected during the latest Rust scanner pass. The scanner now carries modification times through filesystem walking, computes changed files during discovery, and exposes them through Rust and Node getters. The CLI adds ChangesRelated PRs: Sequence Diagram(s)sequenceDiagram
participant CLI
participant createPollingWatcher
participant getRebuildStrategy
participant Scanner
CLI->>createPollingWatcher: start(pollInterval, callback)
loop each poll interval
createPollingWatcher->>CLI: invoke callback
CLI->>Scanner: scan()
CLI->>getRebuildStrategy: files, fullRebuildPaths
getRebuildStrategy-->>CLI: rebuild strategy
CLI->>CLI: write(output)
end
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/oxide/tests/scanner.rs`:
- Around line 1090-1091: The test in scanner.rs is using too short a delay
before rewriting src/index.html, which can cause the filesystem mtime to stay
unchanged and make the scan flaky. Increase the sleep in this test to match the
more reliable delay used elsewhere in the file, so the mtime differs before the
rewrite and the assertion in the same test remains stable.
In `@packages/`@tailwindcss-cli/src/commands/build/index.ts:
- Around line 578-637: The polling rebuild path in build/index.ts is using
scanner.scan() as if it were the incremental delta, but scan() returns the full
candidate set and causes unnecessary rebuilds. In the watcher/polling flow
around getRebuildStrategy and compiler.build, switch the non-full rebuild path
to derive candidates from strategy.changedFiles using scanner.scanFiles(...) or
an equivalent delta-producing call, and only feed that changed-file candidate
set into compiler.build so --watch --poll preserves no-op/incremental behavior.
In `@packages/`@tailwindcss-cli/src/commands/help/index.ts:
- Around line 119-123: The help formatter in the option rendering loop is making
`--poll` look like it requires a value even though `handle()` accepts a bare
flag and maps `true` to the default delay. Update the usage/help generation in
the help command (the logic that formats `options` and the shared `buildUsage()`
path) so boolean-or-value flags like `poll` render their value as optional, e.g.
using bracketed syntax instead of a required `=placeholder` form.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: e199b8e6-61df-44c1-b1f3-0c6ba6eb2b82
📒 Files selected for processing (10)
CHANGELOG.mdcrates/node/src/lib.rscrates/oxide/src/scanner/mod.rscrates/oxide/tests/scanner.rsintegrations/cli/index.test.tspackages/@tailwindcss-cli/src/commands/build/index.tspackages/@tailwindcss-cli/src/commands/help/index.tspackages/@tailwindcss-cli/src/index.tspackages/@tailwindcss-cli/src/utils/args.test.tspackages/@tailwindcss-cli/src/utils/args.ts
e1bd806 to
3cc1089
Compare
The idea for `--poll` is that we don't have to rely on file system events but instead we poll the file system. The Oxide scanner has a cache of mtimes for changed files. This allows us to skip parsing files that we know haven't changed. This also adds a small new internal api `scan_incremental` which results in changed files that were scanned and their combined `candidates`. With this information we can compute the next CSS output, and we can figure out whether we need to perform a full rebuild or not. We could simplify this, and just keep polling over and over again, but it would spam the terminal output as well which is not ideal.
3cc1089 to
67ab08d
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
packages/@tailwindcss-cli/src/commands/build/index.ts (2)
586-588: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winExclude generated files before scanning, not after.
scanner.scan()has already walked and extracted from changed files before this filter runs. If--outputor an external--maplives under the scanner root, the polling loop can scan generated artifacts and mutate scanner state before discarding them fromfiles. Add negated scanner sources for generated output/map paths when creating the scanner, or otherwise exclude them at the scanner source level.Proposed direction
if (inputFilePath !== null) { sources.push({ base: path.dirname(inputFilePath), pattern: path.basename(inputFilePath), negated: false, }) } + + for (let generated of [ + args['--output'] && args['--output'] !== '-' ? args['--output'] : null, + typeof args['--map'] === 'string' ? args['--map'] : null, + ]) { + if (generated !== null) { + sources.push({ + base: path.dirname(generated), + pattern: path.basename(generated), + negated: true, + }) + } + }
794-798: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winAvoid raw error spam from the polling loop.
A persistent
scanner.scan()/build/write error will currentlyconsole.errorevery polling interval. Route these errors through the polling renderer and de-dupe repeated messages so--polldoes not spam the terminal on every tick.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: cba53c0d-f586-4c41-ba9e-6ddd3c11f065
📒 Files selected for processing (7)
CHANGELOG.mdcrates/node/src/lib.rscrates/oxide/src/scanner/mod.rscrates/oxide/tests/scanner.rspackages/@tailwindcss-cli/src/commands/build/index.tspackages/@tailwindcss-cli/src/index.tspackages/@tailwindcss-cli/src/utils/args.test.ts
✅ Files skipped from review due to trivial changes (2)
- packages/@tailwindcss-cli/src/index.ts
- CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (3)
- crates/node/src/lib.rs
- packages/@tailwindcss-cli/src/utils/args.test.ts
- crates/oxide/tests/scanner.rs
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: f3644028-201d-4a07-b4b5-3822ff87eeca
📒 Files selected for processing (7)
CHANGELOG.mdcrates/node/src/lib.rscrates/oxide/src/scanner/mod.rscrates/oxide/tests/scanner.rspackages/@tailwindcss-cli/src/commands/build/index.tspackages/@tailwindcss-cli/src/index.tspackages/@tailwindcss-cli/src/utils/args.test.ts
✅ Files skipped from review due to trivial changes (2)
- CHANGELOG.md
- packages/@tailwindcss-cli/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- crates/node/src/lib.rs
- crates/oxide/tests/scanner.rs
- crates/oxide/src/scanner/mod.rs
This PR re-adds the
--polloption to the@tailwindcss/clithat we had in Tailwind CSS v3, but didn't in Tailwind CSS v4.In Tailwind CSS v4, we started using
@parcel/watcherinstead of chokidar for our watcher in the CLI. However, this currently doesn't support a--polloption.This PR implements our own
--polloption such that you can use it in environments where fs events don't work properly (e.g. Docker).Polling can be enabled by using
--watch --poll, in this case we will poll every250ms(I'm open for a different default value). You can also pick your own interval by using--watch --poll 500which is defined in milliseconds.The
--polloption will be less efficient than a normal--watch. But if you are in a situation where you can't use--watchon its own then this is a good fallback.One thing you can do today is run the build command manually. If you do have some tooling that does work on your machine (such as
watchexec) then you can automatically perform a full build. The biggest downside of this approach is that you are doing a full build every time, instead of an incremental build.With this PR, we try to fix that by still allowing incremental builds. This should result in the same behavior as the normal
--watchfunction:@utilityare available, and@themevalues are updated).The implementation is a little bit more complex just because I didn't want to spam the terminal output even if we are polling every
250ms.In the Oxide scanner we do track the modified times of each file. Every
250mswe traverse the file system and skip the files that we know didn't change (since the mtime is the same). If the file was touched, then we will parse it again to extract possible Tailwind CSS classes. We will also track which files were scanned such that we can know whether we have to trigger a full-rebuild or not (in case the input.css file or any of its dependencies was changed).Fixes: #18109
Fixes: #18540
Fixes: #15750
Test plan
--polloptionAnnotated:
We check the file system every
250msby default, but we won't log to prevent spamming the terminal.