站点配置配置 talizen.config.ts

配置 talizen.config.ts

面向 AI 智能体的站点配置规范:字段分组与求值时机、html/body 标签属性、head/bodyEnd 注入代码、按请求的 (ctx) => 值。

当 AI 编程智能体需要配置站点级行为(依赖、路由、metadata、文档外壳、注入代码)时,请遵循本规范。talizen.config.ts 是站点级配置的唯一入口,以下规则是实现约束,不是可选的界面建议。

它管什么,不管什么

判据只有一句:有自己 URL 的产物是文件,没有 URL、作用于所有页面的是配置。

配置

talizen.config.ts 里的字段没有独立 URL,它们是每个页面 HTML 里的站点级部分。

端点文件

/robots.ts/robots.txt/sitemap.ts/sitemap.xml/llms.ts/llms.txt。各自产出一个独立响应体,所以是文件。

页面文件

/pages/about.tsx/about。页面自己的 metadata / generateMetadata 只作用于这一个页面。

没有 layout.tsx

平台不存在这个约定文件。写了不会报错,会被静默忽略。文档外壳用下面的 html / body / head / bodyEnd 表达。

字段与求值时机

配置文件必须 export default 一个普通对象,不要导入包(import type 除外),不要使用 defineConfig

字段分两组,界线是「平台是否在请求之前就需要它」:

  • 必须是静态值:importMapi18nredirects。打包与建路由都发生在任何请求之前。
  • 可以写成 (ctx) => 值metadatahtmlbodyheadbodyEndviewport。它们只塑造最终 HTML,因此可以按请求求值。

硬性要求:importMapi18nredirects 写成函数会在站点加载时报错。这三个字段在请求之前就要被读取,函数会被整段忽略 —— 页面看起来正常,配置等于没生效,所以平台选择直接报错而不是静默降级。

标准配置

import type { TalizenConfig, TalizenConfigContext } from 'talizen'

export default {
  // 必须静态:打包与建路由发生在请求之前
  importMap: {
    imports: { 'framer-motion': 'https://esm.talizen.com/framer-motion@12' },
  },
  i18n: {
    defaultLocale: 'zh-CN',
    locales: ['zh-CN', 'en'],
  },
  redirects: [
    { source: '/old', destination: '/new', permanent: true },
  ],

  // 文档输出:可以是值,也可以是 (ctx) => 值
  metadata: (ctx: TalizenConfigContext) => ({
    title: {
      template: ctx.locale === 'en' ? '%s | Acme' : '%s | Acme',
      default: 'Acme',
    },
    description: ctx.locale === 'en' ? 'English description' : '中文描述',
  }),

  html: { className: 'dark scroll-smooth' },
  body: { className: 'antialiased bg-neutral-950 text-neutral-100' },

  head: (ctx: TalizenConfigContext) =>
    ctx.host.endsWith('.cn')
      ? '<script async src="https://hm.baidu.com/hm.js?xxx"></script>'
      : '<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXX"></script>',

  bodyEnd: '<script src="/widget.js" defer></script>',
} satisfies TalizenConfig

satisfies TalizenConfig 而不是 as:前者在编译期就能拦住"把静态字段写成函数"这类错误,后者会把错误压掉。

html 与 body:文档标签属性

htmlbody 是标签属性对象,直接落在服务端渲染出的 <html> / <body> 上。

  • classNameclass 等价,写哪个都可以。
  • 不要写 lang:平台按当前语言自动填充。只有在需要覆盖平台推导时才显式给出。
  • 属性名与值都会被 HTML 转义。
  • 这是暗色模式类名的正确位置:写在配置里的属性直接进服务端 HTML,没有客户端脚本造成的闪烁。

硬性要求:共享的头部导航、页脚等需要交互的 UI 不属于这里。文档外壳是静态声明,给不了水合;把它们写成组件由页面 import

head 与 bodyEnd:注入代码

两者都是 HTML 字符串:head 注入 </head> 之前,bodyEnd 注入 </body> 之前。

它们取代 customCode.head / customCode.body,关键差别是可以按请求求值 —— 例如按域名给不同的统计脚本,这是静态 customCode 表达不了的。

  • 注入顺序为:平台标签 → customCodehead / bodyEnd。因此新写法可以覆盖旧写法。
  • 第三方脚本请自行写 asyncdefer,否则 head 里的同步脚本会拖慢首屏渲染。
  • 不要在这里重复 metadata 已经能表达的标签(titledescription、Open Graph),否则会输出两份。

按请求求值的 ctx

写成函数的字段会在每次渲染时被调用,入参只有以下字段:

interface TalizenConfigContext {
  locale: string                 // 当前语言,单语言站点为空字符串
  locales?: string[]             // i18n.locales
  defaultLocale?: string         // 内容基准语言
  routingDefaultLocale?: string  // 当前域名的无前缀默认语言
  host: string                   // 请求域名,用于按域名分支
  path: string                   // 去掉语言前缀的请求路径
}

硬性要求:ctx 刻意不提供 cookies 与 CMS 访问。读 cookie 会让页面按 cookie 分桶缓存;配置层取数据则无法参与缓存失效。不要在配置字段里取数据,保持它们同步且简单。函数抛错时请求会失败,而不是静默丢掉站点级配置。

metadata 分层

站点级 metadata 由配置提供,页面 metadata 叠在它之上:

  • title.template 写在配置里,会套用页面给出的字面标题。
  • title.default 原样输出,套用 template —— 它是"页面没给标题时用这个"。
  • 页面 title: { absolute: '…' } 绕过 template。
  • 需要按语言给站点 metadata 时,把 metadata 写成函数并用 ctx.locale 分支。metadata._i18n 仍然可用但已废弃,新代码不要用。

实现检查清单

  • export default 一个普通对象,不导入包(import type 除外),不使用 defineConfig
  • importMapi18nredirects 为静态值。
  • 只在确实需要按 localehost 分支时才把字段写成函数;不需要就直接写值。
  • html / body 不写 lang
  • 第三方脚本带 asyncdefer
  • satisfies TalizenConfig 让类型在编译期兜住。
  • 推送后在真实预览 URL 上查看页面源码,确认 <html> / <body> 属性、<title>、注入脚本的位置都符合预期;多语言站点逐个语言前缀验证一遍。

禁止的实现

  • 不要创建 layout.tsx:平台没有这个约定,写了会被静默忽略。
  • 不要把 importMapi18nredirects 写成函数。
  • 不要在配置字段里取数据(CMS、fetch、读 cookie)。
  • 不要在 head / bodyEnd 里手写 metadata 已能表达的 SEO 标签。
  • 不要为了新功能继续用 customCode:它是静态的,无法按语言或域名分支。
  • 不要把共享的交互式头尾放进文档外壳;它们是页面 import 的组件。
  • 不要把 viewport 写在 metadata 里,它是独立的顶层字段。

Render diagnostics