#!/usr/bin/env node
/**
* C 批:JS 色板收编
* 将 后面, 前面)
const templateIdx = result.indexOf('')
if (templateIdx !== -1 && !importAdded) {
const importLine = `\nimport { ${[...new Set(JS_RULES.map(r => r.token.split('.')[1]))]
.sort().join(', ')} } from '@/utils/brand-colors.js'\n`
result = before + importLine + result.slice(match.index)
importAdded = true
}
result = result.slice(0, match.index) +
match[1] + match[2] + '>' + newBody + '' +
result.slice(SCRIPT_RE.lastIndex)
SCRIPT_RE.lastIndex = result.indexOf('', match.index) + ''.length
}
}
if (!dryRun && changed) {
fs.writeFileSync(result === text ? text : result, text) // bug guard
return { changed: true, replacements: totalRepl, needsImport }
}
return { changed: false, replacements: totalRepl, needsImport }
}
// 简化版:直接处理整个文件
function processFileSimple(filePath, dryRun = false) {
const src = fs.readFileSync(filePath, 'utf8')
let newSrc = src
let totalRepl = 0
// 收集文件中用到的 token
const usedTokens = new Set()
for (const rule of JS_RULES) {
const escaped = rule.hex.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
const re = new RegExp(`['"]${escaped}(?![0-9a-fA-F])['"]`, 'g')
const m = src.match(re)
if (m) {
totalRepl += m.length
usedTokens.add(rule.token.split('.')[1])
}
}
if (totalRepl === 0) return { changed: false, replacements: 0, needsImport: false }
// 替换 script 块内的 hex
const SCRIPT_RE = /( 后添加 import(如果还没加过)
if (usedTokens.size > 0 && !src.includes('brand-colors')) {
const importLine = `\nimport { ${[...usedTokens].sort().join(', ')} } from '@/utils/brand-colors.js'`
newSrc = newSrc.replace(/(<\/script>)/, `$1${importLine}`)
}
if (!dryRun && newSrc !== src) {
fs.writeFileSync(filePath, newSrc, 'utf8')
}
return { changed: newSrc !== src, replacements: totalRepl, needsImport: true }
}
function main() {
const dryRun = process.argv.includes('--dry-run')
const fileArg = process.argv.find(a => a.startsWith('--file='))?.split('=')[1]
const targets = fileArg
? [path.join(ROOT, fileArg)]
: TOP_FILES.map(f => path.join(ROOT, f))
// 如果没指定文件,跑全量扫描(所有 .vue)
if (!fileArg) {
const allFiles = walk(ROOT)
// 过滤出已处理的 TOP 文件,加上其余全部
const topSet = new Set(TOP_FILES.map(f => path.resolve(ROOT, f)))
const remaining = allFiles.filter(f => !topSet.has(f))
targets.push(...remaining)
}
let grandTotal = 0
const log = []
for (const filePath of targets) {
if (!fs.existsSync(filePath)) {
console.warn(`跳过不存在: ${filePath}`)
continue
}
const rel = path.relative(ROOT, filePath)
const result = processFileSimple(filePath, dryRun)
if (result.changed || result.replacements > 0) {
grandTotal += result.replacements
log.push(`${result.replacements.toString().padStart(4)} ${rel}`)
if (!dryRun) console.log(`✓ ${rel} (${result.replacements} 处)`)
} else {
console.log(`- ${rel} (0 处,跳过)`)
}
}
if (!dryRun) {
console.log(`\n=== JS 色板收编完成 ===`)
console.log(`替换总次数: ${grandTotal}`)
log.sort((a, b) => parseInt(b.split(' ')[0]) - parseInt(a.split(' ')[0]))
console.log('')
for (const line of log) console.log(line)
} else {
console.log('\n[dry-run] 预览替换量:', grandTotal)
}
}
main()