import { readFile, writeFile, mkdir } from 'node:fs/promises'; import { resolve, dirname } from 'node:path'; import { pageRoutes } from '../src/server/pages/routes'; import { ROUTE_MIGRATIONS, toPublicPath } from '../src/lib/routes'; const labels: Record = { Index: '首页', SearchMobile: '移动端搜索', Search: '搜索结果', About: '关于我们', Help: '帮助中心', Terms: '服务条款', Privacy: '隐私政策', Compliance2257: '2257 声明', DMCA: '版权声明', DownloadApp: '下载 App', Nav: '官方导航', HomeRoad: '回家的路', List: '短剧列表', MoreList: '短剧推荐栏目', MoreSeriesList: '剧集推荐栏目', Series: '剧集列表', Topic: '专题列表', TopicDetail: '专题详情', Rank: '排行榜', Tags: '标签详情', RecentlyAdded: '最新更新', PopularRecommend: '热门推荐', Play: '短剧播放', SeriesPlay: '剧集播放', Feedback: '意见反馈', }; type CatalogRoute = { id: string; label: string; path: string; legacyPaths: string[]; selectable: boolean }; const routes = new Map(); for (const [legacy, handler] of pageRoutes) { if (!labels[handler]) throw new Error('Missing page label: ' + handler); const path = toPublicPath(legacy); let entry = routes.get(path); if (!entry) { entry = { id: handler + ':' + path, label: labels[handler], path, legacyPaths: [], selectable: !path.includes(':') }; routes.set(path, entry); } if (!entry.legacyPaths.includes(legacy)) entry.legacyPaths.push(legacy); } const catalog = JSON.stringify({ version: 1, routes: [...routes.values()] }, null, 2) + '\n'; const begin = ''; const end = ''; const inventory = [ begin, '以下清单由 `npm run routes:export` 从实际路由注册生成,包含全部页面、筛选和分页模式。含 `:参数` 的路径需要填写真实值;后台导航下拉只展示可直接访问的固定入口,详情页和外链可手动输入。', '', '| 页面 | 原路径 | 当前路径 |', '| --- | --- | --- |', ...[...routes.values()].map(item => `| ${item.label} | ${item.legacyPaths.map(path => '`' + path + '`').join('、')} | \`${item.path}\` |`), '', end, ].join('\n'); const root = resolve(import.meta.dirname, '..'); const args = process.argv.slice(2); const check = args.includes('--check'); const adminIndex = args.indexOf('--admin'); for (let i = 0; i < args.length; i++) { if (args[i] === '--admin') { i++; continue; } if (args[i] !== '--check') throw new Error('Usage: routes:export [--check] [--admin ]'); } if (adminIndex >= 0 && (!args[adminIndex + 1] || args[adminIndex + 1].startsWith('--'))) throw new Error('--admin requires the admin repository path'); const readmePath = resolve(root, 'README.md'); const readme = await readFile(readmePath, 'utf8'); const start = readme.indexOf(begin), finish = readme.indexOf(end); if (start < 0 || finish < start) throw new Error('README route inventory markers are missing'); const updatedReadme = readme.slice(0, start) + inventory + readme.slice(finish + end.length); const outputs: [string, string][] = [[resolve(root, 'src/lib/page-route-catalog.json'), catalog], [readmePath, updatedReadme]]; if (adminIndex >= 0) { const adminRoot = resolve(args[adminIndex + 1]!); const pkg = JSON.parse(await readFile(resolve(adminRoot, 'package.json'), 'utf8')); if (pkg.name !== 'hkdj-web-admin') throw new Error('--admin must target the Huakui web admin repository'); outputs.push([resolve(adminRoot, 'src/constants/hkdj-web-routes.json'), catalog]); } for (const [path, content] of outputs) { if (check) { if (await readFile(path, 'utf8') !== content) throw new Error('Route catalog is stale: ' + path); } else { await mkdir(dirname(path), { recursive: true }); await writeFile(path, content); } } console.log(`${check ? 'Verified' : 'Exported'} ${ROUTE_MIGRATIONS.length} migrations, ${routes.size} page patterns, ${[...routes.values()].filter(item => item.selectable).length} navigation choices.`);