UNPKG

autosnippet

Version:

Extract code patterns into a knowledge base for AI coding assistants

1,154 lines 74 kB
/** * WikiRenderers.js — Wiki 文档渲染函数 * * 从 WikiGenerator.js 中提取的 Markdown 渲染器和 AI Prompt 构建函数。 * 所有函数均为无状态纯函数(不依赖 class 实例)。 * * 类型定义已提取到 WikiTypes.ts,供多文件共享使用。 * * @module WikiRenderers */ import path from 'node:path'; import { LanguageService } from '../../shared/LanguageService.js'; import { getInheritanceRoots, getLangTerms, getModuleSourceFiles, inferModulePurpose, mermaidId, slug, } from './WikiUtils.js'; // ═══ AI Prompt 构建 ════════════════════════════════════════ /** * 为特定主题构建 AI 撰写 prompt (V3 AI-first 核心) * * 关键区别: 不是润色骨架,而是提供丰富数据让 AI 写完整文章 * * @param data { projectInfo, astInfo, moduleInfo, knowledgeInfo } */ export function buildArticlePrompt(topic, data, isZh, codeEntityGraph) { const { projectInfo, astInfo, moduleInfo, knowledgeInfo } = data; const parts = []; const langTerms = getLangTerms(projectInfo.primaryLanguage || 'unknown'); const tl = isZh ? langTerms.typeLabel.zh : langTerms.typeLabel.en; const il = isZh ? langTerms.interfaceLabel.zh : langTerms.interfaceLabel.en; // 公共项目上下文 parts.push(`# 项目: ${projectInfo.name}`); parts.push(`源文件数: ${projectInfo.sourceFiles.length}, 模块: ${moduleInfo.targets.length}, 活跃知识条目: ${knowledgeInfo.recipes.length}`); if (projectInfo.languages) { parts.push(`语言分布: ${Object.entries(projectInfo.languages) .sort((a, b) => b[1] - a[1]) .map(([l, c]) => `${l}(${c})`) .join(', ')}`); } parts.push(''); switch (topic.type) { case 'overview': { parts.push('## 任务: 撰写项目概述文档'); parts.push(''); // 项目类型 const buildTypes = (projectInfo.buildSystems || []).map((b) => b.buildTool); if (projectInfo.hasPackageSwift && !buildTypes.some((t) => t.includes('SPM'))) { buildTypes.push('SPM'); } if (projectInfo.hasPodfile && !buildTypes.includes('CocoaPods')) { buildTypes.push('CocoaPods'); } if (projectInfo.hasXcodeproj) { buildTypes.push('Xcode Project'); } if (buildTypes.length > 0) { parts.push(`构建系统: ${buildTypes.join(' + ')}`); } parts.push(''); // 模块结构 if (moduleInfo.targets.length > 0) { parts.push('### 模块列表'); for (const t of moduleInfo.targets) { const files = getModuleSourceFiles(t, projectInfo); const cls = astInfo.classNamesByModule?.[t.name]?.length || 0; const deps = (t.dependencies || t.info?.dependencies || []).map((d) => (typeof d === 'string' ? d : d.name)); parts.push(`- ${t.name} (${t.type || 'target'}): ${files.length} 文件, ${cls} 个类型${deps.length > 0 ? `, 依赖: ${deps.join(', ')}` : ''}`); } parts.push(''); } // AST 概况 if (astInfo.overview) { parts.push('### 代码规模'); parts.push(`${tl}: ${astInfo.overview.totalClasses || 0}, ${il}: ${astInfo.overview.totalProtocols || 0}, 方法: ${astInfo.overview.totalMethods || 0}`); parts.push(''); } // 可用的其他文档(用于导航链接) const otherTopics = (topic._allTopics || []).filter((t) => t.type !== 'overview'); if (otherTopics.length > 0) { parts.push('### 需要包含的导航链接'); for (const t of otherTopics) { parts.push(`- [${t.title}](${t.path})`); } parts.push(''); } parts.push('要求: 撰写完整的项目概述文档。'); parts.push('包含: 项目简介(解释项目做什么)、模块总览(表格形式)、技术栈分析、核心数据指标、文档导航索引。'); parts.push('不要只列数据 — 要解释项目的定位、各模块的职责和协作关系。'); break; } case 'architecture': { parts.push('## 任务: 撰写架构分析文档'); parts.push(''); if (moduleInfo.targets.length > 0) { parts.push('### 模块及依赖关系'); for (const t of moduleInfo.targets) { const deps = (t.dependencies || t.info?.dependencies || []).map((d) => (typeof d === 'string' ? d : d.name)); parts.push(`- ${t.name} (${t.type || 'target'})${deps.length > 0 ? ` → 依赖: ${deps.join(', ')}` : ''}`); } parts.push(''); } if (astInfo.overview?.topLevelModules && astInfo.overview.topLevelModules.length > 0) { parts.push(`### 顶层模块: ${astInfo.overview.topLevelModules.join(', ')}`); const cpm = astInfo.overview.classesPerModule || {}; for (const mod of astInfo.overview.topLevelModules) { parts.push(` ${mod}: ${cpm[mod] || 0} 个类`); } parts.push(''); } if (astInfo.overview?.entryPoints && astInfo.overview.entryPoints.length > 0) { parts.push(`### 入口点: ${astInfo.overview.entryPoints.join(', ')}`); parts.push(''); } const roots = getInheritanceRoots(codeEntityGraph); if (roots.length > 0) { parts.push('### 核心继承关系'); for (const r of roots.slice(0, 10)) { parts.push(`- ${r.name} → ${(r.children || []).slice(0, 5).join(', ')}`); } parts.push(''); } parts.push('要求: 撰写架构分析文档。'); parts.push('包含: 模块依赖图(使用 Mermaid graph TD 语法)、分层架构分析(解释每层的职责)、模块间协作关系、架构设计决策阐述。'); parts.push('用 Mermaid 绘制依赖关系图和继承层次图。分析为什么采用这种架构。'); break; } case 'module': { const md = topic._moduleData; const target = md.target; const moduleFiles = md.moduleFiles; const moduleClasses = astInfo.classNamesByModule?.[target.name] || []; const moduleProtocols = astInfo.protocolNamesByModule?.[target.name] || []; const deps = target.dependencies || target.info?.dependencies || []; parts.push(`## 任务: 撰写 "${target.name}" 模块的深度文档`); parts.push(''); parts.push('### 模块基本信息'); parts.push(`- 类型: ${target.type || 'target'}`); const tPath = target.path || target.info?.path; if (tPath) { parts.push(`- 路径: ${tPath}`); } if (target.packageName) { parts.push(`- 所属包: ${target.packageName}`); } parts.push(`- 源文件: ${moduleFiles.length} 个`); parts.push(`- ${tl}: ${moduleClasses.length} 个`); parts.push(`- ${il}: ${moduleProtocols.length} 个`); parts.push(''); if (deps.length > 0) { parts.push(`### 依赖: ${deps.map((d) => (typeof d === 'string' ? d : d.name)).join(', ')}`); parts.push(''); } if (moduleClasses.length > 0) { parts.push(`### 类型列表: ${moduleClasses.slice(0, 30).join(', ')}`); parts.push(''); } if (moduleProtocols.length > 0) { parts.push(`### ${il}列表: ${moduleProtocols.slice(0, 20).join(', ')}`); parts.push(''); } // 关键源文件名(帮助 AI 推断模块功能) if (moduleFiles.length > 0) { const keyFiles = moduleFiles.slice(0, 25).map((f) => path.basename(f)); parts.push(`### 关键源文件: ${keyFiles.join(', ')}`); parts.push(''); } // 相关 recipes const related = knowledgeInfo.recipes.filter((r) => { const json = r.toJSON ? r.toJSON() : r; return (json.moduleName === target.name || json.tags?.includes(target.name) || json.title?.includes(target.name)); }); if (related.length > 0) { parts.push(`### 相关知识条目 (${related.length})`); for (const r of related.slice(0, 10)) { const json = r.toJSON ? r.toJSON() : r; parts.push(`- ${json.title}: ${json.description || ''}`); if (json.reasoning?.whyStandard) { parts.push(` 为什么: ${json.reasoning.whyStandard}`); } } parts.push(''); } parts.push('要求: 撰写模块深度分析文档。'); parts.push('包含: 模块职责说明(从文件名和类名推断功能意图)、核心类型分析(不是简单罗列而是解释每个类的角色)、依赖关系分析、设计模式识别。'); parts.push('如果能推断出数据流或协作关系,请用 Mermaid 图表展示。'); break; } case 'getting-started': { parts.push('## 任务: 撰写快速上手指南'); parts.push(''); // 列出检测到的构建系统 const bs = projectInfo.buildSystems || []; if (bs.length > 0) { parts.push(`构建系统: ${bs.map((b) => b.buildTool).join(', ')}`); } else { // 兼容旧数据 if (projectInfo.hasPackageSwift) { parts.push('构建系统: Swift Package Manager'); } if (projectInfo.hasPodfile) { parts.push('构建系统: CocoaPods'); } if (projectInfo.hasXcodeproj) { parts.push('构建系统: Xcode Project'); } } parts.push(''); if (moduleInfo.targets.length > 0) { const mainTargets = moduleInfo.targets.filter((t) => t.type !== 'test'); const testTargets = moduleInfo.targets.filter((t) => t.type === 'test'); if (mainTargets.length > 0) { parts.push(`主要 Target: ${mainTargets.map((t) => t.name).join(', ')}`); } if (testTargets.length > 0) { parts.push(`测试 Target: ${testTargets.map((t) => t.name).join(', ')}`); } parts.push(''); } if (astInfo.overview?.entryPoints && astInfo.overview.entryPoints.length > 0) { parts.push(`入口点: ${astInfo.overview.entryPoints.join(', ')}`); parts.push(''); } parts.push('要求: 撰写开发者快速上手指南。'); parts.push('包含: 环境要求、项目获取、依赖安装、构建步骤(具体命令)、运行测试、项目目录结构说明。'); parts.push('语句清晰,步骤明确,适合新人阅读。'); break; } case 'patterns': { parts.push('## 任务: 撰写代码模式与最佳实践文档'); parts.push(''); const groups = {}; for (const r of knowledgeInfo.recipes) { const json = r.toJSON ? r.toJSON() : r; const cat = json.category || 'Other'; if (!groups[cat]) { groups[cat] = []; } groups[cat].push(json); } for (const [cat, items] of Object.entries(groups).sort()) { parts.push(`### ${cat} (${items.length} 条)`); for (const item of items.slice(0, 8)) { parts.push(`- ${item.title}: ${item.description || 'N/A'}`); if (item.doClause) { parts.push(` 应当: ${item.doClause}`); } if (item.dontClause) { parts.push(` 避免: ${item.dontClause}`); } if (item.content?.pattern) { parts.push(` 代码片段: ${item.content.pattern.slice(0, 200)}`); } } parts.push(''); } parts.push('要求: 撰写代码模式文档。对每个分类进行总结分析,解释模式的意义和应用场景。'); parts.push('不要只列出条目 — 为每个分类写一段总结,解释该类模式的整体意图。附带代码示例(从数据中取)。'); break; } case 'pattern-category': { const pd = topic._patternData; parts.push(`## 任务: 撰写 "${pd.category}" 分类的代码模式文档`); parts.push(''); for (const item of pd.recipes) { parts.push(`### ${item.title}`); if (item.description) { parts.push(`描述: ${item.description}`); } if (item.doClause) { parts.push(`应当: ${item.doClause}`); } if (item.dontClause) { parts.push(`避免: ${item.dontClause}`); } if (item.reasoning?.whyStandard) { parts.push(`原因: ${item.reasoning.whyStandard}`); } if (item.content?.pattern) { parts.push('代码:'); parts.push('```'); parts.push(item.content.pattern.slice(0, 500)); parts.push('```'); } parts.push(''); } parts.push('要求: 撰写该分类的详细代码模式文档。'); parts.push('先写一段总结性概述,然后对每个模式做分析,解释为什么要遵循,给出正确和错误的对比示例。'); break; } case 'reference': { parts.push(`## 任务: 撰写${il}参考文档`); parts.push(''); const protoByModule = astInfo.protocolNamesByModule || {}; for (const [mod, protos] of Object.entries(protoByModule).sort()) { if (protos.length > 0) { parts.push(`### ${mod} 模块: ${protos.join(', ')}`); } } parts.push(''); parts.push(`总计: ${astInfo.protocols.length} 个${il}, ${astInfo.classes.length} 个${tl}`); parts.push(''); parts.push(`要求: 撰写${il}参考文档。按模块分组,分析每个${il}的用途和意义,描述${il}之间的关系和设计意图。`); break; } case 'folder-overview': { const profiles = (topic._folderProfiles || []); parts.push('## 任务: 撰写项目文件夹结构分析文档'); parts.push(''); parts.push('注意: 本项目的代码实体(类/函数/协议等)无法通过 AST 自动提取,'); parts.push('因此以「文件夹画像」方式进行结构分析。'); parts.push(''); parts.push(`### 发现 ${profiles.length} 个重要文件夹`); parts.push(''); for (const fp of profiles) { parts.push(`#### ${fp.relPath}`); parts.push(`- 源文件: ${fp.fileCount} 个, 总大小: ${(fp.totalSize / 1024).toFixed(1)}KB`); parts.push(`- 语言分布: ${Object.entries(fp.langBreakdown) .map(([l, c]) => `${l}(${c})`) .join(', ')}`); if (fp.entryPoints.length > 0) { parts.push(`- 入口文件: ${fp.entryPoints.join(', ')}`); } if (fp.namingPatterns.length > 0) { parts.push(`- 命名约定: ${fp.namingPatterns.join(', ')}`); } if (fp.imports.length > 0) { parts.push(`- 依赖引用: ${fp.imports.join(', ')}`); } if (fp.purpose) { parts.push(`- 推断功能: ${fp.purpose.zh || fp.purpose.en || '-'}`); } if (fp.readme) { parts.push(`- README 摘要: ${fp.readme.slice(0, 200)}`); } if (fp.headerComments.length > 0) { parts.push(`- 代码注释: ${fp.headerComments.join('; ')}`); } parts.push(''); } parts.push('要求: 撰写项目结构分析文档。'); parts.push('重点分析:'); parts.push('1. 项目整体架构分层 — 从文件夹结构推断项目架构(MVC/分层/微服务等)'); parts.push('2. 各文件夹的职责与协作关系 — 从文件命名和 import 关系推断'); parts.push('3. 用 Mermaid graph TD 画出文件夹之间的依赖关系图'); parts.push('4. 从命名约定分析团队编码规范'); parts.push('5. 对照文件夹的文件分布特征,评估项目的工程化程度'); break; } case 'folder-profile': { const fp = topic._folderProfile; parts.push(`## 任务: 撰写 "${fp.name}" 目录的深度分析文档`); parts.push(''); parts.push('注意: 本项目的代码实体无法通过 AST 提取,以下分析基于文件夹画像。'); parts.push(''); parts.push('### 目录信息'); parts.push(`- 路径: ${fp.relPath}`); parts.push(`- 源文件: ${fp.fileCount} 个`); parts.push(`- 总大小: ${(fp.totalSize / 1024).toFixed(1)}KB`); parts.push(`- 语言分布: ${Object.entries(fp.langBreakdown) .map(([l, c]) => `${l}(${c})`) .join(', ')}`); parts.push(''); if (fp.entryPoints.length > 0) { parts.push(`### 入口文件: ${fp.entryPoints.join(', ')}`); parts.push(''); } if (fp.readme) { parts.push('### 目录 README'); parts.push(fp.readme.slice(0, 500)); parts.push(''); } if (fp.fileNames.length > 0) { parts.push(`### 文件列表 (${fp.fileNames.length} 个)`); parts.push(fp.fileNames.slice(0, 40).join(', ')); parts.push(''); } if (fp.namingPatterns.length > 0) { parts.push(`### 命名约定: ${fp.namingPatterns.join(', ')}`); parts.push(''); } if (fp.imports.length > 0) { parts.push(`### 依赖引用: ${fp.imports.join(', ')}`); parts.push(''); } if (fp.headerComments.length > 0) { parts.push('### 关键文件注释'); for (const hc of fp.headerComments) { parts.push(`- ${hc}`); } parts.push(''); } parts.push('要求: 撰写该目录的深度分析文档。'); parts.push('包含: 目录职责推断(从文件名和注释推断)、文件组织分析、命名规范评估、'); parts.push('依赖关系分析(从 import 推断)、关键文件说明。'); parts.push('从文件命名模式推断出这个目录承担的功能角色和设计意图。'); break; } } return parts.join('\n'); } /** * 构建非 AI 降级的丰富模板内容 * 即使没有 AI,也要产出有意义的内容 (不是只有列表罗列) * * @param data { projectInfo, astInfo, moduleInfo, knowledgeInfo } */ export function buildFallbackArticle(topic, data, isZh, codeEntityGraph) { const { projectInfo, astInfo, moduleInfo, knowledgeInfo } = data; switch (topic.type) { case 'overview': return renderIndex(projectInfo, astInfo, moduleInfo, knowledgeInfo, isZh, topic._allTopics || []); case 'architecture': return renderArchitecture(projectInfo, astInfo, moduleInfo, isZh, codeEntityGraph); case 'getting-started': return renderGettingStarted(projectInfo, moduleInfo, astInfo, isZh); case 'module': return renderModule(topic._moduleData.target, astInfo, knowledgeInfo, isZh, projectInfo); case 'patterns': return renderPatterns(knowledgeInfo, isZh); case 'pattern-category': return renderPatternCategory(topic._patternData, isZh); case 'reference': return renderProtocolReference(astInfo, isZh, projectInfo); case 'folder-overview': return renderFolderOverview(topic._folderProfiles, projectInfo, isZh); case 'folder-profile': return renderFolderProfile(topic._folderProfile, projectInfo, isZh); default: return ''; } } // ═══ Markdown 渲染器 ═══════════════════════════════════════ /** 渲染项目概述页 (index.md) */ export function renderIndex(project, ast, modules, knowledge, isZh, allTopics) { const title = isZh ? '项目概述' : 'Project Overview'; const langTerms = getLangTerms(project.primaryLanguage || 'unknown'); const tl = isZh ? langTerms.typeLabel.zh : langTerms.typeLabel.en; const il = isZh ? langTerms.interfaceLabel.zh : langTerms.interfaceLabel.en; const lines = [ `# ${project.name} — ${title}`, '', `> ${isZh ? '本文档由 AutoSnippet Repo Wiki 自动生成' : 'Auto-generated by AutoSnippet Repo Wiki'}`, `> ${isZh ? '生成时间' : 'Generated at'}: ${new Date().toISOString()}`, '', ]; // ── 项目简介 ── lines.push(`## ${isZh ? '简介' : 'Introduction'}`); lines.push(''); // 从 buildSystems 或 legacy 字段推断项目类型标签 const types = []; if (project.buildSystems && project.buildSystems.length > 0) { for (const bs of project.buildSystems) { types.push(bs.buildTool); } } else { if (project.hasPackageSwift) { types.push('SPM'); } if (project.hasPodfile) { types.push('CocoaPods'); } if (project.hasXcodeproj) { types.push('Xcode Project'); } } const projectTypeLabel = types.join(' + ') || (project.primaryLanguage ? LanguageService.displayName(project.primaryLanguage) : 'Software'); const overview = ast.overview || {}; const mainTargets = modules.targets.filter((t) => t.type !== 'test'); const testTargets = modules.targets.filter((t) => t.type === 'test'); if (isZh) { lines.push(`**${project.name}** 是一个 ${projectTypeLabel} 项目,` + `包含 ${project.sourceFiles.length} 个源文件` + (overview.totalClasses ? `、${overview.totalClasses} 个${tl}` : '') + (overview.totalProtocols ? `、${overview.totalProtocols} 个${il}` : '') + `。`); if (mainTargets.length > 0) { lines.push(`项目由 ${mainTargets.length} 个功能模块组成` + (testTargets.length > 0 ? `,配备 ${testTargets.length} 个测试模块` : '') + `。`); } } else { lines.push(`**${project.name}** is a ${projectTypeLabel} project ` + `containing ${project.sourceFiles.length} source files` + (overview.totalClasses ? `, ${overview.totalClasses} ${tl}` : '') + (overview.totalProtocols ? `, ${overview.totalProtocols} ${il}` : '') + `.`); if (mainTargets.length > 0) { lines.push(`The project consists of ${mainTargets.length} functional modules` + (testTargets.length > 0 ? ` with ${testTargets.length} test modules` : '') + `.`); } } lines.push(''); // ── 模块总览 ── if (modules.targets.length > 0) { lines.push(`## ${isZh ? '模块总览' : 'Module Overview'}`); lines.push(''); lines.push(`| ${isZh ? '模块' : 'Module'} | ${isZh ? '类型' : 'Type'} | ${isZh ? '源文件' : 'Files'} | ${tl} | ${il} |`); lines.push('|--------|------|--------|--------|----------|'); for (const t of modules.targets) { const moduleFiles = getModuleSourceFiles(t, project); const classCount = ast.classNamesByModule?.[t.name]?.length || 0; const protoCount = ast.protocolNamesByModule?.[t.name]?.length || 0; const hasDoc = allTopics?.some((tp) => tp.type === 'module' && tp._moduleData?.target.name === t.name); const nameCol = hasDoc ? `[${t.name}](modules/${slug(t.name)}.md)` : t.name; lines.push(`| ${nameCol} | ${t.type || 'target'} | ${moduleFiles.length || '-'} | ${classCount || '-'} | ${protoCount || '-'} |`); } lines.push(''); } else if (project.sourceFilesByModule && Object.keys(project.sourceFilesByModule).length >= 2) { // 无 moduleService targets → 使用 sourceFilesByModule 推断的模块 const sfm = project.sourceFilesByModule; const sorted = Object.entries(sfm).sort((a, b) => b[1].length - a[1].length); lines.push(`## ${isZh ? '模块总览' : 'Module Overview'}`); lines.push(''); lines.push(isZh ? `项目代码按目录结构可划分为 ${sorted.length} 个模块:` : `The project code is organized into ${sorted.length} modules:`); lines.push(''); lines.push(`| ${isZh ? '模块' : 'Module'} | ${isZh ? '源文件' : 'Files'} | ${isZh ? '说明' : 'Description'} |`); lines.push('|--------|--------|------|'); for (const [modName, modFiles] of sorted.slice(0, 15)) { const hasDoc = allTopics?.some((tp) => tp.type === 'module' && tp.title === modName); const nameCol = hasDoc ? `[${modName}](modules/${slug(modName)}.md)` : modName; const purpose = inferModulePurpose(modName, [], [], modFiles); const desc = purpose ? (isZh ? purpose.zh : purpose.en) : '-'; lines.push(`| ${nameCol} | ${modFiles.length} | ${desc} |`); } lines.push(''); } // ── 技术栈 ── lines.push(`## ${isZh ? '技术栈' : 'Tech Stack'}`); lines.push(''); if (project.languages && Object.keys(project.languages).length > 0) { lines.push(`| ${isZh ? '语言' : 'Language'} | ${isZh ? '文件数' : 'Files'} | ${isZh ? '占比' : 'Share'} |`); lines.push('|--------|-------|------|'); const langMap = project.languages; const total = Object.values(langMap).reduce((a, b) => a + b, 0); for (const [lang, count] of Object.entries(langMap).sort((a, b) => b[1] - a[1])) { const pct = total > 0 ? ((count / total) * 100).toFixed(1) : 0; lines.push(`| ${lang} | ${count} | ${pct}% |`); } lines.push(''); } // ── 核心数据 ── lines.push(`## ${isZh ? '核心数据' : 'Key Metrics'}`); lines.push(''); lines.push(`| ${isZh ? '指标' : 'Metric'} | ${isZh ? '数量' : 'Count'} |`); lines.push('|--------|-------|'); lines.push(`| ${isZh ? '源文件数' : 'Source Files'} | ${project.sourceFiles.length} |`); if (overview.totalClasses) { lines.push(`| ${tl} | ${overview.totalClasses} |`); } if (overview.totalProtocols) { lines.push(`| ${il} | ${overview.totalProtocols} |`); } if (overview.totalMethods) { lines.push(`| ${isZh ? '方法总数' : 'Methods'} | ${overview.totalMethods} |`); } if (modules.targets.length > 0) { lines.push(`| ${isZh ? '模块数' : 'Modules'} | ${modules.targets.length} |`); } if (knowledge.recipes.length > 0) { lines.push(`| ${isZh ? '知识库条目' : 'KB Recipes'} | ${knowledge.recipes.length} |`); } lines.push(''); // ── 文档导航 (动态,基于实际生成的主题) ── const navTopics = (allTopics || []).filter((t) => t.type !== 'overview'); if (navTopics.length > 0) { lines.push('---'); lines.push(''); lines.push(`## ${isZh ? '📖 文档导航' : '📖 Documentation'}`); lines.push(''); for (const t of navTopics) { lines.push(`- [${t.title}](${t.path})`); } lines.push(''); } return lines.join('\n'); } /** 渲染架构总览文档 (architecture.md) */ export function renderArchitecture(project, ast, modules, isZh, codeEntityGraph) { const lines = [ `# ${isZh ? '架构总览' : 'Architecture Overview'}`, '', `> ${isZh ? '本文档由 AutoSnippet Repo Wiki 自动生成' : 'Auto-generated by AutoSnippet Repo Wiki'}`, '', ]; // 依赖图 (Mermaid) if (modules.targets.length > 0) { lines.push(`## ${isZh ? '模块依赖图' : 'Module Dependency Graph'}`); lines.push(''); lines.push('```mermaid'); lines.push('graph TD'); // 渲染 target 节点和依赖边 const rendered = new Set(); for (const target of modules.targets) { const sid = mermaidId(target.name); if (!rendered.has(sid)) { const shape = target.type === 'test' ? `${sid}[["${target.name} (Test)"]]` : `${sid}["${target.name}"]`; lines.push(` ${shape}`); rendered.add(sid); } } // 如果有依赖图数据,渲染边 if (modules.depGraph) { const edges = modules.depGraph.edges || []; for (const edge of Array.isArray(edges) ? edges : []) { if (edge.from && edge.to) { const fromId = mermaidId(edge.from.split('::').pop() || edge.from); const toId = mermaidId(edge.to.split('::').pop() || edge.to); lines.push(` ${fromId} --> ${toId}`); } } } lines.push('```'); lines.push(''); } else if (project.sourceFilesByModule && Object.keys(project.sourceFilesByModule).length >= 2) { // 无 moduleService → 从 sourceFilesByModule 推断模块结构图 const sfm = project.sourceFilesByModule; // 只显示有实质内容的模块(>= 2 个文件),避免单文件噪声 const sorted = Object.entries(sfm) .filter(([, files]) => files.length >= 2) .sort((a, b) => b[1].length - a[1].length); const topModules = sorted.slice(0, 15); lines.push(`## ${isZh ? '模块结构图' : 'Module Structure'}`); lines.push(''); lines.push('```mermaid'); lines.push('graph TD'); lines.push(` Root["${project.name}"]`); for (const [modName] of topModules) { const sid = mermaidId(modName); lines.push(` ${sid}["${modName}"]`); lines.push(` Root --> ${sid}`); } lines.push('```'); lines.push(''); // 模块详情表 lines.push(`## ${isZh ? '模块详情' : 'Module Details'}`); lines.push(''); lines.push(`| ${isZh ? '模块' : 'Module'} | ${isZh ? '源文件数' : 'Files'} | ${isZh ? '说明' : 'Description'} |`); lines.push('|--------|-------|------|'); for (const [modName, modFiles] of topModules) { const purpose = inferModulePurpose(modName, [], [], modFiles); const desc = purpose ? (isZh ? purpose.zh : purpose.en) : '-'; lines.push(`| ${modName} | ${modFiles.length} | ${desc} |`); } lines.push(''); } // 分层架构 if (ast.overview) { const modules = ast.overview.topLevelModules || []; if (modules.length > 0) { lines.push(`## ${isZh ? '顶层模块' : 'Top-Level Modules'}`); lines.push(''); lines.push(`| ${isZh ? '模块' : 'Module'} | ${isZh ? '类数量' : 'Classes'} |`); lines.push('|--------|---------|'); const cpm = ast.overview.classesPerModule || {}; for (const mod of modules) { lines.push(`| ${mod} | ${cpm[mod] || 0} |`); } lines.push(''); } // 入口点 if (ast.overview?.entryPoints && ast.overview.entryPoints.length > 0) { lines.push(`## ${isZh ? '入口点' : 'Entry Points'}`); lines.push(''); for (const ep of ast.overview.entryPoints) { lines.push(`- \`${ep}\``); } lines.push(''); } } // 继承层次 (from CodeEntityGraph) if (codeEntityGraph) { try { const topClasses = getInheritanceRoots(codeEntityGraph); if (topClasses.length > 0) { lines.push(`## ${isZh ? '核心继承层次' : 'Key Inheritance Hierarchy'}`); lines.push(''); lines.push('```mermaid'); lines.push('classDiagram'); for (const root of topClasses.slice(0, 20)) { lines.push(` class ${mermaidId(root.name)}`); for (const child of root.children || []) { lines.push(` ${mermaidId(root.name)} <|-- ${mermaidId(child)}`); } } lines.push('```'); lines.push(''); } } catch { /* non-critical */ } } lines.push(`[← ${isZh ? '返回概述' : 'Back to Overview'}](index.md)`); lines.push(''); return lines.join('\n'); } /** 渲染模块详情文档 (modules/{name}.md) */ export function renderModule(target, ast, knowledge, isZh, projectInfo) { const langTerms = getLangTerms(projectInfo?.primaryLanguage || 'unknown'); const tl = isZh ? langTerms.typeLabel.zh : langTerms.typeLabel.en; const il = isZh ? langTerms.interfaceLabel.zh : langTerms.interfaceLabel.en; const lines = [ `# ${target.name}`, '', `> ${isZh ? '模块文档 — 由 AutoSnippet Repo Wiki 自动生成' : 'Module doc — Auto-generated by AutoSnippet Repo Wiki'}`, '', ]; // 收集模块数据 const moduleFiles = projectInfo ? getModuleSourceFiles(target, projectInfo) : []; const moduleClasses = ast.classNamesByModule?.[target.name] || []; const moduleProtocols = ast.protocolNamesByModule?.[target.name] || []; const deps = target.dependencies || target.info?.dependencies || []; // ── 模块概述 ── lines.push(`## ${isZh ? '概述' : 'Overview'}`); lines.push(''); // 推断模块功能 (基于名称和内容) const purpose = inferModulePurpose(target.name, moduleClasses, moduleProtocols, moduleFiles); if (purpose) { lines.push(isZh ? `**${target.name}** ${purpose.zh},包含 ${moduleFiles.length} 个源文件、${moduleClasses.length} 个${tl}${moduleProtocols.length > 0 ? `、${moduleProtocols.length} 个${il}` : ''}。` : `**${target.name}** ${purpose.en}, containing ${moduleFiles.length} source files, ${moduleClasses.length} ${tl}${moduleProtocols.length > 0 ? `, ${moduleProtocols.length} ${il}` : ''}.`); } else { lines.push(isZh ? `**${target.name}** 是项目中的一个 ${target.type || 'target'} 模块,包含 ${moduleFiles.length} 个源文件、${moduleClasses.length} 个${tl}。` : `**${target.name}** is a ${target.type || 'target'} module in the project, containing ${moduleFiles.length} source files and ${moduleClasses.length} ${tl}.`); } lines.push(''); // ── 模块信息表 ── lines.push(`| ${isZh ? '属性' : 'Property'} | ${isZh ? '值' : 'Value'} |`); lines.push('|--------|------|'); lines.push(`| ${isZh ? '类型' : 'Type'} | ${target.type || 'target'} |`); if (target.packageName) { lines.push(`| ${isZh ? '所属包' : 'Package'} | ${target.packageName} |`); } if (target.path || target.info?.path) { lines.push(`| ${isZh ? '路径' : 'Path'} | \`${target.path || target.info?.path}\` |`); } if (moduleFiles.length > 0) { lines.push(`| ${isZh ? '源文件数' : 'Source Files'} | ${moduleFiles.length} |`); } if (moduleClasses.length > 0) { lines.push(`| ${tl} | ${moduleClasses.length} |`); } if (moduleProtocols.length > 0) { lines.push(`| ${il} | ${moduleProtocols.length} |`); } if (deps.length > 0) { lines.push(`| ${isZh ? '依赖数' : 'Dependencies'} | ${deps.length} |`); } lines.push(''); // ── 依赖 ── if (deps.length > 0) { lines.push(`## ${isZh ? '依赖关系' : 'Dependencies'}`); lines.push(''); lines.push(isZh ? `${target.name} 依赖以下 ${deps.length} 个模块:` : `${target.name} depends on ${deps.length} module(s):`); lines.push(''); for (const dep of deps) { const depName = typeof dep === 'string' ? dep : dep.name || String(dep); lines.push(`- \`${depName}\``); } lines.push(''); } // ── 核心类型分析 ── if (moduleClasses.length > 0 || moduleProtocols.length > 0) { lines.push(`## ${isZh ? '核心类型' : 'Core Types'}`); lines.push(''); if (moduleProtocols.length > 0) { lines.push(`### ${il} (${moduleProtocols.length})`); lines.push(''); lines.push(isZh ? `${target.name} 定义了 ${moduleProtocols.length} 个${il},用于规范模块的接口边界:` : `${target.name} defines ${moduleProtocols.length} ${il} establishing the module's interface contracts:`); lines.push(''); const sorted = [...moduleProtocols].sort(); for (const p of sorted.slice(0, 20)) { lines.push(`- \`${p}\``); } if (sorted.length > 20) { lines.push(`- ... ${isZh ? `还有 ${sorted.length - 20} 个` : `and ${sorted.length - 20} more`}`); } lines.push(''); } if (moduleClasses.length > 0) { lines.push(`### ${tl} (${moduleClasses.length})`); lines.push(''); const sorted = [...moduleClasses].sort(); for (const c of sorted.slice(0, 30)) { lines.push(`- \`${c}\``); } if (sorted.length > 30) { lines.push(`- ... ${isZh ? `还有 ${sorted.length - 30} 个` : `and ${sorted.length - 30} more`}`); } lines.push(''); } } // ── 源文件分布 ── if (moduleFiles.length > 0) { lines.push(`## ${isZh ? '源文件分布' : 'Source File Distribution'}`); lines.push(''); // 按语言统计 const langCount = {}; for (const f of moduleFiles) { const ext = path.extname(f); const lang = LanguageService.displayNameFromExt(ext); langCount[lang] = (langCount[lang] || 0) + 1; } lines.push(`| ${isZh ? '语言' : 'Language'} | ${isZh ? '文件数' : 'Files'} |`); lines.push('|--------|-------|'); for (const [lang, count] of Object.entries(langCount).sort((a, b) => b[1] - a[1])) { lines.push(`| ${lang} | ${count} |`); } lines.push(''); } // ── 该模块相关的 Recipes ── if (knowledge.recipes.length > 0) { const related = knowledge.recipes.filter((r) => { const json = r.toJSON ? r.toJSON() : r; return (json.moduleName === target.name || json.tags?.includes(target.name) || json.title?.includes(target.name)); }); if (related.length > 0) { lines.push(`## ${isZh ? '相关知识条目' : 'Related Recipes'}`); lines.push(''); lines.push(isZh ? `团队知识库中有 ${related.length} 条与 ${target.name} 相关的条目:` : `The team knowledge base contains ${related.length} entries related to ${target.name}:`); lines.push(''); for (const r of related) { const json = r.toJSON ? r.toJSON() : r; lines.push(`### ${json.title}`); lines.push(''); if (json.description) { lines.push(json.description); } if (json.doClause) { lines.push(`\n**${isZh ? '✅ 应当' : '✅ Do'}**: ${json.doClause}`); } if (json.dontClause) { lines.push(`**${isZh ? '❌ 避免' : "❌ Don't"}**: ${json.dontClause}`); } lines.push(''); } } } lines.push(`[← ${isZh ? '返回概述' : 'Back to Overview'}](../index.md)`); lines.push(''); return lines.join('\n'); } /** 渲染代码模式文档 (patterns.md) */ export function renderPatterns(knowledge, isZh) { const lines = [ `# ${isZh ? '代码模式与最佳实践' : 'Code Patterns & Best Practices'}`, '', `> ${isZh ? '团队沉淀的代码模式与最佳实践(来自 AutoSnippet 知识库)' : 'Code patterns and best practices from AutoSnippet knowledge base'}`, '', ]; // 按 category 分组 const groups = {}; for (const r of knowledge.recipes) { const json = r.toJSON ? r.toJSON() : r; const cat = json.category || 'Other'; if (!groups[cat]) { groups[cat] = []; } groups[cat].push(json); } // 总结 const totalRecipes = knowledge.recipes.length; const catCount = Object.keys(groups).length; lines.push(isZh ? `本项目团队在 ${catCount} 个分类下共沉淀了 **${totalRecipes}** 条代码模式和最佳实践。以下按分类进行展示和分析。` : `The team has accumulated **${totalRecipes}** code patterns across ${catCount} categories. Below they are organized and analyzed by category.`); lines.push(''); for (const [cat, items] of Object.entries(groups).sort()) { lines.push(`## ${cat} (${items.length})`); lines.push(''); // 分类概述 lines.push(isZh ? `${cat} 分类包含 ${items.length} 条规则,覆盖了该领域的核心规范。` : `The ${cat} category contains ${items.length} rules covering core conventions in this area.`); lines.push(''); for (const item of items) { lines.push(`### ${item.title}`); lines.push(''); if (item.description) { lines.push(item.description); lines.push(''); } if (item.content?.pattern) { lines.push(`\`\`\`${item.language || 'text'}`); lines.push(item.content.pattern); lines.push('```'); lines.push(''); } if (item.doClause) { lines.push(`**${isZh ? '✅ 应当' : '✅ Do'}**: ${item.doClause}`); lines.push(''); } if (item.dontClause) { lines.push(`**${isZh ? '❌ 避免' : "❌ Don't"}**: ${item.dontClause}`); lines.push(''); } if (item.reasoning?.whyStandard) { lines.push(`> ${isZh ? '💡 原因' : '💡 Rationale'}: ${item.reasoning.whyStandard}`); lines.push(''); } } } lines.push(`[← ${isZh ? '返回概述' : 'Back to Overview'}](index.md)`); lines.push(''); return lines.join('\n'); } // ═══ V3 新增渲染器 ════════════════════════════════════════ /** 快速上手指南 (非 AI 降级模板) */ export function renderGettingStarted(project, modules, ast, isZh) { const lines = [ `# ${isZh ? '快速上手' : 'Getting Started'}`, '', `> ${isZh ? '本文档由 AutoSnippet Repo Wiki 自动生成' : 'Auto-generated by AutoSnippet Repo Wiki'}`, '', ]; // 从 buildSystems 或 legacy 字段推断 const bs = project.buildSystems || []; const ecoSet = new Set(bs.map((b) => b.eco)); // ── 环境要求 (按检测到的生态系统动态生成) ── lines.push(`## ${isZh ? '环境要求' : 'Prerequisites'}`); lines.push(''); if (ecoSet.has('spm') || project.hasPackageSwift) { lines.push(isZh ? '- Swift 5.5+ (推荐 Swift 5.9+)' : '- Swift 5.5+ (Swift 5.9+ recommended)'); lines.push(isZh ? '- Xcode 14+' : '- Xcode 14+'); const hasCocoaPods = bs.some((b) => b.buildTool === 'CocoaPods'); if (hasCocoaPods || project.hasPodfile) { lines.push(isZh ? '- CocoaPods 1.10+' : '- CocoaPods 1.10+'); } } if (project.hasXcodeproj) { lines.push(isZh ? '- Xcode (最新稳定版)' : '- Xcode (latest stable version)'); // Xcode 项目额外环境提示 if (!ecoSet.has('spm') && !project.hasPackageSwift && !project.hasPodfile) { lines.push(isZh ? '- macOS (建议最新版本)' : '- macOS (latest version recommended)'); lines.push(isZh ? '- Apple Developer Account (如需真机调试)' : '- Apple Developer Account (for device testing)'); } } if (ecoSet.has('node')) { lines.push(isZh ? '- Node.js 18+ (推荐 20 LTS)' : '- Node.js 18+ (20 LTS recommended)'); const hasYarn = bs.some((b) => b.buildTool === 'Yarn'); const hasPnpm = bs.some((b) => b.buildTool === 'pnpm'); lines.push(hasYarn ? '- Yarn' : hasPnpm ? '- pnpm' : '- npm'); } if (ecoSet.has('python')) { lines.push(isZh ? '- Python 3.8+' : '- Python 3.8+'); const hasPipenv = bs.some((b) => b.buildTool === 'Pipenv'); const hasPoetry = bs.some((b) => b.buildTool === 'Poetry'); if (hasPipenv) { lines.push('- Pipenv'); } else if (hasPoetry) { lines.push('- Poetry'); } } if (ecoSet.has('go')) { lines.push(isZh ? '- Go 1.21+' : '- Go 1.21+'); } if (ecoSet.has('rust')) { lines.push(isZh ? '- Rust (最新 stable)' : '- Rust (latest stable)'); lines.push('- Cargo'); } if (ecoSet.has('jvm')) { const hasGradle = bs.some((b) => b.buildTool?.startsWith('Gradle')); lines.push(isZh ? '- JDK 17+' : '- JDK 17+'); lines.push(hasGradle ? '- Gradle' : '- Maven'); } if (ecoSet.has('dart')) { lines.push(isZh ? '- Flutter / Dart SDK' : '- Flutter / Dart SDK'); } if (ecoSet.has('dotnet')) { lines.push(isZh ? '- .NET 6+ SDK' : '- .NET 6+ SDK'); } if (ecoSet.has('ruby')) { lines.push(isZh ? '- Ruby 3.0+' : '- Ruby 3.0+'); lines.push('- Bundler'); } lines.push(''); // ── 项目目录结构 ── lines.push(`## ${isZh ? '项目结构' : 'Project Structure'}`); lines.push(''); lines.push('```'); lines.push(`${project.name}/`); if (modules.targets.length > 0) { const mainTargets = modules.targets.filter((t) => t.type !== 'test'); const testTargets = modules.targets.filter((t) => t.type === 'test'); if (mainTargets.length > 0) { const srcDir = ecoSet.has('spm') || project.hasPackageSwift ? 'Sources' : 'src'; lines.push(`├── ${srcDir}/`); for (let i = 0; i < mainTargets.length; i++) { const prefix = i === mainTargets.length - 1 && testTargets.length === 0 ? '│ └──' : '│ ├──'; lines.push(`${prefix} ${mainTargets[i].name}/`); } } if (testTargets.length > 0) { const testDir = ecoSet.has('spm') || project.hasPackageSwift ? 'Tests' : 'test'; lines.push(`├── ${testDir}/`); for (let i = 0; i < testTargets.length; i++) { const prefix = i === testTargets.length - 1 ? '│ └──' : '│ ├──'; lines.push(`${prefix} ${testTargets[i].name}/`); } } } // 显示构建配置文件 for (const b of bs) { const marker = BUILD_SYSTEM_FILES[b.buildTool]; if (marker) { lines.push(`├── ${marker}`); } } // legacy 兜底 if (bs.length === 0) { if (project.hasPackageSwift) { lines.push('├── Package.swift'); } if (project.hasPodfile) { lines.push('├── Podfile'); } } lines.push('```'); lines.push(''); // ── 构建步骤 (按检测到的生态系统动态生成) ── lines.push(`## ${isZh ? '构建与运行' : 'Build & Run'}`); lines.push(''); for (const b of bs) { _pushBuildSteps(lines, b, project.name, isZh); } // legacy 兜底 — 如果没有检测到 buildSystems if (bs.length === 0) { if (project.hasPackageSwift) { _pushBuildSteps(lines, { eco: 'spm', buildTool: 'SPM' }, project.name, isZh); } if (project.hasPodfile) { _pushBuildSteps(lines, { eco: 'spm', buildTool: 'CocoaPods' }, project.name, isZh); } // Xcode 项目兜底 (无 SPM / CocoaPods) if (!project.hasPackageSwift && !project.hasPodfile && project.hasXcodeproj) { _pushBuildSteps(lines, { eco: 'xcode', buildTool: 'Xcode' }, project.name, isZh); } } // ── 源文件统计 (增强无 moduleService 场景) ── if (modules.targets.length === 0 && project.sourceFilesByModule) { const sfm = project.sourceFilesByModule; const modEntries = Object.entries(sfm).sort((a, b) => b[1].length - a[1].length); if (modEntries.length > 0) { lines.push(`## ${isZh ? '项目模块概览' : 'Module Overview'}`); lines.push(''); lines.push(`| ${isZh ? '模块' : 'Module'} | ${isZh ? '源文件数' : 'Files'} | ${isZh ? '说明' : 'Description'} |`); lines.push('|--------|-------|------|'); for (const [modName, modFiles] of modEntries.slice(0, 15)) { const purpose = inferModulePurpose(modName, [], [], modFiles); const desc = purpose ? (isZh ? purpose.zh : purpose.en) : '-'; lines.push(`| ${modName} | ${modFiles.length} | ${desc} |`); } lines.push(''); } } // ── 模块说明 ── if (modules.targets.length > 0) { const mainTargets = modules.targets.filter((t) => t.type !== 'test'); if (mainTargets.length > 0) { lines.push(`## ${isZh ? '核心模块' : 'Core Modules'}`); lines.push(''); lines.push(`| ${isZh ? '模块' : 'Module'} | ${isZh ? '类型' : 'Type'} | ${isZh ? '类型数' : 'Types'} | ${isZh ? '说明' : 'Description'} |`); lines.push('|--------|------|--------|------|'); for (const t of mainTargets) { const cls = (ast.classNamesByModule?.[t.name] || []).length; const purpose = inferModulePurpose(t.name, ast.classNamesByModule?.[t.name] || [], ast.protocolNamesByModule?.[t.name] || [], []); const desc = purpose ? (isZh ? purpose.zh : purpose.en) : '-'; lines.push(`| ${t.name} | ${t.type || 'library'} | ${cls} | ${desc} |`); } lines.push(''); } } lines.push(`[← ${isZh ? '返回概述' : 'Back to Overview'}](index.md)`); lines.push(''); return lines.join('\n'); } /* 构建配置文件名映射 (用于目录树显示) — 从 LanguageService.buildSystemMarkers 动态派生 */ const BUILD_SYSTEM_FILES = Object.fromEntries(LanguageService.buildSystemMarkers.map((m) => [m.buildTool, m.file])); /** * 按生态系统输出构建步骤 */ function _pushBuildSteps(lines, buildSys, projectName, isZh) { const { eco, buildTool } = buildSys; lines.push(`### ${isZh ? `使用 ${buildTool}` : `Using ${buildTool}`}`); lines.push(''); lines.push('```bash'); lines.push(isZh ? '# 获取项目' : '# Clone the project'); lines.push(`git clone <repository-url>`); lines.push(`cd ${projectName}`); lines.push(''); switch (eco) { case 'spm': if (buildTool === 'CocoaPods') { lines.push('pod install'); lines.push('open *.xcworkspace'); } else { lines.push(isZh ? '# 解析依赖' : '# Resolve dependencies'); lines.push('swift package resolve'); lines.push(''); lines.push(isZh ? '# 构建' : '# Build'); lines.push('swift build'); lines.push(''); lines.push(isZh ? '# 运行测试' : '# Run tests'); lines.push('swift test'); } break; case 'node': if (buildTool === 'Yarn') { lines.push('yarn install'); lines.push('yarn build'); lines.push('yarn test'); } else if (buildTool === 'pnpm') { lines.push('pnpm install'); lines.push('pnpm build'); lines.push('pnpm test'); } else { lines.push('npm install'); lines.push('npm run build'); lines.push('npm test'); } break; case 'python': if (buildTool === 'Poetry') { lines.push('poetry install'); lines.push('poetry run pytest'); } else if (buildTool === 'Pipenv') { lines.push('pipenv install'); lin