react-dep-analyzer
Advanced tools
Comparing version 1.0.10 to 1.0.11
@@ -35,3 +35,5 @@ interface ComponentPathConfig { | ||
private generateMermaidFlowchart; | ||
private generateComponentContent; | ||
private generateComponentMarkdown; | ||
private generateFullMarkdown; | ||
generateMarkDown(outputPath?: string): void; | ||
@@ -38,0 +40,0 @@ generateDependencyTree(outputPath?: string): void; |
@@ -222,17 +222,15 @@ // src/index.ts | ||
} | ||
generateComponentMarkdown(componentName, data) { | ||
const componentPathConfig = this.componentPaths.find( | ||
(config) => data.file.includes(path.relative(this.projectRoot, config.path)) | ||
); | ||
if (!componentPathConfig) { | ||
console.warn(`\u8B66\u544A: \u627E\u4E0D\u5230\u5143\u4EF6 ${componentName} \u7684\u8DEF\u5F91\u914D\u7F6E`); | ||
return ""; | ||
generateComponentContent(componentName, data, options = {}) { | ||
const { | ||
includeHeader = true, | ||
includeSeparator = false | ||
} = options; | ||
let output = ""; | ||
if (includeHeader) { | ||
output += `# ${componentName} | ||
`; | ||
} | ||
const relativeFilePath = path.relative(this.projectRoot, data.file); | ||
const targetDir = path.join(this.projectRoot, path.dirname(relativeFilePath)); | ||
const componentFileName = `${componentName}.md`; | ||
const componentFilePath = path.join(targetDir, componentFileName); | ||
let output = `# ${componentName} | ||
`; | ||
output += `> File Path: \`${data.file}\` | ||
const filePath = path.relative(this.projectRoot, data.file); | ||
output += `> File Path: \`${filePath}\` | ||
@@ -242,42 +240,21 @@ `; | ||
output += this.generateMermaidFlowchart(/* @__PURE__ */ new Set(), /* @__PURE__ */ new Set(), { name: componentName, data }); | ||
if (data.dependencies.length > 0) { | ||
const dependencyGroups = this.componentPaths.reduce((groups, pathConfig) => { | ||
const groupName = pathConfig.path.split("/").pop() || "other"; | ||
groups[groupName] = data.dependencies.filter( | ||
(d) => d.path.startsWith(pathConfig.importPrefix) | ||
); | ||
return groups; | ||
}, {}); | ||
const otherDeps = data.dependencies.filter( | ||
(d) => !this.componentPaths.some( | ||
(config) => d.path.startsWith(config.importPrefix) | ||
) | ||
output += "\n"; | ||
const dependencyGroups = this.componentPaths.reduce((groups, pathConfig) => { | ||
const groupName = pathConfig.path.split("/").pop() || "other"; | ||
groups[groupName] = data.dependencies.filter( | ||
(d) => d.path.startsWith(pathConfig.importPrefix) | ||
); | ||
Object.entries(dependencyGroups).forEach(([groupName, deps]) => { | ||
if (deps.length > 0) { | ||
output += `## ${groupName.charAt(0).toUpperCase() + groupName.slice(1)} Dependencies | ||
return groups; | ||
}, {}); | ||
Object.entries(dependencyGroups).forEach(([groupName, deps]) => { | ||
if (deps.length > 0) { | ||
output += `## ${groupName.charAt(0).toUpperCase() + groupName.slice(1)} Dependencies | ||
`; | ||
deps.forEach((dep) => { | ||
const importsList = dep.imports.join(", "); | ||
const relativePath = path.relative(this.projectRoot, dep.file); | ||
output += `> - **${dep.path}** | ||
`; | ||
output += `> - File: \`${relativePath}\` | ||
`; | ||
output += `> - Imports: \`${importsList}\` | ||
`; | ||
}); | ||
output += "\n"; | ||
} | ||
}); | ||
if (otherDeps.length > 0) { | ||
output += "## Other Dependencies\n"; | ||
otherDeps.forEach((dep) => { | ||
const importsList = dep.imports.join(", "); | ||
const relativePath = path.relative(this.projectRoot, dep.file); | ||
deps.forEach((dep) => { | ||
const depPath = path.relative(this.projectRoot, dep.file); | ||
output += `> - **${dep.path}** | ||
`; | ||
output += `> - File: \`${relativePath}\` | ||
output += `> - File: \`${depPath}\` | ||
`; | ||
output += `> - Imports: \`${importsList}\` | ||
output += `> - Imports: \`${dep.imports.join(", ")}\` | ||
`; | ||
@@ -287,3 +264,3 @@ }); | ||
} | ||
} | ||
}); | ||
if (data.usedInPages.length > 0) { | ||
@@ -297,6 +274,45 @@ output += "## Used in Pages\n"; | ||
} | ||
if (includeSeparator) { | ||
output += "---\n\n"; | ||
} | ||
return output; | ||
} | ||
generateComponentMarkdown(componentName, data) { | ||
const componentPathConfig = this.componentPaths.find( | ||
(config) => data.file.includes(path.relative(this.projectRoot, config.path)) | ||
); | ||
if (!componentPathConfig) { | ||
console.warn(`\u8B66\u544A: \u627E\u4E0D\u5230\u5143\u4EF6 ${componentName} \u7684\u8DEF\u5F91\u914D\u7F6E`); | ||
return ""; | ||
} | ||
const relativeFilePath = path.relative(this.projectRoot, data.file); | ||
const targetDir = path.join(this.projectRoot, path.dirname(relativeFilePath)); | ||
const componentFileName = `${componentName}.md`; | ||
const componentFilePath = path.join(targetDir, componentFileName); | ||
const content = this.generateComponentContent(componentName, data, { | ||
includeHeader: true, | ||
includeSeparator: false | ||
}); | ||
fs.mkdirSync(targetDir, { recursive: true }); | ||
fs.writeFileSync(componentFilePath, output, "utf8"); | ||
fs.writeFileSync(componentFilePath, content, "utf8"); | ||
return componentFilePath; | ||
} | ||
generateFullMarkdown() { | ||
let output = `# ${this.name} \u5B8C\u6574\u5143\u4EF6\u6587\u6A94 | ||
`; | ||
output += "## \u76EE\u9304\n\n"; | ||
Object.entries(this.targetUsage).sort(([nameA], [nameB]) => nameA.localeCompare(nameB)).forEach(([componentName]) => { | ||
output += `- [${componentName}](#${componentName.toLowerCase()}) | ||
`; | ||
}); | ||
output += "\n---\n\n"; | ||
Object.entries(this.targetUsage).sort(([nameA], [nameB]) => nameA.localeCompare(nameB)).forEach(([componentName, data]) => { | ||
output += this.generateComponentContent(componentName, data, { | ||
includeHeader: true, | ||
includeSeparator: true | ||
}); | ||
}); | ||
return output; | ||
} | ||
generateMarkDown(outputPath) { | ||
@@ -316,6 +332,19 @@ console.log("\u958B\u59CB\u751F\u6210 Markdown \u5831\u544A..."); | ||
const componentFilePath = this.generateComponentMarkdown(componentName, data); | ||
const relativeFilePath = path.relative(outputDir, componentFilePath); | ||
const componentPathConfig = this.componentPaths.find( | ||
(config) => componentFilePath.includes(path.relative(this.projectRoot, config.path)) | ||
); | ||
if (!componentPathConfig) { | ||
console.warn(`\u8B66\u544A: \u627E\u4E0D\u5230\u5143\u4EF6 ${componentName} \u7684\u8DEF\u5F91\u914D\u7F6E`); | ||
return { | ||
name: componentName, | ||
path: componentFilePath, | ||
dependencies: data.dependencies.length, | ||
usedInPages: data.usedInPages.length | ||
}; | ||
} | ||
const relativePath = path.relative(this.projectRoot, componentFilePath); | ||
const absolutePath = `/${relativePath}`; | ||
return { | ||
name: componentName, | ||
path: relativeFilePath, | ||
path: absolutePath, | ||
dependencies: data.dependencies.length, | ||
@@ -326,3 +355,3 @@ usedInPages: data.usedInPages.length | ||
componentFiles.sort((a, b) => a.name.localeCompare(b.name)).forEach(({ name, path: path2, dependencies, usedInPages }) => { | ||
indexContent += `- [${name}](./${path2}) | ||
indexContent += `- [${name}](${path2}) | ||
`; | ||
@@ -336,5 +365,9 @@ indexContent += ` - Dependencies: ${dependencies} | ||
fs.writeFileSync(indexPath, indexContent, "utf8"); | ||
const fullDocPath = path.join(outputDir, "full-documentation.md"); | ||
const fullDocContent = this.generateFullMarkdown(); | ||
fs.writeFileSync(fullDocPath, fullDocContent, "utf8"); | ||
console.log(`Markdown \u6587\u6A94\u5DF2\u751F\u6210\u65BC: ${outputDir}`); | ||
console.log(`\u5206\u6790\u4E86 ${componentFiles.length} \u500B\u5143\u4EF6`); | ||
console.log(`\u7D22\u5F15\u6587\u4EF6: ${indexPath}`); | ||
console.log(`\u5B8C\u6574\u6587\u6A94: ${fullDocPath}`); | ||
} | ||
@@ -341,0 +374,0 @@ generateDependencyTree(outputPath) { |
{ | ||
"name": "react-dep-analyzer", | ||
"version": "1.0.10", | ||
"version": "1.0.11", | ||
"description": "分析 React 元件依賴關係和使用情況的工具", | ||
@@ -5,0 +5,0 @@ "main": "./dist/index.js", |
197
src/index.ts
@@ -43,2 +43,7 @@ import fs from 'fs'; | ||
interface ComponentContentOptions { | ||
includeHeader?: boolean; | ||
includeSeparator?: boolean; | ||
} | ||
export class ComponentUsageAnalyzer { | ||
@@ -319,27 +324,22 @@ private name: string; | ||
private generateComponentMarkdown( | ||
private generateComponentContent( | ||
componentName: string, | ||
data: ComponentUsage | ||
data: ComponentUsage, | ||
options: ComponentContentOptions = {} | ||
): string { | ||
// 找到對應的 componentPath 配置 | ||
const componentPathConfig = this.componentPaths.find(config => | ||
data.file.includes(path.relative(this.projectRoot, config.path)) | ||
); | ||
const { | ||
includeHeader = true, | ||
includeSeparator = false | ||
} = options; | ||
if (!componentPathConfig) { | ||
console.warn(`警告: 找不到元件 ${componentName} 的路徑配置`); | ||
return ''; | ||
let output = ''; | ||
// 添加標題(可選) | ||
if (includeHeader) { | ||
output += `# ${componentName}\n\n`; | ||
} | ||
// 從完整檔案路徑中取得相對路徑部分 | ||
const relativeFilePath = path.relative(this.projectRoot, data.file); | ||
const targetDir = path.join(this.projectRoot, path.dirname(relativeFilePath)); | ||
// 使用原始元件名稱(保持大小寫)作為檔案名 | ||
const componentFileName = `${componentName}.md`; | ||
const componentFilePath = path.join(targetDir, componentFileName); | ||
// 生成元件文檔內容 | ||
let output = `# ${componentName}\n`; | ||
output += `> File Path: \`${data.file}\`\n\n`; | ||
// 使用相對於專案根目錄的路徑 | ||
const filePath = path.relative(this.projectRoot, data.file); | ||
output += `> File Path: \`${filePath}\`\n\n`; | ||
@@ -349,54 +349,26 @@ // 添加元件依賴關係圖 | ||
output += this.generateMermaidFlowchart(new Set(), new Set(), { name: componentName, data }); | ||
output += '\n'; | ||
if (data.dependencies.length > 0) { | ||
// 根據 componentPaths 配置來分組依賴 | ||
const dependencyGroups = this.componentPaths.reduce((groups, pathConfig) => { | ||
// 使用路徑最後一段作為分組名稱(例如:components, elements) | ||
const groupName = pathConfig.path.split('/').pop() || 'other'; | ||
// 過濾出屬於該分組的依賴 | ||
groups[groupName] = data.dependencies.filter(d => | ||
d.path.startsWith(pathConfig.importPrefix) | ||
); | ||
return groups; | ||
}, {} as Record<string, DependencyInfo[]>); | ||
// 找出未匹配任何已配置路徑的其他依賴 | ||
const otherDeps = data.dependencies.filter(d => | ||
!this.componentPaths.some(config => | ||
d.path.startsWith(config.importPrefix) | ||
) | ||
// 根據 componentPaths 配置來分組依賴 | ||
const dependencyGroups = this.componentPaths.reduce((groups, pathConfig) => { | ||
const groupName = pathConfig.path.split('/').pop() || 'other'; | ||
groups[groupName] = data.dependencies.filter(d => | ||
d.path.startsWith(pathConfig.importPrefix) | ||
); | ||
return groups; | ||
}, {} as Record<string, DependencyInfo[]>); | ||
// 輸出每個分組的依賴 | ||
Object.entries(dependencyGroups).forEach(([groupName, deps]) => { | ||
if (deps.length > 0) { | ||
// 將分組名稱首字母大寫 | ||
output += `## ${groupName.charAt(0).toUpperCase() + groupName.slice(1)} Dependencies\n`; | ||
deps.forEach((dep) => { | ||
// 合併所有引用的名稱 | ||
const importsList = dep.imports.join(', '); | ||
// 轉換為相對於專案根目錄的路徑 | ||
const relativePath = path.relative(this.projectRoot, dep.file); | ||
// 使用引用格式輸出依賴資訊 | ||
output += `> - **${dep.path}**\n`; | ||
output += `> - File: \`${relativePath}\`\n`; | ||
output += `> - Imports: \`${importsList}\`\n`; | ||
}); | ||
output += '\n'; | ||
} | ||
}); | ||
// 輸出其他未分組的依賴 | ||
if (otherDeps.length > 0) { | ||
output += '## Other Dependencies\n'; | ||
otherDeps.forEach((dep) => { | ||
const importsList = dep.imports.join(', '); | ||
const relativePath = path.relative(this.projectRoot, dep.file); | ||
// 輸出每個分組的依賴 | ||
Object.entries(dependencyGroups).forEach(([groupName, deps]) => { | ||
if (deps.length > 0) { | ||
output += `## ${groupName.charAt(0).toUpperCase() + groupName.slice(1)} Dependencies\n`; | ||
deps.forEach((dep) => { | ||
const depPath = path.relative(this.projectRoot, dep.file); | ||
output += `> - **${dep.path}**\n`; | ||
output += `> - File: \`${relativePath}\`\n`; | ||
output += `> - Imports: \`${importsList}\`\n`; | ||
output += `> - File: \`${depPath}\`\n`; | ||
output += `> - Imports: \`${dep.imports.join(', ')}\`\n`; | ||
}); | ||
output += '\n'; | ||
} | ||
} | ||
}); | ||
@@ -412,2 +384,38 @@ // 輸出使用此元件的頁面列表 | ||
// 添加分隔線(可選) | ||
if (includeSeparator) { | ||
output += '---\n\n'; | ||
} | ||
return output; | ||
} | ||
private generateComponentMarkdown( | ||
componentName: string, | ||
data: ComponentUsage | ||
): string { | ||
// 找到對應的 componentPath 配置 | ||
const componentPathConfig = this.componentPaths.find(config => | ||
data.file.includes(path.relative(this.projectRoot, config.path)) | ||
); | ||
if (!componentPathConfig) { | ||
console.warn(`警告: 找不到元件 ${componentName} 的路徑配置`); | ||
return ''; | ||
} | ||
// 從完整檔案路徑中取得相對路徑部分 | ||
const relativeFilePath = path.relative(this.projectRoot, data.file); | ||
const targetDir = path.join(this.projectRoot, path.dirname(relativeFilePath)); | ||
// 使用原始元件名稱(保持大小寫)作為檔案名 | ||
const componentFileName = `${componentName}.md`; | ||
const componentFilePath = path.join(targetDir, componentFileName); | ||
// 生成元件文檔內容 | ||
const content = this.generateComponentContent(componentName, data, { | ||
includeHeader: true, | ||
includeSeparator: false | ||
}); | ||
// 確保目標目錄存在 | ||
@@ -417,6 +425,32 @@ fs.mkdirSync(targetDir, { recursive: true }); | ||
// 寫入元件文檔檔案 | ||
fs.writeFileSync(componentFilePath, output, 'utf8'); | ||
fs.writeFileSync(componentFilePath, content, 'utf8'); | ||
return componentFilePath; | ||
} | ||
private generateFullMarkdown(): string { | ||
let output = `# ${this.name} 完整元件文檔\n\n`; | ||
output += '## 目錄\n\n'; | ||
// 先生成目錄 | ||
Object.entries(this.targetUsage) | ||
.sort(([nameA], [nameB]) => nameA.localeCompare(nameB)) | ||
.forEach(([componentName]) => { | ||
output += `- [${componentName}](#${componentName.toLowerCase()})\n`; | ||
}); | ||
output += '\n---\n\n'; | ||
// 生成每個元件的完整文檔 | ||
Object.entries(this.targetUsage) | ||
.sort(([nameA], [nameB]) => nameA.localeCompare(nameB)) | ||
.forEach(([componentName, data]) => { | ||
output += this.generateComponentContent(componentName, data, { | ||
includeHeader: true, | ||
includeSeparator: true | ||
}); | ||
}); | ||
return output; | ||
} | ||
generateMarkDown(outputPath?: string): void { | ||
@@ -443,8 +477,25 @@ console.log('開始生成 Markdown 報告...'); | ||
const componentFilePath = this.generateComponentMarkdown(componentName, data); | ||
const relativeFilePath = path.relative(outputDir, componentFilePath); | ||
// 返回元件名稱和檔案路徑,用於生成索引 | ||
// 找到對應的 componentPath 配置 | ||
const componentPathConfig = this.componentPaths.find(config => | ||
componentFilePath.includes(path.relative(this.projectRoot, config.path)) | ||
); | ||
if (!componentPathConfig) { | ||
console.warn(`警告: 找不到元件 ${componentName} 的路徑配置`); | ||
return { | ||
name: componentName, | ||
path: componentFilePath, | ||
dependencies: data.dependencies.length, | ||
usedInPages: data.usedInPages.length | ||
}; | ||
} | ||
// 從完整路徑中提取相對路徑部分 | ||
const relativePath = path.relative(this.projectRoot, componentFilePath); | ||
const absolutePath = `/${relativePath}`; | ||
return { | ||
name: componentName, | ||
path: relativeFilePath, | ||
path: absolutePath, | ||
dependencies: data.dependencies.length, | ||
@@ -459,3 +510,3 @@ usedInPages: data.usedInPages.length | ||
.forEach(({ name, path, dependencies, usedInPages }) => { | ||
indexContent += `- [${name}](./${path})\n`; | ||
indexContent += `- [${name}](${path})\n`; | ||
indexContent += ` - Dependencies: ${dependencies}\n`; | ||
@@ -469,5 +520,11 @@ indexContent += ` - Used in Pages: ${usedInPages}\n`; | ||
// 生成完整文檔 | ||
const fullDocPath = path.join(outputDir, 'full-documentation.md'); | ||
const fullDocContent = this.generateFullMarkdown(); | ||
fs.writeFileSync(fullDocPath, fullDocContent, 'utf8'); | ||
console.log(`Markdown 文檔已生成於: ${outputDir}`); | ||
console.log(`分析了 ${componentFiles.length} 個元件`); | ||
console.log(`索引文件: ${indexPath}`); | ||
console.log(`完整文檔: ${fullDocPath}`); | ||
} | ||
@@ -474,0 +531,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
69878
1511