import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const componentsDir = path.join(root, 'node_modules/element-plus/es/components')
const directivesDir = path.join(root, 'node_modules/element-plus/es/directives')
const packageJson = JSON.parse(fs.readFileSync(path.join(root, 'node_modules/element-plus/package.json'), 'utf8'))
const elementPlus = await import('element-plus')
const componentSlugs = fs
.readdirSync(componentsDir, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name)
.sort()
const directiveSlugs = fs
.readdirSync(directivesDir, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name)
.sort()
const publicExports = Object.keys(elementPlus).filter((name) => /^El/.test(name)).sort()
const groupRules = [
['基础', ['affix', 'backtop', 'button', 'button-group', 'config-provider', 'divider', 'icon', 'link', 'scrollbar', 'space', 'text', 'watermark']],
['布局', ['aside', 'card', 'col', 'collapse', 'collapse-item', 'container', 'footer', 'header', 'main', 'row', 'splitter', 'splitter-panel']],
['表单', ['autocomplete', 'cascader', 'cascader-panel', 'checkbox', 'checkbox-button', 'checkbox-group', 'color-picker', 'color-picker-panel', 'date-picker', 'date-picker-panel', 'form', 'form-item', 'input', 'input-number', 'input-otp', 'input-tag', 'mention', 'option', 'option-group', 'radio', 'radio-button', 'radio-group', 'rate', 'select', 'select-v2', 'slider', 'switch', 'time-picker', 'time-select', 'transfer', 'tree-select', 'upload']],
['数据展示', ['avatar', 'avatar-group', 'badge', 'calendar', 'carousel', 'carousel-item', 'check-tag', 'countdown', 'descriptions', 'descriptions-item', 'empty', 'image', 'image-viewer', 'pagination', 'progress', 'result', 'skeleton', 'skeleton-item', 'statistic', 'table', 'table-column', 'table-v2', 'tag', 'tree', 'tree-v2']],
['导航', ['anchor', 'anchor-link', 'breadcrumb', 'breadcrumb-item', 'dropdown', 'dropdown-item', 'dropdown-menu', 'menu', 'menu-item', 'menu-item-group', 'page-header', 'segmented', 'step', 'steps', 'sub-menu', 'tab-pane', 'tabs', 'timeline', 'timeline-item']],
['反馈与覆盖层', ['alert', 'dialog', 'drawer', 'loading', 'message', 'message-box', 'notification', 'overlay', 'popconfirm', 'popover', 'popper', 'tooltip', 'tour', 'tour-step']],
]
const priorities = new Map(
[
'button',
'button-group',
'form',
'form-item',
'input',
'input-number',
'select',
'select-v2',
'option',
'option-group',
'date-picker',
'table',
'table-column',
'table-v2',
'pagination',
'tag',
'menu',
'menu-item',
'sub-menu',
'breadcrumb',
'breadcrumb-item',
'tabs',
'tab-pane',
'dropdown',
'dropdown-menu',
'dropdown-item',
'dialog',
'drawer',
'tree',
'tree-v2',
'tree-select',
'upload',
'descriptions',
'descriptions-item',
'alert',
'message',
'message-box',
'notification',
'popconfirm',
'tooltip',
'loading',
].map((slug) => [slug, '高'])
)
const contextOnly = new Set([
'anchor-link',
'aside',
'avatar-group',
'breadcrumb-item',
'button-group',
'carousel-item',
'cascader-panel',
'checkbox-button',
'checkbox-group',
'col',
'collapse-item',
'color-picker-panel',
'date-picker-panel',
'descriptions-item',
'dropdown-item',
'dropdown-menu',
'footer',
'form-item',
'header',
'image-viewer',
'main',
'menu-item',
'menu-item-group',
'option',
'option-group',
'radio-button',
'radio-group',
'skeleton-item',
'splitter-panel',
'step',
'sub-menu',
'tab-pane',
'table-column',
'timeline-item',
'tour-step',
])
function titleFromSlug(slug) {
const fixed = new Map([
['otp', 'OTP'],
['v2', 'V2'],
])
return slug
.split('-')
.map((part) => fixed.get(part) ?? part.charAt(0).toUpperCase() + part.slice(1))
.join(' ')
}
function exportNameFromSlug(slug) {
return `El${slug
.split('-')
.map((part) => (part === 'v2' ? 'V2' : part === 'otp' ? 'Otp' : part.charAt(0).toUpperCase() + part.slice(1)))
.join('')}`
}
function groupFor(slug) {
const match = groupRules.find(([, slugs]) => slugs.includes(slug))
return match?.[0] ?? '内部基础'
}
function codeFor(slug, tag, name) {
const snippets = {
alert: '',
autocomplete: '',
badge: '待办',
breadcrumb: '销售管理销售订单',
button: '新建订单',
calendar: '',
card: '订单摘要SO-10642',
checkbox: '仅看异常',
'color-picker': '',
'date-picker': '',
descriptions: '上海辰光零售',
dialog: '确认通过该订单?',
drawer: 'SO-10642',
dropdown: '更多操作导出',
empty: '',
form: '',
image: '',
input: '',
'input-number': '',
'input-tag': '',
link: '查看详情',
menu: '销售订单',
pagination: '',
popconfirm: '作废',
progress: '',
radio: '已审批',
rate: '',
result: '',
segmented: "",
select: '',
'select-v2': '',
skeleton: '',
slider: '',
statistic: '',
switch: '',
table: '',
'table-v2': '',
tabs: '订单摘要',
tag: '已审批',
text: '业务提示',
timeline: '完成审批',
'time-picker': '',
'time-select': '',
tooltip: '审计',
transfer: '',
tree: '',
'tree-select': '',
upload: '上传附件',
}
if (snippets[slug]) return snippets[slug]
if (contextOnly.has(slug)) return `\n<${tag}>${name}${tag}>`
return `<${tag}>${name}${tag}>`
}
const componentRecords = componentSlugs.map((slug) => {
const name = titleFromSlug(slug)
const exportName = exportNameFromSlug(slug)
return {
slug,
name,
tag: `el-${slug}`,
exportName,
hasPublicExport: publicExports.includes(exportName),
group: groupFor(slug),
priority: priorities.get(slug) ?? (groupFor(slug) === '内部基础' ? '低' : '中'),
contextOnly: contextOnly.has(slug),
code: codeFor(slug, `el-${slug}`, name),
}
})
const publicExportRecords = publicExports.map((name) => ({
name,
kind: componentRecords.some((record) => record.exportName === name) ? 'component' : 'service-or-subpart',
}))
const directiveRecords = directiveSlugs.map((slug) => ({
slug,
name: titleFromSlug(slug),
code: `v-${slug}`,
}))
const content = `// Generated by scripts/generate-element-plus-inventory.mjs from the local Element Plus package.\n// Do not hand-edit component counts; rerun the generator after upgrading Element Plus.\n\nexport const elementPlusInventoryMeta = ${JSON.stringify(
{
packageName: packageJson.name,
packageVersion: packageJson.version,
componentDirectories: componentRecords.length,
publicElExports: publicExportRecords.length,
directiveDirectories: directiveRecords.length,
},
null,
2
)}\n\nexport const elementPlusComponentRecords = ${JSON.stringify(componentRecords, null, 2)}\n\nexport const elementPlusPublicExportRecords = ${JSON.stringify(publicExportRecords, null, 2)}\n\nexport const elementPlusDirectiveRecords = ${JSON.stringify(directiveRecords, null, 2)}\n`
fs.writeFileSync(path.join(root, 'src/data/elementPlusInventory.js'), content)