| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- #!/usr/bin/env node
- /**
- * D31: script 段维度色批量收编 BrandColors
- * 只替换「整段字符串就是 hex」的写法('#10B981' / "#10B981"),避免破坏模板字符串/渐变拼接。
- * 自动按文件深度计算相对 import 路径;已 import 的文件不重复注入。
- *
- * 用法: node scripts/d31-script-hex-harvest.js [--dry]
- */
- const fs = require('fs');
- const path = require('path');
- const ROOT = path.resolve(__dirname, '..');
- const DRY = process.argv.includes('--dry');
- const EXCLUDE_DIRS = new Set(['node_modules', 'dist', 'unpackage', '.git', 'scripts', '.tmp_equiv']);
- // hex -> BrandColors key(仅登记已存在于 utils/brand-colors.js 的键)
- const MAP = {
- '#10B981': 'success',
- '#F59E0B': 'wealth',
- '#FF6B9D': 'mind',
- '#FF8C42': 'body',
- '#F97316': 'primary',
- '#6366F1': 'wisdom',
- '#FFFFFF': 'white',
- '#3B82F6': 'infoBlue',
- '#EF4444': 'danger',
- '#94A3B8': 'appTextSubtle',
- '#8B5CF6': 'violet',
- '#EA580C': 'orangeDeep',
- '#0EA5E9': 'infoSky',
- '#EC4899': 'pink',
- '#6B7280': 'textSlate',
- '#4CAF50': 'matGreen',
- '#FF6B35': 'mindAccent',
- '#8D6E63': 'brownWarm',
- // ---- D40:跨文件重复的 script 色值(≥2 处)----
- '#A8A29E': 'stone400',
- '#64748B': 'slate500',
- '#444444': 'textDeep',
- '#444': 'textDeep',
- '#FFFBEB': 'amber50',
- '#1E293B': 'slate800',
- '#78716C': 'stone500',
- '#E7E5E4': 'stone200',
- '#999999': 'textMuted',
- '#999': 'textMuted',
- '#ECFDF5': 'emerald50',
- '#06B6D4': 'cyan500',
- '#1C1917': 'stone900',
- '#CBD5E1': 'slate300',
- '#475569': 'slate600',
- '#F56C6C': 'elementDanger',
- '#C0C0C0': 'silver',
- '#FFF0F5': 'mindTint',
- '#4F46E5': 'indigo600',
- '#EEF2FF': 'indigo50',
- '#DB2777': 'pink600',
- '#FDF2F8': 'pink50',
- '#F5F5F4': 'stone100',
- '#000000': 'black',
- '#4A5568': 'gray700',
- '#FFB74D': 'orange300',
- '#E2E8F0': 'slate200',
- '#EEF2F7': 'slateGhost',
- '#667EEA': 'indigoPeriwinkle',
- '#FFF': 'white',
- '#FFFFFF': 'white',
- '#F3E5F5': 'tintPurpleSoft',
- '#FFF3E0': 'tintOrangeSoft',
- '#E8F5E9': 'tintGreenSoft',
- '#FCE4EC': 'tintPinkSoft',
- };
- const bcSrc = fs.readFileSync(path.join(ROOT, 'utils/brand-colors.js'), 'utf8');
- for (const key of Object.values(MAP)) {
- if (!new RegExp('^\\s*' + key + ':', 'm').test(bcSrc)) {
- console.error('缺少 BrandColors 键: ' + key);
- process.exit(1);
- }
- }
- 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;
- }
- const files = walk(ROOT);
- let changedFiles = 0;
- let replacements = 0;
- const log = [];
- for (const abs of files) {
- const src = fs.readFileSync(abs, 'utf8');
- const styleIdx = src.search(/<style/);
- if (styleIdx === -1) continue;
- let head = src.slice(0, styleIdx);
- const tail = src.slice(styleIdx);
- let count = 0;
- for (const [hex, key] of Object.entries(MAP)) {
- const re = new RegExp("(['\"])" + hex + "\\1", 'g');
- head = head.replace(re, () => {
- count++;
- return 'BrandColors.' + key;
- });
- }
- if (count === 0) continue;
- // 追加 import:已有 brand-colors 具名导入则并入 BrandColors,否则新增一行
- if (!/\bBrandColors\b[^}]*\}\s*from\s*['"][^'"]*brand-colors/.test(head)) {
- const existing = head.match(/import\s*\{([^}]*)\}\s*from\s*(['"][^'"]*brand-colors(?:\.js)?['"])/);
- if (existing) {
- head = head.replace(existing[0], 'import {' + existing[1].replace(/\s*$/, '') + (existing[1].trim().endsWith(',') ? ' ' : ', ') + 'BrandColors } from ' + existing[2]);
- } else {
- const rel = path.relative(path.dirname(abs), path.join(ROOT, 'utils/brand-colors.js')).split(path.sep).join('/');
- const importLine = "import { BrandColors } from '" + (rel.startsWith('.') ? rel : './' + rel) + "'";
- const m = head.match(/<script[^>]*>\r?\n/);
- if (!m) {
- console.warn('SKIP(no script tag): ' + path.relative(ROOT, abs));
- continue;
- }
- head = head.replace(m[0], m[0] + importLine + (m[0].includes('\r\n') ? '\r\n' : '\n'));
- }
- }
- if (!DRY) fs.writeFileSync(abs, head + tail, 'utf8');
- changedFiles++;
- replacements += count;
- log.push(String(count).padStart(3) + ' ' + path.relative(ROOT, abs).split(path.sep).join('/'));
- }
- log.sort((a, b) => parseInt(b) - parseInt(a));
- console.log((DRY ? '[DRY] ' : '') + '文件: ' + changedFiles + ' 替换: ' + replacements);
- log.slice(0, 40).forEach(l => console.log(l));
- if (log.length > 40) console.log('... 共 ' + log.length + ' 个文件');
|