// 扫描 el-radio / el-radio-button / el-checkbox / el-checkbox-button 上的 label 属性。
|
//
|
// element-plus 2.13:`label` 仍可作为「值」使用(actualValue 会回退到 label),
|
// 但同时 `label` 又是默认插槽的兜底文案(`renderSlot(..., () => label)`)。
|
// 所以:
|
// - 有子节点(插槽)的元素,把 label 改名 value 是安全的(文案仍来自插槽)
|
// - 没有子节点的元素,label 同时承担文案与值,改名会让文案变空 → 必须人工
|
// - 已经有 value 的元素,label 只是文案,无 deprecation 告警,不用动
|
// 用法:node scripts/migrate/scan-radio-label.mjs <dir...>
|
import { readFileSync, readdirSync, statSync } from 'node:fs'
|
import { join, extname } from 'node:path'
|
import { parse } from '@vue/compiler-sfc'
|
|
const FAMILY = new Set(['el-radio', 'el-radio-button', 'el-checkbox', 'el-checkbox-button'])
|
|
function collect(dir, out = []) {
|
for (const e of readdirSync(dir)) {
|
const p = join(dir, e)
|
if (statSync(p).isDirectory()) collect(p, out)
|
else if (extname(p) === '.vue') out.push(p)
|
}
|
return out
|
}
|
|
/** 元素上的 label 属性(兼容 `label="x"` 与 `:label="x"`) */
|
function labelAttr(node) {
|
return (node.props || []).find(p =>
|
(p.type === 6 && p.name === 'label') ||
|
(p.type === 7 && p.name === 'bind' && p.arg && p.arg.content === 'label')
|
)
|
}
|
function hasValue(node) {
|
return (node.props || []).some(p =>
|
(p.type === 6 && p.name === 'value') ||
|
(p.type === 7 && p.name === 'bind' && p.arg && p.arg.content === 'value')
|
)
|
}
|
|
const targets = process.argv.slice(2)
|
const files = targets.flatMap(t => (statSync(t).isDirectory() ? collect(t) : [t]))
|
let ok = 0
|
let manual = 0
|
let skip = 0
|
|
for (const f of files) {
|
let descriptor
|
try {
|
descriptor = parse(readFileSync(f, 'utf8'), { filename: f }).descriptor
|
} catch {
|
continue
|
}
|
const ast = descriptor.template?.ast
|
if (!ast) continue
|
const visit = n => {
|
if (!n || typeof n !== 'object') return
|
if (n.type === 1 && FAMILY.has(n.tag)) {
|
const attr = labelAttr(n)
|
if (attr) {
|
const where = `<${n.tag}> L${n.loc.start.line}`
|
if (hasValue(n)) {
|
skip++
|
} else if ((n.children || []).some(c => c.type !== 2 || c.content.trim())) {
|
ok++
|
if (process.env.VERBOSE) console.log(` 可改 ${f} ${where}`)
|
} else {
|
manual++
|
console.log(` 人工 ${f} ${where} ← 无子节点,label 兼作文案`)
|
}
|
}
|
}
|
for (const c of n.children || []) visit(c)
|
}
|
visit(ast)
|
}
|
|
console.log(`\n=== 有子节点、无 value(可机械改 label→value):${ok} 处`)
|
console.log(`=== 无子节点(需人工):${manual} 处`)
|
console.log(`=== 已有 value(label 仅作文案,不动):${skip} 处`)
|