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}` return `<${tag}>${name}` } 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)