func2md 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +6 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +6 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -171,10 +171,11 @@ function resolveLinkPrefix(outDir) {
|
|
|
171
171
|
}
|
|
172
172
|
function readExistingGroupText(pagesPath) {
|
|
173
173
|
if (!(0, fs.existsSync)(pagesPath)) return /* @__PURE__ */ new Map();
|
|
174
|
-
const
|
|
175
|
-
|
|
174
|
+
const raw = (0, fs.readFileSync)(pagesPath, "utf-8").trim();
|
|
175
|
+
const arrayMatch = raw.match(/const\s+pages\s*=\s*(\[[\s\S]*\])\s*;?/) ?? raw.match(/export\s+default\s+(\[[\s\S]*\])\s*;?\s*$/);
|
|
176
|
+
if (!arrayMatch) return /* @__PURE__ */ new Map();
|
|
176
177
|
try {
|
|
177
|
-
const data = JSON.parse(
|
|
178
|
+
const data = JSON.parse(arrayMatch[1]);
|
|
178
179
|
const map = /* @__PURE__ */ new Map();
|
|
179
180
|
const walk = (nodes) => {
|
|
180
181
|
for (const node of nodes) if (node.items && Array.isArray(node.items)) {
|
|
@@ -200,7 +201,7 @@ function sortPages(nodes) {
|
|
|
200
201
|
for (const node of nodes) if (node.items && Array.isArray(node.items)) sortPages(node.items);
|
|
201
202
|
}
|
|
202
203
|
function generatePages(functions, srcDir, outDir) {
|
|
203
|
-
const pagesDir = (0, path.join)(outDir, ".vitepress");
|
|
204
|
+
const pagesDir = (0, path.join)(findVitepressRoot(outDir) ?? outDir, ".vitepress");
|
|
204
205
|
if (!(0, fs.existsSync)(pagesDir)) (0, fs.mkdirSync)(pagesDir, { recursive: true });
|
|
205
206
|
const pagesPath = (0, path.join)(pagesDir, "_pages.js");
|
|
206
207
|
const existingGroupText = readExistingGroupText(pagesPath);
|
|
@@ -241,7 +242,7 @@ function generatePages(functions, srcDir, outDir) {
|
|
|
241
242
|
});
|
|
242
243
|
}
|
|
243
244
|
if (root.items) sortPages(root.items);
|
|
244
|
-
(0, fs.writeFileSync)(pagesPath,
|
|
245
|
+
(0, fs.writeFileSync)(pagesPath, `// @ts-nocheck\n/* eslint-disable */\n/* prettier-ignore-start */\n/**\n * 此文件由 func2md 自动生成。\n * 仅允许修改菜单分组的 text 字段。\n */\nconst pages = ${JSON.stringify(root.items ?? [], null, 2)}\n/* prettier-ignore-end */\n\nexport default pages\n\n/**\n * 获取页面记录。\n * @param text 仅支持一级分组的 text;为空时返回全部。\n */\nexport function getRecords(text?: string) {\n if (!text) {\n return pages\n }\n return pages.filter((item) => item.text === text)\n}\n`);
|
|
245
246
|
}
|
|
246
247
|
async function scanAndGenerateDocs(srcDir, outDir) {
|
|
247
248
|
console.log(`Scanning ${srcDir} and generating docs to ${outDir}`);
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["sep"],"sources":["../src/utils/file-utils.ts","../src/utils/jsdoc-parser.ts","../src/utils/function-extractor.ts","../src/generators/markdown-generator.ts","../src/core/scanner.ts","../src/index.ts"],"sourcesContent":["import { readdirSync, statSync, existsSync } from 'fs'\nimport { join, extname } from 'path'\n\nexport function findAllFiles(dir: string, extensions: string[]): string[] {\n if (!existsSync(dir)) {\n return []\n }\n\n let results: string[] = []\n\n function traverse(currentDir: string) {\n const files = readdirSync(currentDir)\n\n for (const file of files) {\n const filePath = join(currentDir, file)\n const stat = statSync(filePath)\n\n if (stat.isDirectory()) {\n traverse(filePath)\n } else if (stat.isFile() && extensions.includes(extname(file))) {\n results.push(filePath)\n }\n }\n }\n\n traverse(dir)\n return results\n}","export interface JSDocInfo {\n title?: string\n menuTitle?: string\n desc?: string\n example?: string\n params?: Array<{\n name: string\n type: string\n description: string\n }>\n returns?: {\n type: string\n description: string\n }\n signature?: string\n}\n\nexport function parseJSDoc(jsdoc: string): JSDocInfo {\n const lines = jsdoc.trim().split('\\n').map(line => line.replace(/^\\s*\\*\\s?/, ''))\n \n const info: JSDocInfo = {}\n \n // Extract title (first line or @title tag)\n const titleMatch = lines.find(line => line.startsWith('@title'))\n if (titleMatch) {\n info.title = titleMatch.replace('@title', '').trim()\n } else if (lines.length > 0) {\n info.title = lines[0]\n }\n\n // Extract menu title (@MenuTitle)\n const menuTitleMatch = lines.find(line => /@MenuTitle\\b/i.test(line))\n if (menuTitleMatch) {\n info.menuTitle = menuTitleMatch.replace(/@MenuTitle/i, '').trim()\n }\n \n // Extract description - all lines that don't start with @\n const descLines: string[] = []\n let inExample = false\n for (const line of lines) {\n // Stop collecting description when we hit @example or @param or @returns\n if (line.startsWith('@example') || line.startsWith('@param') || line.startsWith('@returns') || line.startsWith('@return')) {\n break\n }\n \n // Only add non-empty lines that don't start with @title to description\n if (!line.startsWith('@title') && line.trim() !== '') {\n descLines.push(line)\n }\n }\n \n if (descLines.length > 0) {\n info.desc = descLines.join(' ').trim()\n }\n \n // Extract example\n const exampleLines: string[] = []\n let collectingExample = false\n for (const line of lines) {\n if (line.startsWith('@example')) {\n collectingExample = true\n const exampleContent = line.replace('@example', '').trim()\n if (exampleContent) {\n exampleLines.push(exampleContent)\n }\n continue\n }\n \n // Continue collecting example lines until we hit another tag\n if (collectingExample) {\n if (line.startsWith('@')) {\n collectingExample = false\n } else {\n exampleLines.push(line)\n }\n }\n }\n \n if (exampleLines.length > 0) {\n info.example = exampleLines.join('\\n').trim()\n }\n \n // Extract parameters\n info.params = []\n lines.forEach(line => {\n // Match @param with or without type: @param {string} name - description or @param name - description\n const paramMatch = line.match(/@param\\s+(?:{([^}]+)})?\\s*(\\w+)\\s*-\\s*(.+)/)\n if (paramMatch) {\n info.params!.push({\n name: paramMatch[2],\n type: paramMatch[1] || 'any',\n description: paramMatch[3]\n })\n }\n })\n \n // Extract return value\n const returnMatch = lines.find(line => line.startsWith('@returns') || line.startsWith('@return'))\n if (returnMatch) {\n const returnInfo = returnMatch.replace(/@(returns?)/, '').trim()\n const typeMatch = returnInfo.match(/^{([^}]+)}\\s*(.*)/)\n if (typeMatch) {\n info.returns = {\n type: typeMatch[1],\n description: typeMatch[2]\n }\n } else {\n info.returns = {\n type: 'unknown',\n description: returnInfo\n }\n }\n }\n \n return info\n}\n","import { parseJSDoc, JSDocInfo } from './jsdoc-parser'\n\nexport interface FunctionInfo {\n name: string\n jsdoc: JSDocInfo\n signature?: string\n filePath: string\n}\n\nexport function extractFunctions(content: string, filePath: string): FunctionInfo[] {\n // This is a simplified implementation\n // In a real implementation, we would use AST parsing (e.g., with TypeScript Compiler API)\n \n const functions: FunctionInfo[] = []\n \n // Match function declarations with JSDoc comments\n // Using a string to avoid regex escaping issues\n const functionRegex = new RegExp(\n String.raw`/\\*\\*((?:.|\\n|\\r)*?)\\*/\\s*(export\\s+)?(async\\s+)?function\\s+(\\w+)`,\n 'g'\n )\n let match\n \n while ((match = functionRegex.exec(content)) !== null) {\n const [, jsdocRaw, , , functionName] = match\n const jsdoc = parseJSDoc(jsdocRaw)\n \n functions.push({\n name: functionName,\n jsdoc,\n filePath\n })\n }\n \n return functions\n}","import { FunctionInfo } from '../utils/function-extractor'\n\n// Generate the main title section\nexport function generateTitleSection(func: FunctionInfo): string {\n const { name, jsdoc } = func\n const title = jsdoc.title || name\n return `# ${title}\\n\\n`\n}\n\n// Generate the description section\nexport function generateDescriptionSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.desc) {\n return `## 说明\\n\\n${jsdoc.desc}\\n\\n`\n }\n return ''\n}\n\n// Generate the signature section\nexport function generateSignatureSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.signature) {\n return `## 签名\\n\\n\\`\\`\\`ts\\n${jsdoc.signature}\\n\\`\\`\\`\\n\\n`\n }\n return ''\n}\n\n// Generate the parameters section\nexport function generateParametersSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.params && jsdoc.params.length > 0) {\n let section = `## 参数\\n\\n`\n section += `| 参数名 | 类型 | 说明 |\\n|--------|------|------|\\n`\n jsdoc.params.forEach(param => {\n section += `| ${param.name} | \\`${param.type}\\` | ${param.description} |\\n`\n })\n section += `\\n`\n return section\n }\n return ''\n}\n\n// Generate the returns section\nexport function generateReturnsSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.returns) {\n return `## 返回值\\n\\n- 类型: \\`${jsdoc.returns.type}\\`\\n- 说明: ${jsdoc.returns.description}\\n\\n`\n }\n return ''\n}\n\n// Generate the example section\nexport function generateExampleSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.example) {\n return `## 示例\\n\\n\\`\\`\\`ts\\n${jsdoc.example}\\n\\`\\`\\`\\n\\n`\n }\n return ''\n}\n\n// Main function to generate markdown content\nexport function generateMarkdown(func: FunctionInfo): string {\n let md = generateTitleSection(func)\n md += generateDescriptionSection(func)\n md += generateSignatureSection(func)\n md += generateParametersSection(func)\n md += generateReturnsSection(func)\n md += generateExampleSection(func)\n return md\n}","import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'\nimport { join, relative, dirname, resolve, sep } from 'path'\nimport { findAllFiles } from '../utils/file-utils'\nimport { extractFunctions } from '../utils/function-extractor'\nimport { generateMarkdown } from '../generators/markdown-generator'\n\ninterface PageNode {\n text: string\n items?: PageNode[]\n link?: string\n dir?: string\n}\n\nfunction toPosixPath(pathValue: string) {\n return pathValue.split(sep).join('/')\n}\n\nfunction findVitepressRoot(outDir: string): string | null {\n let current = resolve(outDir)\n while (true) {\n const configDir = join(current, '.vitepress')\n if (existsSync(configDir)) {\n const hasConfig = ['config.ts', 'config.js', 'config.mjs', 'config.cjs']\n .some(file => existsSync(join(configDir, file)))\n if (hasConfig) {\n return current\n }\n }\n const parent = dirname(current)\n if (parent === current) {\n break\n }\n current = parent\n }\n return null\n}\n\nfunction resolveLinkPrefix(outDir: string): string {\n const resolvedOutDir = resolve(outDir)\n const vitepressRoot = findVitepressRoot(resolvedOutDir)\n if (vitepressRoot) {\n const rel = relative(vitepressRoot, resolvedOutDir)\n return toPosixPath(rel)\n }\n const rel = relative(process.cwd(), resolvedOutDir)\n const parts = rel.split(sep).filter(Boolean)\n return parts.slice(1).join('/')\n}\n\nfunction readExistingGroupText(pagesPath: string): Map<string, string> {\n if (!existsSync(pagesPath)) {\n return new Map()\n }\n\n const raw = readFileSync(pagesPath, 'utf-8').trim()\n const match = raw.match(/export\\s+default\\s+(\\[.*\\])\\s*;?\\s*$/s)\n if (!match) {\n return new Map()\n }\n\n try {\n const data = JSON.parse(match[1]) as PageNode[]\n const map = new Map<string, string>()\n const walk = (nodes: PageNode[]) => {\n for (const node of nodes) {\n if (node.items && Array.isArray(node.items)) {\n if (typeof node.dir === 'string' && typeof node.text === 'string' && node.text.trim() !== '') {\n map.set(node.dir, node.text)\n }\n walk(node.items)\n }\n }\n }\n walk(data)\n return map\n } catch {\n return new Map()\n }\n}\n\nfunction sortPages(nodes: PageNode[]) {\n nodes.sort((a, b) => {\n const aIsGroup = Array.isArray(a.items)\n const bIsGroup = Array.isArray(b.items)\n if (aIsGroup && !bIsGroup) return -1\n if (!aIsGroup && bIsGroup) return 1\n if (aIsGroup && bIsGroup) {\n return (a.dir || '').localeCompare(b.dir || '')\n }\n return (a.link || '').localeCompare(b.link || '')\n })\n\n for (const node of nodes) {\n if (node.items && Array.isArray(node.items)) {\n sortPages(node.items)\n }\n }\n}\n\nfunction generatePages(functions: ReturnType<typeof extractFunctions>, srcDir: string, outDir: string) {\n const pagesDir = join(outDir, '.vitepress')\n if (!existsSync(pagesDir)) {\n mkdirSync(pagesDir, { recursive: true })\n }\n\n const pagesPath = join(pagesDir, '_pages.js')\n const existingGroupText = readExistingGroupText(pagesPath)\n const linkPrefix = resolveLinkPrefix(outDir)\n\n const root: PageNode = { text: '', items: [] }\n const sortedFunctions = [...functions].sort((a, b) => {\n const fileCompare = a.filePath.localeCompare(b.filePath)\n if (fileCompare !== 0) return fileCompare\n return a.name.localeCompare(b.name)\n })\n\n for (const func of sortedFunctions) {\n const relativePath = relative(srcDir, func.filePath)\n const dirPath = dirname(relativePath)\n const parts = dirPath === '.' ? [] : dirPath.split(sep)\n\n let current = root\n let currentDir = ''\n for (const part of parts) {\n currentDir = currentDir ? `${currentDir}/${part}` : part\n if (!current.items) {\n current.items = []\n }\n let next = current.items.find(item => item.items && item.dir === currentDir)\n if (!next) {\n const preservedText = existingGroupText.get(currentDir)\n next = {\n text: preservedText ?? '',\n items: [],\n dir: currentDir\n }\n current.items.push(next)\n }\n current = next\n }\n\n const linkPath = [linkPrefix, func.name].filter(Boolean).join('/')\n if (!current.items) {\n current.items = []\n }\n current.items.push({\n text: func.jsdoc.menuTitle ?? '',\n link: `/${linkPath}`\n })\n }\n\n if (root.items) {\n sortPages(root.items)\n }\n\n const output = `export default ${JSON.stringify(root.items ?? [], null, 2)}\\n`\n writeFileSync(pagesPath, output)\n}\n\nexport async function scanAndGenerateDocs(srcDir: string, outDir: string) {\n console.log(`Scanning ${srcDir} and generating docs to ${outDir}`)\n\n if (!existsSync(outDir)) {\n mkdirSync(outDir, { recursive: true })\n }\n \n // Find all .ts and .js files in srcDir\n const files = findAllFiles(srcDir, ['.ts', '.js'])\n \n const allFunctions = [] as ReturnType<typeof extractFunctions>\n\n for (const file of files) {\n const content = readFileSync(file, 'utf-8')\n const functions = extractFunctions(content, file)\n\n allFunctions.push(...functions)\n \n for (const func of functions) {\n const mdContent = generateMarkdown(func)\n const outputPath = join(outDir, `${func.name}.md`)\n writeFileSync(outputPath, mdContent)\n console.log(`Generated documentation: ${outputPath}`)\n }\n }\n\n generatePages(allFunctions, srcDir, outDir)\n}\n","import type { Plugin } from 'vite'\nimport { existsSync, mkdirSync } from 'fs'\nimport { resolve } from 'path'\nimport { scanAndGenerateDocs } from './core/scanner'\n\nexport { scanAndGenerateDocs }\n\nexport interface Func2MdOptions {\n /**\n * Source directory to scan for functions\n * @default 'src'\n */\n srcDir?: string\n \n /**\n * Output directory for generated markdown files\n * @default 'docs'\n */\n outDir?: string\n \n /**\n * Whether to watch for changes\n * @default false\n */\n watch?: boolean\n}\n\nexport default function func2md(options: Func2MdOptions = {}): Plugin {\n const { \n srcDir = 'src', \n outDir = 'docs',\n watch = false\n } = options\n \n const resolvedSrcDir = resolve(srcDir)\n const resolvedOutDir = resolve(outDir)\n \n return {\n name: 'func2md',\n \n async buildStart() {\n if (!existsSync(resolvedOutDir)) {\n mkdirSync(resolvedOutDir, { recursive: true })\n }\n \n // Scan files and generate docs\n await scanAndGenerateDocs(resolvedSrcDir, resolvedOutDir)\n },\n \n async handleHotUpdate(ctx) {\n if (watch && ctx.file.includes(resolvedSrcDir)) {\n await scanAndGenerateDocs(resolvedSrcDir, resolvedOutDir)\n }\n }\n }\n}"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["sep"],"sources":["../src/utils/file-utils.ts","../src/utils/jsdoc-parser.ts","../src/utils/function-extractor.ts","../src/generators/markdown-generator.ts","../src/core/scanner.ts","../src/index.ts"],"sourcesContent":["import { readdirSync, statSync, existsSync } from 'fs'\nimport { join, extname } from 'path'\n\nexport function findAllFiles(dir: string, extensions: string[]): string[] {\n if (!existsSync(dir)) {\n return []\n }\n\n let results: string[] = []\n\n function traverse(currentDir: string) {\n const files = readdirSync(currentDir)\n\n for (const file of files) {\n const filePath = join(currentDir, file)\n const stat = statSync(filePath)\n\n if (stat.isDirectory()) {\n traverse(filePath)\n } else if (stat.isFile() && extensions.includes(extname(file))) {\n results.push(filePath)\n }\n }\n }\n\n traverse(dir)\n return results\n}","export interface JSDocInfo {\n title?: string\n menuTitle?: string\n desc?: string\n example?: string\n params?: Array<{\n name: string\n type: string\n description: string\n }>\n returns?: {\n type: string\n description: string\n }\n signature?: string\n}\n\nexport function parseJSDoc(jsdoc: string): JSDocInfo {\n const lines = jsdoc.trim().split('\\n').map(line => line.replace(/^\\s*\\*\\s?/, ''))\n \n const info: JSDocInfo = {}\n \n // Extract title (first line or @title tag)\n const titleMatch = lines.find(line => line.startsWith('@title'))\n if (titleMatch) {\n info.title = titleMatch.replace('@title', '').trim()\n } else if (lines.length > 0) {\n info.title = lines[0]\n }\n\n // Extract menu title (@MenuTitle)\n const menuTitleMatch = lines.find(line => /@MenuTitle\\b/i.test(line))\n if (menuTitleMatch) {\n info.menuTitle = menuTitleMatch.replace(/@MenuTitle/i, '').trim()\n }\n \n // Extract description - all lines that don't start with @\n const descLines: string[] = []\n let inExample = false\n for (const line of lines) {\n // Stop collecting description when we hit @example or @param or @returns\n if (line.startsWith('@example') || line.startsWith('@param') || line.startsWith('@returns') || line.startsWith('@return')) {\n break\n }\n \n // Only add non-empty lines that don't start with @title to description\n if (!line.startsWith('@title') && line.trim() !== '') {\n descLines.push(line)\n }\n }\n \n if (descLines.length > 0) {\n info.desc = descLines.join(' ').trim()\n }\n \n // Extract example\n const exampleLines: string[] = []\n let collectingExample = false\n for (const line of lines) {\n if (line.startsWith('@example')) {\n collectingExample = true\n const exampleContent = line.replace('@example', '').trim()\n if (exampleContent) {\n exampleLines.push(exampleContent)\n }\n continue\n }\n \n // Continue collecting example lines until we hit another tag\n if (collectingExample) {\n if (line.startsWith('@')) {\n collectingExample = false\n } else {\n exampleLines.push(line)\n }\n }\n }\n \n if (exampleLines.length > 0) {\n info.example = exampleLines.join('\\n').trim()\n }\n \n // Extract parameters\n info.params = []\n lines.forEach(line => {\n // Match @param with or without type: @param {string} name - description or @param name - description\n const paramMatch = line.match(/@param\\s+(?:{([^}]+)})?\\s*(\\w+)\\s*-\\s*(.+)/)\n if (paramMatch) {\n info.params!.push({\n name: paramMatch[2],\n type: paramMatch[1] || 'any',\n description: paramMatch[3]\n })\n }\n })\n \n // Extract return value\n const returnMatch = lines.find(line => line.startsWith('@returns') || line.startsWith('@return'))\n if (returnMatch) {\n const returnInfo = returnMatch.replace(/@(returns?)/, '').trim()\n const typeMatch = returnInfo.match(/^{([^}]+)}\\s*(.*)/)\n if (typeMatch) {\n info.returns = {\n type: typeMatch[1],\n description: typeMatch[2]\n }\n } else {\n info.returns = {\n type: 'unknown',\n description: returnInfo\n }\n }\n }\n \n return info\n}\n","import { parseJSDoc, JSDocInfo } from './jsdoc-parser'\n\nexport interface FunctionInfo {\n name: string\n jsdoc: JSDocInfo\n signature?: string\n filePath: string\n}\n\nexport function extractFunctions(content: string, filePath: string): FunctionInfo[] {\n // This is a simplified implementation\n // In a real implementation, we would use AST parsing (e.g., with TypeScript Compiler API)\n \n const functions: FunctionInfo[] = []\n \n // Match function declarations with JSDoc comments\n // Using a string to avoid regex escaping issues\n const functionRegex = new RegExp(\n String.raw`/\\*\\*((?:.|\\n|\\r)*?)\\*/\\s*(export\\s+)?(async\\s+)?function\\s+(\\w+)`,\n 'g'\n )\n let match\n \n while ((match = functionRegex.exec(content)) !== null) {\n const [, jsdocRaw, , , functionName] = match\n const jsdoc = parseJSDoc(jsdocRaw)\n \n functions.push({\n name: functionName,\n jsdoc,\n filePath\n })\n }\n \n return functions\n}","import { FunctionInfo } from '../utils/function-extractor'\n\n// Generate the main title section\nexport function generateTitleSection(func: FunctionInfo): string {\n const { name, jsdoc } = func\n const title = jsdoc.title || name\n return `# ${title}\\n\\n`\n}\n\n// Generate the description section\nexport function generateDescriptionSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.desc) {\n return `## 说明\\n\\n${jsdoc.desc}\\n\\n`\n }\n return ''\n}\n\n// Generate the signature section\nexport function generateSignatureSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.signature) {\n return `## 签名\\n\\n\\`\\`\\`ts\\n${jsdoc.signature}\\n\\`\\`\\`\\n\\n`\n }\n return ''\n}\n\n// Generate the parameters section\nexport function generateParametersSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.params && jsdoc.params.length > 0) {\n let section = `## 参数\\n\\n`\n section += `| 参数名 | 类型 | 说明 |\\n|--------|------|------|\\n`\n jsdoc.params.forEach(param => {\n section += `| ${param.name} | \\`${param.type}\\` | ${param.description} |\\n`\n })\n section += `\\n`\n return section\n }\n return ''\n}\n\n// Generate the returns section\nexport function generateReturnsSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.returns) {\n return `## 返回值\\n\\n- 类型: \\`${jsdoc.returns.type}\\`\\n- 说明: ${jsdoc.returns.description}\\n\\n`\n }\n return ''\n}\n\n// Generate the example section\nexport function generateExampleSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.example) {\n return `## 示例\\n\\n\\`\\`\\`ts\\n${jsdoc.example}\\n\\`\\`\\`\\n\\n`\n }\n return ''\n}\n\n// Main function to generate markdown content\nexport function generateMarkdown(func: FunctionInfo): string {\n let md = generateTitleSection(func)\n md += generateDescriptionSection(func)\n md += generateSignatureSection(func)\n md += generateParametersSection(func)\n md += generateReturnsSection(func)\n md += generateExampleSection(func)\n return md\n}","import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'\nimport { join, relative, dirname, resolve, sep } from 'path'\nimport { findAllFiles } from '../utils/file-utils'\nimport { extractFunctions } from '../utils/function-extractor'\nimport { generateMarkdown } from '../generators/markdown-generator'\n\ninterface PageNode {\n text: string\n items?: PageNode[]\n link?: string\n dir?: string\n}\n\nfunction toPosixPath(pathValue: string) {\n return pathValue.split(sep).join('/')\n}\n\nfunction findVitepressRoot(outDir: string): string | null {\n let current = resolve(outDir)\n while (true) {\n const configDir = join(current, '.vitepress')\n if (existsSync(configDir)) {\n const hasConfig = ['config.ts', 'config.js', 'config.mjs', 'config.cjs']\n .some(file => existsSync(join(configDir, file)))\n if (hasConfig) {\n return current\n }\n }\n const parent = dirname(current)\n if (parent === current) {\n break\n }\n current = parent\n }\n return null\n}\n\nfunction resolveLinkPrefix(outDir: string): string {\n const resolvedOutDir = resolve(outDir)\n const vitepressRoot = findVitepressRoot(resolvedOutDir)\n if (vitepressRoot) {\n const rel = relative(vitepressRoot, resolvedOutDir)\n return toPosixPath(rel)\n }\n const rel = relative(process.cwd(), resolvedOutDir)\n const parts = rel.split(sep).filter(Boolean)\n return parts.slice(1).join('/')\n}\n\nfunction readExistingGroupText(pagesPath: string): Map<string, string> {\n if (!existsSync(pagesPath)) {\n return new Map()\n }\n\n const raw = readFileSync(pagesPath, 'utf-8').trim()\n const arrayMatch = raw.match(/const\\s+pages\\s*=\\s*(\\[[\\s\\S]*\\])\\s*;?/)\n ?? raw.match(/export\\s+default\\s+(\\[[\\s\\S]*\\])\\s*;?\\s*$/)\n if (!arrayMatch) {\n return new Map()\n }\n\n try {\n const data = JSON.parse(arrayMatch[1]) as PageNode[]\n const map = new Map<string, string>()\n const walk = (nodes: PageNode[]) => {\n for (const node of nodes) {\n if (node.items && Array.isArray(node.items)) {\n if (typeof node.dir === 'string' && typeof node.text === 'string' && node.text.trim() !== '') {\n map.set(node.dir, node.text)\n }\n walk(node.items)\n }\n }\n }\n walk(data)\n return map\n } catch {\n return new Map()\n }\n}\n\nfunction sortPages(nodes: PageNode[]) {\n nodes.sort((a, b) => {\n const aIsGroup = Array.isArray(a.items)\n const bIsGroup = Array.isArray(b.items)\n if (aIsGroup && !bIsGroup) return -1\n if (!aIsGroup && bIsGroup) return 1\n if (aIsGroup && bIsGroup) {\n return (a.dir || '').localeCompare(b.dir || '')\n }\n return (a.link || '').localeCompare(b.link || '')\n })\n\n for (const node of nodes) {\n if (node.items && Array.isArray(node.items)) {\n sortPages(node.items)\n }\n }\n}\n\nfunction generatePages(functions: ReturnType<typeof extractFunctions>, srcDir: string, outDir: string) {\n const vitepressRoot = findVitepressRoot(outDir)\n const pagesBaseDir = vitepressRoot ?? outDir\n const pagesDir = join(pagesBaseDir, '.vitepress')\n if (!existsSync(pagesDir)) {\n mkdirSync(pagesDir, { recursive: true })\n }\n\n const pagesPath = join(pagesDir, '_pages.js')\n const existingGroupText = readExistingGroupText(pagesPath)\n const linkPrefix = resolveLinkPrefix(outDir)\n\n const root: PageNode = { text: '', items: [] }\n const sortedFunctions = [...functions].sort((a, b) => {\n const fileCompare = a.filePath.localeCompare(b.filePath)\n if (fileCompare !== 0) return fileCompare\n return a.name.localeCompare(b.name)\n })\n\n for (const func of sortedFunctions) {\n const relativePath = relative(srcDir, func.filePath)\n const dirPath = dirname(relativePath)\n const parts = dirPath === '.' ? [] : dirPath.split(sep)\n\n let current = root\n let currentDir = ''\n for (const part of parts) {\n currentDir = currentDir ? `${currentDir}/${part}` : part\n if (!current.items) {\n current.items = []\n }\n let next = current.items.find(item => item.items && item.dir === currentDir)\n if (!next) {\n const preservedText = existingGroupText.get(currentDir)\n next = {\n text: preservedText ?? '',\n items: [],\n dir: currentDir\n }\n current.items.push(next)\n }\n current = next\n }\n\n const linkPath = [linkPrefix, func.name].filter(Boolean).join('/')\n if (!current.items) {\n current.items = []\n }\n current.items.push({\n text: func.jsdoc.menuTitle ?? '',\n link: `/${linkPath}`\n })\n }\n\n if (root.items) {\n sortPages(root.items)\n }\n\n const pagesJson = JSON.stringify(root.items ?? [], null, 2)\n const output = `// @ts-nocheck\\n/* eslint-disable */\\n/* prettier-ignore-start */\\n/**\\n * 此文件由 func2md 自动生成。\\n * 仅允许修改菜单分组的 text 字段。\\n */\\nconst pages = ${pagesJson}\\n/* prettier-ignore-end */\\n\\nexport default pages\\n\\n/**\\n * 获取页面记录。\\n * @param text 仅支持一级分组的 text;为空时返回全部。\\n */\\nexport function getRecords(text?: string) {\\n if (!text) {\\n return pages\\n }\\n return pages.filter((item) => item.text === text)\\n}\\n`\n writeFileSync(pagesPath, output)\n}\n\nexport async function scanAndGenerateDocs(srcDir: string, outDir: string) {\n console.log(`Scanning ${srcDir} and generating docs to ${outDir}`)\n\n if (!existsSync(outDir)) {\n mkdirSync(outDir, { recursive: true })\n }\n \n // Find all .ts and .js files in srcDir\n const files = findAllFiles(srcDir, ['.ts', '.js'])\n \n const allFunctions = [] as ReturnType<typeof extractFunctions>\n\n for (const file of files) {\n const content = readFileSync(file, 'utf-8')\n const functions = extractFunctions(content, file)\n\n allFunctions.push(...functions)\n \n for (const func of functions) {\n const mdContent = generateMarkdown(func)\n const outputPath = join(outDir, `${func.name}.md`)\n writeFileSync(outputPath, mdContent)\n console.log(`Generated documentation: ${outputPath}`)\n }\n }\n\n generatePages(allFunctions, srcDir, outDir)\n}\n","import type { Plugin } from 'vite'\nimport { existsSync, mkdirSync } from 'fs'\nimport { resolve } from 'path'\nimport { scanAndGenerateDocs } from './core/scanner'\n\nexport { scanAndGenerateDocs }\n\nexport interface Func2MdOptions {\n /**\n * Source directory to scan for functions\n * @default 'src'\n */\n srcDir?: string\n \n /**\n * Output directory for generated markdown files\n * @default 'docs'\n */\n outDir?: string\n \n /**\n * Whether to watch for changes\n * @default false\n */\n watch?: boolean\n}\n\nexport default function func2md(options: Func2MdOptions = {}): Plugin {\n const { \n srcDir = 'src', \n outDir = 'docs',\n watch = false\n } = options\n \n const resolvedSrcDir = resolve(srcDir)\n const resolvedOutDir = resolve(outDir)\n \n return {\n name: 'func2md',\n \n async buildStart() {\n if (!existsSync(resolvedOutDir)) {\n mkdirSync(resolvedOutDir, { recursive: true })\n }\n \n // Scan files and generate docs\n await scanAndGenerateDocs(resolvedSrcDir, resolvedOutDir)\n },\n \n async handleHotUpdate(ctx) {\n if (watch && ctx.file.includes(resolvedSrcDir)) {\n await scanAndGenerateDocs(resolvedSrcDir, resolvedOutDir)\n }\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -170,10 +170,11 @@ function resolveLinkPrefix(outDir) {
|
|
|
170
170
|
}
|
|
171
171
|
function readExistingGroupText(pagesPath) {
|
|
172
172
|
if (!existsSync(pagesPath)) return /* @__PURE__ */ new Map();
|
|
173
|
-
const
|
|
174
|
-
|
|
173
|
+
const raw = readFileSync(pagesPath, "utf-8").trim();
|
|
174
|
+
const arrayMatch = raw.match(/const\s+pages\s*=\s*(\[[\s\S]*\])\s*;?/) ?? raw.match(/export\s+default\s+(\[[\s\S]*\])\s*;?\s*$/);
|
|
175
|
+
if (!arrayMatch) return /* @__PURE__ */ new Map();
|
|
175
176
|
try {
|
|
176
|
-
const data = JSON.parse(
|
|
177
|
+
const data = JSON.parse(arrayMatch[1]);
|
|
177
178
|
const map = /* @__PURE__ */ new Map();
|
|
178
179
|
const walk = (nodes) => {
|
|
179
180
|
for (const node of nodes) if (node.items && Array.isArray(node.items)) {
|
|
@@ -199,7 +200,7 @@ function sortPages(nodes) {
|
|
|
199
200
|
for (const node of nodes) if (node.items && Array.isArray(node.items)) sortPages(node.items);
|
|
200
201
|
}
|
|
201
202
|
function generatePages(functions, srcDir, outDir) {
|
|
202
|
-
const pagesDir = join(outDir, ".vitepress");
|
|
203
|
+
const pagesDir = join(findVitepressRoot(outDir) ?? outDir, ".vitepress");
|
|
203
204
|
if (!existsSync(pagesDir)) mkdirSync(pagesDir, { recursive: true });
|
|
204
205
|
const pagesPath = join(pagesDir, "_pages.js");
|
|
205
206
|
const existingGroupText = readExistingGroupText(pagesPath);
|
|
@@ -240,7 +241,7 @@ function generatePages(functions, srcDir, outDir) {
|
|
|
240
241
|
});
|
|
241
242
|
}
|
|
242
243
|
if (root.items) sortPages(root.items);
|
|
243
|
-
writeFileSync(pagesPath,
|
|
244
|
+
writeFileSync(pagesPath, `// @ts-nocheck\n/* eslint-disable */\n/* prettier-ignore-start */\n/**\n * 此文件由 func2md 自动生成。\n * 仅允许修改菜单分组的 text 字段。\n */\nconst pages = ${JSON.stringify(root.items ?? [], null, 2)}\n/* prettier-ignore-end */\n\nexport default pages\n\n/**\n * 获取页面记录。\n * @param text 仅支持一级分组的 text;为空时返回全部。\n */\nexport function getRecords(text?: string) {\n if (!text) {\n return pages\n }\n return pages.filter((item) => item.text === text)\n}\n`);
|
|
244
245
|
}
|
|
245
246
|
async function scanAndGenerateDocs(srcDir, outDir) {
|
|
246
247
|
console.log(`Scanning ${srcDir} and generating docs to ${outDir}`);
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/utils/file-utils.ts","../src/utils/jsdoc-parser.ts","../src/utils/function-extractor.ts","../src/generators/markdown-generator.ts","../src/core/scanner.ts","../src/index.ts"],"sourcesContent":["import { readdirSync, statSync, existsSync } from 'fs'\nimport { join, extname } from 'path'\n\nexport function findAllFiles(dir: string, extensions: string[]): string[] {\n if (!existsSync(dir)) {\n return []\n }\n\n let results: string[] = []\n\n function traverse(currentDir: string) {\n const files = readdirSync(currentDir)\n\n for (const file of files) {\n const filePath = join(currentDir, file)\n const stat = statSync(filePath)\n\n if (stat.isDirectory()) {\n traverse(filePath)\n } else if (stat.isFile() && extensions.includes(extname(file))) {\n results.push(filePath)\n }\n }\n }\n\n traverse(dir)\n return results\n}","export interface JSDocInfo {\n title?: string\n menuTitle?: string\n desc?: string\n example?: string\n params?: Array<{\n name: string\n type: string\n description: string\n }>\n returns?: {\n type: string\n description: string\n }\n signature?: string\n}\n\nexport function parseJSDoc(jsdoc: string): JSDocInfo {\n const lines = jsdoc.trim().split('\\n').map(line => line.replace(/^\\s*\\*\\s?/, ''))\n \n const info: JSDocInfo = {}\n \n // Extract title (first line or @title tag)\n const titleMatch = lines.find(line => line.startsWith('@title'))\n if (titleMatch) {\n info.title = titleMatch.replace('@title', '').trim()\n } else if (lines.length > 0) {\n info.title = lines[0]\n }\n\n // Extract menu title (@MenuTitle)\n const menuTitleMatch = lines.find(line => /@MenuTitle\\b/i.test(line))\n if (menuTitleMatch) {\n info.menuTitle = menuTitleMatch.replace(/@MenuTitle/i, '').trim()\n }\n \n // Extract description - all lines that don't start with @\n const descLines: string[] = []\n let inExample = false\n for (const line of lines) {\n // Stop collecting description when we hit @example or @param or @returns\n if (line.startsWith('@example') || line.startsWith('@param') || line.startsWith('@returns') || line.startsWith('@return')) {\n break\n }\n \n // Only add non-empty lines that don't start with @title to description\n if (!line.startsWith('@title') && line.trim() !== '') {\n descLines.push(line)\n }\n }\n \n if (descLines.length > 0) {\n info.desc = descLines.join(' ').trim()\n }\n \n // Extract example\n const exampleLines: string[] = []\n let collectingExample = false\n for (const line of lines) {\n if (line.startsWith('@example')) {\n collectingExample = true\n const exampleContent = line.replace('@example', '').trim()\n if (exampleContent) {\n exampleLines.push(exampleContent)\n }\n continue\n }\n \n // Continue collecting example lines until we hit another tag\n if (collectingExample) {\n if (line.startsWith('@')) {\n collectingExample = false\n } else {\n exampleLines.push(line)\n }\n }\n }\n \n if (exampleLines.length > 0) {\n info.example = exampleLines.join('\\n').trim()\n }\n \n // Extract parameters\n info.params = []\n lines.forEach(line => {\n // Match @param with or without type: @param {string} name - description or @param name - description\n const paramMatch = line.match(/@param\\s+(?:{([^}]+)})?\\s*(\\w+)\\s*-\\s*(.+)/)\n if (paramMatch) {\n info.params!.push({\n name: paramMatch[2],\n type: paramMatch[1] || 'any',\n description: paramMatch[3]\n })\n }\n })\n \n // Extract return value\n const returnMatch = lines.find(line => line.startsWith('@returns') || line.startsWith('@return'))\n if (returnMatch) {\n const returnInfo = returnMatch.replace(/@(returns?)/, '').trim()\n const typeMatch = returnInfo.match(/^{([^}]+)}\\s*(.*)/)\n if (typeMatch) {\n info.returns = {\n type: typeMatch[1],\n description: typeMatch[2]\n }\n } else {\n info.returns = {\n type: 'unknown',\n description: returnInfo\n }\n }\n }\n \n return info\n}\n","import { parseJSDoc, JSDocInfo } from './jsdoc-parser'\n\nexport interface FunctionInfo {\n name: string\n jsdoc: JSDocInfo\n signature?: string\n filePath: string\n}\n\nexport function extractFunctions(content: string, filePath: string): FunctionInfo[] {\n // This is a simplified implementation\n // In a real implementation, we would use AST parsing (e.g., with TypeScript Compiler API)\n \n const functions: FunctionInfo[] = []\n \n // Match function declarations with JSDoc comments\n // Using a string to avoid regex escaping issues\n const functionRegex = new RegExp(\n String.raw`/\\*\\*((?:.|\\n|\\r)*?)\\*/\\s*(export\\s+)?(async\\s+)?function\\s+(\\w+)`,\n 'g'\n )\n let match\n \n while ((match = functionRegex.exec(content)) !== null) {\n const [, jsdocRaw, , , functionName] = match\n const jsdoc = parseJSDoc(jsdocRaw)\n \n functions.push({\n name: functionName,\n jsdoc,\n filePath\n })\n }\n \n return functions\n}","import { FunctionInfo } from '../utils/function-extractor'\n\n// Generate the main title section\nexport function generateTitleSection(func: FunctionInfo): string {\n const { name, jsdoc } = func\n const title = jsdoc.title || name\n return `# ${title}\\n\\n`\n}\n\n// Generate the description section\nexport function generateDescriptionSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.desc) {\n return `## 说明\\n\\n${jsdoc.desc}\\n\\n`\n }\n return ''\n}\n\n// Generate the signature section\nexport function generateSignatureSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.signature) {\n return `## 签名\\n\\n\\`\\`\\`ts\\n${jsdoc.signature}\\n\\`\\`\\`\\n\\n`\n }\n return ''\n}\n\n// Generate the parameters section\nexport function generateParametersSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.params && jsdoc.params.length > 0) {\n let section = `## 参数\\n\\n`\n section += `| 参数名 | 类型 | 说明 |\\n|--------|------|------|\\n`\n jsdoc.params.forEach(param => {\n section += `| ${param.name} | \\`${param.type}\\` | ${param.description} |\\n`\n })\n section += `\\n`\n return section\n }\n return ''\n}\n\n// Generate the returns section\nexport function generateReturnsSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.returns) {\n return `## 返回值\\n\\n- 类型: \\`${jsdoc.returns.type}\\`\\n- 说明: ${jsdoc.returns.description}\\n\\n`\n }\n return ''\n}\n\n// Generate the example section\nexport function generateExampleSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.example) {\n return `## 示例\\n\\n\\`\\`\\`ts\\n${jsdoc.example}\\n\\`\\`\\`\\n\\n`\n }\n return ''\n}\n\n// Main function to generate markdown content\nexport function generateMarkdown(func: FunctionInfo): string {\n let md = generateTitleSection(func)\n md += generateDescriptionSection(func)\n md += generateSignatureSection(func)\n md += generateParametersSection(func)\n md += generateReturnsSection(func)\n md += generateExampleSection(func)\n return md\n}","import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'\nimport { join, relative, dirname, resolve, sep } from 'path'\nimport { findAllFiles } from '../utils/file-utils'\nimport { extractFunctions } from '../utils/function-extractor'\nimport { generateMarkdown } from '../generators/markdown-generator'\n\ninterface PageNode {\n text: string\n items?: PageNode[]\n link?: string\n dir?: string\n}\n\nfunction toPosixPath(pathValue: string) {\n return pathValue.split(sep).join('/')\n}\n\nfunction findVitepressRoot(outDir: string): string | null {\n let current = resolve(outDir)\n while (true) {\n const configDir = join(current, '.vitepress')\n if (existsSync(configDir)) {\n const hasConfig = ['config.ts', 'config.js', 'config.mjs', 'config.cjs']\n .some(file => existsSync(join(configDir, file)))\n if (hasConfig) {\n return current\n }\n }\n const parent = dirname(current)\n if (parent === current) {\n break\n }\n current = parent\n }\n return null\n}\n\nfunction resolveLinkPrefix(outDir: string): string {\n const resolvedOutDir = resolve(outDir)\n const vitepressRoot = findVitepressRoot(resolvedOutDir)\n if (vitepressRoot) {\n const rel = relative(vitepressRoot, resolvedOutDir)\n return toPosixPath(rel)\n }\n const rel = relative(process.cwd(), resolvedOutDir)\n const parts = rel.split(sep).filter(Boolean)\n return parts.slice(1).join('/')\n}\n\nfunction readExistingGroupText(pagesPath: string): Map<string, string> {\n if (!existsSync(pagesPath)) {\n return new Map()\n }\n\n const raw = readFileSync(pagesPath, 'utf-8').trim()\n const match = raw.match(/export\\s+default\\s+(\\[.*\\])\\s*;?\\s*$/s)\n if (!match) {\n return new Map()\n }\n\n try {\n const data = JSON.parse(match[1]) as PageNode[]\n const map = new Map<string, string>()\n const walk = (nodes: PageNode[]) => {\n for (const node of nodes) {\n if (node.items && Array.isArray(node.items)) {\n if (typeof node.dir === 'string' && typeof node.text === 'string' && node.text.trim() !== '') {\n map.set(node.dir, node.text)\n }\n walk(node.items)\n }\n }\n }\n walk(data)\n return map\n } catch {\n return new Map()\n }\n}\n\nfunction sortPages(nodes: PageNode[]) {\n nodes.sort((a, b) => {\n const aIsGroup = Array.isArray(a.items)\n const bIsGroup = Array.isArray(b.items)\n if (aIsGroup && !bIsGroup) return -1\n if (!aIsGroup && bIsGroup) return 1\n if (aIsGroup && bIsGroup) {\n return (a.dir || '').localeCompare(b.dir || '')\n }\n return (a.link || '').localeCompare(b.link || '')\n })\n\n for (const node of nodes) {\n if (node.items && Array.isArray(node.items)) {\n sortPages(node.items)\n }\n }\n}\n\nfunction generatePages(functions: ReturnType<typeof extractFunctions>, srcDir: string, outDir: string) {\n const pagesDir = join(outDir, '.vitepress')\n if (!existsSync(pagesDir)) {\n mkdirSync(pagesDir, { recursive: true })\n }\n\n const pagesPath = join(pagesDir, '_pages.js')\n const existingGroupText = readExistingGroupText(pagesPath)\n const linkPrefix = resolveLinkPrefix(outDir)\n\n const root: PageNode = { text: '', items: [] }\n const sortedFunctions = [...functions].sort((a, b) => {\n const fileCompare = a.filePath.localeCompare(b.filePath)\n if (fileCompare !== 0) return fileCompare\n return a.name.localeCompare(b.name)\n })\n\n for (const func of sortedFunctions) {\n const relativePath = relative(srcDir, func.filePath)\n const dirPath = dirname(relativePath)\n const parts = dirPath === '.' ? [] : dirPath.split(sep)\n\n let current = root\n let currentDir = ''\n for (const part of parts) {\n currentDir = currentDir ? `${currentDir}/${part}` : part\n if (!current.items) {\n current.items = []\n }\n let next = current.items.find(item => item.items && item.dir === currentDir)\n if (!next) {\n const preservedText = existingGroupText.get(currentDir)\n next = {\n text: preservedText ?? '',\n items: [],\n dir: currentDir\n }\n current.items.push(next)\n }\n current = next\n }\n\n const linkPath = [linkPrefix, func.name].filter(Boolean).join('/')\n if (!current.items) {\n current.items = []\n }\n current.items.push({\n text: func.jsdoc.menuTitle ?? '',\n link: `/${linkPath}`\n })\n }\n\n if (root.items) {\n sortPages(root.items)\n }\n\n const output = `export default ${JSON.stringify(root.items ?? [], null, 2)}\\n`\n writeFileSync(pagesPath, output)\n}\n\nexport async function scanAndGenerateDocs(srcDir: string, outDir: string) {\n console.log(`Scanning ${srcDir} and generating docs to ${outDir}`)\n\n if (!existsSync(outDir)) {\n mkdirSync(outDir, { recursive: true })\n }\n \n // Find all .ts and .js files in srcDir\n const files = findAllFiles(srcDir, ['.ts', '.js'])\n \n const allFunctions = [] as ReturnType<typeof extractFunctions>\n\n for (const file of files) {\n const content = readFileSync(file, 'utf-8')\n const functions = extractFunctions(content, file)\n\n allFunctions.push(...functions)\n \n for (const func of functions) {\n const mdContent = generateMarkdown(func)\n const outputPath = join(outDir, `${func.name}.md`)\n writeFileSync(outputPath, mdContent)\n console.log(`Generated documentation: ${outputPath}`)\n }\n }\n\n generatePages(allFunctions, srcDir, outDir)\n}\n","import type { Plugin } from 'vite'\nimport { existsSync, mkdirSync } from 'fs'\nimport { resolve } from 'path'\nimport { scanAndGenerateDocs } from './core/scanner'\n\nexport { scanAndGenerateDocs }\n\nexport interface Func2MdOptions {\n /**\n * Source directory to scan for functions\n * @default 'src'\n */\n srcDir?: string\n \n /**\n * Output directory for generated markdown files\n * @default 'docs'\n */\n outDir?: string\n \n /**\n * Whether to watch for changes\n * @default false\n */\n watch?: boolean\n}\n\nexport default function func2md(options: Func2MdOptions = {}): Plugin {\n const { \n srcDir = 'src', \n outDir = 'docs',\n watch = false\n } = options\n \n const resolvedSrcDir = resolve(srcDir)\n const resolvedOutDir = resolve(outDir)\n \n return {\n name: 'func2md',\n \n async buildStart() {\n if (!existsSync(resolvedOutDir)) {\n mkdirSync(resolvedOutDir, { recursive: true })\n }\n \n // Scan files and generate docs\n await scanAndGenerateDocs(resolvedSrcDir, resolvedOutDir)\n },\n \n async handleHotUpdate(ctx) {\n if (watch && ctx.file.includes(resolvedSrcDir)) {\n await scanAndGenerateDocs(resolvedSrcDir, resolvedOutDir)\n }\n }\n }\n}"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/utils/file-utils.ts","../src/utils/jsdoc-parser.ts","../src/utils/function-extractor.ts","../src/generators/markdown-generator.ts","../src/core/scanner.ts","../src/index.ts"],"sourcesContent":["import { readdirSync, statSync, existsSync } from 'fs'\nimport { join, extname } from 'path'\n\nexport function findAllFiles(dir: string, extensions: string[]): string[] {\n if (!existsSync(dir)) {\n return []\n }\n\n let results: string[] = []\n\n function traverse(currentDir: string) {\n const files = readdirSync(currentDir)\n\n for (const file of files) {\n const filePath = join(currentDir, file)\n const stat = statSync(filePath)\n\n if (stat.isDirectory()) {\n traverse(filePath)\n } else if (stat.isFile() && extensions.includes(extname(file))) {\n results.push(filePath)\n }\n }\n }\n\n traverse(dir)\n return results\n}","export interface JSDocInfo {\n title?: string\n menuTitle?: string\n desc?: string\n example?: string\n params?: Array<{\n name: string\n type: string\n description: string\n }>\n returns?: {\n type: string\n description: string\n }\n signature?: string\n}\n\nexport function parseJSDoc(jsdoc: string): JSDocInfo {\n const lines = jsdoc.trim().split('\\n').map(line => line.replace(/^\\s*\\*\\s?/, ''))\n \n const info: JSDocInfo = {}\n \n // Extract title (first line or @title tag)\n const titleMatch = lines.find(line => line.startsWith('@title'))\n if (titleMatch) {\n info.title = titleMatch.replace('@title', '').trim()\n } else if (lines.length > 0) {\n info.title = lines[0]\n }\n\n // Extract menu title (@MenuTitle)\n const menuTitleMatch = lines.find(line => /@MenuTitle\\b/i.test(line))\n if (menuTitleMatch) {\n info.menuTitle = menuTitleMatch.replace(/@MenuTitle/i, '').trim()\n }\n \n // Extract description - all lines that don't start with @\n const descLines: string[] = []\n let inExample = false\n for (const line of lines) {\n // Stop collecting description when we hit @example or @param or @returns\n if (line.startsWith('@example') || line.startsWith('@param') || line.startsWith('@returns') || line.startsWith('@return')) {\n break\n }\n \n // Only add non-empty lines that don't start with @title to description\n if (!line.startsWith('@title') && line.trim() !== '') {\n descLines.push(line)\n }\n }\n \n if (descLines.length > 0) {\n info.desc = descLines.join(' ').trim()\n }\n \n // Extract example\n const exampleLines: string[] = []\n let collectingExample = false\n for (const line of lines) {\n if (line.startsWith('@example')) {\n collectingExample = true\n const exampleContent = line.replace('@example', '').trim()\n if (exampleContent) {\n exampleLines.push(exampleContent)\n }\n continue\n }\n \n // Continue collecting example lines until we hit another tag\n if (collectingExample) {\n if (line.startsWith('@')) {\n collectingExample = false\n } else {\n exampleLines.push(line)\n }\n }\n }\n \n if (exampleLines.length > 0) {\n info.example = exampleLines.join('\\n').trim()\n }\n \n // Extract parameters\n info.params = []\n lines.forEach(line => {\n // Match @param with or without type: @param {string} name - description or @param name - description\n const paramMatch = line.match(/@param\\s+(?:{([^}]+)})?\\s*(\\w+)\\s*-\\s*(.+)/)\n if (paramMatch) {\n info.params!.push({\n name: paramMatch[2],\n type: paramMatch[1] || 'any',\n description: paramMatch[3]\n })\n }\n })\n \n // Extract return value\n const returnMatch = lines.find(line => line.startsWith('@returns') || line.startsWith('@return'))\n if (returnMatch) {\n const returnInfo = returnMatch.replace(/@(returns?)/, '').trim()\n const typeMatch = returnInfo.match(/^{([^}]+)}\\s*(.*)/)\n if (typeMatch) {\n info.returns = {\n type: typeMatch[1],\n description: typeMatch[2]\n }\n } else {\n info.returns = {\n type: 'unknown',\n description: returnInfo\n }\n }\n }\n \n return info\n}\n","import { parseJSDoc, JSDocInfo } from './jsdoc-parser'\n\nexport interface FunctionInfo {\n name: string\n jsdoc: JSDocInfo\n signature?: string\n filePath: string\n}\n\nexport function extractFunctions(content: string, filePath: string): FunctionInfo[] {\n // This is a simplified implementation\n // In a real implementation, we would use AST parsing (e.g., with TypeScript Compiler API)\n \n const functions: FunctionInfo[] = []\n \n // Match function declarations with JSDoc comments\n // Using a string to avoid regex escaping issues\n const functionRegex = new RegExp(\n String.raw`/\\*\\*((?:.|\\n|\\r)*?)\\*/\\s*(export\\s+)?(async\\s+)?function\\s+(\\w+)`,\n 'g'\n )\n let match\n \n while ((match = functionRegex.exec(content)) !== null) {\n const [, jsdocRaw, , , functionName] = match\n const jsdoc = parseJSDoc(jsdocRaw)\n \n functions.push({\n name: functionName,\n jsdoc,\n filePath\n })\n }\n \n return functions\n}","import { FunctionInfo } from '../utils/function-extractor'\n\n// Generate the main title section\nexport function generateTitleSection(func: FunctionInfo): string {\n const { name, jsdoc } = func\n const title = jsdoc.title || name\n return `# ${title}\\n\\n`\n}\n\n// Generate the description section\nexport function generateDescriptionSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.desc) {\n return `## 说明\\n\\n${jsdoc.desc}\\n\\n`\n }\n return ''\n}\n\n// Generate the signature section\nexport function generateSignatureSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.signature) {\n return `## 签名\\n\\n\\`\\`\\`ts\\n${jsdoc.signature}\\n\\`\\`\\`\\n\\n`\n }\n return ''\n}\n\n// Generate the parameters section\nexport function generateParametersSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.params && jsdoc.params.length > 0) {\n let section = `## 参数\\n\\n`\n section += `| 参数名 | 类型 | 说明 |\\n|--------|------|------|\\n`\n jsdoc.params.forEach(param => {\n section += `| ${param.name} | \\`${param.type}\\` | ${param.description} |\\n`\n })\n section += `\\n`\n return section\n }\n return ''\n}\n\n// Generate the returns section\nexport function generateReturnsSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.returns) {\n return `## 返回值\\n\\n- 类型: \\`${jsdoc.returns.type}\\`\\n- 说明: ${jsdoc.returns.description}\\n\\n`\n }\n return ''\n}\n\n// Generate the example section\nexport function generateExampleSection(func: FunctionInfo): string {\n const { jsdoc } = func\n if (jsdoc.example) {\n return `## 示例\\n\\n\\`\\`\\`ts\\n${jsdoc.example}\\n\\`\\`\\`\\n\\n`\n }\n return ''\n}\n\n// Main function to generate markdown content\nexport function generateMarkdown(func: FunctionInfo): string {\n let md = generateTitleSection(func)\n md += generateDescriptionSection(func)\n md += generateSignatureSection(func)\n md += generateParametersSection(func)\n md += generateReturnsSection(func)\n md += generateExampleSection(func)\n return md\n}","import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'\nimport { join, relative, dirname, resolve, sep } from 'path'\nimport { findAllFiles } from '../utils/file-utils'\nimport { extractFunctions } from '../utils/function-extractor'\nimport { generateMarkdown } from '../generators/markdown-generator'\n\ninterface PageNode {\n text: string\n items?: PageNode[]\n link?: string\n dir?: string\n}\n\nfunction toPosixPath(pathValue: string) {\n return pathValue.split(sep).join('/')\n}\n\nfunction findVitepressRoot(outDir: string): string | null {\n let current = resolve(outDir)\n while (true) {\n const configDir = join(current, '.vitepress')\n if (existsSync(configDir)) {\n const hasConfig = ['config.ts', 'config.js', 'config.mjs', 'config.cjs']\n .some(file => existsSync(join(configDir, file)))\n if (hasConfig) {\n return current\n }\n }\n const parent = dirname(current)\n if (parent === current) {\n break\n }\n current = parent\n }\n return null\n}\n\nfunction resolveLinkPrefix(outDir: string): string {\n const resolvedOutDir = resolve(outDir)\n const vitepressRoot = findVitepressRoot(resolvedOutDir)\n if (vitepressRoot) {\n const rel = relative(vitepressRoot, resolvedOutDir)\n return toPosixPath(rel)\n }\n const rel = relative(process.cwd(), resolvedOutDir)\n const parts = rel.split(sep).filter(Boolean)\n return parts.slice(1).join('/')\n}\n\nfunction readExistingGroupText(pagesPath: string): Map<string, string> {\n if (!existsSync(pagesPath)) {\n return new Map()\n }\n\n const raw = readFileSync(pagesPath, 'utf-8').trim()\n const arrayMatch = raw.match(/const\\s+pages\\s*=\\s*(\\[[\\s\\S]*\\])\\s*;?/)\n ?? raw.match(/export\\s+default\\s+(\\[[\\s\\S]*\\])\\s*;?\\s*$/)\n if (!arrayMatch) {\n return new Map()\n }\n\n try {\n const data = JSON.parse(arrayMatch[1]) as PageNode[]\n const map = new Map<string, string>()\n const walk = (nodes: PageNode[]) => {\n for (const node of nodes) {\n if (node.items && Array.isArray(node.items)) {\n if (typeof node.dir === 'string' && typeof node.text === 'string' && node.text.trim() !== '') {\n map.set(node.dir, node.text)\n }\n walk(node.items)\n }\n }\n }\n walk(data)\n return map\n } catch {\n return new Map()\n }\n}\n\nfunction sortPages(nodes: PageNode[]) {\n nodes.sort((a, b) => {\n const aIsGroup = Array.isArray(a.items)\n const bIsGroup = Array.isArray(b.items)\n if (aIsGroup && !bIsGroup) return -1\n if (!aIsGroup && bIsGroup) return 1\n if (aIsGroup && bIsGroup) {\n return (a.dir || '').localeCompare(b.dir || '')\n }\n return (a.link || '').localeCompare(b.link || '')\n })\n\n for (const node of nodes) {\n if (node.items && Array.isArray(node.items)) {\n sortPages(node.items)\n }\n }\n}\n\nfunction generatePages(functions: ReturnType<typeof extractFunctions>, srcDir: string, outDir: string) {\n const vitepressRoot = findVitepressRoot(outDir)\n const pagesBaseDir = vitepressRoot ?? outDir\n const pagesDir = join(pagesBaseDir, '.vitepress')\n if (!existsSync(pagesDir)) {\n mkdirSync(pagesDir, { recursive: true })\n }\n\n const pagesPath = join(pagesDir, '_pages.js')\n const existingGroupText = readExistingGroupText(pagesPath)\n const linkPrefix = resolveLinkPrefix(outDir)\n\n const root: PageNode = { text: '', items: [] }\n const sortedFunctions = [...functions].sort((a, b) => {\n const fileCompare = a.filePath.localeCompare(b.filePath)\n if (fileCompare !== 0) return fileCompare\n return a.name.localeCompare(b.name)\n })\n\n for (const func of sortedFunctions) {\n const relativePath = relative(srcDir, func.filePath)\n const dirPath = dirname(relativePath)\n const parts = dirPath === '.' ? [] : dirPath.split(sep)\n\n let current = root\n let currentDir = ''\n for (const part of parts) {\n currentDir = currentDir ? `${currentDir}/${part}` : part\n if (!current.items) {\n current.items = []\n }\n let next = current.items.find(item => item.items && item.dir === currentDir)\n if (!next) {\n const preservedText = existingGroupText.get(currentDir)\n next = {\n text: preservedText ?? '',\n items: [],\n dir: currentDir\n }\n current.items.push(next)\n }\n current = next\n }\n\n const linkPath = [linkPrefix, func.name].filter(Boolean).join('/')\n if (!current.items) {\n current.items = []\n }\n current.items.push({\n text: func.jsdoc.menuTitle ?? '',\n link: `/${linkPath}`\n })\n }\n\n if (root.items) {\n sortPages(root.items)\n }\n\n const pagesJson = JSON.stringify(root.items ?? [], null, 2)\n const output = `// @ts-nocheck\\n/* eslint-disable */\\n/* prettier-ignore-start */\\n/**\\n * 此文件由 func2md 自动生成。\\n * 仅允许修改菜单分组的 text 字段。\\n */\\nconst pages = ${pagesJson}\\n/* prettier-ignore-end */\\n\\nexport default pages\\n\\n/**\\n * 获取页面记录。\\n * @param text 仅支持一级分组的 text;为空时返回全部。\\n */\\nexport function getRecords(text?: string) {\\n if (!text) {\\n return pages\\n }\\n return pages.filter((item) => item.text === text)\\n}\\n`\n writeFileSync(pagesPath, output)\n}\n\nexport async function scanAndGenerateDocs(srcDir: string, outDir: string) {\n console.log(`Scanning ${srcDir} and generating docs to ${outDir}`)\n\n if (!existsSync(outDir)) {\n mkdirSync(outDir, { recursive: true })\n }\n \n // Find all .ts and .js files in srcDir\n const files = findAllFiles(srcDir, ['.ts', '.js'])\n \n const allFunctions = [] as ReturnType<typeof extractFunctions>\n\n for (const file of files) {\n const content = readFileSync(file, 'utf-8')\n const functions = extractFunctions(content, file)\n\n allFunctions.push(...functions)\n \n for (const func of functions) {\n const mdContent = generateMarkdown(func)\n const outputPath = join(outDir, `${func.name}.md`)\n writeFileSync(outputPath, mdContent)\n console.log(`Generated documentation: ${outputPath}`)\n }\n }\n\n generatePages(allFunctions, srcDir, outDir)\n}\n","import type { Plugin } from 'vite'\nimport { existsSync, mkdirSync } from 'fs'\nimport { resolve } from 'path'\nimport { scanAndGenerateDocs } from './core/scanner'\n\nexport { scanAndGenerateDocs }\n\nexport interface Func2MdOptions {\n /**\n * Source directory to scan for functions\n * @default 'src'\n */\n srcDir?: string\n \n /**\n * Output directory for generated markdown files\n * @default 'docs'\n */\n outDir?: string\n \n /**\n * Whether to watch for changes\n * @default false\n */\n watch?: boolean\n}\n\nexport default function func2md(options: Func2MdOptions = {}): Plugin {\n const { \n srcDir = 'src', \n outDir = 'docs',\n watch = false\n } = options\n \n const resolvedSrcDir = resolve(srcDir)\n const resolvedOutDir = resolve(outDir)\n \n return {\n name: 'func2md',\n \n async buildStart() {\n if (!existsSync(resolvedOutDir)) {\n mkdirSync(resolvedOutDir, { recursive: true })\n }\n \n // Scan files and generate docs\n await scanAndGenerateDocs(resolvedSrcDir, resolvedOutDir)\n },\n \n async handleHotUpdate(ctx) {\n if (watch && ctx.file.includes(resolvedSrcDir)) {\n await scanAndGenerateDocs(resolvedSrcDir, resolvedOutDir)\n }\n }\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED