@ coupages/content-处理器
Ecopages的构建时间内容收藏. 在构建时间扫描MDX(或其他已配置的扩展),验证前题,并曝光每个收藏作为打入的虚拟模块: ecopages:content/<collection>。 。 。 .
心理模式
捐款使用一个 plugins(app) 方法。 收集模块和类型生成于 setup(),而一个能解决收藏的建筑首先等待同代. ecopages build 向 dist/server/ 并登记在 server-modules.json生产 ecopages start 从地图恢复,不调用 setup()。当编辑 MDX 文件时,开发仍然会重新生成收藏。 服务器解析器和浏览器解析器声明单独的环境,因此浏览器编译无法通过注册命令选择服务器执行.
- 申报收藏 内
eco.config.ts——内容目录,前题计划,和排序顺序. - 构建时扫描 – 处理器验证前题并写入生成的模块
.eco/cache/无效的前题失败ecopages build而不是第一个请求。 制作复制汇编的收集模块dist/server/。 。 。 . - 导入虚拟模块 ——使用页和版式
ecopages:content/<collection>页:1entries,元数据,以及MDX组件. - 拥有你的前题 - 以任意 标准Schema-相容图书馆(佐德,瓦利博特,ArkType等). 处理器添加
slug财务报告和审定财务报表segments从文件路径.
这个软件包拥有什么
| 层 | 拥有者 |
|---|---|
| 文件发现、 前题验证、 显示排序 | @ecopages/content-processor |
已生成 ecopages:content/* 模块entries, (中文(简体) ), getComponent, (...) (中文(简体) ) | @ecopages/content-processor |
ContentScanner 用于发布应用程序脚本 llms.txt | @ecopages/content-processor |
路线eco.page),布局,元数据,URL形状 | 你的应用 |
| 面条、面包屑、花纹 | 你的应用程序——来自 entries 和前题字段,例如 title, (中文(简体) ), group,以及 order |
处理器在构建时会给您一个打入的列表和MDX组件. 以你对其它数据来源的同样方式 将它们连接到Ecopage的路径和UI中.
特征
- 构建时间扫描 ——内容元数据在页面捆绑船前解决;MDX入门模块按服务器上每发子弹的要求加载.
- 标准Schema验证 ——前题计划留在您的应用程序中;库通过标准Schema接口验证.
- 输入的虚拟模块 —— 说
ecopages:content/<collection>生成Entry类型. - 多个收藏 —docs, blog, changelog, 或任何您配置的按键收藏 .
ContentScanner——在一次性构建脚本中再用相同的扫描/验证路径. 扫描器不写llms.txt;应用程序脚本有.
在开发过程中,编辑到一个条目的 MDX 机构会使其编译的服务器收藏捆绑无效,即使其前题和生成的显示没有变化. 下一个请求重建了这个捆绑,使新的身体.
内容处理器拥有已编译的收藏文物及其无效生成. Core提供共享的服务器-模块构建路径;收集身份,源跟踪,并缓存寿命与内容处理器同住.
在一个收藏目录中共同设置非输入文件( 例如) survey-form.config.ts,帮助模块,或JSON数据)也使已编译的服务器集无效而无需重写生成的条目列表.
安装
bun add @ecopages/content-processor
同伴依赖 : @ecopages/core。 。 。 .
配置
将处理器注册到 eco.config.ts数字 :
import { defineConfig } from '@ecopages/core/config';
import { contentProcessorPlugin } from '@ecopages/content-processor/plugin';
import { compareEntriesByField } from '@ecopages/content-processor';
import { blogFrontmatterSchema } from './src/content/blog-schema';
import { docsFrontmatterSchema } from './src/content/docs-schema';
export default defineConfig({
processors: [
contentProcessorPlugin({
options: {
collections: {
docs: {
contentDir: 'content/docs',
orderBy: compareEntriesByField('order'),
schema: docsFrontmatterSchema,
entryType: './src/content/docs-schema#DocsFrontmatter',
},
blog: {
contentDir: 'content/blog',
orderBy: compareEntriesByField('title'),
schema: blogFrontmatterSchema,
entryType: './src/content/blog-schema#BlogFrontmatter',
extensions: ['.mdx', '.md'],
},
},
},
}),
],
});
收藏选项
| 选项 | 需求 | 说明 |
|---|---|---|
contentDir | 对 | 相对于应用的目录 srcDir,比如说. content/docs。 。 。 . |
schema | 对 | 前题的标准Schema验证器 . |
entryType | 没有 | 用于生成的虚拟模块类型的前题类型 。 格式 : ./path/to/schema#TypeName。处理器将其包装为 ContentEntry<YourFrontmatter>。 。 。 . |
orderBy | 没有 | 显示排序的比较函数 。 默认 :\\ link 比较 EntertriesBySlug}. 对前题字段使用 QQlink 比较 EnterriesBy Field . |
extensions | 没有 | 需要扫描的文件扩展名 。 默认 : ['.mdx']多点扩展.radiant.mdx, (中文(简体) ), .react.mdx)与最长的顺序匹配;声明令无关. |
routePrefix | 没有 | 条目的公共 URL 前缀, 例如 . /docs用于 devPrewarm。 。 。 . |
devPrewarm | 没有 | 预温 : 'first', (中文(简体) ), 'all', (中文(简体) ), { slugs },或 { limit }. HMR 已准备好的管道存在之后的 SSR 核心预温; 预温时间表只生成且不控制 HTML 缓存录入 . |
收藏密钥必须是 kebab 大小写 (docs, (中文(简体) ), api-reference) (中文(简体) ). 每个键变成 ecopages:content/<key>。 。 。 .
前事计划(应用程序)
在应用程序中定义策略和前题类型 。 Zod 4是一个常见的选择:
// src/content/docs-schema.ts
import { z } from 'zod';
export const docsFrontmatterSchema = z.object({
title: z.string(),
description: z.string(),
group: z.string().optional(),
order: z.coerce.number().optional(),
});
export type DocsFrontmatter = z.infer<typeof docsFrontmatterSchema>;
重要内容: 不添加 slug 或者说 segments 你的计谋。 处理器从文件路径中获取并曝光它们 ContentEntry<T>数字 :
import type { ContentEntry } from '@ecopages/content-processor';
type DocsEntry = ContentEntry<DocsFrontmatter>;
// DocsEntry includes: title, description, group?, order?, slug, segments
点 entryType 地点 前题类型 (单位:千美元)DocsFrontmatter),不是手卷式输入类型.
类型脚本设置
在应用程序中添加一个导入 modules.d.ts数字 :
import '@ecopages/content-processor/types';
也就是说 ecopages:content/*, (中文(简体) ), /server,以及 /component 立即解决进口问题.
收集的具体声明在编程/构建时间生成到
node_modules/@types/ecopages-content-processor,哪些类型脚本自动加载.
运行 ecopages dev 或者说 ecopages build 在等待精确的收集条目类型之前。 重新启动 TypeScript 服务器,如果生成的类型在更改了计划或收藏配置后看起来是 stale .
虚拟模块 API
每个收藏都曝光 ecopages:content/<collection> 元数据, ecopages:content/<collection>/server 仅用于服务器依赖性发现,以及 ecopages:content/<collection>/component 对于在服务器和浏览器上工作的懒惰的MDX组件加载器:
import { entries, getEntry, getEntryBySegments } from 'ecopages:content/docs';
import { getComponent, getEntryDependencies } from 'ecopages:content/docs/server';
import { loadComponent } from 'ecopages:content/docs/component';
import type { Entry } from 'ecopages:content/docs';
| 导出 | 模块 | 说明 |
|---|---|---|
entries | 条目 | 只读所有条目的列表,按 orderBy。 。 。 . |
getEntry(slug) | 条目 | 通过联合弹道搜索,例如:. 'getting-started/intro'投篮 HttpError.NotFound 当失踪. |
getEntryBySegments(segments) | 条目 | 按段数组查询,例如:. ['getting-started', 'intro']投篮 HttpError.NotFound 当失踪. |
getComponent(slug) | 服务器 | Promise 用于输入的 MDX 组件(每发子弹装入液态)。 拒绝使用 HttpError.NotFound 当失踪. |
getEntryDependencies(slug) | 服务器 | 回返 { components: [entry] } 所以收集会走 MDX 身份。 不复制依赖包。 掷出 HttpError.NotFound 当失踪. |
loadComponent(slug) | 组件 | Promise 用于条目的 MDX 组件。 服务器图:为交叉集成渲染所包裹的条目. 页面浏览器图:由浏览器编译的条目. 拒绝使用 HttpError.NotFound 当失踪. loadComponent.collection 是收藏名称,当HMR再次评价页面时,它保持不变。 与 createLazyCollectionComponents() 从 @ecopages/react/lazy-collection-components 当一个可水合反应页面生成组件时. |
Entry | 条目 | 仅输入. ContentEntry<YourFrontmatter> 前题字段加 slug 财务报告和审定财务报表 segments。 。 。 . |
/component 是一个有两项决议的规格。 服务器图形以相同的服务器模块备份它 /server,因此,一个导入它的页,每个弹片共享一个组件 getComponent()。在同样加载浏览器中的条目的Pages中使用它,例如可水合反应页面. /component 没有发现资产:需要该条目自己的资产返回的页面 getEntryDependencies() 从 /server。页面浏览器 Graph 将其与浏览器加载器相背并仍然拒绝 /server因此导入 /component 从未使服务器代码浏览器可访问 .
重要内容: Entry 仅存在于生成中 .d.ts 文件,不是运行时缓存模块。 将类型导入单独保存 import type 在被捆绑的页面文件中的行。 混合进口如 import { entries, type Entry } 会导致捆绑者治疗 Entry 作为运行时间导出并失败 MISSING_EXPORT。 。 。 .
页面使用量
定义全程或每入场路线 eco.page从虚拟模块中导入收藏清单和MDX组件 :
import { eco } from '@ecopages/core';
import { entries, getEntryBySegments } from 'ecopages:content/docs';
import { getComponent, getEntryDependencies } from 'ecopages:content/docs/server';
import type { Entry } from 'ecopages:content/docs';
export default eco.page<{ entry: Entry }>({
staticPaths: async () => ({
paths: entries.map((post) => ({
params: { slug: post.segments },
})),
}),
staticProps: async ({ pathname }) => {
const segments = Array.isArray(pathname.params.slug)
? pathname.params.slug
: [pathname.params.slug];
return {
props: { entry: getEntryBySegments(segments) },
};
},
dependencies: async ({ props }) => getEntryDependencies(props.entry.slug),
metadata: ({ props: { entry } }) => ({
title: entry.title,
description: entry.description,
}),
render: async ({ entry }) => {
const Content = await getComponent(entry.slug);
return <Content />;
},
});
返回时 getEntryDependencies 仅当页面本身没有相对资产时。 合并本地页面 scripts, (中文(简体) ), stylesheets,或 modules 与 mergePageDependencies() 所以每个文件都有所有权.
水相反应页面
当反应页必须将 MDX 组件保存在水合树上时, 请从中创建一个懒惰组件 /component 装填器并装入 <Suspense> 边界。 服务器在HTML中等待该条目并发送;水合物在浏览器装入该条目之前保留了HTML. getEntryDependencies() 仍然仅保留服务器,并从 Page 浏览器图形中剥离。 在开发中,Page模块的HMR使已经装入的条目在不使用悬存倒计时的情况下再次被加载,一个经过编辑的MDX文件在不重新装入文档的情况下重新发送该条目.
import { Suspense } from 'react';
import { createLazyCollectionComponents } from '@ecopages/react/lazy-collection-components';
import { getEntry } from 'ecopages:content/docs';
import { loadComponent } from 'ecopages:content/docs/component';
import { getEntryDependencies } from 'ecopages:content/docs/server';
const docs = createLazyCollectionComponents(loadComponent);
export default eco.page({
staticProps: async ({ pathname }) => ({ props: { entry: getEntry(pathname.params.slug) } }),
dependencies: ({ props }) => getEntryDependencies(props.entry.slug),
render: ({ entry }) => {
const Content = docs.get(entry.slug);
return (
<Suspense fallback={null}>
<Content />
</Suspense>
);
},
});
将边界保留在页中的固定位置。 在客户端在条目之间的导航过程中,React将当前条目保留在屏幕上,直到下一个条目被加载,只要每个弹出时边界不被重挂.
未知弹出 HttpError.NotFound,所以包罗万象 匹配 /docs/[...slug] 仍然服务于应用404页,而不是500页.
内容 MDX 条目可能导入交互演示和样式而无需手写 config.dependencies 包装 MDX加载器发现了它们:
import { WeatherApp } from '@/components/weather-app/weather-app';
import './weather-app.css';
# Weather app
<WeatherApp />
getComponent() 懒散地装入每发一发MDX模块,并返回带有加载器属性的默认组件 config 页:1 说对了 getEntryDependencies() 因此只有活动条目为页面浏览器图表提供内容.
getEntryDependencies(slug) 返回时 { components: [entry] }它没有复制 config.dependencies 未设置 ownerFile相对 scripts, (中文(简体) ), stylesheets,以及 modules 在MDX模块上宣布的解析来自该组件的特性,而不是包罗万象的路径.
将 Page- 本地相对资产与 mergePageDependencies()。两袋副本的物体分布 ownerFile 进入对方的相对路径 :
import { eco, mergePageDependencies } from '@ecopages/core';
import { getComponent, getEntryDependencies } from 'ecopages:content/posts/server';
export default eco.page({
dependencies: async ({ props }) =>
mergePageDependencies({ stylesheets: ['./post.css'] }, await getEntryDependencies(props.slug)),
render: async ({ slug }) => {
const Content = await getComponent(slug);
return <Content />;
},
});
导航( 负面)
导航是应用代码. 地图 entries 连接到使用前题和路径字段的链接
import { entries } from 'ecopages:content/docs';
export const docsNav = entries.map((entry) => ({
title: entry.title,
href: `/docs/${entry.segments.join('/')}`,
}));
在您的应用程序中分组或排序, 例如: group 前题字段和一个 order 编号。 见 apps/docs (单位:千美元)src/lib/content-nav.ts和 templates/docs-starter (单位:千美元)src/content-nav.ts),用于带节和侧边栏的全版布局.
构建脚本
使用 ContentScanner 当一个脚本需要外页列表捆绑时:
import { join } from 'node:path';
import { ContentScanner, compareEntriesByField } from '@ecopages/content-processor';
import { docsFrontmatterSchema } from '../src/content/docs-schema';
const scanner = new ContentScanner({
contentRoot: join(srcDir, 'content/docs'),
orderBy: compareEntriesByField('order'),
schema: docsFrontmatterSchema,
});
const manifest = await scanner.getManifest();
const raw = await scanner.getRawContent('getting-started/intro');
软件包出口
| 导入 | 目的 |
|---|---|
@ecopages/content-processor | ContentScanner,排序帮助者,共享类型. |
@ecopages/content-processor/plugin | contentProcessorPlugin() 页:1 eco.config.ts。 。 。 . |
@ecopages/content-processor/types | ContentEntry, (中文(简体) ), ContentCollectionModule, (中文(简体) ), EntryComparator。 。 。 . |
@ecopages/content-processor/mdx | remarkFrontmatter, (中文(简体) ), withContentMdxPlugins()。 。 。 . |
出品文物
| 路径 | 目的 |
|---|---|
.eco/cache/ecopages-content-processor/<collection>.ts | 生成收集模块(明显 + MDX 导入). |
.eco/cache/ecopages-content-processor/<collection>.browser.ts | 浏览器安全 MDX 加载器 /component 中。 磁盘上此文件的相对导入解析 . |
.eco/.server-collections/<collection>/<collection>-<identity>.mjs | 懒惰制造的服务器文物,为一个收藏品. |
node_modules/@types/ecopages-content-processor/virtual-module.d.ts | 生成虚拟模块的TypeScript声明. |
不要手动编辑生成的文件 .
校验错误
无效的前题抛出 SchemaError 从 @standard-schema/utils 在扫描/构建过程中。 修复MDX前题 或放松应用程序中的计划 在开发中,错误出现在浏览器覆盖中,并保存了固定文件在不重启服务器的情况下恢复了收藏.
Dev / 高频
开发中, 处理器将查看收藏文件以重生 ecopages:content/* 显示MDX改变时。 生成的服务器文物被懒散地建在第一个服务器导入上并被其内容衍生出的身份所再用;MDX在下个Page构建前编辑该文物无效. Collection MDX仍然是服务器源:Ecopages使服务器模块无效并运行了HMR,因此页面导入取出更新的MDX组件.
仅监视配置驱动显示再生 。 资产所有权(将跳过服务器无效)需要宣布的处理器能力. 内容处理器不主张 MDX 为资产,所以没有 capabilities 需要在 eco.config.ts。 。 。 .
MDX 整合
内容文件为MDX组件. 确保您的 JSX 或 React 集成配置于 eco.config.ts 带有 mdx 选项,将收藏项编译为组件.
YAML 前题需要的内容收藏 remark-frontmatter 输入 MDX 编译管道。处理器在扫描时间验证前题(vfile-matter); MDX 提供时间需要注释插件这样 --- 块不作为内容发布.
使用 @ecopages/content-processor/mdx 用于编辑 MDX 的集成 :
import { withContentMdxPlugins } from '@ecopages/content-processor/mdx';
import remarkGfm from 'remark-gfm';
ecopagesJsxPlugin({
mdx: {
...withContentMdxPlugins({
remarkPlugins: [remarkGfm],
rehypePlugins: [/* app-specific rehype plugins */],
}),
},
});
reactPlugin({
mdx: {
...withContentMdxPlugins(),
},
});
withContentMdxPlugins() 预支 remarkFrontmatter。当您需要更细的控制时,直接导入:
import { remarkFrontmatter } from '@ecopages/content-processor/mdx';
App 特定演示文稿插件(语法突出显示, GFM, 表格包装) 留在您的应用中—— 例如 src/mdx/plugins.ts。 。 。 .
内容输入所有权
编译 MDX 条目的集成也拥有其渲染: 由 mdx.extensions: ['.react.mdx'] 在React集成上是一个React组件,必须通过React拥有的路由(例如: [...slug].react.tsx {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}你还好吧? 从 JSX (Radiant) 或 Kita 渲染树上调用它.
生成的 getComponent() 保留该所有权的时间:直接在自己车道上引用一个条目,而受支持的外国儿童通过自己的整合使车道排队。 如果没有交接跑出时间,它随入道而抛出,拥有集成,活性集成. 当两个集成为同一集编译 MDX 时, 声明脱节 mdx.extensions 每个集成件——因为React MDX加载器从所申报的扩展中取出其过滤器,仅限 .react.mdx 使用 React 编译文件. mdx.extensions 替换 compilerOptions.mdxExtensions 而不是与它合并.