CLI Watch never end 2 CPU cores at 100%
What version of Tailwind CSS are you using?
v4.0.14
What version of Node.js are you using?
v20.18.0
What operating system are you using?
Windows 11
Reproduction URL
I just use the Tailwind4 directory of https://github.com/pinzonjulian/tailwind_css_cli_watch_bug.git
git clone https://github.com/pinzonjulian/tailwind_css_cli_watch_bug.git
cd tailwind4
npm install tailwindcss @tailwindcss/cli
npx @tailwindcss/cli -i input.css -o output.css --watch
Describe your issue
Command never end, there is no output, 2 cores at 100%.
If I remove output.css, the process don't create it.
When I remove --watch. It works :
> $env:DEBUG=1; npx @tailwindcss/cli -i input.css -o output.css
≈ tailwindcss v4.0.14
Done in 91ms
[95.04ms] [@tailwindcss/cli] (initial build)
[14.09ms] ↳ Setup compiler
[38.04ms] ↳ Scan for candidates
[33.33ms] ↳ Build CSS
[ 1.06ms] ↳ Write output
Could a global configuration be the cause of the problem?
I encounter the same problem with:
tailwindcss.exe -i input.css -o output.css --watch$env:NODE_PATH=""; $env:DEBUG=1; npx @tailwindcss/cli -i input.css -o output.css -w$env:NODE_PATH=""; $env:DEBUG=1; npx @tailwindcss/cli -i input.css -o output.css -wafter renaming ~/AppData/Roaming npm and npm-cache (same with Appdata/Local...)
Hey! I tried running your example on macOS and it works fine (I tried with your exact Node version and v22 (what I had installed before):
Going to try this out on Windows 11 later too, but I wonder what happens if you run the --watch command with the DEBUG=1 flag?
Command never end
This is expected for the --watch command as it needs to stay alive to detect file changes
With DEBUG=1, there is no output and 2 cores at 100%.
I got the same result with Node.js v22.14.0 (npm 10.9.2).
I tried on another windows (v10). It works
Tailwind CSS seems to be incompatible with the Watchman (Meta) software. Can you reproduce the issue by installing it via Chocolatey?
choco install watchman
If I delete the shim-generated file:
C:\ProgramData\chocolatey\bin\watchman.exe,
Tailwind CSS starts working again.
This suggests that Tailwind CSS is likely using Watchman in the background for file watching. When it launches the 2025 version found in the PATH (the one installed via Chocolatey), it may trigger unexpected behavior (infinite loop?).
I see on @parcel/watcher:
@parcel/watcher has the following watcher backends, listed in priority order:
[FSEvents](https://developer.apple.com/documentation/coreservices/file_system_events) on macOS [Watchman](https://facebook.github.io/watchman/) if installed [inotify](http://man7.org/linux/man-pages/man7/inotify.7.html) on Linux [ReadDirectoryChangesW](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365465%28v%3Dvs.85%29.aspx) on Windows [kqueue](https://man.freebsd.org/cgi/man.cgi?kqueue) on FreeBSD, or as an alternative to FSEvents on macOS
A log about the watcher used or a compatibility check on the version could be a plus to facilitate problem identification. If Tailwind CSS does not work regardless of the Watchman version, prohibiting the use of Watchman at the code level would then be a solution.
@deguich Hey! Thanks for digging into it. Are you able to reproduce the problem when using @parcel/watcher directly? If so, it would be awesome if we can create an upstream bug report for that project and get it fixed for everyone using @parcel/watcher!
I'm experiencing same problem on Mac M2 using Node v22.14.0 and Yarn 1.22.22. When running npx @tailwindcss/cli -i ./app/assets/stylesheets/application.tailwind.css -o ./app/assets/builds/application.css the CPU (dockerized) goes to >100% and stays there. The Orbstack Helper (on the host OS) reports over a thousand % CPU usage 😓 .
I get the same issue when running on host OS using Node v23.7.0 (different than dockerized), yarn 1.22.22.
I can confirm that locking version at 4.0.15 solves the issue for me. Uninstall any existing tailwind packages (locally and globally) and clear the cache npm cache clean --force. Then install 4.0.15:
npm install @tailwindcss/[email protected]
npx @tailwindcss/cli -i app.scss -o app.css # builds
@lenart The issue you're running into might be https://github.com/tailwindlabs/tailwindcss/issues/17379 and is unrelated to watch mode. Do you mind taking a look there and, if possible, can provide us with a reproduction of that issue? Thanks!
I can reproduce what seems to be this issue when I have watchman installed; and it goes away when I uninstall watchman.
With watchman installed, on a Linux x86_64 GNU libc system (native, not in a container) running with DEBUG=1, I see:
22:14:29 css.1 | [81399.36ms] [@tailwindcss/cli] (initial build)
22:14:29 css.1 | [ 12.32ms] ↳ Setup compiler
22:14:29 css.1 | [ 33.67ms] ↳ Scan for candidates
22:14:29 css.1 | [ 11.92ms] ↳ Build CSS
22:14:29 css.1 | [ 1.04ms] ↳ Optimize CSS
22:14:29 css.1 | [ 1.03ms] ↳ Write output
The initial startup takes 81 seconds. After that startup, everything seems responsive and changes to files result in:
22:15:42 css.1 | Done in 1ms
22:15:42 css.1 |
22:15:42 css.1 | [1.34ms] [@tailwindcss/cli] (watcher)
22:15:42 css.1 | [1.13ms] ↳ Scan for candidates
Without watchman installed, though, startup is 1000x faster:
22:16:59 css.1 | Done in 71ms
22:16:59 css.1 |
22:16:59 css.1 | [71.90ms] [@tailwindcss/cli] (initial build)
22:16:59 css.1 | [12.04ms] ↳ Setup compiler
22:16:59 css.1 | [26.72ms] ↳ Scan for candidates
22:16:59 css.1 | [15.86ms] ↳ Build CSS
22:16:59 css.1 | [ 1.23ms] ↳ Optimize CSS
22:16:59 css.1 | [ 0.81ms] ↳ Write output
This was happening to me on Node v20.16.0 on macOS 15.3.2 with ARM. Upgrading to Node v22.14.0 resolved it.
This is happening to me in 22.14.0 too. 197% CPU usage always. I use mac m1
@flavorjones What specific versions of watchman and Tailwind CLI did you use to reproduce this? I've been playing around on a Debian based x64 linux (no containers) now too and with both these versions (one installed via brew and one via apt), the repro from @deguich worked fine:
I tried it with both the latest standalone build and with the latest CLI and Node.js
@silva96/ @ChadMoran Was your issue also watchman based? Do you have a repro that you can share?
@deguich Can also not reproduce the issue on Windows 11 Node 20 I'm afraid:
Maybe this requires a very specific version of watchman to trigger?
HI,
The npx @tailwindcss/cli -i input.css -o output.css --watch command hangs indefinitely, consumes high CPU (2 cores at 100%), and fails to produce any output (including not creating output.css if it's deleted beforehand).
// --- @tailwindcss/cli --watch logic ---
FUNCTION main_cli_handler(arguments): // 1. Parse arguments input_file = arguments.get_input_file() output_file = arguments.get_output_file() is_watch_mode = arguments.has_watch_flag() config_file_path = resolve_tailwind_config_path() // (e.g., tailwind.config.js)
// 2. Initial Setup (Common for both watch and single build)
TRY
compiler = setup_tailwind_compiler(config_file_path, input_file)
CATCH error
LOG "Error setting up compiler: " + error
EXIT with error code
END TRY
// 3. Perform an initial build
FUNCTION perform_build(current_compiler, output_path):
LOG_DEBUG "Starting build..."
start_time = get_current_time()
TRY
LOG_DEBUG "Scanning for candidates..."
candidates = current_compiler.scan_for_candidates() // Scan content files
LOG_DEBUG "Building CSS..."
css_result = current_compiler.build_css(candidates)
LOG_DEBUG "Writing output to: " + output_path
write_file(output_path, css_result.css)
LOG "Build successful. Done in " + (get_current_time() - start_time) + "ms"
RETURN true
CATCH build_error
LOG_ERROR "Build failed: " + build_error
RETURN false
END TRY
END FUNCTION
perform_build(compiler, output_file) // This part works as per the bug report
// 4. If not in watch mode, exit
IF NOT is_watch_mode:
EXIT successfully
END IF
// --- Watch Mode Specific Logic ---
LOG_INFO "Entering watch mode..."
// 4.1. Determine paths to watch
// - Content files specified in tailwind.config.js
// - The input CSS file (input.css)
// - The Tailwind config file itself (tailwind.config.js)
// - IMPORTANT: Explicitly EXCLUDE the output_file to prevent infinite loops!
paths_to_watch = get_content_globs_from_config(config_file_path)
ADD input_file TO paths_to_watch
ADD config_file_path TO paths_to_watch
LOG_DEBUG "Watching paths: " + paths_to_watch
LOG_DEBUG "Ignoring output file: " + resolve_absolute_path(output_file) // Crucial logging
// 4.2. Initialize File Watcher (e.g., Chokidar, or Node's fs.watch)
// This is the most likely area of the bug on Windows.
TRY
// **DEBUG POINT 1: Watcher options for Windows**
// Consider Windows-specific options: usePolling, binaryInterval, etc.
// Some file systems or setups on Windows behave better with polling,
// though it's more CPU intensive if not configured well.
watcher_options = {
ignored: [resolve_absolute_path(output_file)], // CRITICAL: Ensure output is ignored
persistent: true,
ignoreInitial: true, // Don't fire 'add' events for existing files at startup
awaitWriteFinish: { // Helps with atomic writes
stabilityThreshold: 200, // ms
pollInterval: 100 // ms
}
// MAYBE_TRY_ON_WINDOWS: usePolling: true, interval: 500 (as a diagnostic step)
}
file_watcher = initialize_file_watcher(paths_to_watch, watcher_options)
CATCH watcher_init_error
LOG_ERROR "Failed to initialize file watcher: " + watcher_init_error
EXIT with error code
END TRY
// 4.3. Define Watcher Event Handler
// Debounce rebuilds to handle bursts of file changes (e.g., save all)
debounce_timer = NULL
rebuild_pending = false
is_rebuilding = false // Lock to prevent concurrent rebuilds
FUNCTION on_file_change(event_type, changed_path):
LOG_DEBUG "File event: " + event_type + " on " + changed_path
// **DEBUG POINT 2: Log *every* event to see if unexpected events are firing**
// or if specific paths are causing issues.
// **DEBUG POINT 3: Check if the changed path is the output file**
// This should not happen if `ignored` option works. If it does, it's a major bug.
IF resolve_absolute_path(changed_path) == resolve_absolute_path(output_file):
LOG_WARNING "Watcher event triggered for output file! This should be ignored. Path: " + changed_path
RETURN
END IF
IF is_rebuilding:
LOG_DEBUG "Rebuild already in progress. Queuing change for: " + changed_path
rebuild_pending = true // Mark that a change occurred during rebuild
RETURN
END IF
CLEAR_TIMER(debounce_timer)
debounce_timer = SET_TIMER(200ms, FUNCTION(): // Debounce for 200ms
IF is_rebuilding: // Double check
rebuild_pending = true
RETURN
END IF
is_rebuilding = true
rebuild_pending = false // Reset pending flag as we are starting a rebuild
LOG_INFO "Change detected. Rebuilding..."
// Re-setup compiler IF tailwind.config.js changed, as theme/plugins might be different
IF changed_path == config_file_path:
LOG_INFO "Tailwind config changed. Re-initializing compiler."
TRY
compiler = setup_tailwind_compiler(config_file_path, input_file)
CATCH error
LOG_ERROR "Error re-initializing compiler: " + error
is_rebuilding = false
IF rebuild_pending: // If another change came in
on_file_change("queued_change", "unknown") // Trigger a new debounced rebuild
END IF
RETURN // Don't proceed with build if compiler setup failed
END TRY
END IF
build_success = perform_build(compiler, output_file)
is_rebuilding = false
IF rebuild_pending: // If another change came in during this rebuild
on_file_change("queued_change", "unknown") // Trigger a new debounced rebuild
END IF
END FUNCTION)
END FUNCTION
// 4.4. Attach Event Listeners
file_watcher.on("add", FUNCTION(path): on_file_change("add", path))
file_watcher.on("change", FUNCTION(path): on_file_change("change", path))
file_watcher.on("unlink", FUNCTION(path): on_file_change("unlink", path)) // For deleted content files
file_watcher.on("error", FUNCTION(error):
LOG_ERROR "Watcher error: " + error
// **DEBUG POINT 4: What kind of errors? Permissions? Too many files?**
// Consider if the process should exit or try to recover.
)
LOG_INFO "Tailwind CSS CLI is watching for changes. Press Ctrl+C to stop."
// 4.5. Keep Process Alive (typically handled by the watcher being persistent)
// No explicit loop needed here if watcher is set up correctly.
// 4.6. Handle Graceful Shutdown (Ctrl+C)
ON_PROCESS_INTERRUPT (SIGINT):
LOG_INFO "Stopping watcher..."
IF file_watcher:
file_watcher.close()
END IF
LOG_INFO "Exited."
EXIT successfully
END ON_PROCESS_INTERRUPT
END FUNCTION
// --- Helper Functions (Conceptual) --- FUNCTION resolve_absolute_path(path_string): // Convert relative path to absolute, normalized for the OS (Windows in this case) RETURN os_specific_normalized_absolute_path(path_string) END FUNCTION
FUNCTION get_content_globs_from_config(config_file_path):
// Read tailwind.config.js and extract content paths/globs
// Ensure these paths are resolved relative to the config file's location
config_data = read_tailwind_config(config_file_path)
content_paths = config_data.content
// DEBUG POINT 5: Path normalization for globs on Windows
// Ensure globs use forward slashes even on Windows, as many glob libraries prefer this.
// e.g., C:\path\to\project\src\**\*.html -> C:/path/to/project/src/**/*.html
FOR EACH path IN content_paths:
path = normalize_glob_path_for_watcher(path)
END FOR
RETURN content_paths
END FUNCTION
// --- Potential Areas for Investigation for the Bug ---
// 1. File Watcher Library on Windows:
// - chokidar is commonly used. Check its version and known issues on Windows.
// - Test with usePolling: true as a diagnostic. If it works (but is slow), it points to native event issues.
// - Ensure all paths passed to the watcher are correctly formatted and absolute.
// 2. Path Normalization: Windows path handling (\, drive letters, case-insensitivity) can be tricky.
// Ensure consistency.
// 3. Output File Watching: Double- and triple-check that the output.css file is never watched.
// If the watcher is triggered by its own output, it creates an infinite loop.
// 4. Glob Patterns on Windows: How globs like ./src/**/*.html are interpreted by the watcher library on Windows.
// 5. Resource Limits/Permissions: Less likely to cause a hang but possible.
// 6. Concurrency Control: The is_rebuilding lock is important to prevent multiple builds from stomping on each other or the watcher.
// 7. Node.js fs module behavior on Windows: If using native fs.watch, it has known quirks on Windows.
Good luck!
SUMAN SUHAG
@philipp-spiess Thanks for looking into this. I reproduce this consistently on:
- Ubuntu 24.10 ("oracular"), native x86_64
- watchman 4.9.0-7build4 (installed via apt)
- which installs libpcre3 2:8.39-15.1 as a dependency
- tailwindcss CLI v4.1.6 (precompiled binary)
Here's a full console log that might be helpful to see what's going on and potentially reproduce. Note that the tailwindcss CLI is installed via the tailwindcss-ruby gem, but it's simply the precompiled executable shipped by the tailwindcss project -- the full command is included below.
$ git clone https://github.com/rails/tailwindcss-rails
...
$ cd tailwindcss-rails && bundle install
...
$ ./test/integration/user_install_test.sh
...
$ cd "My Workspace/test-install"
$ DEBUG=1 bin/rails tailwindcss:watch[verbose]
Running: /home/flavorjones/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/tailwindcss-ruby-4.1.6-x86_64-linux-gnu/exe/x86_64-linux-gnu/tailwindcss -i /home/flavorjones/code/oss/tailwindcss-rails/My\ Workspace/test-install/app/assets/tailwind/application.css -o /home/flavorjones/code/oss/tailwindcss-rails/My\ Workspace/test-install/app/assets/builds/tailwind.css --minify -w
≈ tailwindcss v4.1.6
sh: 1: watchman: not found
Done in 74ms
[74.78ms] [@tailwindcss/cli] (initial build)
[15.17ms] ↳ Setup compiler
[15.64ms] ↳ Scan for candidates
[20.87ms] ↳ Build CSS
[ 2.26ms] ↳ Optimize CSS
[ 0.76ms] ↳ Write output
^C
$ sudo apt install watchman
Installing:
watchman
Installing dependencies:
libpcre3
Suggested packages:
python3-pywatchman
Summary:
Upgrading: 0, Installing: 2, Removing: 0, Not Upgrading: 1
Download size: 576 kB
Space needed: 1,559 kB / 1,399 GB available
Continue? [Y/n] y
Get:1 http://archive.ubuntu.com/ubuntu oracular/universe amd64 libpcre3 amd64 2:8.39-15.1 [253 kB]
Get:2 http://archive.ubuntu.com/ubuntu oracular/universe amd64 watchman amd64 4.9.0-7build4 [323 kB]
Fetched 576 kB in 0s (1,404 kB/s)
Selecting previously unselected package libpcre3:amd64.
(Reading database ... 335218 files and directories currently installed.)
Preparing to unpack .../libpcre3_2%3a8.39-15.1_amd64.deb ...
Unpacking libpcre3:amd64 (2:8.39-15.1) ...
Selecting previously unselected package watchman.
Preparing to unpack .../watchman_4.9.0-7build4_amd64.deb ...
Unpacking watchman (4.9.0-7build4) ...
Setting up libpcre3:amd64 (2:8.39-15.1) ...
Setting up watchman (4.9.0-7build4) ...
Processing triggers for man-db (2.12.1-3) ...
Processing triggers for libc-bin (2.40-1ubuntu3) ...
$ DEBUG=1 bin/rails tailwindcss:watch[verbose]
Running: /home/flavorjones/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/tailwindcss-ruby-4.1.6-x86_64-linux-gnu/exe/x86_64-linux-gnu/tailwindcss -i /home/flavorjones/code/oss/tailwindcss-rails/My\ Workspace/test-install/app/assets/tailwind/application.css -o /home/flavorjones/code/oss/tailwindcss-rails/My\ Workspace/test-install/app/assets/builds/tailwind.css --minify -w
≈ tailwindcss v4.1.6
Done in 1m
[81330.90ms] [@tailwindcss/cli] (initial build)
[ 15.92ms] ↳ Setup compiler
[ 12.08ms] ↳ Scan for candidates
[ 23.07ms] ↳ Build CSS
[ 6.28ms] ↳ Optimize CSS
[ 2.28ms] ↳ Write output
^C
@flavorjones Hmm this is getting bizarre. I just provisioned a new VPS with your repro and still can't reproduce this:
root@ubuntu-4gb-hel1-2:~/tailwindcss-rails/My Workspace/test-install# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 24.04.2 LTS
Release: 24.04
Codename: noble
root@ubuntu-4gb-hel1-2:~/tailwindcss-rails/My Workspace/test-install# ruby -v
ruby 3.2.3 (2024-01-18 revision 52bb2ac0a6) [x86_64-linux-gnu]
root@ubuntu-4gb-hel1-2:~/tailwindcss-rails/My Workspace/test-install# watchman -v
4.9.0
root@ubuntu-4gb-hel1-2:~/tailwindcss-rails/My Workspace/test-install# DEBUG=1 bin/rails tailwindcss:watch[verbose]
Running: /var/lib/gems/3.2.0/gems/tailwindcss-ruby-4.1.6-x86_64-linux-gnu/exe/x86_64-linux-gnu/tailwindcss -i /root/tailwindcss-rails/My\ Workspace/test-install/app/assets/tailwind/application.css -o /root/tailwindcss-rails/My\ Workspace/test-install/app/assets/builds/tailwind.css --minify -w
≈ tailwindcss v4.1.6
Done in 164ms
[165.34ms] [@tailwindcss/cli] (initial build)
[ 53.65ms] ↳ Setup compiler
[ 15.24ms] ↳ Scan for candidates
[ 41.02ms] ↳ Build CSS
[ 4.13ms] ↳ Optimize CSS
[ 1.68ms] ↳ Write output
I wonder if this happens with plain @parcel/watcher for you, too! (I would love to try it with plain watchman but I don't know if you can do that with the CLI only). Here's what I did:
apt install nodejs npm
mkdir test
npm init
npm add @parcel/watcher
# paste in the content below into index.js
node index.js
# in different session
touch index.js
const watcher = require("@parcel/watcher");
watcher.subscribe(".", async (err, events) => {
if (err) {
console.error(err);
return;
}
console.log(events);
}).then(console.log);
For me this immediately logs the unsubscribe function and also logs updates immediatly:
root@ubuntu-4gb-hel1-2:~/test# node index.js
{ unsubscribe: [Function: unsubscribe] }
[ { path: '/root/test/index.js', type: 'update' } ]
^C
Looking at the implementation a bit, I wonder if it works if you set WATCHMAN_SOCK to /dev/null or something?
https://github.com/parcel-bundler/watcher/blob/master/src/watchman/WatchmanBackend.cc#L44C22-L58
WATCHMAN_SOCK=/dev/null node index.js
@philipp-spiess A thing I noticed is that watchman, when run, spins up a daemon that runs in the background for subsequent runs (I think? I'm not very familiar with the tool).
This issue -- the long initial build step -- only seems to happen when the daemon is not already running. If the daemon is running already, it's fine:
$ pgrep -a watchman
# ... no output ...
$ DEBUG=1 bin/rails tailwindcss:watch[verbose]
Running: /home/flavorjones/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/tailwindcss-ruby-4.1.6-x86_64-linux-gnu/exe/x86_64-linux-gnu/tailwindcss -i /home/flavorjones/code/oss/tailwindcss-rails/My\ Workspace/test-install/app/assets/tailwind/application.css -o /home/flavorjones/code/oss/tailwindcss-rails/My\ Workspace/test-install/app/assets/builds/tailwind.css --minify -w
≈ tailwindcss v4.1.6
Done in 1m
[81424.61ms] [@tailwindcss/cli] (initial build)
[ 14.71ms] ↳ Setup compiler
[ 10.04ms] ↳ Scan for candidates
[ 16.06ms] ↳ Build CSS
[ 2.30ms] ↳ Optimize CSS
[ 1.15ms] ↳ Write output
^C
$ pgrep -a watchman
3030265 watchman --output-encoding=bser get-sockname
$ DEBUG=1 bin/rails tailwindcss:watch[verbose]
Running: /home/flavorjones/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/tailwindcss-ruby-4.1.6-x86_64-linux-gnu/exe/x86_64-linux-gnu/tailwindcss -i /home/flavorjones/code/oss/tailwindcss-rails/My\ Workspace/test-install/app/assets/tailwind/application.css -o /home/flavorjones/code/oss/tailwindcss-rails/My\ Workspace/test-install/app/assets/builds/tailwind.css --minify -w
≈ tailwindcss v4.1.6
Done in 57ms
[57.74ms] [@tailwindcss/cli] (initial build)
[14.95ms] ↳ Setup compiler
[ 8.54ms] ↳ Scan for candidates
[12.88ms] ↳ Build CSS
[ 2.16ms] ↳ Optimize CSS
[ 0.69ms] ↳ Write output
^C
That said, if the daemon is not running and I set WATCHMAN_SOCK=/dev/null then yes, it runs quickly:
$ pgrep -a watchman
# ... no output ...
$ WATCHMAN_SOCK=/dev/null DEBUG=1 bin/rails tailwindcss:watch[verbose]
Running: /home/flavorjones/.local/share/mise/installs/ruby/3.4.2/lib/ruby/gems/3.4.0/gems/tailwindcss-ruby-4.1.6-x86_64-linux-gnu/exe/x86_64-linux-gnu/tailwindcss -i /home/flavorjones/code/oss/tailwindcss-rails/My\ Workspace/test-install/app/assets/tailwind/application.css -o /home/flavorjones/code/oss/tailwindcss-rails/My\ Workspace/test-install/app/assets/builds/tailwind.css --minify -w
≈ tailwindcss v4.1.6
Done in 55ms
[55.56ms] [@tailwindcss/cli] (initial build)
[14.70ms] ↳ Setup compiler
[ 7.36ms] ↳ Scan for candidates
[15.88ms] ↳ Build CSS
[ 2.19ms] ↳ Optimize CSS
[ 0.91ms] ↳ Write output
^C
Yeah so it seems that for me, too, watchman is slower when it boots up the first time. However that's 1 second and not 80s 😬
I've been playing a round a bit to figure out how the watchman CLI works in order to be able to test this independelty from tailwind and @parcel/watcher. Can you, in the same folder as above and with two terminal windows, try this:
watchman shutdown-server
watchman watch-del-all
watchman --persistent log-level debug
And in the other terminal
watchman watch .
I wonder specifically if these startups log and that initial crawl are stuck for you there too!
Thanks for your patience on this by the way, really appreciated!
@philipp-spiess I think I understand what's going on here.
First: I did as you asked, and ran these commands in two different terminal sessions in the Rails project directory. Nothing seems to be hanging for me. The logs seem to show immediate responsiveness, and when I touched a local file ("README.md") saw it immediately show up in the logs.
Here's the output from the debug command, in case it's useful: watch.log
Same result if I run the tailwindcss watch command after the log-level debug command.
Then I shut down watchman, ran the tailwindcss watch command again, and then turned on logging. I saw some interesting entries in the logs related to "bootsnap" files like:
tmp/cache/bootsnap/compile-cache-iseq/65/f7dcfb114e9906
Bootsnap is a library used by default by Rails to pre-parse and pre-compile Ruby code to speed up process start time. For this demo app, it generates 1600+ files spread across a nested directory hierarchy:
tmp/cache/bootsnap/compile-cache-iseq
├── 00
│ ├── 140445af65fb1a
│ ├── 20f662291b6eea
│ ├── 5ebb88599d2acd
│ ├── 6f59cfde8ca882
│ ├── b0f4bba1a2ba16
│ └── f4f975c4ab732f
├── 01
│ ├── 42c131cc81a33c
│ ├── 58142a3f4bcacb
│ ├── 6592f88488d32e
│ ├── 8ea536f537ac5b
│ └── e1d4ee43d1e7ad
├── 02
│ ├── 4fe871faf2aeea
│ └── d4ee747b08c0ae
├── 03
│ ├── 20d3fdc40a28bc
│ ├── 503b549529cbe8
│ ├── 56be1978cb5c95
│ ├── 8bf6383989658f
│ ├── a7c17f2f116ed0
│ ├── c7b09be8be7401
│ ├── de386bc5005c5c
│ └── eb3c372a4cbdcb
├── 04
│ ├── 0320d78aaffdd4
│ ├── 7d0454d70096c7
│ ├── 8ec7c67663ffd2
│ └── bfd92164cd134f
... ✀ ...
├── fe
│ ├── 35d6b178df2c44
│ ├── 5f4418d96d5fa3
│ └── b922f959e9f8c8
└── ff
├── 4070c3691c82ba
├── 5a321ff348d0ad
├── 650592d6de9c74
└── a586489a434f99
256 directories, 1355 files
Playing around with it, I can turn off bootsnap but so long as those files are present, the tailwindcss watch command startup is slow.
I think this behavior is related to this issue: https://github.com/tailwindlabs/tailwindcss/issues/15750. Because the watch command should not be looking at files in the tmp/ directory in the first place. WDYT?
@flavorjones Ah yeah that is very helpful. It does seem like a blanked listen for all changes in this directory recursively won't cut it, which is annoying because @parcel/watcher only allows listening to directories and it's always recursive.
One option I am looking into is the ignore list now. We may be able to compute something for that in Oxide but before we go ahead and try that, I'd love to understand if this fixes your perf issues. Can I ask you for one more favor here? Try the following parcel script and let me know if the inclusion of that ignore option makes any difference:
const watcher = require("@parcel/watcher");
watcher
.subscribe(
".",
async (err, events) => {
if (err) {
console.error(err);
return;
}
console.log(events);
},
{ ignore: "./tmp" }
)
.then(console.log);
I tried starting the rails app in my repro Linux VM but even though my tmp dir looks the same, it doesn't take that long to start. That said I'm on a Hetzner VPS where they do claim that they use NVMe drives so that might explain why it's fast for me. 🤔
So I naturally duplicated the tmp/ folder a dozen times and now it's getting slower (4sec already!). This does indeed seem like the issue is related to #15750.
Now let me try if that ignore setup would work.
Try the following parcel script and let me know if the inclusion of that ignore option makes any difference
It doesn't seem to make a difference for me. It takes about 1m32s with or without the ignore parameter.
Thank you @flavorjones, uninstall watchman works for me.
sudo apt-get remove watchman for anyone using Ubuntu.