Regression / regression (push) Canceled after 0s
## 品牌标识(本次会话) 起因:品牌此前没有任何图形标识 —— 唯一 favicon 是内联 data-URI 里的字母「A」, 那是 v2.0.0「Aurora Admin → Kole UI」改名漏掉的一处(PC 顶栏也是「A」, 移动端站已是「K」;移动端文档站则完全没有 favicon)。 - 几何:24 网格三个互不接触的笔画(竖 + 两斜),圆头描边; 描边 2.25 → 16px 标签页尺寸下正好 1.5px = 规范原文「描边1.5px」 - 取色分两套(刻意):favicon 硬编码品牌蓝/白(渲染在浏览器标签栏,不继承 kole-dark); 顶栏标记走 currentColor(实测暗色下自动转 rgb(20,22,28)) - 新增 theme-color 双条(light #FFFFFF / dark #1C1F26,取 --kole-color-card-bg) - 修 site/app.js hero 标语 KOLE ADMIN → KOLE UI(改名变形残留) - 移动端 7 个模板补 favicon(此前计数 0) 验收:门禁 9 条全 OK(site-routing/site-routes/mobile-docs/mobile-site/isolation/ theme/nav/i18n/icons);PC 回归 1464/1464 · 移动端 807/807,各连跑 8 次一致; 两端 favicon 405 字节逐字节一致;PC 站控制台错误 1→0。 ## 并行会话成果(本次一并入库) - 图标系统:2576 图标(TDesign/Element Plus,MIT)+ 11 端注入 + 5 个构建门禁工具 + IconPreview 预览页 + ICON-SPEC.md 冻结规格 - 移动端平台:47 组件 × 6 端 + 文档站 53 页 + 隔离门禁 - PC 组件:103 个大后台组件 / 组件11 批次 - uni-app:PC 端试点 + 移动端端实现 + 真实编译验证 ## 工程 - .gitignore 补 .scratch/ 与 .zcode-preexisting-*.txt(会话中间产物,实测 9.1MB,不入库) - CHANGELOG 补品牌标识条目 - ROADMAP 登 S8-P4(品牌标识任务包 + og:image/apple-touch-icon 未做部分)
113 lines
7.3 KiB
JavaScript
113 lines
7.3 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* verify-site-routing.mjs — 校验组件端入口、SPA 路由契约和 sitemap 覆盖。
|
||
* 只使用 Node 内置模块,不启动服务、不修改文件。
|
||
*/
|
||
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
||
import { join } from 'node:path';
|
||
|
||
const ROOT = process.cwd();
|
||
const SITE = join(ROOT, 'site');
|
||
const COMPONENTS = join(SITE, 'components');
|
||
const app = readFileSync(join(SITE, 'app.js'), 'utf8');
|
||
const data = JSON.parse(readFileSync(join(SITE, 'data.json'), 'utf8'));
|
||
const sitemap = readFileSync(join(ROOT, 'sitemap.xml'), 'utf8');
|
||
const platforms = ['h5', 'react', 'vue2', 'vue3'];
|
||
const failures = [];
|
||
const check = (ok, message) => { if (!ok) failures.push(message); };
|
||
|
||
const components = data.components || [];
|
||
check(components.length === JSON.parse(readFileSync(join(ROOT, '.design_library', 'kole-ui', 'components', 'index.json'), 'utf8')).components.length, `component count differs from index: ${components.length}`);
|
||
check(app.includes("(?:\\/([a-z0-9-]+))?"), 'SPA platform route parser is missing');
|
||
check(app.includes("PLATFORM_KINDS = { h5: 'html', react: 'jsx', vue2: 'vue2', vue3: 'vue3' }"), 'platform kind map is missing');
|
||
check(app.includes("a.href = componentPageHref(c, platform)"), 'other-platform links do not use component pages');
|
||
|
||
/* ---- 路由形态:History API(URL 里没有 #) ----
|
||
站点根由 pathname 里第一段 "/site/" 反推,site/index.html 的内联脚本与 app.js
|
||
各算一次,两处必须用同一条规则;服务器(nginx / dev-server)与 Pages 发布包
|
||
必须有对应的深链兜底,否则深层路径直接 404。 */
|
||
const indexHtml = readFileSync(join(SITE, 'index.html'), 'utf8');
|
||
const devServer = readFileSync(join(SITE, 'dev-server.js'), 'utf8');
|
||
const nginx = readFileSync(join(ROOT, 'nginx.conf'), 'utf8');
|
||
const pagesWorkflow = readFileSync(join(ROOT, '.github/workflows/deploy-pages.yml'), 'utf8');
|
||
check(app.includes("p.indexOf('/site/')"), 'app.js SITE_BASE rule is missing');
|
||
check(indexHtml.includes("p.indexOf('/site/')"), 'index.html SITE_BASE rule is missing');
|
||
check(indexHtml.includes("document.write('<base href="), 'index.html <base> bootstrap is missing');
|
||
check(!/href="(?:\.\.\/|\.\/)?(?:index\.html)?#\//.test(indexHtml), 'index.html still links to hash routes');
|
||
check(indexHtml.includes("window.KOLE_SITE_BASE"), 'index.html does not expose KOLE_SITE_BASE');
|
||
check(nginx.includes('try_files $uri $uri/ /site/index.html'), 'nginx.conf lacks the SPA fallback');
|
||
check(/location ~\* \^\/\(\?:\[0-9\]\+\\\.\[0-9\]\+\\\.\[0-9\]\+\/\)\?site\/\.\+\\\.\[a-zA-Z0-9\]\+\$/.test(nginx), 'nginx.conf lacks the extension guard for missing assets');
|
||
check(devServer.includes('isSpaRoute'), 'dev-server.js lacks the SPA fallback');
|
||
check(pagesWorkflow.includes('site-dist/404.html'), 'Pages package lacks the deep-link 404.html');
|
||
|
||
/* ---- 历史版本快照(/<x.y.z>/site/…,tools/snapshot-site.mjs 产出) ----
|
||
站点根是按 pathname 里第一段 "/site/" 反推的,所以快照目录名必须叫 site;
|
||
服务器两侧都要有对应的深链兜底,否则版本页一刷新就 404。 */
|
||
check(/\^\/\(\[0-9\]\+\\\.\[0-9\]\+\\\.\[0-9\]\+\)\/site\//.test(nginx), 'nginx.conf lacks the versioned SPA fallback');
|
||
check(nginx.includes('site/versions\\.json'), 'nginx.conf lacks the versions.json cache rule');
|
||
check(/VERSION_DIR = \/\^\\d\+\\\.\\d\+\\\.\\d\+\$\//.test(devServer), 'dev-server.js does not allow version snapshot directories');
|
||
check(/spaMatch\[1\]/.test(devServer), 'dev-server.js does not resolve the versioned SPA shell');
|
||
check(app.includes("versionsManifestUrl"), 'app.js version manifest loader is missing');
|
||
check(indexHtml.includes('id="version-trigger"') && indexHtml.includes('id="version-menu"'), 'index.html version switcher markup is missing');
|
||
|
||
let shellCount = 0;
|
||
for (const component of components) {
|
||
const slug = component.slug;
|
||
const legacy = join(COMPONENTS, `${slug}.html`);
|
||
check(existsSync(legacy), `missing legacy shell: ${slug}.html`);
|
||
if (existsSync(legacy)) {
|
||
const legacyText = readFileSync(legacy, 'utf8');
|
||
check(!legacyText.includes('kole-ui/"site/components'), `legacy canonical is malformed: ${slug}`);
|
||
check(legacyText.includes('url=../component/' + slug), `wrong legacy route target: ${slug}.html`);
|
||
check(!legacyText.includes('#/'), `legacy shell still uses a hash route: ${slug}.html`);
|
||
}
|
||
for (const platform of platforms) {
|
||
const shell = join(COMPONENTS, slug, `${platform}.html`);
|
||
shellCount += 1;
|
||
check(existsSync(shell), `missing platform shell: ${slug}/${platform}.html`);
|
||
if (!existsSync(shell)) continue;
|
||
const text = readFileSync(shell, 'utf8');
|
||
check(text.includes(`url=../../component/${slug}/${platform}`), `wrong route target: ${slug}/${platform}`);
|
||
check(!text.includes('#/'), `shell still uses a hash route: ${slug}/${platform}`);
|
||
check(text.includes(`/site/components/${slug}/${platform}.html`), `wrong canonical: ${slug}/${platform}`);
|
||
check(!text.includes('kole-ui/"site/components'), `malformed canonical: ${slug}/${platform}`);
|
||
}
|
||
}
|
||
|
||
const urlCount = (sitemap.match(/<url><loc>/g) || []).length;
|
||
/* 场景页(S4-P11 模板页库)也在 sitemap 里,按磁盘实际存在的 .html 逐个核对 ——
|
||
比硬编码 5 个文件名更强:新增/删除模板页而忘了同步 build-site.ps1 时这里会红。 */
|
||
const scenarioDir = join(SITE, 'scenario');
|
||
const scenarioPages = existsSync(scenarioDir)
|
||
? readdirSync(scenarioDir).filter((f) => f.endsWith('.html'))
|
||
: [];
|
||
check(scenarioPages.length > 0, 'site/scenario has no template pages');
|
||
for (const f of scenarioPages) {
|
||
check(sitemap.includes(`/site/scenario/${f}`), `scenario page missing from sitemap: ${f}`);
|
||
}
|
||
check(urlCount === 1 + components.length * (1 + platforms.length) + scenarioPages.length,
|
||
`sitemap URL count is ${urlCount}, expected ${1 + components.length * (1 + platforms.length) + scenarioPages.length}`);
|
||
/* 基址由 SITE_URL_BASE 决定(build-site.ps1),故这里只校验「索引条目存在且路径正确」,
|
||
不锁定主机名——旧写法写死了占位域名,传入真实基址后必然误报。
|
||
反例:SITE_URL_BASE=https://kole-ui.mymoyu.top/ 时旧写法报 'site index is missing from sitemap',
|
||
而 sitemap 里确有 <loc>https://kole-ui.mymoyu.top/site/index.html</loc>。 */
|
||
const indexLoc = (sitemap.match(/<loc>([^<]*\/site\/index\.html)<\/loc>/) || [])[1];
|
||
check(Boolean(indexLoc), 'site index is missing from sitemap');
|
||
/* 占位域名不得随包发布(tools/release.mjs 的既有约定)——这里由误报改为真实断言 */
|
||
check(!sitemap.includes('YOUR-ACCOUNT'), 'sitemap still uses the placeholder domain; set SITE_URL_BASE before packaging');
|
||
|
||
const dirs = readdirSync(COMPONENTS).filter((name) => {
|
||
const path = join(COMPONENTS, name);
|
||
return existsSync(path) && statSync(path).isDirectory();
|
||
});
|
||
check(dirs.length === components.length, `platform shell directories are ${dirs.length}, expected ${components.length}`);
|
||
check(shellCount === components.length * platforms.length, `platform shell count is ${shellCount}`);
|
||
|
||
if (failures.length) {
|
||
console.error(`[routing] FAIL (${failures.length})`);
|
||
failures.forEach((failure) => console.error(`- ${failure}`));
|
||
process.exit(1);
|
||
}
|
||
|
||
console.log(`[routing] OK: ${components.length} components / ${shellCount} platform shells / ${urlCount} sitemap URLs`);
|