吴羡
2 天以前 1193ed5cf72c8d1093d0c061b9eefe61a28b0cb7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// 找出「相邻兄弟元素带同名 slot」的写法。
//
// 为什么单独查这个:`.sync`/`slot` 那套 codemod 会把每个带 slot="x" 的元素单独包一层
// <template #x>。如果两个相邻兄弟带的是同一个 slot 名(典型是 v-if / v-else 两段
// footer),就会被拆成两个 <template #x>,既触发「v-else 没有相邻 v-if」,又触发
// 「同名插槽重复」。正确写法是把这一串兄弟合并进同一个 <template #x>。
//
// 用法:node scripts/migrate/find-dup-slot.mjs <dir...>
import { readFileSync, readdirSync, statSync } from 'node:fs'
import { join, extname } from 'node:path'
import { parse } from '@vue/compiler-sfc'
 
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
}
 
function slotNameOf(node) {
  const a = (node.props || []).find(
    p => (p.type === 6 && p.name === 'slot') || (p.type === 7 && p.name === 'slot' && p.arg)
  )
  if (!a) return null
  return a.type === 6 ? a.value && a.value.content : a.arg.content
}
 
const targets = process.argv.slice(2)
const files = targets.flatMap(t => collect(t))
let sites = 0
const filesHit = new Set()
 
for (const f of files) {
  const src = readFileSync(f, 'utf8')
  let descriptor
  try {
    descriptor = parse(src, { 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) {
      const kids = (n.children || []).filter(c => c.type === 1)
      for (let i = 1; i < kids.length; i++) {
        const a = slotNameOf(kids[i - 1])
        const b = slotNameOf(kids[i])
        if (a && b && a === b) {
          sites++
          filesHit.add(f)
          console.log(`  ${f} L${kids[i].loc.start.line}  <${n.tag}> 相邻同名 slot="${a}"`)
          break
        }
      }
    }
    for (const c of n.children || []) visit(c)
  }
  visit(ast)
}
 
console.log(`\n=== 相邻同名 slot:${sites} 处 / ${filesHit.size} 文件`)