#!/usr/bin/env node /** * D38: 第九轮——跨页重复色(≥3 处)提升为 uni.scss 全局令牌 * * 依据 AGENTS.md / uni.scss §14 约定:同一色值在 ≥2~4 个文件出现即提升全局, * 单页专有色阶仍留在页面级 `$c-*`。本脚本幂等:已存在的令牌/规则不会重复写入。 * * 用法: node scripts/d38-round9-tokens.js [--dry] */ const fs = require('fs'); const TOKENS = [ ['$facade-fill-slate-25', '#F2F4F7', '冷调极浅底(教师端表单/计划卡)'], ['$facade-fill-slate-50-2', '#F7F9FC', '冷调浅底(与既有 $facade-fill-slate-50 #F8FAFC 区分)'], ['$facade-text-slate-600', '#5A6478', '深岩灰正文(教师端计划/AI 提示)'], ['$facade-text-gray-600', '#4B5563', '中性灰正文(活动详情/通用说明)'], ['$facade-ink-navy', '#1A2A4A', '藏青标题(门禁/深色卡片标题)'], ['$status-danger-light-2', '#FCA5A5', '错误浅色(挑战卡/失败态描边)'], ['$status-danger-strong-3', '#E53E3E', '错误深色(问卷/报告异常值)'], ['$status-danger-tint-4', '#FDE8E8', '错误极浅底(驳回/异常标签底)'], ['$status-danger-tint-5', '#FFF0F0', '错误极浅底之二(报告确认提示底)'], ['$status-warning-amber', '#FFC107', '琥珀警示(评分/星级)'], ['$status-warning-4', '#E6A23C', 'Element 警示橙(反馈弹窗)'], ['$status-warning-gold', '#F0B429', '暖金警示(报告列表徽标)'], ['$mat-green-700', '#388E3C', 'Material 绿 700(完成/通过)'], ['$mat-deep-orange', '#FF5722', 'Material 深橙(记录详情强调)'], ['$tint-blue-300', '#93C5FD', '蓝 300 描边/进度(空间测试)'], ['$accent-violet-300', '#C4B5FD', '紫 300 强调(DAN 报告)'], ['$tint-sky-soft-2', '#EAF4FC', '天蓝浅底(健康反馈/我的反馈)'], ['$tint-sky-ghost', '#F0F7FF', '天蓝极浅底(报告上传/列表)'], ['$tint-green-ghost', '#F0F9EB', '绿极浅底(成员列表/成功提示)'], ['$accent-pink-tint-2', '#FCE4EC', '粉浅底(报告详情/情绪标签)'], ['$wechat-green', '#07C160', '微信品牌绿(授权/分享入口)'], ]; const DRY = process.argv.includes('--dry'); // ---- 1) uni.scss ---- const SCSS = 'uni.scss'; let scss = fs.readFileSync(SCSS, 'utf8'); const eolS = scss.includes('\r\n') ? '\r\n' : '\n'; const missing = TOKENS.filter(([name]) => !new RegExp('^\\s*\\' + name + '\\s*:', 'm').test(scss)); if (missing.length) { const block = eolS + '// ==================== §15 第九轮 · 跨页重复色提升(2026-10-21) ====================' + eolS + '// 判据:同一色值在 ≥3 个页面/组件出现,且不属于页面专有色阶;单页色阶仍用 $c-* 留在页面内。' + eolS + missing.map(([name, hex, desc]) => `${name}: ${hex};`.padEnd(40) + `// ${desc}`).join(eolS) + eolS; scss += block; if (!DRY) fs.writeFileSync(SCSS, scss, 'utf8'); console.log('uni.scss 新增令牌:', missing.length); } else { console.log('uni.scss 令牌已齐备,跳过'); } // ---- 2) replace-hex-to-token.js(脚本文件统一 LF,避免整文件行尾翻新) ---- const RULES = 'scripts/replace-hex-to-token.js'; let rules = fs.readFileSync(RULES, 'utf8').replace(/\r\n/g, '\n'); const eolR = '\n'; const newRules = []; for (const [name, hex] of TOKENS) { if (rules.toUpperCase().includes("'" + hex.toUpperCase() + "'")) continue; newRules.push(` ['${hex.toUpperCase()}', '${name}'],`); newRules.push(` ['${hex.toLowerCase()}', '${name}'],`); } if (newRules.length) { const marker = '\n].sort((a, b) => b[0].length - a[0].length)'; const at = rules.indexOf(marker); if (at === -1) { console.log('FAIL: 规则数组锚点未找到'); process.exit(1); } const block = eolR + ' // ---- 第九轮(跨页重复色提升,2026-10-21)----' + eolR + newRules.join(eolR); rules = rules.slice(0, at) + block + rules.slice(at); if (!DRY) fs.writeFileSync(RULES, rules, 'utf8'); console.log('replace-hex-to-token.js 新增规则:', newRules.length); } else { console.log('替换规则已齐备,跳过'); }