Skip to content

dogfood: watch content dirs the app reads from outside its appDir #894

Description

@vivek7405

Problem

The dev file watcher only watches the app's own tree, so an edit to content the app READS from outside its appDir does not trigger a reload. Concrete case found while previewing the website: website/ renders blog posts read from the REPO-ROOT blog/ directory (website/modules/blog/queries/list-posts.server.ts resolves blog/ four levels up, a sibling of website/). Editing blog/<post>.md produces no reload because blog/ is outside every watched path. On Node the node --watch set is app / components / modules / lib / actions + middleware.* (packages/cli/lib/dev-supervisor.js:51-55); the in-process fs.watch watches app.appDir recursively only (packages/server/src/dev.js ~L1545). Neither sees blog/.

Any app that reads data files (markdown content, JSON fixtures, a local data dir) from outside its appDir hits this: editing that content requires a manual browser refresh, which reads as "hot reload is broken".

Design / approach

Let an app declare EXTRA watch paths that the dev watcher includes alongside the appDir, resolved relative to the app root and allowed to point outside it. A webjs.dev.watch config array (package.json webjs block) is the natural home next to the existing webjs.dev.before / webjs.dev.parallel orchestration (#550). The in-process fs.watch loop adds one recursive watcher per declared path; a change there runs the same debounced app.rebuild() + SSE reload as an in-tree edit. (An edited content file only needs a reload + a per-request re-read, not a module re-import, so the existing rebuild path is sufficient.)

Keep it opt-in and explicit (no attempt to auto-derive read paths from the import graph, which cannot see a readFile(resolve(...)) at runtime). Then set webjs.dev.watch: ["../blog"] in website/package.json so the website's own blog previews reload.

Implementation notes (for the implementing agent)

  • Where to edit:
    • packages/server/src/dev.js startServer() in-process watcher (~L1536-1560): today it starts ONE fsWatch(app.appDir, {recursive:true}). Read the configured extra paths (a new readDevWatchPathsFromApp(appDir) alongside the sibling readXFromApp readers, e.g. readBodyLimitsFromApp at L414) and start one additional recursive watcher per path, each funnelling into the SAME debounced rebuild(). Resolve each relative to app.appDir; allow escaping it (../blog). Reuse the single watcherAbort so all watchers tear down together.
    • packages/cli/lib/dev-supervisor.js: if the boot-once restart path is kept for Node (see the sibling issue), extra watch paths that need a restart would go here too; for pure content reload the in-process watcher suffices, so this file may not need changes.
    • website/package.json webjs block: add "dev": { ..., "watch": ["../blog"] } so the demo app that surfaced this actually benefits.
  • Landmines / gotchas:
    • shouldIgnoreWatchPath (dev.js:1490) matches relative filenames from an fs.watch event; keep applying it per watcher so node_modules/.git/.webjs/db noise under an extra path is still ignored.
    • A path outside the appDir means event.filename is relative to THAT watch root, not the appDir; do not assume appDir-relative paths downstream.
    • Do not watch huge/unbounded dirs by default; this is opt-in precisely so an app does not accidentally watch its parent repo. Validate the configured paths exist (skip missing, like the supervisor's exists() guard) and log which extra paths are watched at boot.
    • Guard against watching a path that CONTAINS the appDir (double events) or the appDir itself (redundant); de-dupe.
  • Invariants to respect: packages/ stays plain JS + JSDoc. Opt-in only, no default behaviour change for apps without the config. Prod (webjs start) does not watch, unchanged.
  • Tests + docs: unit packages/server/test/dev/* (a change under a configured extra path triggers a rebuild / SSE reload; a missing configured path is skipped without crashing; shouldIgnoreWatchPath still applies). Docs: agent-docs/configuration.md (the webjs.dev block gains watch), WebjsConfig type + the config JSON Schema, packages/cli/AGENTS.md if the webjs dev description mentions watched paths.

Acceptance criteria

  • With webjs.dev.watch: ["../blog"] set, editing a file under blog/ while running webjs dev in website/ reloads the browser automatically.
  • An app WITHOUT the config behaves exactly as today (appDir-only watching).
  • A configured path that does not exist is skipped without crashing the watcher.
  • shouldIgnoreWatchPath still filters noise under the extra paths.
  • A counterfactual proves the extra-path reload test actually fires.
  • Tests cover the new behaviour; agent-docs/configuration.md + WebjsConfig + JSON Schema updated.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Fields

No fields configured for issues without a type.

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions