WORK-267
ID:WORK-267Status:done

contributePages pipeline phase

Add the Plugin.contributePages hook and a contribution phase in the content loader (after file-page registration, before aggregation) that collects ContributedPage[] and runs them through the normal pipeline — the underlying primitive both the entityRoutes adapter and third-party plugins build on.

Priority:highComplexity:complexMilestone:v0.16.0Source:SPEC-069
claude/v0.16.0 View source

Criteria completion

Criteria completion: 0 of 6 (0%) checked; tracking started on May 25, no incremental history yet0%25%50%75%100%May 25Jun 15

Tracking started May 25 — check back for trends.

Branches 2
claude/v0.16.0 current done
claude/milestone-v0.16.0 readymain ready
History 2
  1. 020ab26
    Created (done)by bjornolofandersson
  2. 7ab7615
    Content editedby Claude
    plan: add v0.16.0 milestone and work breakdown for SPEC-069/070/071

Acceptance Criteria

  • Plugin.contributePages interface defined (optional; sync or async; context with registry, projectRoot, site config, pipeline ctx)
  • Contribution phase runs after file-page registration, before aggregation; contributed pages flow through parse + transform + register + aggregate + postProcess identically to file pages
  • ContributedPage shape { url, title, frontmatter, content }; output pages carry source.type === "contributed" (plugin + ruleIndex)
  • URL collisions: file-backed wins over contributed (with warning); two contributed at one URL fail the build naming both sources
  • Contributed pages appear in sitemap, search index, route enumeration, and nav-auto; resolvable by {% ref %}
  • Plugin error in contributePages → build warning, build continues with that plugin skipped; empty return is a no-op

Dependencies

None — core pipeline work; WORK-268 builds the entityRoutes adapter on top.

References

  • SPEC-069

Resolution

Completed: 2026-05-25

Branch: claude/v0.16.0

What was done

  • packages/types/src/pipeline.ts: added Plugin.contributePages hook (Phase 2.5; sync/async), ContributedPage ({ url, title?, frontmatter?, content, source? }) and ContributePagesContext (registry, projectRoot?, siteConfig?) types; added 'contribute' to the PipelineWarning phase union. Re-exported from the types index.
  • packages/content/src/pipeline.ts: runPipeline gains RunPipelineOptions and a contribution phase between register and aggregate — collects contributions from every hookSet, renders each via a loader callback, handles collisions (file-backed wins with a warning; contributed-vs-contributed errors), then runs a second register pass over the new pages so their own entities index (one level deep — no recursive contribution). Result/stat assembly switched to the combined allPages.
  • packages/content/src/site.ts: SitePage.source (file | contributed); a renderContributed callback that applies basePath, resolves the layout + tint cascade for the synthetic URL, and runs the same transform (incl. deferBody capture) as a file page; file pages tagged source.type='file'; siteConfig threaded through to contributePages.
  • Tests: packages/content/test/contribute-pages.test.ts (4) — registry visible to contributePages, contributed page rendered + post-processed + counted, file-wins collision (warn), contributed collision (error), no-op. Full content suite (155) green.

Notes

  • Sitemap/search/nav/{% ref %} inclusion is satisfied by construction — contributed pages join the same pages array and go through register/aggregate/postProcess indistinguishably from file pages.
  • The built-in entityRoutes adapter that uses this is WORK-268.