#!/usr/bin/env node
/**
* 批量替换硬编码色值为 SCSS 令牌(CSS style 块内,零视觉风险)
* 用法:
* node scripts/replace-hex-to-token.js --batch gray # 仅灰阶
* node scripts/replace-hex-to-token.js --batch brand # 仅品牌/状态色
* node scripts/replace-hex-to-token.js # 全部
*/
const fs = require('fs')
const path = require('path')
const ROOT = path.resolve(__dirname, '..')
const EXCLUDE_DIRS = new Set(['node_modules', 'dist', 'unpackage', '.git', 'docs'])
const ALL_RULES = [
// ---- 灰阶(neutral)----
['#FFFFFF', '$app-surface'],
['#ffffff', '$app-surface'],
['#FFF', '$app-surface'],
['#fff', '$app-surface'],
['#333333', '$facade-text-strong'],
['#333', '$facade-text-strong'],
['#666666', '$facade-text-body'],
['#666', '$facade-text-body'],
['#999999', '$facade-text-muted'],
['#999', '$facade-text-muted'],
['#F5F5F5', '$facade-fill-2'],
['#f5f5f5', '$facade-fill-2'],
['#F0F0F0', '$facade-fill'],
['#f0f0f0', '$facade-fill'],
['#FAFAFA', '$facade-fill-ghost'],
['#fafafa', '$facade-fill-ghost'],
['#F9FAFB', '$facade-fill-gray-50'],
['#f9fafb', '$facade-fill-gray-50'],
['#BBBBBB', '$facade-text-ghost-light'],
['#bbbbbb', '$facade-text-ghost-light'],
['#BBB', '$facade-text-ghost-light'],
['#bbb', '$facade-text-ghost-light'],
['#CCCCCC', '$facade-text-ghost'],
['#cccccc', '$facade-text-ghost'],
['#CCC', '$facade-text-ghost'],
['#ccc', '$facade-text-ghost'],
['#DDDDDD', '$facade-border-soft'],
['#dddddd', '$facade-border-soft'],
['#DDD', '$facade-border-soft'],
['#ddd', '$facade-border-soft'],
['#EEEEEE', '$facade-hairline-2'],
['#eeeeee', '$facade-hairline-2'],
['#EEE', '$facade-hairline-2'],
['#eee', '$facade-hairline-2'],
// ---- 品牌/状态色(brand/status)----
['#F97316', '$brand-primary'],
['#f97316', '$brand-primary'],
['#FF8C42', '$dim-body'],
['#ff8c42', '$dim-body'],
['#FFF7ED', '$uni-primary-pale'],
['#fff7ed', '$uni-primary-pale'],
['#FB923C', '$brand-primary-light'],
['#fb923c', '$brand-primary-light'],
['#10B981', '$status-success'],
['#10b981', '$status-success'],
['#EF4444', '$status-danger'],
['#ef4444', '$status-danger'],
['#6366F1', '$dim-wisdom'],
['#6366f1', '$dim-wisdom'],
['#C62828', '$status-danger-strong-2'],
['#c62828', '$status-danger-strong-2'],
// ---- 第二轮(高频色,2026-10-20 批量收割)----
['#94A3B8', '$app-text-subtle'],
['#94a3b8', '$app-text-subtle'],
['#1E293B', '$uni-text-color'],
['#1e293b', '$uni-text-color'],
['#64748B', '$uni-text-color-secondary'],
['#64748b', '$uni-text-color-secondary'],
['#E2E8F0', '$uni-border-color-light'],
['#e2e8f0', '$uni-border-color-light'],
['#F5F7FA', '$app-bg'],
['#f5f7fa', '$app-bg'],
['#667EEA', '$accent-indigo'],
['#667eea', '$accent-indigo'],
['#9CA3AF', '$facade-text-gray-400'],
['#9ca3af', '$facade-text-gray-400'],
['#FFF3E0', '$fortune-1-tint'],
['#fff3e0', '$fortune-1-tint'],
['#FED7AA', '$brand-primary-light-2'],
['#fed7aa', '$brand-primary-light-2'],
['#F1F5F9', '$facade-fill-slate'],
['#f1f5f9', '$facade-fill-slate'],
['#4CAF50', '$mat-green'],
['#4caf50', '$mat-green'],
['#E8F5E9', '$mat-green-tint'],
['#e8f5e9', '$mat-green-tint'],
['#F59E0B', '$dim-wealth'],
['#f59e0b', '$dim-wealth'],
['#F8F8F8', '$facade-fill-page'],
['#f8f8f8', '$facade-fill-page'],
['#FEF3C7', '$status-warning-soft'],
['#fef3c7', '$status-warning-soft'],
['#E65100', '$mat-orange-strong'],
['#e65100', '$mat-orange-strong'],
['#2E7D32', '$mat-green-strong'],
['#2e7d32', '$mat-green-strong'],
['#888888', '$facade-text-faint'],
['#888', '$facade-text-faint'],
['#F3F4F6', '$facade-fill-gray-100'],
['#f3f4f6', '$facade-fill-gray-100'],
['#E3F2FD', '$mat-blue-tint'],
['#e3f2fd', '$mat-blue-tint'],
['#EA580C', '$brand-primary-dark'],
['#ea580c', '$brand-primary-dark'],
['#E5E7EB', '$facade-gray-200'],
['#e5e7eb', '$facade-gray-200'],
['#E0E0E0', '$facade-hairline'],
['#e0e0e0', '$facade-hairline'],
['#1F2937', '$facade-text-gray-800'],
['#1f2937', '$facade-text-gray-800'],
['#6B7280', '$facade-text-slate'],
['#6b7280', '$facade-text-slate'],
['#F5FAFE', '$facade-fill-blue-ghost'],
['#f5fafe', '$facade-fill-blue-ghost'],
['#CBD5E1', '$uni-border-color'],
['#cbd5e1', '$uni-border-color'],
['#374151', '$facade-text-gray-700'],
['#374151', '$facade-text-gray-700'],
['#764BA2', '$accent-indigo-deep'],
['#764ba2', '$accent-indigo-deep'],
['#F8FAFC', '$facade-fill-slate-50'],
['#f8fafc', '$facade-fill-slate-50'],
['#FFD700', '$accent-gold'],
['#ffd700', '$accent-gold'],
['#1565C0', '$mat-blue-strong'],
['#1565c0', '$mat-blue-strong'],
['#DC2626', '$status-danger-deep'],
['#dc2626', '$status-danger-deep'],
['#475569', '$facade-text-slate-deep'],
['#475569', '$facade-text-slate-deep'],
['#8B5CF6', '$facade-violet'],
['#8b5cf6', '$facade-violet'],
['#334155', '$facade-text-slate-700'],
['#334155', '$facade-text-slate-700'],
['#FFB366', '$dim-body-light'],
['#ffb366', '$dim-body-light'],
['#FFEBEE', '$fortune-0-tint'],
['#ffebee', '$fortune-0-tint'],
['#34D399', '$status-success-light-3'],
['#34d399', '$status-success-light-3'],
['#1A1A2E', '$app-ink-night'],
['#1a1a2e', '$app-ink-night'],
['#E8E8E8', '$facade-fill-4'],
['#e8e8e8', '$facade-fill-4'],
['#16A34A', '$status-success-strong-2'],
['#16a34a', '$status-success-strong-2'],
['#FEE2E2', '$status-danger-soft'],
['#fee2e2', '$status-danger-soft'],
['#C2410C', '$brand-primary-deep'],
['#c2410c', '$brand-primary-deep'],
['#FFF7E6', '$brand-primary-tint-2'],
['#fff7e6', '$brand-primary-tint-2'],
['#22C55E', '$status-success-2'],
['#22c55e', '$status-success-2'],
['#3B82F6', '$status-info'],
['#3b82f6', '$status-info'],
['#2196F3', '$mat-blue-base'],
['#2196f3', '$mat-blue-base'],
['#D97706', '$status-warning-deep'],
['#d97706', '$status-warning-deep'],
['#0EA5E9', '$uni-info'],
['#0ea5e9', '$uni-info'],
['#DCFCE7', '$status-success-soft-2'],
['#dcfce7', '$status-success-soft-2'],
['#555555', '$facade-text-deep'],
['#555', '$facade-text-deep'],
['#818CF8', '$dim-wisdom-light'],
['#818cf8', '$dim-wisdom-light'],
['#FF9800', '$mat-orange-base'],
['#ff9800', '$mat-orange-base'],
['#F5F9FC', '$uni-bg-color'],
['#f5f9fc', '$uni-bg-color'],
['#F0FDF4', '$status-success-tint-2'],
['#f0fdf4', '$status-success-tint-2'],
['#D1FAE5', '$status-success-soft'],
['#d1fae5', '$status-success-soft'],
['#DBEAFE', '$status-info-soft'],
['#dbeafe', '$status-info-soft'],
['#92400E', '$status-warning-strong'],
['#92400e', '$status-warning-strong'],
// ---- 第四轮(flat 设计主题,2026-10-20 收编为 $flat-* 全局令牌)----
['#4ECDC4', '$flat-teal'],
['#4ecdc4', '$flat-teal'],
['#44B09E', '$flat-teal-deep'],
['#44b09e', '$flat-teal-deep'],
['#A8E063', '$flat-green'],
['#a8e063', '$flat-green'],
['#56AB2F', '$flat-green-deep'],
['#56ab2f', '$flat-green-deep'],
['#FFF9F0', '$flat-bg'],
['#fff9f0', '$flat-bg'],
['#FFF5EB', '$flat-card'],
['#fff5eb', '$flat-card'],
['#FFF0E6', '$flat-warm'],
['#fff0e6', '$flat-warm'],
['#F5F0EB', '$flat-line'],
['#f5f0eb', '$flat-line'],
['#2D3436', '$flat-ink'],
['#2d3436', '$flat-ink'],
['#B2BEC3', '$flat-muted'],
['#b2bec3', '$flat-muted'],
['#636E72', '$flat-muted-2'],
['#636e72', '$flat-muted-2'],
['#FFE0E0', '$flat-danger-line'],
['#ffe0e0', '$flat-danger-line'],
['#E17055', '$flat-orange-text'],
['#e17055', '$flat-orange-text'],
['#FFB088', '$flat-orange-soft'],
['#ffb088', '$flat-orange-soft'],
['#FFD93D', '$flat-bar'],
['#ffd93d', '$flat-bar'],
// ---- 第五轮(现有令牌补登记,2026-10-20)----
['#059669', '$status-success-deep'],
['#059669', '$status-success-deep'],
['#FF6B35', '$mind-accent'],
['#ff6b35', '$mind-accent'],
['#D1D5DB', '$app-border-strong'],
['#d1d5db', '$app-border-strong'],
['#FFEDD5', '$facade-brand-tint-2'],
['#ffedd5', '$facade-brand-tint-2'],
['#B45309', '$status-warning-strong-2'],
['#b45309', '$status-warning-strong-2'],
['#16213E', '$app-ink-night-2'],
['#16213e', '$app-ink-night-2'],
['#ECFDF5', '$status-success-tint'],
['#ecfdf5', '$status-success-tint'],
['#FFFBEB', '$dim-wealth-tint'],
['#fffbeb', '$dim-wealth-tint'],
['#9A3412', '$mat-orange-deep'],
['#9a3412', '$mat-orange-deep'],
['#FDE68A', '$status-warning-light'],
['#fde68a', '$status-warning-light'],
['#F0F4FF', '$dim-wisdom-tint-3'],
['#f0f4ff', '$dim-wisdom-tint-3'],
['#2563EB', '$status-info-deep'],
['#2563eb', '$status-info-deep'],
['#FFF8E1', '$mat-amber-tint'],
['#fff8e1', '$mat-amber-tint'],
['#0284C7', '$status-info-deep-2'],
['#0284c7', '$status-info-deep-2'],
['#1890FF', '$status-info-3'],
['#1890ff', '$status-info-3'],
['#EEF2FF', '$dim-wisdom-tint-2'],
['#eef2ff', '$dim-wisdom-tint-2'],
['#F9F9F9', '$facade-fill-ghost-2'],
['#f9f9f9', '$facade-fill-ghost-2'],
['#EC4899', '$accent-pink'],
['#ec4899', '$accent-pink'],
// ---- 第六轮(新增全局令牌,2026-10-20)----
['#FFF0E0', '$warm-peach'],
['#fff0e0', '$warm-peach'],
['#8A94A6', '$text-steel'],
['#8a94a6', '$text-steel'],
['#2A2A4A', '$game-ink'],
['#2a2a4a', '$game-ink'],
['#1F2D3D', '$ink-steel'],
['#1f2d3d', '$ink-steel'],
['#EDE9FE', '$violet-tint-100'],
['#ede9fe', '$violet-tint-100'],
['#8D6E63', '$brown-warm'],
['#8d6e63', '$brown-warm'],
['#F56C6C', '$el-danger'],
['#f56c6c', '$el-danger'],
['#F44336', '$mat-red'],
['#f44336', '$mat-red'],
['#52C41A', '$status-success-3'],
['#52c41a', '$status-success-3'],
['#1A1A1A', '$text-charcoal'],
['#1a1a1a', '$text-charcoal'],
['#AAA', '$text-ghost-soft'],
['#aaa', '$text-ghost-soft'],
// ---- 第七轮(2026-10-21)----
['#FEF2F2', '$status-danger-tint-2'],
['#fef2f2', '$status-danger-tint-2'],
['#FFA500', '$accent-gold-deep'],
['#ffa500', '$accent-gold-deep'],
['#38BDF8', '$status-info-light'],
['#38bdf8', '$status-info-light'],
['#FF4D4F', '$status-danger-2'],
['#ff4d4f', '$status-danger-2'],
['#E6F7FF', '$status-info-tint-3'],
['#e6f7ff', '$status-info-tint-3'],
['#FF6B9D', '$dim-mind'],
['#ff6b9d', '$dim-mind'],
['#1E3A5F', '$accent-navy'],
['#1e3a5f', '$accent-navy'],
['#1976D2', '$mat-blue'],
['#1976d2', '$mat-blue'],
['#FBBF24', '$status-warning'],
['#fbbf24', '$status-warning'],
['#FFF1E6', '$tint-warm-soft'],
['#fff1e6', '$tint-warm-soft'],
['#BAE6FD', '$tint-sky-line'],
['#bae6fd', '$tint-sky-line'],
['#F7F7F7', '$facade-fill-cool'],
['#f7f7f7', '$facade-fill-cool'],
['#F6FFED', '$status-success-tint-3'],
['#f6ffed', '$status-success-tint-3'],
['#FECACA', '$danger-line'],
['#fecaca', '$danger-line'],
['#BBDEFB', '$mat-blue-light'],
['#bbdefb', '$mat-blue-light'],
['#E0E7FF', '$accent-indigo-tint'],
['#e0e7ff', '$accent-indigo-tint'],
['#F57F17', '$mat-amber-strong'],
['#f57f17', '$mat-amber-strong'],
['#F0F9FF', '$tint-sky-soft'],
['#f0f9ff', '$tint-sky-soft'],
['#A5B4FC', '$indigo-light'],
['#a5b4fc', '$indigo-light'],
['#F57C00', '$mat-orange-700'],
['#f57c00', '$mat-orange-700'],
['#7C2D12', '$mat-orange-900'],
['#7c2d12', '$mat-orange-900'],
['#FFE66D', '$accent-yellow-light'],
['#ffe66d', '$accent-yellow-light'],
['#E64340', '$wechat-red'],
['#e64340', '$wechat-red'],
['#8A6D3B', '$brown-olive'],
['#8a6d3b', '$brown-olive'],
['#67C23A', '$el-success'],
['#67c23a', '$el-success'],
['#E5E5E5', '$facade-line'],
['#e5e5e5', '$facade-line'],
['#E0F2FE', '$tint-sky-tint'],
['#e0f2fe', '$tint-sky-tint'],
['#047857', '$status-success-deep-2'],
['#047857', '$status-success-deep-2'],
['#FF6B6B', '$accent-coral'],
['#ff6b6b', '$accent-coral'],
['#FCD34D', '$accent-amber-light'],
['#fcd34d', '$accent-amber-light'],
['#42A5F5', '$mat-blue-400'],
['#42a5f5', '$mat-blue-400'],
['#166534', '$mat-green-800'],
['#166534', '$mat-green-800'],
['#FDBA74', '$mat-orange-300'],
['#fdba74', '$mat-orange-300'],
['#FFE0B2', '$mat-orange-100'],
['#ffe0b2', '$mat-orange-100'],
['#C8E6C9', '$mat-green-100'],
['#c8e6c9', '$mat-green-100'],
['#ECEFF1', '$mat-bluegray-50'],
['#eceff1', '$mat-bluegray-50'],
['#78909C', '$mat-bluegray-400'],
['#78909c', '$mat-bluegray-400'],
['#6D28D9', '$violet-700'],
['#6d28d9', '$violet-700'],
['#D6EAF8', '$tint-sky-blue'],
['#d6eaf8', '$tint-sky-blue'],
['#FF4444', '$danger-bright'],
['#ff4444', '$danger-bright'],
// ---- 第八轮(uni.scss 已有令牌补登记,2026-10-21)----
['#43A047', '$mat-green-base-2'],
['#43a047', '$mat-green-base-2'],
['#EDF2F7', '$uni-bg-color-grey'],
['#edf2f7', '$uni-bg-color-grey'],
['#7C3AED', '$facade-violet-deep'],
['#7c3aed', '$facade-violet-deep'],
['#FA8C16', '$brand-primary-2'],
['#fa8c16', '$brand-primary-2'],
['#065F46', '$status-success-strong'],
['#065f46', '$status-success-strong'],
['#BBF7D0', '$tint-green-line'],
['#bbf7d0', '$tint-green-line'],
['#FFF3E6', '$warm-cream'],
['#fff3e6', '$warm-cream'],
['#F3E5F5', '$fortune-3-tint'],
['#f3e5f5', '$fortune-3-tint'],
['#7B1FA2', '$fortune-3-ink'],
['#7b1fa2', '$fortune-3-ink'],
['#B8860B', '$hint-gold-ink'],
['#b8860b', '$hint-gold-ink'],
['#F9F9FF', '$dim-wisdom-tint-5'],
['#f9f9ff', '$dim-wisdom-tint-5'],
['#66BB6A', '$mat-green-light-2'],
['#66bb6a', '$mat-green-light-2'],
['#A5D6A7', '$mat-green-soft'],
['#a5d6a7', '$mat-green-soft'],
['#FFE4CC', '$tint-warm-edge'],
['#ffe4cc', '$tint-warm-edge'],
['#F87171', '$status-danger-light'],
['#f87171', '$status-danger-light'],
['#A78BFA', '$facade-violet-light'],
['#a78bfa', '$facade-violet-light'],
['#FFF8F0', '$dim-body-tint'],
['#fff8f0', '$dim-body-tint'],
['#3730A3', '$dim-wisdom-ink'],
['#3730a3', '$dim-wisdom-ink'],
['#F5F6FA', '$app-bg-2'],
['#f5f6fa', '$app-bg-2'],
['#F5F3FF', '$facade-violet-tint-2'],
['#f5f3ff', '$facade-violet-tint-2'],
['#F0F0FF', '$dim-wisdom-tint'],
['#f0f0ff', '$dim-wisdom-tint'],
['#4338CA', '$accent-indigo-ink'],
['#4338ca', '$accent-indigo-ink'],
['#1D4ED8', '$status-info-deep-3'],
['#1d4ed8', '$status-info-deep-3'],
['#FFF5F5', '$status-danger-tint'],
['#fff5f5', '$status-danger-tint'],
['#EFF6FF', '$status-info-tint'],
['#eff6ff', '$status-info-tint'],
['#F08A7A', '$uni-warm'],
['#f08a7a', '$uni-warm'],
['#FF8FB1', '$dim-mind-light-2'],
['#ff8fb1', '$dim-mind-light-2'],
['#A7F3D0', '$status-success-light'],
['#a7f3d0', '$status-success-light'],
['#444', '$facade-text-deep-2'],
['#444', '$facade-text-deep-2'],
['#4F46E5', '$dim-wisdom-deep'],
['#4f46e5', '$dim-wisdom-deep'],
['#F7F8FA', '$app-surface-subtle'],
['#f7f8fa', '$app-surface-subtle'],
['#A855F7', '$facade-violet-light-2'],
['#a855f7', '$facade-violet-light-2'],
['#81C784', '$mat-green-light'],
['#81c784', '$mat-green-light'],
['#BFDBFE', '$tint-blue-line'],
['#bfdbfe', '$tint-blue-line'],
['#FFF1F0', '$status-danger-tint-3'],
['#fff1f0', '$status-danger-tint-3'],
['#C7D2FE', '$tint-indigo-line'],
['#c7d2fe', '$tint-indigo-line'],
['#FCE7F3', '$accent-pink-tint'],
['#fce7f3', '$accent-pink-tint'],
['#FEF9E7', '$hint-cream-bg'],
['#fef9e7', '$hint-cream-bg'],
['#F0E6C0', '$hint-cream-border'],
['#f0e6c0', '$hint-cream-border'],
['#F3E8FF', '$tint-purple-soft'],
['#f3e8ff', '$tint-purple-soft'],
].sort((a, b) => b[0].length - a[0].length)
const GRAY_RULES = ALL_RULES.filter(r => { const i = ALL_RULES.indexOf(r); return i < 30 })
const BRAND_RULES = ALL_RULES.filter(r => { const i = ALL_RULES.indexOf(r); return i >= 30 })
const SECOND_RULES = ALL_RULES.filter(r => { const i = ALL_RULES.indexOf(r); return i >= 30 })
const batch = process.argv.find(a => a.startsWith('--batch='))?.split('=')[1]
const rules = batch === 'brand' ? BRAND_RULES : batch === 'gray' ? GRAY_RULES : ALL_RULES
const label = batch === 'brand' ? '品牌/状态色' : batch === 'gray' ? '灰阶' : '全部'
console.log(`\n批次: ${label} 规则数: ${rules.length}\n`)
function walk(dir, out = []) {
for (const name of fs.readdirSync(dir)) {
if (EXCLUDE_DIRS.has(name)) continue
const p = path.join(dir, name)
const st = fs.statSync(p)
if (st.isDirectory()) walk(p, out)
else if (name.endsWith('.vue')) out.push(p)
}
return out
}
function countReplacements(text, rules) {
let n = 0
for (const [hex] of rules) {
const escaped = hex.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
n += (text.match(new RegExp(escaped + '(?![0-9a-fA-F])', 'g')) || []).length
}
return n
}
function applyRules(text, rules) {
let result = text
for (const [hex, token] of rules) {
const escaped = hex.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
result = result.replace(new RegExp(escaped + '(?![0-9a-fA-F])', 'g'), token)
}
return result
}
function processFile(filePath) {
const src = fs.readFileSync(filePath, 'utf8')
// 按 "
const replCount = countReplacements(body, rules)
if (replCount === 0) continue
const newBody = applyRules(body, rules)
totalRepl += replCount
// 若