Content Processor
The @ecopages/content-processor package scans MDX content collections at build time, validates frontmatter with Standard Schema-compatible libraries, and exposes each collection as a typed virtual module: ecopages:content/<collection>.
This docs app uses it for every page under src/content/docs/.
What the processor owns
| Layer | Owner |
|---|---|
| File discovery, frontmatter validation, manifest sort | @ecopages/content-processor |
Generated ecopages:content/* modules (entries, getComponent, …) | @ecopages/content-processor |
ContentScanner for build scripts (for example llms.txt) | @ecopages/content-processor |
| Routes, layouts, sidebar, breadcrumbs, pagination | Your app — derive from entries and frontmatter |
Installation
Peer dependency: @ecopages/core.
Configuration
Register the processor in eco.config.ts and wire MDX frontmatter into your JSX or React integration:
import { ConfigBuilder } from '@ecopages/core/config-builder';
import { ecopagesJsxPlugin } from '@ecopages/ecopages-jsx';
import { contentProcessorPlugin } from '@ecopages/content-processor/plugin';
import { withContentMdxPlugins } from '@ecopages/content-processor/mdx';
import remarkGfm from 'remark-gfm';
import { compareDocsEntries, docsFrontmatterSchema } from './src/content/docs';
export default await new ConfigBuilder()
.setRootDir(import.meta.dirname)
.setIntegrations([
ecopagesJsxPlugin({
mdx: {
enabled: true,
...withContentMdxPlugins({
remarkPlugins: [remarkGfm],
}),
},
}),
])
.setProcessors([
contentProcessorPlugin({
options: {
collections: {
docs: {
contentDir: 'content/docs',
schema: docsFrontmatterSchema,
orderBy: compareDocsEntries,
entryType: './src/content/docs#DocsFrontmatter',
},
},
},
}),
])
.build();React apps use the same helper on reactPlugin({ mdx: { enabled: true, ...withContentMdxPlugins() } }).
Collection keys must be kebab-case. Each key becomes ecopages:content/<key>.
Do not add slug or segments to your frontmatter schema — the processor derives them from the file path.
Dev prewarm
Collections can warm known static content routes during development. Add the public route prefix and choose which manifest entries to render:
docs: {
contentDir: 'content/docs',
schema: docsFrontmatterSchema,
routePrefix: '/docs',
devPrewarm: 'first',
devPrewarmReadiness: 'background',
}devPrewarm accepts 'first', 'all', { limit }, or { slugs }. The default readiness is 'background';
'beforeReady' waits until the selected paths are rendered before Ecopages reports the framework ready signal
(not before the listen port opens). Prewarming stores HTML only for the selected paths on the watch page cache
allowlist and does not enable caching for other development routes. Keep the selection small during interactive
development: 'all' renders every collection route concurrently and can compete with the page you are navigating to.
Docs app wiring
- Frontmatter schema and section config —
src/content/docs.ts - Sidebar navigation —
src/lib/content-nav.tsjoins section config with processorentries - Catch-all route —
src/pages/docs/[...slug]/index.tsxresolves entries and renders MDX - MDX component dependencies — each document imports the components it renders
- Layout chrome —
src/layouts/docs-layout/
Example frontmatter:
---
title: Introduction
description: Get started with Ecopages.
order: 1
---Catch-all render pattern:
import { getComponent } from 'ecopages:content/docs/server';
const Content = await getComponent('getting-started/introduction');
return await Content();Virtual module API
Each collection exposes ecopages:content/<collection> for metadata and ecopages:content/<collection>/server for MDX components:
import { entries, getEntry, getEntryBySegments } from 'ecopages:content/docs';
import { getComponent } from 'ecopages:content/docs/server';
import type { Entry } from 'ecopages:content/docs';| Export | Module | Description |
|---|---|---|
entries | entries | Readonly manifest, sorted by orderBy |
getEntry(slug) | entries | Lookup by joined slug, e.g. 'getting-started/introduction' |
getEntryBySegments(segments) | entries | Lookup by segment array |
getComponent(slug) | server | Promise of the MDX component for the entry |
Entry | entries | Type only — frontmatter fields plus slug and segments |
Keep Entry on a separate import type line in bundled page files.
Package exports
| Import | Purpose |
|---|---|
@ecopages/content-processor | ContentScanner, sort helpers, shared types |
@ecopages/content-processor/plugin | contentProcessorPlugin() for eco.config.ts |
@ecopages/content-processor/types | ContentEntry, ContentCollectionModule, EntryComparator |
@ecopages/content-processor/mdx | remarkFrontmatter, withContentMdxPlugins() |
TypeScript
Add one import to the app modules.d.ts:
import '@ecopages/content-processor/types';Run ecopages dev or ecopages build before expecting IDE types for ecopages:content/* imports.
Related
- Custom Processor — authoring processors
- Plugin Lifecycle — when processor hooks run
- MDX Integration — MDX compile pipeline
- docs-starter template — minimal content-processor setup