Files
web_huakui_web/scripts/style-fixtures.ts
T
谢宇宁andClaude Opus 5.5 d2ddb1c321 [Ai] feat: 花魁短剧 Next.js 站点初始提交
- 原生 Next.js App Router + 严格 TypeScript 实现花魁短剧 PC/H5 页面,按 src/features/{catalog,content,playback} 组织,保留完整 SEO(metadata、JSON-LD、canonical、robots、llms、sitemap、IndexNow)。
- 主域名通过 SITE_URL=https://huakuidj.com 强制覆盖后端 foreverUrlAddr(config/*.env、src/server/config.ts、src/server/seo/shared.ts、src/server/services/shared.ts),支持邮箱改为 support@huakuidj.com。
- 移除登录与账户体系:删除 /account 全部页面、登录/注册/充值弹窗、收藏点赞、评论、金币解锁及对应 BFF 接口;旧账户地址 GET/HEAD 统一 301 到首页(src/lib/routes.ts REMOVED_ACCOUNT_PATHS),保留游客会话、反馈、图片上传、观看上报与搜索联想。
- 播放页:评论位改为同类推荐 + 热播榜单,锁定集改为 App 观看遮罩与底部弹层,新增简介标签、分集图例与独立视觉皮肤(src/features/playback、src/styles/pages/playback.css)。
- 卡片:短剧/视频卡状态与时长移至信息行胶囊标签,封面角标移至左上角(src/components/catalog/Cards.tsx、src/styles/foundations/base.css)。
- 同步更新单元测试、浏览器冒烟脚本、路由清单与 README。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 11:33:31 +07:00

65 lines
3.1 KiB
TypeScript

import { createHash } from "node:crypto";
import { readFile } from "node:fs/promises";
import { basename, join } from "node:path";
import { fileURLToPath } from "node:url";
// Legacy fixture keys stay inside test infrastructure. Production imports semantic CSS paths.
const sourceStyles: Record<string, string> = {
"about-mobile": "src/styles/pages/about-responsive.css",
"about": "src/styles/pages/about.css",
"ads": "src/styles/components/advertising-compat.css",
"base": "src/styles/foundations/base.css",
"dialogAd": "src/styles/components/advertisement-dialog.css",
"downloadPop": "src/styles/components/app-download-dialog.css",
"eighteen": "src/styles/components/age-gate.css",
"feedback": "src/styles/pages/feedback.css",
"footer": "src/styles/components/site-footer.css",
"header": "src/styles/components/site-header.css",
"help": "src/styles/pages/help.css",
"index": "src/styles/pages/home.css",
"installGuide": "src/styles/components/installation-guide.css",
"legal": "src/styles/pages/legal.css",
"list": "src/styles/pages/drama-list.css",
"mobile-search": "src/styles/pages/mobile-search.css",
"mobileTabbar": "src/styles/components/mobile-tabbar.css",
"moreList": "src/styles/pages/expanded-list.css",
"nav": "src/styles/pages/site-navigation.css",
"notFound": "src/styles/pages/not-found.css",
"pagination": "src/styles/components/pagination.css",
"play": "src/styles/pages/playback.css",
"playLinks": "src/styles/components/playback-links.css",
"popularRecommend": "src/styles/pages/popular-recommendations.css",
"promo-grid": "src/styles/components/promotional-grid.css",
"promo": "src/styles/components/promotional-banner.css",
"pwa": "src/styles/overrides/pwa-safe-area.css",
"rank": "src/styles/pages/rankings.css",
"recentlyAdded": "src/styles/pages/recently-added.css",
"search": "src/styles/pages/search-results.css",
"series": "src/styles/pages/series-list.css",
"tags": "src/styles/pages/tag-detail.css",
"toast": "src/styles/components/toast.css",
"topic": "src/styles/pages/topics.css",
"topicDetail": "src/styles/pages/topic-detail.css",
"player": "src/styles/components/video-player.css"
};
export async function createStyleFixture(keys: readonly string[]) {
const root = fileURLToPath(new URL("..", import.meta.url));
const layout = await readFile(join(root, "src/app/layout.tsx"), "utf8");
const order = [...layout.matchAll(/^import ["']@\/([^"']+\.css)["'];/gm)].map((match) => "src/" + match[1]);
const files = [...new Set(keys.map((key) => {
const file = sourceStyles[key];
if (!file) throw new Error("Unknown fixture stylesheet: " + key);
return file;
}))].sort((first, second) => order.indexOf(first) - order.indexOf(second));
for (const file of files) {
if (!order.includes(file)) throw new Error("Fixture stylesheet is not imported by the root layout: " + file);
}
const css = (await Promise.all(files.map((file) => readFile(join(root, file), "utf8")))).join("\n");
return {
css,
href: "/fixture-" + createHash("sha256").update(css).digest("hex").slice(0, 12) + ".css",
tokens: files.map((file) => basename(file, ".css")).join(" "),
};
}