Publish status & environments
Not every page in the registry should render on every build. A page moves
through a lifecycle — planned → writing →
review → ready → published — and which statuses are visible is a
property of the environment, not the SSG. Production shows only published;
a preview build also shows sealed review/ready work so editors can see it
before it goes live.
It’s config, not SSG logic
The status→environment policy lives in gtmesh.config.yaml, and the engine’s
render-manifest applies it. The
mistake to avoid is hard-coding the same rules in the SSG — now the policy lives
in two places and drifts. Instead, the content-types package exposes the same
policy:
# gtmesh.config.yaml
environments:
local: { renders: [writing, review, published] }
dev: { renders: [writing, review, published] }
prod: { renders: [published] }import { renderableStatuses, RENDER_ENVS, viewForEnv } from "@your-project/content-types";
renderableStatuses("prod"); // ["published"]
renderableStatuses("dev"); // ["writing", "review", "published"]
viewForEnv("prod"); // "live" — the manifest link VIEW for this env
viewForEnv("dev"); // "preview"renderableStatuses(env) reads config.environments[env].renders — byte-for-byte
the set render-manifest --env <env> uses. There is no second copy to keep in
sync. viewForEnv(env) is its sibling for links: it maps the same env to the
manifest link view (live = the published/prod render set, preview = wider), so
loadPages({ view: viewForEnv(env) }) gives every page.links.* the right set for
this build — the SSG never branches on live/preview.
renderableStatuses throws on an environment that isn’t configured — a typo
shouldn’t silently render nothing. The configured set is RENDER_ENVS.
Filtering the render set
Every list/load helper takes a status filter. Feed it the environment’s set
and you get exactly the pages that should render:
const env = process.env.DEPLOY_ENV ?? "prod";
const statuses = renderableStatuses(env);
// generateStaticParams / routes:
const slugs = pageSlugs({ status: statuses });
// a typed section listing:
const guides = loadPagesByType<GuidePage>("guide", { status: statuses });Prune edges too. A page hidden in this environment shouldn’t be linked to.
When you resolve a related slug or a hub member, drop any whose page isn’t in the
render set — otherwise a prod build links to a review-only page that 404s.
Build a Set of the renderable slugs once and test membership as you traverse.
The alternative: read the manifest
If you’d rather not import the package for this — or you’re building in a
non-TypeScript stack — gtmesh render-manifest --env <env>
writes the renderable slug set to .gtmesh/render-manifest.<env>.json. Same
policy, emitted as a file your build step can read directly.
Related
- Build a website — the overview
- Navigation & relationships — and why you prune edges to non-rendered pages
- Lifecycle & reconcile — how a page reaches
published render-manifest— the command form of the same policy- Deploying on Cloudflare Workers — picking the right
envat build time when preview and prod are both “production” builds