fix(theme): payload 契约上提 contracts;空路由明错;ui.routeHrefFor;eslint astro ignore

审查修复(Plan 4 Task 3 review):
- 载荷类型单一事实来源:ContentEntry/TaxonomyEntry/TaxonomyTerm/RouteEntry/
  AssetEntry/RenderManifestMeta/SiteConfigPayload/BrandConfigPayload 上提
  @sf/contracts/render-snapshot(纯新增、不动既有导出——冻结面加法无需升版);
  物化端(apps/api build/render-snapshot.ts)与主题 load-render-data 同源引用,
  双份手写镜像漂移即编译期破裂;主题侧 re-export 保持既有 import 面
- loader 空路由表前置明错(正常发布站点至少含首页路由——静默渲染会产零页站点)
- ui.routeHrefFor:targetContentId → 纯路径 html 落点(redirect/查询行不参与,
  无落点 undefined 不虚构——Task 4 分派器入口)+ 测试
- 具夹 assets.json copyPath 去机器相关绝对路径(改仓库相对)
- eslint ignores 增 **/.astro/**(astro build 再生目录)

354 根测试 + 18 主题测试全绿;五段 typecheck;lint(.astro 存在态验证后清理)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-22 08:58:33 +08:00
parent 1cd309d56d
commit eb81a57ebc
7 changed files with 202 additions and 96 deletions
@@ -6,6 +6,7 @@ import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { afterEach, describe, expect, it } from 'vitest'
import { loadRenderData, resetRenderDataCache } from '../src/lib/load-render-data.js'
import { routeHrefFor } from '../src/lib/ui.js'
const FIXTURE = fileURLToPath(new URL('./fixtures/render-input', import.meta.url))
const LOADER_SRC = fileURLToPath(new URL('../src/lib/load-render-data.ts', import.meta.url))
@@ -134,4 +135,33 @@ describe('load-render-data(渲染数据唯一入口)', () => {
rmSync(dir, { recursive: true, force: true })
}
})
it('routes.json 空表 → 明确错误(正常发布站点至少含首页路由)', () => {
const dir = tempCopy('noroutes')
try {
writeFileSync(join(dir, 'routes.json'), JSON.stringify([]))
expect(() => loadRenderData(dir)).toThrow(/路由表为空/)
} finally {
rmSync(dir, { recursive: true, force: true })
}
})
it('routeHrefFor:targetContentId → 落点路由;无落点 undefined;redirect/查询行不参与', () => {
const data = loadRenderData(FIXTURE)
expect(routeHrefFor(data.routes, '33333333-3333-4333-8333-333333333333')).toBe('/news/fixture-news-a.html')
expect(routeHrefFor(data.routes, '44444444-4444-4444-8444-444444444444')).toBe('/product/fixture-product/')
// 无落点(含 redirect 行即便带 targetContentId 也不算内容页公开落点)
expect(routeHrefFor(data.routes, '00000000-0000-4000-8000-000000000000')).toBeUndefined()
const redirectWithTarget: typeof data.routes[number] = {
...data.routes.find((r) => r.routeKind === 'redirect')!,
targetContentId: '33333333-3333-4333-8333-333333333333',
}
expect(routeHrefFor([redirectWithTarget], '33333333-3333-4333-8333-333333333333')).toBeUndefined()
// 带 query_key 的 html 行不是内容页纯路径落点
const queryRoute: typeof data.routes[number] = {
...data.routes.find((r) => r.routePath === '/news/fixture-news-a.html')!,
queryKey: 'attachment_id',
}
expect(routeHrefFor([queryRoute], '33333333-3333-4333-8333-333333333333')).toBeUndefined()
})
})