169 lines
5.5 KiB
TypeScript
169 lines
5.5 KiB
TypeScript
import type { Plugin } from 'vite'
|
|
import fs from 'fs'
|
|
import path from 'path'
|
|
|
|
/**
|
|
* 从组件文件内容中提取复合组件的子组件
|
|
* 例如:从 StepList 组件中提取 StepList.Step
|
|
*/
|
|
function extractSubComponents(fileContent: string, componentName: string): string[] {
|
|
const subComponents: string[] = []
|
|
|
|
// 匹配模式:ComponentName.SubComponent = SubComponent
|
|
const pattern1 = new RegExp(`${componentName}\\.(\\w+)\\s*=\\s*(\\w+)`, 'g')
|
|
let match
|
|
while ((match = pattern1.exec(fileContent)) !== null) {
|
|
const subComponentName = match[1]
|
|
if (!subComponents.includes(subComponentName)) {
|
|
subComponents.push(subComponentName)
|
|
}
|
|
}
|
|
|
|
// 匹配模式:ComponentName.SubComponent = function SubComponent
|
|
const pattern2 = new RegExp(`${componentName}\\.(\\w+)\\s*=\\s*(?:function|const)\\s+(\\w+)`, 'g')
|
|
while ((match = pattern2.exec(fileContent)) !== null) {
|
|
const subComponentName = match[1]
|
|
if (!subComponents.includes(subComponentName)) {
|
|
subComponents.push(subComponentName)
|
|
}
|
|
}
|
|
|
|
return subComponents
|
|
}
|
|
|
|
/**
|
|
* 创建一个 Vite 插件,用于自动扫描和注册 MDX 组件
|
|
* 这个插件会在构建时扫描 src/components 目录,并生成组件索引文件
|
|
*/
|
|
export function mdxComponentsPlugin(options: {
|
|
componentsPath?: string
|
|
outputPath?: string
|
|
}): Plugin {
|
|
const {
|
|
componentsPath = './src/components',
|
|
outputPath = './src/generated/mdx-components.ts'
|
|
} = options
|
|
|
|
return {
|
|
name: 'mdx-components-scanner',
|
|
enforce: 'post',
|
|
buildStart() {
|
|
// 构建开始时,扫描组件并生成索引文件
|
|
try {
|
|
const componentsDir = path.resolve(process.cwd(), componentsPath)
|
|
|
|
if (!fs.existsSync(componentsDir)) {
|
|
console.log(`[MDX] 组件目录不存在: ${componentsDir}`)
|
|
return
|
|
}
|
|
|
|
// 递归扫描所有 .tsx 和 .ts 文件
|
|
const scanDirectory = (dir: string, baseDir: string = dir): string[] => {
|
|
const files: string[] = []
|
|
const entries = fs.readdirSync(dir, { withFileTypes: true })
|
|
|
|
for (const entry of entries) {
|
|
const fullPath = path.join(dir, entry.name)
|
|
const relativePath = path.relative(baseDir, fullPath).replace(/\\/g, '/')
|
|
|
|
if (entry.isDirectory()) {
|
|
files.push(...scanDirectory(fullPath, baseDir))
|
|
} else if (entry.isFile() && /\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
const componentName = entry.name.replace(/\.(tsx?|jsx?)$/, '')
|
|
if (componentName !== 'index' && !componentName.startsWith('.')) {
|
|
files.push(relativePath)
|
|
}
|
|
}
|
|
}
|
|
|
|
return files
|
|
}
|
|
|
|
const componentFiles = scanDirectory(componentsDir)
|
|
|
|
if (componentFiles.length === 0) {
|
|
console.log(`[MDX] 未找到任何组件文件`)
|
|
return
|
|
}
|
|
|
|
console.log(`[MDX] 扫描到 ${componentFiles.length} 个组件:`)
|
|
componentFiles.forEach(file => console.log(` - ${file}`))
|
|
|
|
// 分析每个组件文件,提取子组件信息
|
|
const componentInfo: Array<{
|
|
file: string
|
|
componentName: string
|
|
subComponents: string[]
|
|
}> = []
|
|
|
|
for (const file of componentFiles) {
|
|
const fullPath = path.join(componentsDir, file)
|
|
const componentName = path.basename(file).replace(/\.(tsx?|jsx?)$/, '')
|
|
const fileContent = fs.readFileSync(fullPath, 'utf-8')
|
|
const subComponents = extractSubComponents(fileContent, componentName)
|
|
|
|
componentInfo.push({
|
|
file,
|
|
componentName,
|
|
subComponents
|
|
})
|
|
|
|
if (subComponents.length > 0) {
|
|
console.log(`[MDX] 发现 ${componentName} 的子组件: ${subComponents.join(', ')}`)
|
|
}
|
|
}
|
|
|
|
// 生成组件索引文件
|
|
const imports: string[] = []
|
|
const exports: string[] = []
|
|
const configEntries: string[] = []
|
|
|
|
for (const info of componentInfo) {
|
|
const { file, componentName, subComponents } = info
|
|
const importPath = `../components/${file.replace(/\.(tsx?|jsx?)$/, '')}`
|
|
|
|
// 导入主组件
|
|
imports.push(`import { ${componentName} } from '${importPath}'`)
|
|
exports.push(` ${componentName},`)
|
|
configEntries.push(` ${componentName}: '${importPath}'`)
|
|
|
|
// 如果有子组件,也需要注册它们
|
|
for (const subComponent of subComponents) {
|
|
const subComponentKey = `${componentName}.${subComponent}`
|
|
exports.push(` "${subComponentKey}": ${componentName}.${subComponent},`)
|
|
// 注意:子组件的配置指向父组件
|
|
configEntries.push(` "${subComponentKey}": '${importPath}'`)
|
|
}
|
|
}
|
|
|
|
const content = `// 自动生成的 MDX 组件索引文件
|
|
// 请勿手动编辑此文件
|
|
|
|
${imports.join('\n')}
|
|
|
|
export const MDX_COMPONENTS = {
|
|
${exports.join('\n')}
|
|
}
|
|
|
|
// 导出默认配置对象
|
|
export const mdxComponentsConfig = {
|
|
${configEntries.join(',\n')}
|
|
}
|
|
`
|
|
|
|
// 确保输出目录存在
|
|
const outputDir = path.dirname(outputPath)
|
|
if (!fs.existsSync(outputDir)) {
|
|
fs.mkdirSync(outputDir, { recursive: true })
|
|
}
|
|
|
|
// 写入文件
|
|
fs.writeFileSync(outputPath, content, 'utf-8')
|
|
console.log(`[MDX] 组件索引文件已生成: ${outputPath}`)
|
|
|
|
} catch (error) {
|
|
console.error('[MDX] 扫描组件失败:', error)
|
|
}
|
|
}
|
|
}
|
|
} |