feat(core): add breadcrumbs field to PublicPageContext#414
feat(core): add breadcrumbs field to PublicPageContext#414jdevalk wants to merge 1 commit intoemdash-cms:mainfrom
Conversation
Themes can now publish a breadcrumb trail alongside the rest of the page context, and SEO plugins (or any other page:metadata consumer) can read it verbatim instead of inventing their own per-theme override mechanism. The field is optional and non-breaking: - undefined — theme has no opinion; consumer falls back to its own derivation (path walking, rule maps, etc). - [] — explicit opt-out; consumer should skip BreadcrumbList emission entirely (e.g. homepages, error pages). - Non-empty array — used verbatim by consumers for BreadcrumbList schema output. BreadcrumbItem is also exported from the emdash package root so plugins can import the type directly. Why the context and not a plugin config callback? The descriptor options round-trip through JSON.stringify in generatePluginsModule (packages/core/src/astro/integration/virtual-modules.ts:200), so functions are stripped before they reach the runtime plugin. Pushing the data onto the page context is both technically necessary and semantically cleaner — it's observable, debuggable, and usable by any hook consumer rather than coupling to one plugin's config shape. Refs emdash-cms#413 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: 924c614 The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/blocks
@emdash-cms/cloudflare
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
There was a problem hiding this comment.
Pull request overview
Adds first-class breadcrumb support to the core public page context so themes can provide an explicit breadcrumb trail and page:metadata consumers (e.g. SEO plugins) can use it verbatim.
Changes:
- Introduces
BreadcrumbItemand adds optionalbreadcrumbs?: BreadcrumbItem[]toPublicPageContext, including documented consumer semantics (undefinedvs[]vs non-empty). - Extends
createPublicPageContextto accept/pass throughbreadcrumbsfrom template input. - Exports
BreadcrumbItemfrom the package root and adds unit tests + a changeset for the minor bump.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
packages/core/src/plugins/types.ts |
Defines BreadcrumbItem and adds breadcrumbs? to PublicPageContext with a clear consumer contract. |
packages/core/src/page/context.ts |
Accepts breadcrumbs on input and passes it through to the returned PublicPageContext. |
packages/core/src/index.ts |
Re-exports BreadcrumbItem from the package root for downstream consumers. |
packages/core/tests/unit/plugins/page-context.test.ts |
Adds coverage for pass-through, default undefined, and explicit empty-array preservation. |
.changeset/public-page-context-breadcrumbs.md |
Declares a minor version bump and describes the new API surface/semantics. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Overlapping PRsThis PR modifies files that are also changed by other open PRs:
This may cause merge conflicts or duplicated work. A maintainer will coordinate. |
|
Yes please!! |
Summary
Adds an optional
breadcrumbs?: BreadcrumbItem[]field toPublicPageContextso themes can publish a breadcrumb trail as part of the page context, and SEO plugins (or any otherpage:metadataconsumer) can read it verbatim instead of inventing their own per-theme override mechanism.Closes #413.
What changes
packages/core/src/plugins/types.ts— newBreadcrumbIteminterface andbreadcrumbs?field onPublicPageContext, with doc comment describing theundefined/[]/ non-empty semantics for consumers.packages/core/src/page/context.ts—createPublicPageContextnow accepts and passes throughbreadcrumbsfrom template input.packages/core/src/index.ts—BreadcrumbItemexported from theemdashpackage root alongsidePublicPageContext.packages/core/tests/unit/plugins/page-context.test.ts— three new tests covering verbatim pass-through, default-undefined, and explicit empty-array preservation..changeset/public-page-context-breadcrumbs.md—minorbump foremdash.Consumer contract
When
breadcrumbsis present on the context, SEO plugins should:BreadcrumbListschema emission.breadcrumbs: []as "this page has no breadcrumbs" (e.g. homepage, error pages) and skip emission.When
breadcrumbsisundefined, plugins fall back to whatever they do today (path derivation, rule maps, etc).Why the context and not a plugin config callback?
I considered that first. It doesn't work through the current plugin loader:
generatePluginsModuleatpackages/core/src/astro/integration/virtual-modules.ts:200serializesdescriptor.optionswithJSON.stringify, so functions are stripped before they reach the runtime plugin. Any theme-supplied callback would be silently dropped.Pushing the data onto the page context is cleaner anyway — it's observable, debuggable, and usable by any
page:metadataconsumer rather than coupling to one plugin's config shape.Migration
Non-breaking:
undefined.Downstream consumer
@jdevalk/emdash-plugin-seov0.3.0 ships with path derivation, segment-label overrides, and per-pageTyperule maps as its existing fallback chain. Once this lands, it will consumepage.breadcrumbsas Layer 0 (highest priority) above all of those.Test plan
pnpm --filter emdash typecheckpassesnpx vitest run tests/unit/plugins/page-context.test.ts— 11 passed (3 new)npx vitest run tests/unit/plugins/— 530 passed across 23 files🤖 Generated with Claude Code