/**
* 知识库模块 - 索引管理
*
* 负责加载和管理 M8 框架的知识库文档
*/
import * as fs from "fs";
import * as path from "path";
import { fileURLToPath } from "url";
// 获取当前模块所在目录 (编译后是 dist/knowledge/)
// 使用 import.meta.url 因为使用 NodeNext 模块系统
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// 知识库根目录 (相对于 dist/knowledge/ 目录,向上两级到 npm 包根目录)
const KNOWLEDGE_ROOT = path.resolve(__dirname, "../../data");
export interface ComponentDoc {
name: string;
filename: string;
description: string;
props: string;
events: string;
examples: string[];
rawContent: string;
}
export interface KnowledgeBase {
components: ComponentDoc[];
standards: string; // _index.md 摘要
fullStandards: string; // 所有规范目录的完整内容
standardDocs: Map<string, string>; // 按类别存储的规范文档
utilDocs: Map<string, string>;
}
/**
* 加载知识库
*/
export function loadKnowledgeBase(): KnowledgeBase {
const components = loadComponentDocs();
const { summary: standards, docs: standardDocs, fullContent: fullStandards } = loadAllStandardDocs();
const utilDocs = loadUtilDocs();
return {
components,
standards,
fullStandards,
standardDocs,
utilDocs,
};
}
/**
* 加载 UI 组件文档
*/
function loadComponentDocs(): ComponentDoc[] {
const componentsDir = path.join(KNOWLEDGE_ROOT, "m8mpdoc", "UI组件库");
const components: ComponentDoc[] = [];
if (!fs.existsSync(componentsDir)) {
console.error(`Components directory not found: ${componentsDir}`);
return components;
}
const files = fs.readdirSync(componentsDir).filter(f => f.endsWith(".md"));
for (const file of files) {
try {
const filePath = path.join(componentsDir, file);
const content = fs.readFileSync(filePath, "utf-8");
const doc = parseComponentDoc(file, content);
if (doc) {
components.push(doc);
}
} catch (error) {
console.error(`Failed to parse component doc: ${file}`, error);
}
}
return components;
}
/**
* 解析组件文档
*/
function parseComponentDoc(filename: string, content: string): ComponentDoc | null {
// 从文件名提取组件名 (如 005-button按钮.md -> em-button)
const nameMatch = filename.match(/\d+-(\w+)/);
if (!nameMatch) return null;
const componentName = `em-${nameMatch[1]}`;
// 提取描述 (第一个 ### 介绍 后的内容)
const descMatch = content.match(/### 介绍\s*\n+([\s\S]*?)(?=\n###|\n##|$)/);
const description = descMatch ? descMatch[1].trim() : "";
// 提取 Props 表格
const propsMatch = content.match(/#### Props\s*\n+([\s\S]*?)(?=\n####|\n###|\n##|$)/);
const props = propsMatch ? propsMatch[1].trim() : "无 Props 文档";
// 提取 Events 表格
const eventsMatch = content.match(/#### Events\s*\n+([\s\S]*?)(?=\n####|\n###|\n##|$)/);
const events = eventsMatch ? eventsMatch[1].trim() : "无 Events 文档";
// 提取代码示例
const examples: string[] = [];
const codeBlockRegex = /```(html|vue|js|javascript)\n([\s\S]*?)```/g;
let match;
while ((match = codeBlockRegex.exec(content)) !== null) {
examples.push(`\`\`\`${match[1]}\n${match[2]}\`\`\``);
}
return {
name: componentName,
filename,
description,
props,
events,
examples: examples.slice(0, 5), // 最多保留5个示例
rawContent: content,
};
}
/**
* 加载所有规范文档
* 递归加载 standards 目录下所有 md 文件
*/
function loadAllStandardDocs(): { summary: string; docs: Map<string, string>; fullContent: string } {
const standardsDir = path.join(KNOWLEDGE_ROOT, "standards");
const docs = new Map<string, string>();
let summary = "";
let fullContent = ""; // 存储所有规范的完整内容
// 1. 加载摘要文档 _index.md
const indexPath = path.join(standardsDir, "_index.md");
if (fs.existsSync(indexPath)) {
try {
summary = fs.readFileSync(indexPath, "utf-8");
} catch (error) {
console.error("Failed to load standards index", error);
}
}
// 2. 递归加载所有子目录的 md 文件
const categories = [
{ dir: "01-project", name: "项目规范", keys: ["project", "naming", "structure", "version"] },
{ dir: "02-vue", name: "Vue 规范", keys: ["vue", "component", "basic", "performance", "state"] },
{ dir: "03-css", name: "CSS 规范", keys: ["css", "scss", "style", "bem"] },
{ dir: "04-api", name: "API 规范", keys: ["api", "ajax", "ejs", "util"] },
{ dir: "05-typescript", name: "TypeScript 规范", keys: ["typescript", "ts", "type"] },
{ dir: "06-mock", name: "Mock 规范", keys: ["mock", "test"] },
{ dir: "07-router", name: "路由规范", keys: ["router", "route", "navigation"] },
];
for (const category of categories) {
const categoryDir = path.join(standardsDir, category.dir);
if (!fs.existsSync(categoryDir)) continue;
const files = fs.readdirSync(categoryDir).filter(f => f.endsWith(".md"));
let categoryContent = "";
// 添加分类标题
categoryContent += `\n\n${"=".repeat(60)}\n`;
categoryContent += `## 📋 ${category.name} (${category.dir})\n`;
categoryContent += `${"=".repeat(60)}\n`;
for (const file of files) {
try {
const filePath = path.join(categoryDir, file);
const content = fs.readFileSync(filePath, "utf-8");
categoryContent += `\n\n---\n### 📄 文件: ${file}\n---\n\n${content}`;
} catch (error) {
console.error(`Failed to load standard doc: ${category.dir}/${file}`, error);
}
}
// 为每个关键词注册该类别的内容
if (categoryContent) {
for (const key of category.keys) {
docs.set(key, categoryContent);
}
// 累加到完整内容
fullContent += categoryContent;
}
}
return { summary, docs, fullContent };
}
/**
* 加载 Util 工具文档
*/
function loadUtilDocs(): Map<string, string> {
const utilDocs = new Map<string, string>();
// 1. 加载 m8mpdoc/核心通用Util工具库 目录
const utilDir = path.join(KNOWLEDGE_ROOT, "m8mpdoc", "核心通用Util工具库");
if (fs.existsSync(utilDir)) {
const files = fs.readdirSync(utilDir).filter(f => f.endsWith(".md"));
for (const file of files) {
try {
const filePath = path.join(utilDir, file);
const content = fs.readFileSync(filePath, "utf-8");
const key = file.replace(".md", "").toLowerCase();
utilDocs.set(key, content);
} catch (error) {
console.error(`Failed to load util doc: ${file}`, error);
}
}
} else {
console.error(`Util directory not found: ${utilDir}`);
}
// 2. 加载 standards/04-api/util.md (包含 Util.string 验证方法)
const apiUtilPath = path.join(KNOWLEDGE_ROOT, "standards", "04-api", "util.md");
if (fs.existsSync(apiUtilPath)) {
try {
const content = fs.readFileSync(apiUtilPath, "utf-8");
utilDocs.set("string", content); // 关键词 "string" 可匹配验证方法
utilDocs.set("validate", content); // 关键词 "validate" 用于验证场景
utilDocs.set("mobile", content); // 关键词 "mobile" 用于手机号
utilDocs.set("email", content); // 关键词 "email" 用于邮箱
utilDocs.set("idcard", content); // 关键词 "idcard" 用于身份证
} catch (error) {
console.error(`Failed to load api util doc`, error);
}
}
return utilDocs;
}
/**
* 获取组件文档
*/
export function getComponentDoc(kb: KnowledgeBase, componentName: string): ComponentDoc | undefined {
// 标准化组件名
let normalizedName = componentName.toLowerCase().trim();
if (!normalizedName.startsWith("em-")) {
normalizedName = `em-${normalizedName}`;
}
return kb.components.find(c => c.name === normalizedName);
}
/**
* 获取完整规范文档
* 返回 01-07 目录下所有规范文件的完整内容,确保生成代码时严格遵循
*/
export function getStandardDocs(kb: KnowledgeBase): string {
// 返回完整的规范文档(来自 01-07 所有目录)
const header = `
################################################################################
## ⚠️ 重要警告:以下是必须严格遵循的 M8 开发规范(完整版)
################################################################################
> **生成代码时务必全部遵循以下规范,严禁臆想或使用非规范写法!**
> 规范来源:\`data/standards/01-project\` 至 \`data/standards/07-router\` 目录下的所有文件
`;
// kb.fullStandards 包含所有规范目录的完整内容
return header + kb.fullStandards;
}
/**
* 获取 Util 工具文档
*/
export function getUtilDocs(kb: KnowledgeBase, keyword: string): string | undefined {
const lowerKeyword = keyword.toLowerCase();
// 直接匹配
for (const [key, content] of kb.utilDocs) {
if (key.includes(lowerKeyword) || content.toLowerCase().includes(lowerKeyword)) {
return content;
}
}
return undefined;
}
/**
* 获取规范文档(按关键词检索)
*/
export function getStandardRules(kb: KnowledgeBase, category: string): string | undefined {
const lowerCategory = category.toLowerCase();
// 直接匹配关键词
if (kb.standardDocs.has(lowerCategory)) {
return kb.standardDocs.get(lowerCategory);
}
// 模糊匹配
for (const [key, content] of kb.standardDocs) {
if (key.includes(lowerCategory) || lowerCategory.includes(key)) {
return content;
}
}
// 返回所有规范类别列表
return undefined;
}
/**
* 获取所有规范类别
*/
export function getStandardCategories(): string[] {
return [
"project - 项目规范(命名、结构、版本检测)",
"vue - Vue 开发规范(基础、组件、性能、状态管理)",
"css - CSS/SCSS 规范(BEM命名、样式分离)",
"api - API 规范(ajax、ejs接口、util工具)",
"typescript - TypeScript 规范",
"mock - Mock 数据规范",
"router - 路由规范",
];
}