import { test } from 'node:test' import assert from 'node:assert/strict' import { readFileSync, readdirSync } from 'node:fs' import { fileURLToPath } from 'node:url' import { resolve, dirname } from 'node:path' const here = dirname(fileURLToPath(import.meta.url)) const repoRoot = resolve(here, '..') /** * Element Plus 组件名 → 按需样式目录(ElMessageBox → message-box)。 * 与 element-plus/es/components/<目录>/style/css 的目录名一致。 */ function styleDirOf(component: string) { return component.replace(/^El/, '').replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase() } /** 扫 src 下所有「显式 from 'element-plus' 具名导入的 El* 组件」及其出处 */ function explicitElementPlusImports(): Map { const used = new Map() for (const file of readdirSync(resolve(repoRoot, 'src'), { recursive: true }) as string[]) { if (!/\.(ts|vue)$/.test(file)) continue const source = readFileSync(resolve(repoRoot, 'src', file), 'utf-8') for (const match of source.matchAll(/import\s+(?!type\b)\{([^}]*)\}\s*from\s*['"]element-plus['"]/g)) { for (const raw of match[1].split(',')) { const name = raw.trim().split(/\s+as\s+/)[0].trim() if (!/^El[A-Z]/.test(name)) continue const files = used.get(name) || [] if (!files.includes(file)) files.push(file) used.set(name, files) } } } return used } /** * 显式 import 的 EP 组件不会被 ElementPlusResolver 注入样式(resolver 只处理 * 模板里未解析的组件和自动导入的标识符),漏在入口引入样式就会出现 * 「弹窗跑到文档左上角、和页头文字叠在一起」这类无定位样式的故障。 */ test('test_element_plus_explicit_imports_have_on_demand_style', () => { const entry = readFileSync(resolve(repoRoot, 'src/main.ts'), 'utf-8') // 锚定行首:注释掉的 import 不算数 const styleDirs = [ ...entry.matchAll(/^import\s+['"]element-plus\/es\/components\/([\w-]+)\/style\/css['"]/gm), ].map((match) => match[1]) for (const [component, files] of explicitElementPlusImports()) { const dir = styleDirOf(component) assert.ok( styleDirs.includes(dir), `${component} 在 ${files.join('、')} 被显式 import,按需样式不会自动注入;` + `请在 src/main.ts 增加 import 'element-plus/es/components/${dir}/style/css'`, ) } })