func2md 0.0.2 → 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/bin/func2md.js CHANGED
@@ -14,7 +14,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
14
14
  // Parse arguments
15
15
  const args = process.argv.slice(2)
16
16
  const srcDir = args.find(a => a.startsWith('--src-dir='))?.split('=')[1] || 'src'
17
- const outDir = args.find(a => a.startsWith('--out-dir='))?.split('=')[1] || 'docs'
17
+ const outDir = args.find(a => a.startsWith('--out-dir='))?.split('=')[1] || 'docs/_generated'
18
18
 
19
19
  // Resolve paths
20
20
  const resolvedSrcDir = resolve(srcDir)
package/dist/index.cjs CHANGED
@@ -27,6 +27,8 @@ function parseJSDoc(jsdoc) {
27
27
  const titleMatch = lines.find((line) => line.startsWith("@title"));
28
28
  if (titleMatch) info.title = titleMatch.replace("@title", "").trim();
29
29
  else if (lines.length > 0) info.title = lines[0];
30
+ const menuTitleMatch = lines.find((line) => /@MenuTitle\b/i.test(line));
31
+ if (menuTitleMatch) info.menuTitle = menuTitleMatch.replace(/@MenuTitle/i, "").trim();
30
32
  const descLines = [];
31
33
  for (const line of lines) {
32
34
  if (line.startsWith("@example") || line.startsWith("@param") || line.startsWith("@returns") || line.startsWith("@return")) break;
@@ -140,11 +142,116 @@ function generateMarkdown(func) {
140
142
 
141
143
  //#endregion
142
144
  //#region src/core/scanner.ts
145
+ function toPosixPath(pathValue) {
146
+ return pathValue.split(path.sep).join("/");
147
+ }
148
+ function findVitepressRoot(outDir) {
149
+ let current = (0, path.resolve)(outDir);
150
+ while (true) {
151
+ const configDir = (0, path.join)(current, ".vitepress");
152
+ if ((0, fs.existsSync)(configDir)) {
153
+ if ([
154
+ "config.ts",
155
+ "config.js",
156
+ "config.mjs",
157
+ "config.cjs"
158
+ ].some((file) => (0, fs.existsSync)((0, path.join)(configDir, file)))) return current;
159
+ }
160
+ const parent = (0, path.dirname)(current);
161
+ if (parent === current) break;
162
+ current = parent;
163
+ }
164
+ return null;
165
+ }
166
+ function resolveLinkPrefix(outDir) {
167
+ const resolvedOutDir = (0, path.resolve)(outDir);
168
+ const vitepressRoot = findVitepressRoot(resolvedOutDir);
169
+ if (vitepressRoot) return toPosixPath((0, path.relative)(vitepressRoot, resolvedOutDir));
170
+ return (0, path.relative)(process.cwd(), resolvedOutDir).split(path.sep).filter(Boolean).slice(1).join("/");
171
+ }
172
+ function readExistingGroupText(pagesPath) {
173
+ if (!(0, fs.existsSync)(pagesPath)) return /* @__PURE__ */ new Map();
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();
177
+ try {
178
+ const data = JSON.parse(arrayMatch[1]);
179
+ const map = /* @__PURE__ */ new Map();
180
+ const walk = (nodes) => {
181
+ for (const node of nodes) if (node.items && Array.isArray(node.items)) {
182
+ if (typeof node.dir === "string" && typeof node.text === "string" && node.text.trim() !== "") map.set(node.dir, node.text);
183
+ walk(node.items);
184
+ }
185
+ };
186
+ walk(data);
187
+ return map;
188
+ } catch {
189
+ return /* @__PURE__ */ new Map();
190
+ }
191
+ }
192
+ function sortPages(nodes) {
193
+ nodes.sort((a, b) => {
194
+ const aIsGroup = Array.isArray(a.items);
195
+ const bIsGroup = Array.isArray(b.items);
196
+ if (aIsGroup && !bIsGroup) return -1;
197
+ if (!aIsGroup && bIsGroup) return 1;
198
+ if (aIsGroup && bIsGroup) return (a.dir || "").localeCompare(b.dir || "");
199
+ return (a.link || "").localeCompare(b.link || "");
200
+ });
201
+ for (const node of nodes) if (node.items && Array.isArray(node.items)) sortPages(node.items);
202
+ }
203
+ function generatePages(functions, srcDir, outDir) {
204
+ const pagesDir = (0, path.join)(findVitepressRoot(outDir) ?? outDir, ".vitepress");
205
+ if (!(0, fs.existsSync)(pagesDir)) (0, fs.mkdirSync)(pagesDir, { recursive: true });
206
+ const pagesPath = (0, path.join)(pagesDir, "_pages.js");
207
+ const existingGroupText = readExistingGroupText(pagesPath);
208
+ const linkPrefix = resolveLinkPrefix(outDir);
209
+ const root = {
210
+ text: "",
211
+ items: []
212
+ };
213
+ const sortedFunctions = [...functions].sort((a, b) => {
214
+ const fileCompare = a.filePath.localeCompare(b.filePath);
215
+ if (fileCompare !== 0) return fileCompare;
216
+ return a.name.localeCompare(b.name);
217
+ });
218
+ for (const func of sortedFunctions) {
219
+ const dirPath = (0, path.dirname)((0, path.relative)(srcDir, func.filePath));
220
+ const parts = dirPath === "." ? [] : dirPath.split(path.sep);
221
+ let current = root;
222
+ let currentDir = "";
223
+ for (const part of parts) {
224
+ currentDir = currentDir ? `${currentDir}/${part}` : part;
225
+ if (!current.items) current.items = [];
226
+ let next = current.items.find((item) => item.items && item.dir === currentDir);
227
+ if (!next) {
228
+ next = {
229
+ text: existingGroupText.get(currentDir) ?? "",
230
+ items: [],
231
+ dir: currentDir
232
+ };
233
+ current.items.push(next);
234
+ }
235
+ current = next;
236
+ }
237
+ const linkPath = [linkPrefix, func.name].filter(Boolean).join("/");
238
+ if (!current.items) current.items = [];
239
+ current.items.push({
240
+ text: func.jsdoc.menuTitle ?? "",
241
+ link: `/${linkPath}`
242
+ });
243
+ }
244
+ if (root.items) sortPages(root.items);
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`);
246
+ }
143
247
  async function scanAndGenerateDocs(srcDir, outDir) {
144
248
  console.log(`Scanning ${srcDir} and generating docs to ${outDir}`);
249
+ if (!(0, fs.existsSync)(outDir)) (0, fs.mkdirSync)(outDir, { recursive: true });
145
250
  const files = findAllFiles(srcDir, [".ts", ".js"]);
251
+ const allFunctions = [];
146
252
  for (const file of files) {
147
253
  const functions = extractFunctions((0, fs.readFileSync)(file, "utf-8"), file);
254
+ allFunctions.push(...functions);
148
255
  for (const func of functions) {
149
256
  const mdContent = generateMarkdown(func);
150
257
  const outputPath = (0, path.join)(outDir, `${func.name}.md`);
@@ -152,6 +259,7 @@ async function scanAndGenerateDocs(srcDir, outDir) {
152
259
  console.log(`Generated documentation: ${outputPath}`);
153
260
  }
154
261
  }
262
+ generatePages(allFunctions, srcDir, outDir);
155
263
  }
156
264
 
157
265
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","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 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 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}","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 } from 'fs'\nimport { join } from 'path'\nimport { findAllFiles } from '../utils/file-utils'\nimport { extractFunctions } from '../utils/function-extractor'\nimport { generateMarkdown } from '../generators/markdown-generator'\n\nexport async function scanAndGenerateDocs(srcDir: string, outDir: string) {\n console.log(`Scanning ${srcDir} and generating docs to ${outDir}`)\n \n // Find all .ts and .js files in srcDir\n const files = findAllFiles(srcDir, ['.ts', '.js'])\n \n for (const file of files) {\n const content = readFileSync(file, 'utf-8')\n const functions = extractFunctions(content, file)\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}","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
@@ -1,5 +1,5 @@
1
1
  import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
2
- import { extname, join, resolve } from "path";
2
+ import { dirname, extname, join, relative, resolve, sep } from "path";
3
3
 
4
4
  //#region src/utils/file-utils.ts
5
5
  function findAllFiles(dir, extensions) {
@@ -26,6 +26,8 @@ function parseJSDoc(jsdoc) {
26
26
  const titleMatch = lines.find((line) => line.startsWith("@title"));
27
27
  if (titleMatch) info.title = titleMatch.replace("@title", "").trim();
28
28
  else if (lines.length > 0) info.title = lines[0];
29
+ const menuTitleMatch = lines.find((line) => /@MenuTitle\b/i.test(line));
30
+ if (menuTitleMatch) info.menuTitle = menuTitleMatch.replace(/@MenuTitle/i, "").trim();
29
31
  const descLines = [];
30
32
  for (const line of lines) {
31
33
  if (line.startsWith("@example") || line.startsWith("@param") || line.startsWith("@returns") || line.startsWith("@return")) break;
@@ -139,11 +141,116 @@ function generateMarkdown(func) {
139
141
 
140
142
  //#endregion
141
143
  //#region src/core/scanner.ts
144
+ function toPosixPath(pathValue) {
145
+ return pathValue.split(sep).join("/");
146
+ }
147
+ function findVitepressRoot(outDir) {
148
+ let current = resolve(outDir);
149
+ while (true) {
150
+ const configDir = join(current, ".vitepress");
151
+ if (existsSync(configDir)) {
152
+ if ([
153
+ "config.ts",
154
+ "config.js",
155
+ "config.mjs",
156
+ "config.cjs"
157
+ ].some((file) => existsSync(join(configDir, file)))) return current;
158
+ }
159
+ const parent = dirname(current);
160
+ if (parent === current) break;
161
+ current = parent;
162
+ }
163
+ return null;
164
+ }
165
+ function resolveLinkPrefix(outDir) {
166
+ const resolvedOutDir = resolve(outDir);
167
+ const vitepressRoot = findVitepressRoot(resolvedOutDir);
168
+ if (vitepressRoot) return toPosixPath(relative(vitepressRoot, resolvedOutDir));
169
+ return relative(process.cwd(), resolvedOutDir).split(sep).filter(Boolean).slice(1).join("/");
170
+ }
171
+ function readExistingGroupText(pagesPath) {
172
+ if (!existsSync(pagesPath)) return /* @__PURE__ */ new Map();
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();
176
+ try {
177
+ const data = JSON.parse(arrayMatch[1]);
178
+ const map = /* @__PURE__ */ new Map();
179
+ const walk = (nodes) => {
180
+ for (const node of nodes) if (node.items && Array.isArray(node.items)) {
181
+ if (typeof node.dir === "string" && typeof node.text === "string" && node.text.trim() !== "") map.set(node.dir, node.text);
182
+ walk(node.items);
183
+ }
184
+ };
185
+ walk(data);
186
+ return map;
187
+ } catch {
188
+ return /* @__PURE__ */ new Map();
189
+ }
190
+ }
191
+ function sortPages(nodes) {
192
+ nodes.sort((a, b) => {
193
+ const aIsGroup = Array.isArray(a.items);
194
+ const bIsGroup = Array.isArray(b.items);
195
+ if (aIsGroup && !bIsGroup) return -1;
196
+ if (!aIsGroup && bIsGroup) return 1;
197
+ if (aIsGroup && bIsGroup) return (a.dir || "").localeCompare(b.dir || "");
198
+ return (a.link || "").localeCompare(b.link || "");
199
+ });
200
+ for (const node of nodes) if (node.items && Array.isArray(node.items)) sortPages(node.items);
201
+ }
202
+ function generatePages(functions, srcDir, outDir) {
203
+ const pagesDir = join(findVitepressRoot(outDir) ?? outDir, ".vitepress");
204
+ if (!existsSync(pagesDir)) mkdirSync(pagesDir, { recursive: true });
205
+ const pagesPath = join(pagesDir, "_pages.js");
206
+ const existingGroupText = readExistingGroupText(pagesPath);
207
+ const linkPrefix = resolveLinkPrefix(outDir);
208
+ const root = {
209
+ text: "",
210
+ items: []
211
+ };
212
+ const sortedFunctions = [...functions].sort((a, b) => {
213
+ const fileCompare = a.filePath.localeCompare(b.filePath);
214
+ if (fileCompare !== 0) return fileCompare;
215
+ return a.name.localeCompare(b.name);
216
+ });
217
+ for (const func of sortedFunctions) {
218
+ const dirPath = dirname(relative(srcDir, func.filePath));
219
+ const parts = dirPath === "." ? [] : dirPath.split(sep);
220
+ let current = root;
221
+ let currentDir = "";
222
+ for (const part of parts) {
223
+ currentDir = currentDir ? `${currentDir}/${part}` : part;
224
+ if (!current.items) current.items = [];
225
+ let next = current.items.find((item) => item.items && item.dir === currentDir);
226
+ if (!next) {
227
+ next = {
228
+ text: existingGroupText.get(currentDir) ?? "",
229
+ items: [],
230
+ dir: currentDir
231
+ };
232
+ current.items.push(next);
233
+ }
234
+ current = next;
235
+ }
236
+ const linkPath = [linkPrefix, func.name].filter(Boolean).join("/");
237
+ if (!current.items) current.items = [];
238
+ current.items.push({
239
+ text: func.jsdoc.menuTitle ?? "",
240
+ link: `/${linkPath}`
241
+ });
242
+ }
243
+ if (root.items) sortPages(root.items);
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`);
245
+ }
142
246
  async function scanAndGenerateDocs(srcDir, outDir) {
143
247
  console.log(`Scanning ${srcDir} and generating docs to ${outDir}`);
248
+ if (!existsSync(outDir)) mkdirSync(outDir, { recursive: true });
144
249
  const files = findAllFiles(srcDir, [".ts", ".js"]);
250
+ const allFunctions = [];
145
251
  for (const file of files) {
146
252
  const functions = extractFunctions(readFileSync(file, "utf-8"), file);
253
+ allFunctions.push(...functions);
147
254
  for (const func of functions) {
148
255
  const mdContent = generateMarkdown(func);
149
256
  const outputPath = join(outDir, `${func.name}.md`);
@@ -151,6 +258,7 @@ async function scanAndGenerateDocs(srcDir, outDir) {
151
258
  console.log(`Generated documentation: ${outputPath}`);
152
259
  }
153
260
  }
261
+ generatePages(allFunctions, srcDir, outDir);
154
262
  }
155
263
 
156
264
  //#endregion
@@ -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 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 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}","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 } from 'fs'\nimport { join } from 'path'\nimport { findAllFiles } from '../utils/file-utils'\nimport { extractFunctions } from '../utils/function-extractor'\nimport { generateMarkdown } from '../generators/markdown-generator'\n\nexport async function scanAndGenerateDocs(srcDir: string, outDir: string) {\n console.log(`Scanning ${srcDir} and generating docs to ${outDir}`)\n \n // Find all .ts and .js files in srcDir\n const files = findAllFiles(srcDir, ['.ts', '.js'])\n \n for (const file of files) {\n const content = readFileSync(file, 'utf-8')\n const functions = extractFunctions(content, file)\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}","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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "func2md",
3
3
  "type": "module",
4
- "version": "0.0.2",
4
+ "version": "0.0.4",
5
5
  "description": "A Vite plugin to generate VitePress documentation from JSDoc and TypeScript types",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.mjs",