feat(core): add support for --exclude-files and --include-files to nx watch - #37084
Open
AgentEnder wants to merge 9 commits into
Open
AgentEnder wants to merge 9 commits into
AgentEnder wants to merge 9 commits into
Conversation
Adds two new CLI options to `nx watch` that let users filter which changed file paths re-trigger the watched command using glob patterns. - `--include`: CSV/space-delimited patterns; a file must match at least one pattern to count. Omit to include all files (existing behaviour). - `--exclude`: CSV/space-delimited patterns; a file matching any pattern is always ignored even if it also matched `--include`. Filtering is applied daemon-side in `notifyFileWatcherSockets` so a project is only reported as changed when at least one of its files survives the glob filter. A pure `fileMatchesGlobFilter` helper and its unit tests are added in the file-watching folder. Example: nx watch --all --include="**/*.ts" --exclude="**/*.spec.ts" -- echo \$NX_FILE_CHANGES
…st, precompiled matchers)
…st, precompiled matchers) [Self-Healing CI Rerun]
Every failure mode of `--include`/`--exclude` looked identical from the terminal: the command silently stops re-running and `nx watch` appears to hang. Four separate causes, addressed at the point each one goes wrong. Negated patterns are now rejected. Minimatch implements `!` by inverting that one pattern's own result, and both filters OR their patterns together, so a negated pattern could only ever widen the set it lived in — the gitignore/eslint idiom `--include "**/*.ts" "!**/*.spec.ts"` *kept* the spec files, and `--exclude "!**/*.spec.ts"` meant "drop everything that is not a spec", leaving the watch firing only on spec edits. Both read as the exact opposite of what they did, and negation already has a first-class spelling here: the other flag. Empty patterns (what an unset shell variable expands to) are rejected for the same reason, a leading `./` is stripped rather than matching nothing, and a flag passed with no patterns at all now errors instead of quietly disabling the filter. The active patterns are echoed once at startup, unconditionally rather than behind `--verbose`, next to the root-anchoring rule — that is what turns "nx watch is hung" into "I typed `*.ts`". When a filter drops an entire batch of changed files, the notice now reaches the terminal of the watch that owns the filter, over its own socket, rather than only `daemon.log` — a file the person who typed the pattern has no reason to open, and which the previous comment claimed was enough. It is sent once per subscription, since a filter doing its job hits this on every save, and the daemon log still keeps the full record. `REGISTER_FILE_WATCHER` was the one `handleMessage` branch with no error handling, which was fine while it was an array push but not once it compiles globs. An invalid pattern rejected unhandled and took the workspace-wide daemon down with no explanation; it now responds with the error, so it surfaces in the terminal of the client that sent it. The watch socket therefore has to tell a file-change notification apart from a log line or an error response, which it now does. Also fixes the ordering regression from building watched-project files in a plain object (integer-like keys hoist, so a project named `2024` moved), and replaces a `--include` test that passed with the feature deleted, since yargs collects repeated undeclared flags into an array on its own. Claude-Session: https://claude.ai/code/session_01LsFgrnFJPfpqf9X7qfAXtW
The workspace watching guide is the only prose home for `nx watch` and had nothing on the two new flags, so the rule the whole feature turns on — that patterns are anchored at the workspace root, which is what separates `**/*.ts` from `*.ts` — was written down nowhere a user would look. Also calls out that `nx watch --exclude` filters files while the `--exclude=my-app` in the example above it belongs to the inner `run-many` and filters projects. The example is correct as written, but a reader who moves that flag left of the `--` gets a glob. Claude-Session: https://claude.ai/code/session_01LsFgrnFJPfpqf9X7qfAXtW
`nx watch`'s `--exclude` collided with the workspace-wide `--exclude` that `run-many`/`affected` take, which is a comma-separated list of *project names* rather than space-delimited file globs. `nx watch` never called `withExcludeOption`, so nothing shadowed anything at the yargs level, but a user arriving with `nx affected --exclude=my-app` muscle memory got a glob matching a file literally named `my-app` — a filter that silently excluded nothing. The collision was worse than ambiguity. `rewriteTargetsAndProjects` in `bin/init-local.ts` collapses the space-delimited values of `--projects` / `--exclude` / `--files` / `--target(s)` into one comma-joined value, keyed off the flag spelling alone, for every command. So the real `nx` binary turned `nx watch --all --exclude "**/*.spec.ts" "**/*.md"` into the single pattern `**/*.spec.ts,**/*.md`, which matches nothing. The existing specs parse argv straight through yargs and never saw it. Both flags are introduced by this PR and have never shipped, so this is a clean rename with no alias or deprecation. They are declared camelCase like their `includeGlobalWorkspaceFiles` / `includeDependencies` siblings, so yargs exposes `--include-files` and `--includeFiles` alike. Renamed through the daemon config, the validation error messages (the negation rejection named the opposite flag by name), and the docs. The docs callout about `--exclude` belonging to `run-many` now says `nx watch` has no `--exclude` of its own rather than describing a second one.
…es [Self-Healing CI Rerun]
✅ Deploy Preview for nx-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for nx-dev ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Contributor
|
View your CI Pipeline Execution ↗ for commit ec475e9
☁️ Nx Cloud last updated this comment at |
…es [Self-Healing CI Rerun]
…es [Self-Healing CI Rerun]
Contributor
There was a problem hiding this comment.
Nx Cloud has identified a flaky task in your failed CI:
🔂 Since the failure was identified as flaky, we triggered a CI rerun by adding an empty commit to this branch.
🔔 Heads up, your workspace has pending recommendations ↗ to auto-apply fixes for similar failures.
🎓 Learn more about Self-Healing CI on nx.dev
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Current Behavior
nx watchre-runs the given command for every file change in the watched projects. There is no way to narrow the trigger to specific file types or paths — e.g. watching only**/*.tschanges while ignoring spec files — so commands re-run on irrelevant changes.Expected Behavior
nx watchaccepts two new options:--include: comma/space-delimited glob patterns; a changed file must match at least one to re-trigger the command (defaults to everything).--exclude: comma/space-delimited glob patterns; a changed file matching any of these never triggers, even if it matched an include.Filtering happens daemon-side in
notifyFileWatcherSockets, where the project→files mapping exists, so a project only counts as changed (and only re-triggers a$NX_PROJECT_NAMEcommand) when at least one of its changed files survives the filter. Matching usesminimatchwithdot: true; global workspace files are filtered the same way. When neither flag is passed there is zero overhead (fast-path returns the original arrays).Includes 16 unit tests for the new
glob-filterhelper (include-only, exclude-only, combined, dotfiles, project dropped when all its files are filtered out).Related Issue(s)
N/A — feature work coordinated via Polygraph session
watch-filter-d1d4fdda.View session information ↗