/** * 将带 class="list-table" 的 批量替换为 。 * 同文件多表自动追加 -2/-3;不会误匹配 el-table-column。 * * Usage: node scripts/migrate-list-table.mjs */ import fs from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const srcRoot = path.resolve(__dirname, '../src') const viewsRoot = path.resolve(srcRoot, 'views') const componentsRoot = path.resolve(srcRoot, 'components') /** 匹配真正的 el-table 开标签,排除 el-table-column / el-table-column-group 等 */ const OPEN_RE = /])/g const CLOSE_TAG = '' function walk(dir, out = []) { if (!fs.existsSync(dir)) return out for (const name of fs.readdirSync(dir)) { const full = path.join(dir, name) const st = fs.statSync(full) if (st.isDirectory()) { // 跳过 ListTable 自身,避免把内部 el-table 替换掉 if (full.endsWith(`${path.sep}list-table`)) continue walk(full, out) } else if (name.endsWith('.vue')) out.push(full) } return out } function toKebab(s) { return String(s) .replace(/([a-z0-9])([A-Z])/g, '$1-$2') .replace(/[_\s]+/g, '-') .replace(/-+/g, '-') .toLowerCase() } function baseKeyFromFile(file) { let rel if (file.startsWith(viewsRoot + path.sep) || file.startsWith(viewsRoot + '/')) { rel = path.relative(viewsRoot, file) } else { rel = path.relative(componentsRoot, file) } const parts = rel.split(path.sep) const stem = path.basename(file, '.vue') const dirs = parts.slice(0, -1).map(toKebab) if (!['index', 'Index'].includes(stem)) dirs.push(toKebab(stem)) return dirs.filter(Boolean).join('-') || toKebab(stem) } function hasListTableClass(attrText) { return /class\s*=\s*(['"`])([^'"`]*\blist-table\b[^'"`]*)\1/.test(attrText) } function findOpenTagEnd(content, nameEnd) { let j = nameEnd let quote = null while (j < content.length) { const ch = content[j] if (quote) { if (ch === quote) quote = null } else if (ch === '"' || ch === "'" || ch === '`') { quote = ch } else if (ch === '>') { return j } j++ } return -1 } function transform(content, file) { const baseKey = baseKeyFromFile(file) let tableIndex = 0 let i = 0 let out = '' while (i < content.length) { OPEN_RE.lastIndex = i const m = OPEN_RE.exec(content) if (!m) { out += content.slice(i) break } out += content.slice(i, m.index) const start = m.index const nameEnd = start + '` i = openEnd + 1 continue } out += `` i = openEnd + 1 let depth = 1 while (i < content.length && depth > 0) { OPEN_RE.lastIndex = i const nextOpen = OPEN_RE.exec(content) const nextCloseIdx = content.indexOf(CLOSE_TAG, i) if (nextCloseIdx < 0) { out += content.slice(i) i = content.length depth = 0 break } const openIdx = nextOpen ? nextOpen.index : -1 if (openIdx >= 0 && openIdx < nextCloseIdx) { // 嵌套 el-table:原样拷贝开标签,depth++ out += content.slice(i, openIdx) const nestedNameEnd = openIdx + ' 0 && content !== raw) { fs.writeFileSync(file, content) total += count changed.push(`${path.relative(path.resolve(__dirname, '..'), file)} (${count})`) } } console.log(`Migrated ${total} tables in ${changed.length} files:`) for (const line of changed) console.log(' -', line)