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 未做部分)
194 lines
7.0 KiB
JavaScript
194 lines
7.0 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* migrate-brand-kole-ui.mjs — 品牌重命名迁移:Aurora Admin → Kole UI
|
||
*
|
||
* 用途:把仓库内所有品牌命名一次性迁到新名,含四类命名空间:
|
||
* 1) 文字品牌名 Aurora Admin / Aurora → Kole UI / Kole
|
||
* 2) 路径·包名 aurora-admin[-design|-showcase] → kole-ui[-showcase]
|
||
* 3) 类名前缀 aa- / au- → kole-
|
||
* 4) 令牌前缀 --au- → --kole-
|
||
* 另有精确标识符表(aaLogger / __aaCollectResult / AA_DATA 等),逐条列举而非正则,
|
||
* 避免误伤十六进制哈希(如 9f98c53aa3b2…)与 WCAG「AA」字样。
|
||
*
|
||
* 实现要点:
|
||
* - 以 latin1 逐字节读写:ASCII 模式在 UTF-8 / GBK 等 ASCII 超集编码下均按字节命中,
|
||
* 多字节汉字原样透传;CRLF 与无 BOM 状态自然保持。
|
||
* - 遇到 UTF-16 BOM 或含 NUL 字节的文件跳过并报告(不做猜测性转码)。
|
||
* - 幂等:再次运行第二遍报告 0 处改动。
|
||
*
|
||
* 用法:
|
||
* node tools/migrate-brand-kole-ui.mjs --dry-run # 只报告,不写盘
|
||
* node tools/migrate-brand-kole-ui.mjs # 执行替换
|
||
* node tools/migrate-brand-kole-ui.mjs --verify # 扫描残留旧命名(期望 0)
|
||
* node tools/migrate-brand-kole-ui.mjs --only=Aa # 只跑「来源串含 Aa」的规则
|
||
* ^ 用于增量补规则:全量重跑会把「刻意记录旧名」的
|
||
* 对照表(CHANGELOG 2.0.0 段、AGENTS §七)一起改写,
|
||
* 增量场景一律用 --only 限定规则。
|
||
*/
|
||
|
||
import fs from 'node:fs';
|
||
import path from 'node:path';
|
||
import { fileURLToPath } from 'node:url';
|
||
|
||
const SELF = fileURLToPath(import.meta.url);
|
||
const ROOT = path.resolve(path.dirname(SELF), '..');
|
||
|
||
const argv = process.argv.slice(2);
|
||
const DRY = argv.includes('--dry-run');
|
||
const VERIFY = argv.includes('--verify');
|
||
const ONLY = (argv.find((a) => a.startsWith('--only=')) || '').slice('--only='.length) || null;
|
||
|
||
/** --only=<substr> 时只保留「来源串含该子串」的规则。 */
|
||
function pick(rules) {
|
||
if (!ONLY) return rules;
|
||
return rules.filter(([from]) => String(from).includes(ONLY));
|
||
}
|
||
|
||
const SKIP_DIRS = new Set([
|
||
'.git', 'node_modules', 'dist', 'dist-deploy', '.tmp', '.playwright-mcp', '.zcode',
|
||
]);
|
||
const SKIP_EXT = new Set([
|
||
'.png', '.jpg', '.jpeg', '.gif', '.ico', '.webp', '.bmp', '.exe', '.dll', '.zip',
|
||
'.gz', '.tgz', '.tar', '.woff', '.woff2', '.ttf', '.otf', '.eot', '.pdf', '.mp4', '.webm',
|
||
]);
|
||
|
||
/** 有序替换表:字面量必须排在能作为其子串的规则之后(长优先)。 */
|
||
const LITERAL_RULES = [
|
||
// 1. 包名 / 镜像名(必须先于 aurora-admin)
|
||
['aurora-admin-design', 'kole-ui'],
|
||
['aurora-admin-showcase', 'kole-ui-showcase'],
|
||
['aurora-admin', 'kole-ui'],
|
||
// 2. 文字品牌名
|
||
['Aurora Admin', 'Kole UI'],
|
||
['Aurora', 'Kole'],
|
||
['aurora', 'kole'],
|
||
['AURORA', 'KOLE'],
|
||
// 3. 令牌前缀(必须先于裸 au-)
|
||
['--au-', '--kole-'],
|
||
// 4. 类名前缀
|
||
['aa-', 'kole-'],
|
||
// 5. 精确标识符(逐条列举,避开哈希误伤)
|
||
['AA_VERIFY_DEPS_DIR', 'KOLE_VERIFY_DEPS_DIR'],
|
||
['AA_DATA', 'KOLE_DATA'],
|
||
['AA_PORT', 'KOLE_PORT'],
|
||
['aaLogger', 'koleLogger'],
|
||
['__aaTranslateThemePanel', '__koleTranslateThemePanel'],
|
||
['__aaRefreshModeUI', '__koleRefreshModeUI'],
|
||
['__aaChangelogReady', '__koleChangelogReady'],
|
||
['__aaCollectResult', '__koleCollectResult'],
|
||
['__aaDetailReady', '__koleDetailReady'],
|
||
['__aaBehaviors', '__koleBehaviors'],
|
||
['__aaBehCache', '__koleBehCache'],
|
||
['__aaVersion', '__koleVersion'],
|
||
['__aaResult', '__koleResult'],
|
||
['__aaRun', '__koleRun'],
|
||
['aaZoom', 'koleZoom'],
|
||
['aaSlide', 'koleSlide'],
|
||
['aaFade', 'koleFade'],
|
||
];
|
||
|
||
/** 正则规则:`au-` 前必须不是字母数字连字符(排除 beau- 这类词内误伤)。 */
|
||
const REGEX_RULES = [
|
||
[/(?<![A-Za-z0-9-])au-/g, 'kole-'],
|
||
// 包公开导出名 `AaButton` / 命名空间导入 `import * as Aa` → `KoleButton` / `Kole`。
|
||
// 负向先行断言排除 `Aardvark` 这类「Aa + 小写」的英文词。
|
||
[/\bAa(?![a-z])/g, 'Kole'],
|
||
];
|
||
|
||
const VERIFY_PATTERNS = [
|
||
/aurora/i,
|
||
/\baa-/,
|
||
/--au-/,
|
||
/(?<![A-Za-z0-9-])au-/,
|
||
/\bAa(?![a-z])/,
|
||
/__aa[A-Z]/,
|
||
/\baaLogger\b/,
|
||
/AA_(DATA|PORT|VERIFY_DEPS_DIR)/,
|
||
/aa(Demo|Badge|Tab|Menu|Card|Btn)/,
|
||
];
|
||
|
||
function applyRules(src) {
|
||
let out = src;
|
||
const hits = [];
|
||
for (const [from, to] of pick(LITERAL_RULES)) {
|
||
const n = out.split(from).length - 1;
|
||
if (n) {
|
||
hits.push([from, to, n]);
|
||
out = out.split(from).join(to);
|
||
}
|
||
}
|
||
for (const [re, to] of pick(REGEX_RULES)) {
|
||
const n = (out.match(re) || []).length;
|
||
if (n) {
|
||
hits.push([String(re), to, n]);
|
||
out = out.replace(re, to);
|
||
}
|
||
}
|
||
return [out, hits];
|
||
}
|
||
|
||
function walk(dir, acc = []) {
|
||
for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
|
||
const p = path.join(dir, ent.name);
|
||
if (ent.isDirectory()) {
|
||
if (SKIP_DIRS.has(ent.name)) continue;
|
||
walk(p, acc);
|
||
} else if (ent.isFile()) {
|
||
if (SKIP_EXT.has(path.extname(ent.name).toLowerCase())) continue;
|
||
acc.push(p);
|
||
}
|
||
}
|
||
return acc;
|
||
}
|
||
|
||
const files = walk(ROOT).filter((f) => path.resolve(f) !== path.resolve(SELF));
|
||
const skipped = [];
|
||
const changed = [];
|
||
const ruleTotals = new Map();
|
||
|
||
for (const f of files) {
|
||
const buf = fs.readFileSync(f);
|
||
if (buf.length >= 2 && ((buf[0] === 0xff && buf[1] === 0xfe) || (buf[0] === 0xfe && buf[1] === 0xff))) {
|
||
skipped.push([path.relative(ROOT, f), 'UTF-16 BOM']);
|
||
continue;
|
||
}
|
||
const src = buf.toString('latin1');
|
||
|
||
if (VERIFY) {
|
||
const found = [];
|
||
for (const re of VERIFY_PATTERNS) {
|
||
const m = src.match(new RegExp(re.source, re.flags.includes('g') ? re.flags : re.flags + 'g'));
|
||
if (m) found.push(`${re.source} ×${m.length}`);
|
||
}
|
||
if (found.length) changed.push([path.relative(ROOT, f), found.join(', ')]);
|
||
continue;
|
||
}
|
||
|
||
const [out, hits] = applyRules(src);
|
||
if (out === src) continue;
|
||
const rel = path.relative(ROOT, f).split(path.sep).join('/');
|
||
let total = 0;
|
||
for (const [from, , n] of hits) {
|
||
total += n;
|
||
ruleTotals.set(from, (ruleTotals.get(from) || 0) + n);
|
||
}
|
||
changed.push([rel, `${total} 处`]);
|
||
if (!DRY) fs.writeFileSync(f, Buffer.from(out, 'latin1'));
|
||
}
|
||
|
||
const mode = VERIFY ? 'verify(残留扫描)' : DRY ? 'dry-run(未写盘)' : 'apply(已写盘)';
|
||
console.log(`\n[migrate-brand] 模式 ${mode} · 扫描 ${files.length} 个文本文件 · 命中 ${changed.length} 个\n`);
|
||
for (const [rel, info] of changed) console.log(` ${rel} — ${info}`);
|
||
if (skipped.length) {
|
||
console.log(`\n跳过(非 ASCII 超集编码):`);
|
||
for (const [rel, why] of skipped) console.log(` ${rel} (${why})`);
|
||
}
|
||
if (!VERIFY) {
|
||
console.log(`\n规则命中统计:`);
|
||
for (const [rule, n] of [...ruleTotals.entries()].sort((a, b) => b[1] - a[1])) {
|
||
console.log(` ${String(n).padStart(6)} ${rule}`);
|
||
}
|
||
}
|
||
console.log('');
|
||
if (VERIFY && changed.length) process.exitCode = 1;
|