Skip to content

fix: fail on fs-route pages generating the same URL, dedupe duplicate params - #179

Merged
uhyo merged 1 commit into
masterfrom
claude/file-system-router-audit-sthtts
Aug 30, 2026
Merged

fix: fail on fs-route pages generating the same URL, dedupe duplicate params#179
uhyo merged 1 commit into
masterfrom
claude/file-system-router-audit-sthtts

Conversation

@uhyo

@uhyo uhyo commented Aug 30, 2026

Copy link
Copy Markdown
Owner

Follow-ups from an audit of the experimental file-system router. No major bugs found; this fixes the minor issues that turned up.

Changes

Detect colliding generated URLs, dedupe duplicate params (tree.ts)

Previously, a static page next to a dynamic sibling whose generateStaticParams() resolves to the same URL (e.g. blog/hello/page.tsx + blog/[slug] returning "hello"), or one generateStaticParams() returning the same params twice, produced duplicate pages. The build only failed later with a generic Duplicate entry path: "blog/hello.html" error, and the dev server silently served whichever entry came first.

collectStaticPaths() now:

  • collapses duplicate params from a single route into one page — duplicates are natural when params are derived from data (e.g. tags across posts), and the pages would be identical anyway;
  • fails with an error naming both files when two different routes generate the same URL, since the pages would fight over one output file and route precedence makes one of them unreachable.

Documented in the JSDoc for generateStaticParams and in the file-system routing docs page.

Share deferred-payload ID generation (defer.tsx, fs-routes/entries.tsx)

The fs-routes chunk host duplicated defer()'s payload-ID generation but skipped the part that includes the sanitized debug name in the dev payload file name, so fs-route chunks showed up as bare UUIDs in dev tools. Extracted a shared registerDeferredPayload() helper used by both, so dev chunk payloads are now named like fs-route_lang_page_tsx_en-<uuid> and the logic lives in one place.

Type-import consistency (runtime.tsx)

Replaced React.ComponentType / React.ReactNode UMD-global namespace references with the types the file already imports explicitly, matching the repo's verbatimModuleSyntax convention.

Audit notes (no change needed)

  • Verified against @funstack/router 1.4.0 that the matcher backtracks out of a matched parent whose children all fail (sibling group layouts sharing a path can't shadow each other's pages), and that match params are decodeURIComponent-ed, so percent-encoded URLs round-trip to build-time param values on soft navigation.
  • Probed adapter validation edges ([...], [[slug]], bracket-containing literal directory names — URLPattern treats [ literally) and found no holes.
  • Dev-mode chunk registration growth is already bounded by the evictStale policy (Dev server: deferRegistry grows unboundedly across requests #144), with the hard-navigation fallback covering evicted chunks.

Testing

  • 3 new unit tests for dedupe/collision behavior; all 145 unit tests pass
  • pnpm typecheck, pnpm lint, pnpm format:check, and the full monorepo build pass
  • All 17 fs-routing production e2e tests and all 32 dev-server e2e tests pass

🤖 Generated with Claude Code

https://claude.ai/code/session_012FqKsH47SihjqPZQ19ioR2


Generated by Claude Code

… params

Audit follow-ups for the file-system router:

- collectStaticPaths() now collapses duplicate params returned by one
  generateStaticParams() into a single page, and fails the build with an
  error naming both files when two different routes generate the same URL
  (e.g. a static page next to a dynamic sibling resolving to it). Both
  cases previously surfaced only as a generic "Duplicate entry path" build
  error, and not at all on the dev server.
- The fs-routes chunk host now registers payloads through a shared
  registerDeferredPayload() helper extracted from defer(), so dev payload
  file names include the sanitized route name for debugging instead of a
  bare UUID, and the ID-generation logic lives in one place.
- runtime.tsx references imported ComponentType/ReactNode types instead of
  the React UMD-global namespace, matching the repo's explicit-type-import
  convention.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012FqKsH47SihjqPZQ19ioR2
@uhyo
uhyo merged commit 646e0f0 into master Aug 30, 2026
2 checks passed
@uhyo
uhyo deleted the claude/file-system-router-audit-sthtts branch August 30, 2026 01:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants