Skip to content

fix(cms): normalize trailing slashes in page paths#92

Closed
JonasJesus42 wants to merge 1 commit intomainfrom
JonasJesus42/normalize-page-path
Closed

fix(cms): normalize trailing slashes in page paths#92
JonasJesus42 wants to merge 1 commit intomainfrom
JonasJesus42/normalize-page-path

Conversation

@JonasJesus42
Copy link
Copy Markdown
Contributor

@JonasJesus42 JonasJesus42 commented Apr 24, 2026

Summary

CMS pages created in the admin with a path like /ofertas/datas-promocionais/ were effectively unreachable:

  1. TanStack Router redirects /foo//foo at the edge (307).
  2. After the redirect, findPageByPath("/foo") had to match a block whose page.path was /foo/.

The existing matchPath in loader.ts happened to work for both forms due to a filter(Boolean) side-effect on split("/"), so the page DID render. But page.path was still stored with the trailing slash, which leaked into:

  • src/sdk/sitemap.ts:87 — emitted sitemap URLs ending in /, which crawlers would then see 307 → wasted crawl budget and duplicate-URL noise.
  • src/cms/resolve.ts:1438 — the path field on DecoPageResult carried the un-normalized value to callers (pagePath on the resolved page).

Real-world case that triggered this audit: pages-Datas Promocionais-670910 was created in admin with path: /ofertas/datas-promocionais/. TanStack site rendered it (lucky), but the original Fresh/Deno site 404d (no filter(Boolean) side-effect there).

Changes

  • Add normalizePagePath(path) — strips a single trailing slash, preserves /.
  • Apply in getAllPages() — every consumer now sees the canonical form.
  • Apply in matchPath() — make the normalization explicit instead of relying on the implicit filter(Boolean) behavior.
  • Unit tests covering: trailing slash on the block, trailing slash on the URL, interaction with :param routes, and getAllPages exposure.

Test plan

  • npx vitest run src/cms/loader.test.ts — 8/8 pass (new file)
  • npx vitest run — 75/75 pass, no regressions
  • npx biome check src/cms/loader.ts src/cms/loader.test.ts — clean (1 pre-existing info on an unrelated line)
  • npm run typecheck — same 47/48 pre-existing errors in scripts/migrate/, no new errors

🤖 Generated with Claude Code


Summary by cubic

Normalize CMS page paths by stripping a trailing slash (except /) so routes match the router’s redirect and all consumers see a canonical URL. Fixes unreachable pages saved with a trailing slash and removes 307s from the sitemap and DecoPageResult.path.

  • Bug Fixes
    • Added normalizePagePath(path) to strip a single trailing slash, preserving /.
    • Applied in getAllPages() so exposed pages use the canonical path.
    • Normalized both sides in matchPath() for explicit, consistent matching.
    • Added tests for trailing-slash URLs/blocks, param routes, and getAllPages() output.

Written for commit 4f53ee9. Summary will update on new commits.

Admin editors sometimes save page blocks with a path ending in "/" (e.g.
/ofertas/datas-promocionais/). TanStack Router strips the trailing slash
at the edge (307 to the no-slash form) before matching, so the page was
only reachable via the exact form in the block, which the router never
delivered.

The existing matchPath happened to work for both URL forms because of a
filter(Boolean) side-effect, but page.path was still stored with the
trailing slash, which leaked into sitemap.ts:87 (URLs in the sitemap
would 307 on crawl, wasting budget) and resolveDecoPage result path
(pagePath on the resolved page). Normalize once at load time so every
downstream consumer sees the canonical no-trailing-slash form, and make
matchPath explicit about the normalization as defense-in-depth.

Root path "/" is preserved as-is.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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.

1 participant