feat(build): astro 构建管线与媒体落盘
Plan 4 Task 5:build.service snapshotting 后接真实构建——building 态进 pipeline.ts(spawn `pnpm --filter @sf/theme-pilot-minimal exec astro build` (SF_BUILD_INPUT env;非零退出+stderr 标记判据)→ dist 定位与 target_file 完整性 → 媒体 copyPath→dist/public_path(sha256 全量校验)→ sitemap.xml/ robots.txt(编码 URL 原样)→ route-manifest.json(§12.1 字段面、哈希链复用) → build_artifacts dist/manifest 登记)→ validating(空,Task 6) → 报告+report 行 → succeeded;失败任何一步 failed+error_code/error_summary(stderr 摘要) +input/dist 清残。 dist 双位置纪律:主题包 dist 是暂存,产物汇齐后拷出 <artifactsRoot>/jobs/ <jobId>/dist(job 级权威),暂存随即清空。无网证明:ASTRO_TELEMETRY_DISABLED=1 (实测 @astrojs/telemetry 构建期真调 fetch)+ NODE_OPTIONS 透传(测试注入 fetch/net stub 构建仍成功)。主题集成补面:ProductList 模板 + /products/ 主列表分派特判(DB 物化形状 targetContentId=null,Task 4 夹具曾被占位绕过)。 测试:tests/integration/build-pipeline.test.ts 五用例(全链/夹具直跑/五禁 断言/失败路径);build-snapshot 状态机补 building + 清场补 build_artifacts。 root 359 + theme 26 全绿。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
---
|
||||
// 产品主列表模板(Plan 4 Task 5 集成补面):route-writer finish() 对 productsPath(默认
|
||||
// /products/)落的主列表行 targetContentId=null——Task 4 的分派器只覆盖了 news 主列表
|
||||
// 特判,产品主列表在主题夹具里被"targetContentId 占位"绕过(见 CategoryList 头注记),
|
||||
// DB 真实物化形状(targetContentId=null)即无处落点。本模板补齐:全部 product 卡片
|
||||
// 单页(latestEntries 同款排序),无落点内容不出卡(禁虚构路径)。
|
||||
// 与 news-list 同构(主列表两兄弟);manifest 的 'product-list' 模板键声明性覆盖本页
|
||||
// (appliesTo 单字符串不承载多前缀——/products/ 主列表由分派器特判,/category/ 别名
|
||||
// 前缀同先例)。
|
||||
import BaseLayout from '../layouts/BaseLayout.astro'
|
||||
import Breadcrumbs from '../components/Breadcrumbs.astro'
|
||||
import ProductCard from '../components/ProductCard.astro'
|
||||
import type { RenderData, RouteEntry } from '../lib/load-render-data'
|
||||
import { contentTitle, coverRef, latestEntries, metaDescription, routeHrefFor } from '../lib/ui'
|
||||
|
||||
interface Props {
|
||||
data: RenderData
|
||||
route: RouteEntry
|
||||
}
|
||||
|
||||
const { data, route } = Astro.props
|
||||
const page = { path: route.routePath, title: 'Products' }
|
||||
const allProducts = latestEntries(data.contents, 'product')
|
||||
---
|
||||
|
||||
<BaseLayout data={data} page={{ ...page, description: metaDescription(data.site, page) }}>
|
||||
<Breadcrumbs items={[{ label: 'Home', href: '/' }, { label: 'Products' }]} />
|
||||
<h1>Products</h1>
|
||||
<div class="product-grid">
|
||||
{allProducts.map((c) => {
|
||||
const href = routeHrefFor(data.routes, c.contentId)
|
||||
if (href === undefined) return null
|
||||
return <ProductCard title={contentTitle(c)} href={href} cover={coverRef(c, data.assets)} />
|
||||
})}
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
.product-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: var(--space-3);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user