Validate service and industry content before any page is prerendered #230

Closed
opened 2026-09-10 04:13:35 -05:00 by null · 1 comment
Owner

What is true now. Nothing checks the content layer. A misspelt block type, a missing field or a link to a route that does not exist is found by a visitor. The fallback layout maps benefits and idealFor with no guard, so a missing list crashes the prerender with an unhelpful TypeError. scripts/prerender.js keeps its own route list, so a route added only to src/routes.jsx is never prerendered, and the server answers it with a 404.

What to do.

  • scripts/lib/routes.js: the pure route list, used by prerender. The build fails if a router path is missing from it.
  • scripts/lib/content.js (validateContent), run inside prerender before any page renders, so every pre-commit build and every image build refuses bad content; plus scripts/validate-content.js as a runner for proofs.
  • Rules:
    • required fields in both the long-form and fallback shapes
    • known block types only
    • ids match ^[a-z][a-z0-9]*(-[a-z0-9]+)*$, are unique, and do not collide with contact-form, mobile-nav-content or support-portal
    • the opening block matches the section kind: default p, list, or faq with every h3 followed by a p
    • links resolve to real routes and fragments
    • an image src exists under public/, with alt, width and height
    • no U+2014 or U+FFFD, no HTML tags, no **, backticks or ](, and no sheet directions
  • Warn only when an approved description is over 160 characters. Exit 0, 1, or 2 for nothing checked.

Traps. Content modules are leaf modules: they cannot import services.js (circular), and import.meta.glob fails in Node, where prerender imports the data. Nesting under one page key stops content overriding id or name.

Verify: node scripts/validate-content.js exits 0 on the real content, and each proof mutation (a deleted benefits list, a restored em dash, a sheet direction, type: 'pp', an answer moved below its list, a bad id, a link to /services/contact-centre, #no-such-section, a missing image file) fails npm run build with exactly one finding.

**What is true now.** Nothing checks the content layer. A misspelt block type, a missing field or a link to a route that does not exist is found by a visitor. The fallback layout maps `benefits` and `idealFor` with no guard, so a missing list crashes the prerender with an unhelpful TypeError. `scripts/prerender.js` keeps its own route list, so a route added only to `src/routes.jsx` is never prerendered, and the server answers it with a 404. **What to do.** - `scripts/lib/routes.js`: the pure route list, used by prerender. The build fails if a router path is missing from it. - `scripts/lib/content.js` (`validateContent`), run inside prerender before any page renders, so every pre-commit build and every image build refuses bad content; plus `scripts/validate-content.js` as a runner for proofs. - **Rules:** - required fields in both the long-form and fallback shapes - known block types only - ids match `^[a-z][a-z0-9]*(-[a-z0-9]+)*$`, are unique, and do not collide with `contact-form`, `mobile-nav-content` or `support-portal` - the opening block matches the section kind: default `p`, `list`, or `faq` with every h3 followed by a p - links resolve to real routes and fragments - an image `src` exists under `public/`, with alt, width and height - no U+2014 or U+FFFD, no HTML tags, no `**`, backticks or `](`, and no sheet directions - Warn only when an approved description is over 160 characters. Exit 0, 1, or 2 for nothing checked. **Traps.** Content modules are leaf modules: they cannot import `services.js` (circular), and `import.meta.glob` fails in Node, where prerender imports the data. Nesting under one `page` key stops content overriding `id` or `name`. Verify: `node scripts/validate-content.js` exits 0 on the real content, and each proof mutation (a deleted benefits list, a restored em dash, a sheet direction, `type: 'pp'`, an answer moved below its list, a bad id, a link to `/services/contact-centre`, `#no-such-section`, a missing image file) fails `npm run build` with exactly one finding.
null added this to the Batch 18: Guards and landmines found building Batch 17 milestone 2026-09-10 04:13:35 -05:00
null added the
infra
content
enhancement
labels 2026-09-10 04:13:35 -05:00
null closed this issue 2026-09-10 04:43:07 -05:00
Author
Owner

Done in 0d575f2. scripts/lib/content.js validates the content layer and prerender.js runs it before rendering any page, so the pre-commit hook, npm run verify and the Docker image build all enforce it; scripts/validate-content.js is the standalone runner. scripts/lib/routes.js is now the single route list, and entry-server.jsx exports the router table so the build fails on drift between them. Proven by 17 mutations, each producing exactly one finding: unknown block type, FAQ answer removed, answer moved below its list, duplicate id, digit-leading id, /services/contact-centre, #no-such-section, missing image file, image without dimensions, image without alt, em dash, a website-manager direction, markdown bold, U+FFFD, HTML tag, missing hero h1, empty section; both generated content modules pass unmutated. Against a real build: an em dash in industries.js failed npm run build naming the field, and a /pricing route in src/routes.jsx failed it naming the route. Harness: scratchpad/prove-content.mjs.

Done in 0d575f2. scripts/lib/content.js validates the content layer and prerender.js runs it before rendering any page, so the pre-commit hook, npm run verify and the Docker image build all enforce it; scripts/validate-content.js is the standalone runner. scripts/lib/routes.js is now the single route list, and entry-server.jsx exports the router table so the build fails on drift between them. Proven by 17 mutations, each producing exactly one finding: unknown block type, FAQ answer removed, answer moved below its list, duplicate id, digit-leading id, /services/contact-centre, #no-such-section, missing image file, image without dimensions, image without alt, em dash, a website-manager direction, markdown bold, U+FFFD, HTML tag, missing hero h1, empty section; both generated content modules pass unmutated. Against a real build: an em dash in industries.js failed npm run build naming the field, and a /pricing route in src/routes.jsx failed it naming the route. Harness: scratchpad/prove-content.mjs.
Sign in to join this conversation.
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: null/Queue-North-Website#230
No description provided.