Skip to content

feat: add watch flag to cli#111

Open
RalphK66 wants to merge 4 commits into
privatenumber:developfrom
RalphK66:feat/add-watch-flag
Open

feat: add watch flag to cli#111
RalphK66 wants to merge 4 commits into
privatenumber:developfrom
RalphK66:feat/add-watch-flag

Conversation

@RalphK66

Copy link
Copy Markdown
Contributor

Closes #110

Summary

Adds --watch / -w flag to the CLI. When enabled, .d.ts files are generated/updated/removed in real-time as .module.css files are created, modified, or deleted — no browser or manual re-run needed.

npx vite-css-modules --watch
npx vite-css-modules -w "src/**/*.module.css"

Motivation

In large projects you frequently create or edit .module.css files and expect TypeScript types immediately. Today you must either load the page in the browser (so the transform hook fires) or re-run the CLI manually. Neither works well during active development.

How it works

  1. Run the existing one-shot generation for all matched files
  2. Start an OS-level file watcher on the glob scope directory
  3. On create/update: invalidate the loader cache, regenerate the .d.ts
  4. On delete: remove both .d.ts and .d.ts.map
  5. Ctrl+C cleanly unsubscribes the watcher and exits

Design decisions

File watcher: @parcel/watcher

Evaluated three options:

  • @parcel/watcher (chosen) — native C++ bindings using OS-level APIs (FSEvents/inotify/ReadDirectoryChanges). Delivers batched events via a callback, no "ready" event ceremony needed. subscribe() is async — watcher is live once the promise resolves.
  • chokidar — pure JS, most popular. But v5 dropped glob support and the ignored option no longer accepts globs, requiring manual workarounds. Also hit EMFILE errors when symlinked node_modules caused recursive watching.
  • Node fs.watch — zero deps but unreliable cross-platform (duplicate events, no recursive on Linux < Node 20).

Glob matching: picomatch

@parcel/watcher watches a directory and reports all events. We filter with picomatch matchers against the user's glob patterns, using slash() to normalize paths on Windows.

Debounce: 100ms per-file

Editors often emit multiple FS events for a single save (write temp → rename). Per-file debounce at 100ms prevents partial reads and redundant work without delaying unrelated files. Evaluated 30ms (too aggressive for VS Code's write patterns) and chokidar's awaitWriteFinish (polling-based, adds latency).

Error handling

  • One-shot mode: errors set process.exitCode = 1 (existing behavior)
  • Watch mode: errors log to stderr and continue watching (failOnError: false). Errors always print regardless of --silent.

Ignored directories

node_modules, .git, dist, coverage — passed to @parcel/watcher's native ignore (so the OS never reports events from them) and also checked at runtime via picomatch as a safety net.

Refactoring

Extracted shared logic so one-shot and watch modes use the same code paths:

  • generate-declaration.ts — single-file type generation with failOnError and allowArbitraryNamedExports params (previously allowArbitraryNamedExports was hardcoded to false in the CLI)
  • glob-scope.ts — resolves user globs + cwd vs config root logic
  • load-css-module.ts — added invalidate() method to clear the transform cache for a specific file (required for watch to re-process changed files)

New dependencies

Dep Type Why
@parcel/watcher prod OS-native file watching
picomatch prod Glob matching for watcher events
@types/picomatch dev Type definitions

Tests

6 new tests covering:

  • Generates types for newly created files
  • Regenerates types when existing files change
  • Removes .d.ts when source file is deleted
  • Errors shown even with --silent
  • Success output suppressed with --silent
  • Watch starts and works when no files match initially (prints "No files matched yet; watching for additions.")

All 44 existing CLI tests continue to pass.


NOTE: I used AI to help me

@RalphK66

Copy link
Copy Markdown
Contributor Author

@privatenumber I know it's a reasonably extensive change, but would be cool to see the feature added. Let me know if you want to chat about the implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add watch flag to cli type gen

2 participants