Skip to content

Detect new files added during dev session in file watcher#7491

Open
isaacroldan wants to merge 3 commits intomainfrom
file-watcher-detect-new-files
Open

Detect new files added during dev session in file watcher#7491
isaacroldan wants to merge 3 commits intomainfrom
file-watcher-detect-new-files

Conversation

@isaacroldan
Copy link
Copy Markdown
Contributor

@isaacroldan isaacroldan commented May 7, 2026

Summary

Closes https://github.com/shop/issues-admin-extensibility/issues/2461

The file watcher resolves which extension owns a path via extensionWatchedFiles, a map built once at start() from each extension's watchedFiles() — a globbed snapshot. Paths created at runtime aren't in the snapshot, so their add events get dropped by the not watched by any extension early-return in handleFileEvent.

This change attributes unknown add events to an owning extension at event time:

  • Adds ExtensionInstance.watchPatterns() returning the raw paths + ignore patterns from devSessionWatchConfig (or defaults). watchedFiles() is refactored to call it.
  • In FileWatcher.handleFileEvent: when affectedHandles is empty for an add event, look up the owning extension(s) by directory containment and pattern-match the path against watchPatterns(). If accepted, register in extensionWatchedFiles so subsequent change/unlink events on the same path are O(1).
  • shouldIgnoreEvent short-circuits to accept when the path is already registered for the handle, so subsequent events on a runtime-discovered file aren't dropped by the static-list check.

Why not add a separate "folder" API to extensions: chokidar already watches fullExtensionDirectories recursively today, so the new files reach the watcher — the bug is in attribution, not in what's being watched.

Test plan

  • All existing file-watcher.test.ts tests pass (19/19)
  • All existing extension-instance.test.ts tests pass (42/42)
  • nx type-check passes
  • nx lint passes
  • New tests cover: runtime-added file inside an existing extension → file_created; runtime-added file outside any extension → ignored; subsequent change on a runtime-discovered file → file_updated
  • Manual: shopify app dev, create a new file inside an extension at runtime, verify the dev session picks it up

🤖 Generated with Claude Code

The file watcher resolved file ownership statically at start time using
extension.watchedFiles(), which expanded globs into a literal list of paths.
Files created at runtime weren't in that list, so chokidar's add events were
dropped by the "not watched by any extension" check.

Now attribute unknown 'add' events to an owning extension by directory
containment and watch-pattern matching, register the new path in the dynamic
map, and short-circuit shouldIgnoreEvent for paths already tracked. Adds
ExtensionInstance.watchPatterns() exposing the raw paths + ignore patterns.
@github-actions github-actions Bot added the Area: @shopify/app @shopify/app package issues label May 7, 2026
@isaacroldan isaacroldan marked this pull request as ready for review May 7, 2026 15:49
Copilot AI review requested due to automatic review settings May 7, 2026 15:49
@isaacroldan isaacroldan requested review from a team as code owners May 7, 2026 15:49
Copy link
Copy Markdown
Contributor Author

isaacroldan commented May 7, 2026

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a dev-session gap where files created after FileWatcher.start() were ignored because extension ownership was determined from a one-time snapshot of ExtensionInstance.watchedFiles(). It adds runtime attribution for previously-unknown add events by matching the new file against each extension’s directory and raw watch patterns, then registers the discovered file for fast subsequent event handling.

Changes:

  • Add ExtensionInstance.watchPatterns() (and refactor watchedFiles() to use it) so the watcher can pattern-match runtime-created files without relying on a glob snapshot.
  • Enhance FileWatcher.handleFileEvent to discover owning extension(s) for unknown add events and register them into extensionWatchedFiles.
  • Update ignore logic to accept events for paths already registered for a handle; add tests covering runtime-added files and follow-up change events.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
packages/app/src/cli/services/dev/app-events/file-watcher.ts Adds runtime “owner discovery” for unknown add events and adjusts ignore logic for runtime-registered paths.
packages/app/src/cli/services/dev/app-events/file-watcher.test.ts Adds test coverage for runtime file creation attribution and subsequent updates.
packages/app/src/cli/models/extensions/extension-instance.ts Introduces watchPatterns() and centralizes default ignore patterns; refactors watchedFiles() to use it.
.changeset/file-watcher-detect-new-files.md Adds a patch changeset documenting the dev-session behavior improvement.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/app/src/cli/services/dev/app-events/file-watcher.ts Outdated
Comment on lines +264 to +273
// For 'add' events on paths we don't yet track, try to attribute them to an
// existing extension by directory containment + watch pattern matching. This
// is what allows files created during a running dev session to be picked up.
if (isUnknownExtension && event === 'add' && !isExtensionToml) {
const discovered = this.discoverFileOwners(normalizedPath)
if (discovered.size > 0) {
this.extensionWatchedFiles.set(normalizedPath, discovered)
affectedHandles = discovered
isUnknownExtension = false
}
knip flags the constant as an unused export since it's only consumed
inside extension-instance.ts. Make it a module-local const.
Two review-comment fixes from #7491:

- shouldIgnoreEvent looked up event.path raw, but extensionWatchedFiles
  is keyed by normalizePath(file). On Windows, chokidar emits
  backslash-separated paths and the lookup would miss, causing
  runtime-discovered files to be re-filtered by the static-list check.
  Normalize before the lookup.

- Runtime discovery added entries to extensionWatchedFiles but never
  removed them on unlink. In a long-running dev session the map grew
  unbounded with stale ownership data. Delete the normalized path from
  the map after pushing file_deleted; subsequent timeouts for other
  handles are no-ops on the now-missing key.
@melissaluu
Copy link
Copy Markdown
Contributor

/snapit

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

🫰✨ Thanks @melissaluu! Your snapshot has been published to npm.

Test the snapshot by installing your package globally:

pnpm i -g --@shopify:registry=https://registry.npmjs.org @shopify/cli@0.0.0-snapshot-20260507161656

Caution

After installing, validate the version by running shopify version in your terminal.
If the versions don't match, you might have multiple global instances installed.
Use which shopify to find out which one you are running and uninstall it.

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

Labels

Area: @shopify/app @shopify/app package issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants