diff --git a/.changeset/quiet-initial-route-scan.md b/.changeset/quiet-initial-route-scan.md new file mode 100644 index 000000000..b4a5b2df7 --- /dev/null +++ b/.changeset/quiet-initial-route-scan.md @@ -0,0 +1,5 @@ +--- +"@solidjs/start": patch +--- + +Don't emit route "reload" events during the initial file-system route scan. The scan runs on the first dev request, and the events invalidated the just-served routes manifest ~200ms later, pushing a spurious HMR update of the app/router module chain that raced hydration — intermittently duplicating pages, breaking client-side navigation, and detaching actions in dev. diff --git a/packages/start/src/config/fs-routes/router.ts b/packages/start/src/config/fs-routes/router.ts index bb2e3e21c..328c45480 100644 --- a/packages/start/src/config/fs-routes/router.ts +++ b/packages/start/src/config/fs-routes/router.ts @@ -55,8 +55,13 @@ export class BaseFileSystemRouter extends EventTarget { } async buildRoutes(): Promise { - for (var src of glob(this.glob())) { - await this.addRoute(src); + this.initialScan = true; + try { + for (var src of glob(this.glob())) { + await this.addRoute(src); + } + } finally { + this.initialScan = false; } return this.routes; @@ -119,7 +124,15 @@ export class BaseFileSystemRouter extends EventTarget { } } + // "reload" listeners invalidate the routes manifest module, so the events + // must stay quiet while buildRoutes first discovers the existing files: + // a manifest already served to the browser would otherwise be invalidated + // ~200ms into the first request, racing hydration with an HMR update of + // the app/router module chain. + private initialScan = false; + reload(route: string, type: "update" | "remove" | "add") { + if (this.initialScan) return; this.dispatchEvent( new CustomEvent("reload", { detail: {