Ecopages0.2.0-rc.1

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

LayerOwner
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, paginationYour app — derive from entries and frontmatter

Installation

npm install @ecopages/content-processor

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 configsrc/content/docs.ts
  • Sidebar navigationsrc/lib/content-nav.ts joins section config with processor entries
  • Catch-all routesrc/pages/docs/[...slug]/index.tsx resolves entries and renders MDX
  • MDX component dependencies — each document imports the components it renders
  • Layout chromesrc/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';
ExportModuleDescription
entriesentriesReadonly manifest, sorted by orderBy
getEntry(slug)entriesLookup by joined slug, e.g. 'getting-started/introduction'
getEntryBySegments(segments)entriesLookup by segment array
getComponent(slug)serverPromise of the MDX component for the entry
EntryentriesType only — frontmatter fields plus slug and segments

Keep Entry on a separate import type line in bundled page files.

Package exports

ImportPurpose
@ecopages/content-processorContentScanner, sort helpers, shared types
@ecopages/content-processor/plugincontentProcessorPlugin() for eco.config.ts
@ecopages/content-processor/typesContentEntry, ContentCollectionModule, EntryComparator
@ecopages/content-processor/mdxremarkFrontmatter, 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