Skip to content

feat(core): add support for --exclude-files and --include-files to nx watch - #37084

Open
AgentEnder wants to merge 9 commits into
masterfrom
feat/watch-include-exclude-globs
Open

AgentEnder wants to merge 9 commits into
masterfrom
feat/watch-include-exclude-globs

Conversation

@AgentEnder

Copy link
Copy Markdown
Member

Current Behavior

nx watch re-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 **/*.ts changes while ignoring spec files — so commands re-run on irrelevant changes.

Expected Behavior

nx watch accepts 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.
nx watch --all --includeFiles="**/*.ts" --excludeFiles="**/*.spec.ts" -- echo $NX_FILE_CHANGES

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_NAME command) when at least one of its changed files survives the filter. Matching uses minimatch with dot: 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-filter helper (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 ↗

AgentEnder and others added 7 commits September 3, 2026 17:18
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) [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.
@AgentEnder
AgentEnder marked this pull request as ready for review September 17, 2026 03:51
@AgentEnder
AgentEnder requested a review from a team as a code owner September 17, 2026 03:51
@netlify

netlify Bot commented Sep 17, 2026

Copy link
Copy Markdown

Deploy Preview for nx-docs ready!

Name Link
🔨 Latest commit 1c88a64
🔍 Latest deploy log https://app.netlify.com/projects/nx-docs/deploys/6aab7cef4837b60008070f5a
😎 Deploy Preview https://deploy-preview-37084--nx-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Sep 17, 2026

Copy link
Copy Markdown

Deploy Preview for nx-dev ready!

Name Link
🔨 Latest commit 1c88a64
🔍 Latest deploy log https://app.netlify.com/projects/nx-dev/deploys/6aab7cef39e0590008694a43
😎 Deploy Preview https://deploy-preview-37084--nx-dev.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@nx-cloud

nx-cloud Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit ec475e9

Command Status Duration Result
nx affected --targets=lint,oxlint,test,build,e2... ✅ Succeeded 6m 53s View ↗
nx run-many -t check-imports check-lock-files c... ✅ Succeeded 3s View ↗
nx-cloud record -- pnpm nx-cloud conformance:check ✅ Succeeded 42s View ↗
nx build workspace-plugin ✅ Succeeded <1s View ↗
nx-cloud record -- nx sync:check ✅ Succeeded 17s View ↗
nx-cloud record -- nx format:check ✅ Succeeded <1s View ↗

☁️ Nx Cloud last updated this comment at 2026-09-17 05:52:02 UTC

nx-cloud[bot]

This comment was marked as outdated.

@nx-cloud nx-cloud Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Nx Cloud View detailed reasoning in Nx Cloud ↗

🔔 Heads up, your workspace has pending recommendations ↗ to auto-apply fixes for similar failures.


🎓 Learn more about Self-Healing CI on nx.dev

@AgentEnder AgentEnder added scope: core core nx functionality type: feature labels Sep 20, 2026
@AgentEnder AgentEnder self-assigned this Sep 20, 2026

This branch has not been deployed

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

Labels

scope: core core nx functionality type: feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant