Files
pangolin/tools/check-l1-sync.mjs
T
wangjia f6ce5267d5 feat(design): 支付渠道 switch 原型 + segswitch 原子 + 图标/语言同源闸
- .segswitch 段控原子入 atoms.css + 登记 index.html
- purchase/ui-mobile/ui-desktop 三屏:购买页支付渠道 switch(USDT→crypto 默认 / 人民币→alipay),套餐价随渠道分币种显示,删两步弹层 + nezha
- 订单列表/详情集成进移动+桌面主框架
- icons.js 58 图标 sprite 单源 + gen_flutter_icons codegen + check-l1-sync 同源闸(图标同集 + l10n 完整)
- pay-v2 开发决策日志 docs/pay-v2-dev-log.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-11 23:45:55 +08:00

63 lines
3.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
// check-l1-sync.mjs —— L1 跨端同源闸(ds-flow)。零依赖。
// ① 图标集合:Flutter 图标集 == 原型 sprite 图标集(camel 化后逐名相等)。
// 真源 design/prototype/icons.js;镜像 client/lib/widgets/pangolin_icons.dart。
// ② l10n 完整性:app_text.dart 声明的每个 getter,6 个语言文件必须全部实现
// (谁少 key 就红 —— 防「某语言漏字串」漂移,如支付字串只加 zh/en)。
// 任一不通过 → 列出并 exit 1。
import { readFileSync, existsSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, resolve } from 'node:path';
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = resolve(__dirname, '..');
let failed = 0;
/* ① 图标集合 */
const spriteKeys = new Set(
[...readFileSync(resolve(root, 'design/prototype/icons.js'), 'utf8')
.matchAll(/'([a-z0-9-]+)'\s*:\s*'</g)].map((m) => m[1].replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase()))
);
const dartKeys = new Set(
[...readFileSync(resolve(root, 'client/lib/widgets/pangolin_icons.dart'), 'utf8')
.matchAll(/LucideIcons\.([A-Za-z0-9]+)/g)].map((m) => m[1])
);
const iconMissing = [...spriteKeys].filter((k) => !dartKeys.has(k)).sort();
const iconExtra = [...dartKeys].filter((k) => !spriteKeys.has(k)).sort();
if (iconMissing.length === 0 && iconExtra.length === 0) {
console.log(`✓ 图标同集 OK —— ${spriteKeys.size} 个 sprite ↔ Flutter 逐名相等`);
} else {
failed++;
console.error('✗ 图标同集 MISMATCH:');
if (iconMissing.length) console.error(' sprite 有 / Flutter 缺:', iconMissing.join(', '));
if (iconExtra.length) console.error(' Flutter 多 / sprite 无:', iconExtra.join(', '));
console.error(' 修复:node design/codegen/gen_flutter_icons.mjs');
}
/* ② Flutter l10n 完整性 */
const L10N = resolve(root, 'client/lib/l10n');
const BASE = resolve(L10N, 'app_text.dart');
const LANGS = ['zh', 'en', 'ja', 'ko', 'ru', 'es'];
const getterSet = (src) => new Set([...src.matchAll(/\bString\s+get\s+(\w+)\b/g)].map((m) => m[1]));
if (!existsSync(BASE)) {
console.log('· l10n 完整性:跳过(无 app_text.dart');
} else {
// canonical = 仅抽象 getter`String get X;` 分号结尾);块体默认 `String get X {…}` 是继承默认,不要求 langs 实现
const canonical = new Set([...readFileSync(BASE, 'utf8').matchAll(/\bString\s+get\s+(\w+)\s*;/g)].map((m) => m[1]));
const gaps = [];
for (const lang of LANGS) {
const f = resolve(L10N, `strings_${lang}.dart`);
if (!existsSync(f)) { gaps.push(` ${lang}: 文件缺失`); continue; }
const miss = [...canonical].filter((k) => !getterSet(readFileSync(f, 'utf8')).has(k)).sort();
if (miss.length) gaps.push(` ${lang}: 缺 ${miss.length} 个 → ${miss.slice(0, 12).join(', ')}${miss.length > 12 ? ' …' : ''}`);
}
if (gaps.length === 0) {
console.log(`✓ l10n 完整 OK —— ${canonical.size} 个 getter × ${LANGS.length} 语言全实现`);
} else {
failed++;
console.error('✗ l10n 不完整(某语言漏 key:');
gaps.forEach((g) => console.error(g));
}
}
process.exit(failed ? 1 : 0);