docmk 1.0.6 → 1.0.7
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.js +202 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/builder/vite-dev.ts +19 -17
- package/src/cli/commands/dev.ts +3 -2
- package/src/cli/commands/preview.ts +3 -2
- package/src/cli/index.ts +1 -7
- package/src/utils/port.ts +128 -0
package/dist/index.js
CHANGED
|
@@ -306,7 +306,7 @@ import vue from "@vitejs/plugin-vue";
|
|
|
306
306
|
import path2 from "path";
|
|
307
307
|
import { fileURLToPath } from "url";
|
|
308
308
|
var __dirname = path2.dirname(fileURLToPath(import.meta.url));
|
|
309
|
-
var packageRoot = path2.resolve(__dirname, "
|
|
309
|
+
var packageRoot = path2.resolve(__dirname, "..");
|
|
310
310
|
async function createViteDevServer(options) {
|
|
311
311
|
let currentConfig = options.config;
|
|
312
312
|
const server = await createServer({
|
|
@@ -324,22 +324,20 @@ async function createViteDevServer(options) {
|
|
|
324
324
|
{
|
|
325
325
|
name: "docgen-dev",
|
|
326
326
|
configureServer(server2) {
|
|
327
|
-
server2.middlewares.use("/api/config", (req, res, next) => {
|
|
328
|
-
if (req.method === "GET") {
|
|
329
|
-
res.setHeader("Content-Type", "application/json");
|
|
330
|
-
res.end(JSON.stringify(currentConfig));
|
|
331
|
-
} else {
|
|
332
|
-
next();
|
|
333
|
-
}
|
|
334
|
-
});
|
|
335
327
|
return () => {
|
|
328
|
+
server2.middlewares.use("/api/config", (req, res, next) => {
|
|
329
|
+
if (req.method === "GET") {
|
|
330
|
+
res.setHeader("Content-Type", "application/json");
|
|
331
|
+
res.end(JSON.stringify(currentConfig));
|
|
332
|
+
} else {
|
|
333
|
+
next();
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
336
|
server2.middlewares.use((req, res, next) => {
|
|
337
|
-
if (req.url?.startsWith("/api/") || req.url?.startsWith("/@") || req.url?.includes(".
|
|
337
|
+
if (req.url?.startsWith("/api/") || req.url?.startsWith("/@") || req.url?.includes(".")) {
|
|
338
338
|
return next();
|
|
339
339
|
}
|
|
340
|
-
|
|
341
|
-
req.url = "/index.html";
|
|
342
|
-
}
|
|
340
|
+
req.url = "/index.html";
|
|
343
341
|
next();
|
|
344
342
|
});
|
|
345
343
|
};
|
|
@@ -387,7 +385,8 @@ async function createViteDevServer(options) {
|
|
|
387
385
|
}
|
|
388
386
|
});
|
|
389
387
|
await server.listen();
|
|
390
|
-
const
|
|
388
|
+
const address = server.httpServer.address();
|
|
389
|
+
const actualPort = typeof address === "object" && address ? address.port : options.port;
|
|
391
390
|
const originalClose = server.close.bind(server);
|
|
392
391
|
server.close = async () => {
|
|
393
392
|
await watcher?.close();
|
|
@@ -399,10 +398,195 @@ async function createViteDevServer(options) {
|
|
|
399
398
|
// src/cli/commands/dev.ts
|
|
400
399
|
init_scanner();
|
|
401
400
|
init_parser();
|
|
401
|
+
|
|
402
|
+
// src/utils/port.ts
|
|
403
|
+
var UNSAFE_PORTS = /* @__PURE__ */ new Set([
|
|
404
|
+
1,
|
|
405
|
+
// tcpmux
|
|
406
|
+
7,
|
|
407
|
+
// echo
|
|
408
|
+
9,
|
|
409
|
+
// discard
|
|
410
|
+
11,
|
|
411
|
+
// systat
|
|
412
|
+
13,
|
|
413
|
+
// daytime
|
|
414
|
+
15,
|
|
415
|
+
// netstat
|
|
416
|
+
17,
|
|
417
|
+
// qotd
|
|
418
|
+
19,
|
|
419
|
+
// chargen
|
|
420
|
+
20,
|
|
421
|
+
// ftp data
|
|
422
|
+
21,
|
|
423
|
+
// ftp access
|
|
424
|
+
22,
|
|
425
|
+
// ssh
|
|
426
|
+
23,
|
|
427
|
+
// telnet
|
|
428
|
+
25,
|
|
429
|
+
// smtp
|
|
430
|
+
37,
|
|
431
|
+
// time
|
|
432
|
+
42,
|
|
433
|
+
// name
|
|
434
|
+
43,
|
|
435
|
+
// nicname
|
|
436
|
+
53,
|
|
437
|
+
// domain
|
|
438
|
+
69,
|
|
439
|
+
// tftp
|
|
440
|
+
77,
|
|
441
|
+
// priv-rjs
|
|
442
|
+
79,
|
|
443
|
+
// finger
|
|
444
|
+
87,
|
|
445
|
+
// ttylink
|
|
446
|
+
95,
|
|
447
|
+
// supdup
|
|
448
|
+
101,
|
|
449
|
+
// hostriame
|
|
450
|
+
102,
|
|
451
|
+
// iso-tsap
|
|
452
|
+
103,
|
|
453
|
+
// gppitnp
|
|
454
|
+
104,
|
|
455
|
+
// acr-nema
|
|
456
|
+
109,
|
|
457
|
+
// pop2
|
|
458
|
+
110,
|
|
459
|
+
// pop3
|
|
460
|
+
111,
|
|
461
|
+
// sunrpc
|
|
462
|
+
113,
|
|
463
|
+
// auth
|
|
464
|
+
115,
|
|
465
|
+
// sftp
|
|
466
|
+
117,
|
|
467
|
+
// uucp-path
|
|
468
|
+
119,
|
|
469
|
+
// nntp
|
|
470
|
+
123,
|
|
471
|
+
// NTP
|
|
472
|
+
135,
|
|
473
|
+
// loc-srv / epmap
|
|
474
|
+
137,
|
|
475
|
+
// netbios
|
|
476
|
+
139,
|
|
477
|
+
// netbios
|
|
478
|
+
143,
|
|
479
|
+
// imap2
|
|
480
|
+
161,
|
|
481
|
+
// snmp
|
|
482
|
+
179,
|
|
483
|
+
// BGP
|
|
484
|
+
389,
|
|
485
|
+
// ldap
|
|
486
|
+
427,
|
|
487
|
+
// SLP (Also used by determine_refmode)
|
|
488
|
+
465,
|
|
489
|
+
// smtp+ssl
|
|
490
|
+
512,
|
|
491
|
+
// print / exec
|
|
492
|
+
513,
|
|
493
|
+
// login
|
|
494
|
+
514,
|
|
495
|
+
// shell
|
|
496
|
+
515,
|
|
497
|
+
// printer
|
|
498
|
+
526,
|
|
499
|
+
// tempo
|
|
500
|
+
530,
|
|
501
|
+
// courier
|
|
502
|
+
531,
|
|
503
|
+
// chat
|
|
504
|
+
532,
|
|
505
|
+
// netnews
|
|
506
|
+
540,
|
|
507
|
+
// uucp
|
|
508
|
+
548,
|
|
509
|
+
// AFP (Apple Filing Protocol)
|
|
510
|
+
554,
|
|
511
|
+
// rtsp
|
|
512
|
+
556,
|
|
513
|
+
// remotefs
|
|
514
|
+
563,
|
|
515
|
+
// nntp+ssl
|
|
516
|
+
587,
|
|
517
|
+
// smtp (rfc6409)
|
|
518
|
+
601,
|
|
519
|
+
// syslog-conn (rfc3195)
|
|
520
|
+
636,
|
|
521
|
+
// ldap+ssl
|
|
522
|
+
989,
|
|
523
|
+
// ftps-data
|
|
524
|
+
990,
|
|
525
|
+
// ftps
|
|
526
|
+
993,
|
|
527
|
+
// ldap+ssl
|
|
528
|
+
995,
|
|
529
|
+
// pop3+ssl
|
|
530
|
+
1719,
|
|
531
|
+
// h323gatestat
|
|
532
|
+
1720,
|
|
533
|
+
// h323hostcall
|
|
534
|
+
1723,
|
|
535
|
+
// pptp
|
|
536
|
+
2049,
|
|
537
|
+
// nfs
|
|
538
|
+
3659,
|
|
539
|
+
// apple-sasl / PasswordServer
|
|
540
|
+
4045,
|
|
541
|
+
// lockd
|
|
542
|
+
5060,
|
|
543
|
+
// sip
|
|
544
|
+
5061,
|
|
545
|
+
// sips
|
|
546
|
+
6e3,
|
|
547
|
+
// X11
|
|
548
|
+
6566,
|
|
549
|
+
// sane-port
|
|
550
|
+
6665,
|
|
551
|
+
// Alternate IRC [Apple addition]
|
|
552
|
+
6666,
|
|
553
|
+
// Alternate IRC [Apple addition]
|
|
554
|
+
6667,
|
|
555
|
+
// Standard IRC [Apple addition]
|
|
556
|
+
6668,
|
|
557
|
+
// Alternate IRC [Apple addition]
|
|
558
|
+
6669,
|
|
559
|
+
// Alternate IRC [Apple addition]
|
|
560
|
+
6697,
|
|
561
|
+
// IRC + TLS
|
|
562
|
+
10080
|
|
563
|
+
// Amanda
|
|
564
|
+
]);
|
|
565
|
+
function isPortSafe(port) {
|
|
566
|
+
return !UNSAFE_PORTS.has(port);
|
|
567
|
+
}
|
|
568
|
+
function validatePort(port) {
|
|
569
|
+
if (isPortSafe(port)) {
|
|
570
|
+
return { port, wasUnsafe: false };
|
|
571
|
+
}
|
|
572
|
+
const safePort = findNextSafePort(port);
|
|
573
|
+
console.warn(`\u26A0\uFE0F \u7AEF\u53E3 ${port} \u88AB\u6D4F\u89C8\u5668\u89C6\u4E3A\u4E0D\u5B89\u5168\u7AEF\u53E3\uFF0C\u5DF2\u81EA\u52A8\u5207\u6362\u5230 ${safePort}`);
|
|
574
|
+
console.warn(` \u4E0D\u5B89\u5168\u7AEF\u53E3\u4F1A\u88AB\u6D4F\u89C8\u5668\u963B\u6B62\u8BBF\u95EE (ERR_UNSAFE_PORT)`);
|
|
575
|
+
return { port: safePort, wasUnsafe: true };
|
|
576
|
+
}
|
|
577
|
+
function findNextSafePort(startPort) {
|
|
578
|
+
let port = startPort;
|
|
579
|
+
while (!isPortSafe(port) && port < 65535) {
|
|
580
|
+
port++;
|
|
581
|
+
}
|
|
582
|
+
return port < 65535 ? port : 3e3;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// src/cli/commands/dev.ts
|
|
402
586
|
async function devCommand(options) {
|
|
403
587
|
console.log("\u{1F680} Starting DocGen development server...");
|
|
404
588
|
const sourceDir = path3.resolve(process.cwd(), options.dir);
|
|
405
|
-
const port = parseInt(options.port, 10);
|
|
589
|
+
const { port } = validatePort(parseInt(options.port, 10));
|
|
406
590
|
try {
|
|
407
591
|
console.log(`\u{1F4C1} Scanning source directory: ${sourceDir}`);
|
|
408
592
|
const directories = await scanSkillsDirectory(sourceDir);
|
|
@@ -602,7 +786,7 @@ import { createServer as createServer2 } from "http";
|
|
|
602
786
|
async function previewCommand(options) {
|
|
603
787
|
console.log("\u{1F440} Starting DocGen preview server...");
|
|
604
788
|
const outputDir = path6.resolve(process.cwd(), options.output);
|
|
605
|
-
const port = parseInt(options.port, 10);
|
|
789
|
+
const { port } = validatePort(parseInt(options.port, 10));
|
|
606
790
|
try {
|
|
607
791
|
try {
|
|
608
792
|
await fs3.access(outputDir);
|
|
@@ -640,10 +824,8 @@ async function previewCommand(options) {
|
|
|
640
824
|
|
|
641
825
|
// src/cli/index.ts
|
|
642
826
|
var program = new Command();
|
|
643
|
-
program.name("docmk").description("CLI tool for generating documentation from any directory").version("1.0.0")
|
|
644
|
-
|
|
645
|
-
});
|
|
646
|
-
program.command("dev").description("Start development server").option("-p, --port <port>", "Port to run dev server on", "3000").option("-d, --dir <directory>", "Source directory path", "./docs").action(devCommand);
|
|
827
|
+
program.name("docmk").description("CLI tool for generating documentation from any directory").version("1.0.0");
|
|
828
|
+
program.command("dev", { isDefault: true }).description("Start development server").option("-p, --port <port>", "Port to run dev server on", "3000").option("-d, --dir <directory>", "Source directory path", "./docs").action(devCommand);
|
|
647
829
|
program.command("build").description("Build static documentation site").option("-d, --dir <directory>", "Source directory path", "./docs").option("-o, --output <directory>", "Output directory", "dist").action(buildCommand);
|
|
648
830
|
program.command("preview").description("Preview built documentation site").option("-p, --port <port>", "Port to run preview server on", "4173").option("-o, --output <directory>", "Built site directory", "dist").action(previewCommand);
|
|
649
831
|
program.parse();
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/scanner/index.ts","../src/parser/index.ts","../src/cli/index.ts","../src/cli/commands/dev.ts","../src/builder/vite-dev.ts","../src/cli/commands/build.ts","../src/builder/index.ts","../src/cli/commands/preview.ts"],"sourcesContent":["import fs from 'fs/promises'\nimport path from 'path'\nimport { SkillDirectory, SkillFile } from '../types/index.js'\n\nexport async function scanSkillsDirectory(sourceDir: string): Promise<SkillDirectory[]> {\n try {\n await fs.access(sourceDir)\n } catch {\n throw new Error(`Source directory not found: ${sourceDir}`)\n }\n\n const entries = await fs.readdir(sourceDir, { withFileTypes: true })\n const directories: SkillDirectory[] = []\n\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const dirPath = path.join(sourceDir, entry.name)\n const skillDirectory = await scanDirectory(dirPath, entry.name)\n directories.push(skillDirectory)\n }\n }\n\n return directories\n}\n\nasync function scanDirectory(dirPath: string, name: string): Promise<SkillDirectory> {\n const entries = await fs.readdir(dirPath, { withFileTypes: true })\n const children: (SkillDirectory | SkillFile)[] = []\n let skillFile: SkillFile | undefined\n\n for (const entry of entries) {\n const entryPath = path.join(dirPath, entry.name)\n\n if (entry.isDirectory()) {\n const subDir = await scanDirectory(entryPath, entry.name)\n children.push(subDir)\n } else if (entry.isFile() && entry.name.endsWith('.md')) {\n const file = await parseMarkdownFile(entryPath, entry.name)\n \n if (entry.name === 'SKILL.md') {\n skillFile = file\n } else {\n children.push(file)\n }\n }\n }\n\n return {\n path: dirPath,\n name,\n children,\n skillFile\n }\n}\n\nasync function parseMarkdownFile(filePath: string, fileName: string): Promise<SkillFile> {\n const content = await fs.readFile(filePath, 'utf-8')\n const stats = await fs.stat(filePath)\n\n // Basic frontmatter extraction\n const frontmatterMatch = content.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n/)\n const frontmatter: Record<string, any> = {}\n let markdownContent = content\n\n if (frontmatterMatch) {\n // Remove frontmatter from content\n markdownContent = content.slice(frontmatterMatch[0].length)\n \n // Parse frontmatter key-value pairs\n const fmLines = frontmatterMatch[1].split(/\\r?\\n/)\n for (const line of fmLines) {\n const colonIndex = line.indexOf(':')\n if (colonIndex > 0) {\n const key = line.slice(0, colonIndex).trim()\n const value = line.slice(colonIndex + 1).trim().replace(/^[\"']|[\"']$/g, '')\n if (key && value) {\n frontmatter[key] = value\n }\n }\n }\n }\n\n return {\n path: filePath,\n name: fileName,\n title: frontmatter.title || fileName.replace('.md', ''),\n description: frontmatter.description,\n content: markdownContent,\n frontmatter,\n lastModified: stats.mtime.getTime()\n }\n}\n\nexport async function watchSkillsDirectory(\n sourceDir: string, \n callback: (directories: SkillDirectory[]) => void\n) {\n const chokidar = await import('chokidar')\n \n const watcher = chokidar.watch(sourceDir, {\n ignored: /node_modules/,\n persistent: true,\n ignoreInitial: true\n })\n\n let debounceTimer: NodeJS.Timeout\n\n const handleChange = () => {\n clearTimeout(debounceTimer)\n debounceTimer = setTimeout(async () => {\n try {\n const directories = await scanSkillsDirectory(sourceDir)\n callback(directories)\n } catch (error) {\n console.error('Error rescanning source directory:', error)\n }\n }, 300)\n }\n\n watcher.on('add', handleChange)\n watcher.on('change', handleChange)\n watcher.on('unlink', handleChange)\n watcher.on('addDir', handleChange)\n watcher.on('unlinkDir', handleChange)\n\n return watcher\n}","import matter from 'gray-matter'\nimport MarkdownIt from 'markdown-it'\nimport { createHighlighter, Highlighter } from 'shiki'\nimport { SkillDirectory, SkillFile, SiteConfig, DocGenConfig, Navigation } from '../types/index.js'\n\nlet highlighter: Highlighter | null = null\n\nasync function getHighlighter() {\n if (!highlighter) {\n highlighter = await createHighlighter({\n themes: ['github-dark', 'github-light'],\n langs: ['javascript', 'typescript', 'bash', 'shell', 'json', 'html', 'css', 'vue', 'jsx', 'tsx', 'python', 'markdown', 'yaml', 'sql', 'go', 'rust', 'java', 'c', 'cpp']\n })\n }\n return highlighter\n}\n\nconst md = new MarkdownIt({\n html: true,\n linkify: true,\n typographer: true,\n highlight: function (str: string, lang: string) {\n // Synchronous fallback - actual highlighting done in enhanceFileContent\n return `<pre class=\"shiki-pending\" data-lang=\"${lang || 'text'}\"><code>${md.utils.escapeHtml(str)}</code></pre>`\n }\n})\n\nexport async function parseSkillsToConfig(\n directories: SkillDirectory[],\n siteConfig: SiteConfig\n): Promise<DocGenConfig> {\n const files: SkillFile[] = []\n const navigation = await generateNavigation(directories)\n\n // Collect all files for search indexing\n collectAllFiles(directories, files)\n\n // Enhance files with parsed content\n for (const file of files) {\n await enhanceFileContent(file)\n }\n\n return {\n siteConfig,\n navigation,\n files,\n directories\n }\n}\n\nfunction collectAllFiles(items: (SkillDirectory | SkillFile)[], files: SkillFile[]) {\n for (const item of items) {\n if ('content' in item) {\n // It's a SkillFile\n files.push(item)\n } else {\n // It's a SkillDirectory\n if (item.skillFile) {\n files.push(item.skillFile)\n }\n collectAllFiles(item.children, files)\n }\n }\n}\n\nasync function enhanceFileContent(file: SkillFile) {\n try {\n let content = file.content\n\n // Update title and description from frontmatter if available\n file.title = file.frontmatter.title || file.title\n file.description = file.frontmatter.description || file.description\n\n // Remove duplicate h1 if it matches frontmatter title\n if (file.frontmatter.title) {\n // Match first h1 heading in content (may have leading whitespace/newlines)\n const h1Match = content.match(/^\\s*#\\s+(.+)$/m)\n if (h1Match) {\n const h1Text = h1Match[1].trim()\n // If h1 matches title, remove it to avoid duplication\n if (h1Text === file.frontmatter.title) {\n content = content.replace(/^\\s*#\\s+.+\\n*/, '')\n }\n }\n }\n\n // Render markdown to HTML\n let html = md.render(content)\n \n // Apply syntax highlighting with shiki\n html = await highlightCodeBlocks(html)\n file.frontmatter.html = html\n\n // Extract headings for TOC\n const headings = extractHeadings(content)\n file.frontmatter.headings = headings\n\n } catch (error) {\n console.warn(`Failed to enhance content for ${file.path}:`, error)\n try {\n file.frontmatter.html = md.render(file.content)\n } catch (e) {\n console.error(`Failed to render markdown for ${file.path}:`, e)\n }\n }\n}\n\nasync function highlightCodeBlocks(html: string): Promise<string> {\n const hl = await getHighlighter()\n \n // Find all pending shiki code blocks and highlight them\n const codeBlockRegex = /<pre class=\"shiki-pending\" data-lang=\"([^\"]*)\"[^>]*><code>([^]*?)<\\/code><\\/pre>/g\n \n const matches = [...html.matchAll(codeBlockRegex)]\n \n for (const match of matches) {\n const [fullMatch, lang, escapedCode] = match\n // Unescape HTML entities\n const code = escapedCode\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/&/g, '&')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n \n try {\n const validLang = hl.getLoadedLanguages().includes(lang) ? lang : 'text'\n const highlighted = hl.codeToHtml(code, {\n lang: validLang,\n theme: 'github-dark'\n })\n html = html.replace(fullMatch, highlighted)\n } catch (e) {\n // Keep original if highlighting fails\n console.warn(`Failed to highlight ${lang}:`, e)\n }\n }\n \n return html\n}\n\nfunction extractHeadings(content: string) {\n const headings: Array<{ level: number; text: string; anchor: string }> = []\n const lines = content.split('\\n')\n\n for (const line of lines) {\n const match = line.match(/^(#{1,6})\\\\s+(.+)$/)\n if (match) {\n const level = match[1].length\n const text = match[2].trim()\n const anchor = text.toLowerCase()\n .replace(/[^\\\\w\\\\s-]/g, '')\n .replace(/\\\\s+/g, '-')\n .trim()\n\n headings.push({ level, text, anchor })\n }\n }\n\n return headings\n}\n\nasync function generateNavigation(directories: SkillDirectory[]): Promise<Navigation[]> {\n const navigation: Navigation[] = []\n\n for (const dir of directories) {\n const navItem: Navigation = {\n text: dir.skillFile?.title || formatDirName(dir.name),\n link: dir.skillFile ? getFileRoute(dir.skillFile) : undefined\n }\n\n if (dir.children.length > 0) {\n navItem.children = []\n \n for (const child of dir.children) {\n if ('content' in child) {\n // It's a file\n navItem.children.push({\n text: child.title || formatFileName(child.name),\n link: getFileRoute(child)\n })\n } else {\n // It's a subdirectory\n const subNav = await generateNavigation([child])\n navItem.children.push(...subNav)\n }\n }\n }\n\n navigation.push(navItem)\n }\n\n return navigation\n}\n\nfunction getFileRoute(file: SkillFile): string {\n // Import the utility function from client utils\n // For now, use inline implementation\n const filePath = file.path\n \n // Try common patterns\n const patterns = ['my-docs', 'docs', 'documentation', '.claude/skills']\n let relativePath = ''\n \n for (const pattern of patterns) {\n const index = filePath.indexOf(pattern)\n if (index !== -1) {\n relativePath = filePath.slice(index + pattern.length)\n break\n }\n }\n \n if (!relativePath) {\n // Fallback: use last 2 segments\n const segments = filePath.split('/').filter(Boolean)\n if (segments.length >= 2) {\n relativePath = '/' + segments.slice(-2).join('/')\n } else {\n return '/'\n }\n }\n \n const segments = relativePath.split('/').filter(Boolean)\n \n if (segments.length === 0) return '/'\n \n // Remove file extension\n const lastSegment = segments[segments.length - 1]\n if (lastSegment.endsWith('.md')) {\n segments[segments.length - 1] = lastSegment.slice(0, -3)\n }\n \n // SKILL.md and README.md should map to parent directory route\n const finalSegment = segments[segments.length - 1]\n if (finalSegment === 'SKILL' || finalSegment === 'README') {\n segments.pop()\n }\n \n return '/' + segments.join('/')\n}\n\nfunction formatDirName(name: string): string {\n return name.split('-').map(word => \n word.charAt(0).toUpperCase() + word.slice(1)\n ).join(' ')\n}\n\nfunction formatFileName(name: string): string {\n const baseName = name.replace('.md', '')\n return formatDirName(baseName)\n}\n\nexport { md as markdownRenderer }","#!/usr/bin/env node\n\nimport { Command } from 'commander'\nimport { devCommand } from './commands/dev.js'\nimport { buildCommand } from './commands/build.js'\nimport { previewCommand } from './commands/preview.js'\n\nconst program = new Command()\n\nprogram\n .name('docmk')\n .description('CLI tool for generating documentation from any directory')\n .version('1.0.0')\n .argument('[directory]', 'Source directory path (starts dev server)', './docs')\n .option('-p, --port <port>', 'Port to run dev server on', '3000')\n .action((directory, options) => {\n // Default action: start dev server\n devCommand({ dir: directory, port: options.port })\n })\n\nprogram\n .command('dev')\n .description('Start development server')\n .option('-p, --port <port>', 'Port to run dev server on', '3000')\n .option('-d, --dir <directory>', 'Source directory path', './docs')\n .action(devCommand)\n\nprogram\n .command('build')\n .description('Build static documentation site')\n .option('-d, --dir <directory>', 'Source directory path', './docs')\n .option('-o, --output <directory>', 'Output directory', 'dist')\n .action(buildCommand)\n\nprogram\n .command('preview')\n .description('Preview built documentation site')\n .option('-p, --port <port>', 'Port to run preview server on', '4173')\n .option('-o, --output <directory>', 'Built site directory', 'dist')\n .action(previewCommand)\n\nprogram.parse()","import path from 'path'\nimport { createViteDevServer } from '../../builder/vite-dev.js'\nimport { scanSkillsDirectory } from '../../scanner/index.js'\nimport { parseSkillsToConfig } from '../../parser/index.js'\n\ninterface DevOptions {\n port: string\n dir: string\n}\n\nexport async function devCommand(options: DevOptions) {\n console.log('🚀 Starting DocGen development server...')\n \n const sourceDir = path.resolve(process.cwd(), options.dir)\n const port = parseInt(options.port, 10)\n \n try {\n // Check if source directory exists\n console.log(`📁 Scanning source directory: ${sourceDir}`)\n const directories = await scanSkillsDirectory(sourceDir)\n \n console.log(`✅ Found ${directories.length} directories`)\n \n // Parse to config\n const config = await parseSkillsToConfig(directories, {\n title: 'Documentation',\n description: 'Documentation generated from source directory',\n baseUrl: '/',\n skillsDir: sourceDir,\n outputDir: 'dist'\n })\n \n // Start Vite dev server\n const { server, actualPort } = await createViteDevServer({\n port,\n skillsDir: sourceDir,\n config\n })\n\n console.log(`🎉 Dev server running at http://localhost:${actualPort}`)\n \n // Handle graceful shutdown\n process.on('SIGINT', async () => {\n console.log('\\\\n👋 Shutting down dev server...')\n await server.close()\n process.exit(0)\n })\n \n } catch (error) {\n console.error('❌ Failed to start dev server:', error)\n process.exit(1)\n }\n}","import { createServer, ViteDevServer } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\nimport { DocGenConfig } from '../types/index.js'\nimport { scanSkillsDirectory, watchSkillsDirectory } from '../scanner/index.js'\nimport { parseSkillsToConfig } from '../parser/index.js'\n\n// Get the package root directory (where src/client is located)\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\nconst packageRoot = path.resolve(__dirname, '../..')\n\ninterface DevServerOptions {\n port: number\n skillsDir: string\n config: DocGenConfig\n}\n\nexport async function createViteDevServer(options: DevServerOptions): Promise<ViteDevServer> {\n let currentConfig = options.config\n\n const server = await createServer({\n root: path.resolve(packageRoot, 'src/client'),\n server: {\n port: options.port,\n host: true // Listen on all addresses\n },\n publicDir: false, // Disable public dir in dev mode\n plugins: [\n vue(),\n // Custom plugin to inject config and handle API routes\n {\n name: 'docgen-dev',\n configureServer(server) {\n // API endpoint for configuration\n server.middlewares.use('/api/config', (req, res, next) => {\n if (req.method === 'GET') {\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(currentConfig))\n } else {\n next()\n }\n })\n\n // SPA fallback - return index.html for all non-API routes\n return () => {\n server.middlewares.use((req, res, next) => {\n // Skip API routes and vite's own routes\n if (req.url?.startsWith('/api/') || req.url?.startsWith('/@') || req.url?.includes('.html')) {\n return next()\n }\n // For all other routes, serve index.html for SPA routing\n if (req.url && !req.url.includes('.')) {\n req.url = '/index.html'\n }\n next()\n })\n }\n },\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n // Inject lightweight config placeholder\n // Full config will be loaded via API to avoid HTML parsing issues\n const configScript = `\n <script>\n // Config will be loaded from /api/config\n globalThis.__DOCGEN_CONFIG__ = null;\n </script>\n `\n return html.replace('<head>', `<head>${configScript}`)\n }\n }\n }\n ],\n resolve: {\n alias: {\n '@': path.resolve(packageRoot, 'src/client')\n }\n },\n define: {\n __DOCGEN_CONFIG__: JSON.stringify(currentConfig)\n }\n })\n\n // Watch skills directory for changes\n const watcher = await watchSkillsDirectory(options.skillsDir, async (directories) => {\n console.log('📝 Skills directory changed, updating configuration...')\n \n try {\n currentConfig = await parseSkillsToConfig(directories, currentConfig.siteConfig)\n \n // Notify all connected clients to reload\n server.ws.send({\n type: 'full-reload'\n })\n \n console.log('✅ Configuration updated')\n } catch (error) {\n console.error('❌ Failed to update configuration:', error)\n \n // Send error to clients\n server.ws.send({\n type: 'error',\n err: {\n message: `Failed to update configuration: ${error.message}`,\n stack: error.stack\n }\n })\n }\n })\n\n // Start the server\n await server.listen()\n\n // Get actual port (in case requested port was taken)\n const actualPort = server.config.server.port\n\n // Extend server with cleanup method\n const originalClose = server.close.bind(server)\n server.close = async () => {\n await watcher?.close()\n return originalClose()\n }\n\n return { server, actualPort }\n}\n\nexport async function createProductionBuild() {\n // This will be implemented in the builder\n}","import path from 'path'\nimport { buildSite } from '../../builder/index.js'\nimport { scanSkillsDirectory } from '../../scanner/index.js'\nimport { parseSkillsToConfig } from '../../parser/index.js'\n\ninterface BuildOptions {\n dir: string\n output: string\n}\n\nexport async function buildCommand(options: BuildOptions) {\n console.log('🏗️ Building DocGen documentation site...')\n \n const sourceDir = path.resolve(process.cwd(), options.dir)\n const outputDir = path.resolve(process.cwd(), options.output)\n \n try {\n // Scan source directory\n console.log(`📁 Scanning source directory: ${sourceDir}`)\n const directories = await scanSkillsDirectory(sourceDir)\n \n console.log(`✅ Found ${directories.length} directories`)\n \n // Parse to config\n const config = await parseSkillsToConfig(directories, {\n title: 'Documentation',\n description: 'Documentation generated from source directory',\n baseUrl: '/',\n skillsDir: sourceDir,\n outputDir\n })\n \n // Build site\n console.log(`📦 Building to: ${outputDir}`)\n await buildSite({\n input: sourceDir,\n output: outputDir,\n mode: 'production'\n }, config)\n \n console.log('✅ Build completed successfully!')\n console.log(`📂 Built files are in: ${outputDir}`)\n \n } catch (error) {\n console.error('❌ Build failed:', error)\n process.exit(1)\n }\n}","import { build } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\nimport fs from 'fs/promises'\nimport { DocGenConfig, BuildOptions } from '../types/index.js'\n\n// Get the package root directory\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\nconst packageRoot = path.resolve(__dirname, '../..')\n\nexport async function buildSite(options: BuildOptions, config: DocGenConfig) {\n console.log('🏗️ Building documentation site...')\n\n // Ensure output directory exists\n await fs.mkdir(options.output, { recursive: true })\n\n // Write config to a temporary file for the build process\n const configPath = path.join(process.cwd(), 'temp-config.json')\n await fs.writeFile(configPath, JSON.stringify(config, null, 2))\n\n try {\n // Build the client application\n await build({\n root: path.resolve(packageRoot, 'src/client'),\n base: config.siteConfig.baseUrl,\n build: {\n outDir: options.output,\n emptyOutDir: true,\n rollupOptions: {\n input: {\n main: path.resolve(packageRoot, 'src/client/index.html')\n }\n }\n },\n plugins: [\n vue(),\n // Plugin to inject config during build\n {\n name: 'docgen-build',\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n // Encode config as base64 with proper UTF-8 handling\n const configJson = JSON.stringify(config)\n // Use encodeURIComponent to handle UTF-8 properly before base64\n const utf8Encoded = unescape(encodeURIComponent(configJson))\n const configBase64 = Buffer.from(utf8Encoded, 'binary').toString('base64')\n // Decode: atob -> decodeURIComponent(escape()) to restore UTF-8\n const configScript = `<script>globalThis.__DOCGEN_CONFIG__=JSON.parse(decodeURIComponent(escape(atob(\"${configBase64}\"))));</script>`\n return html.replace('</head>', `${configScript}\\n</head>`)\n }\n }\n }\n ],\n resolve: {\n alias: {\n '@': path.resolve(packageRoot, 'src/client')\n }\n },\n define: {\n __DOCGEN_CONFIG__: JSON.stringify(config)\n }\n })\n \n // Generate additional static files\n await generateSitemap(options.output, config)\n await generateRobotsTxt(options.output, config)\n await copyAssets(options.output)\n \n console.log('✅ Build completed successfully!')\n \n } finally {\n // Clean up temporary config file\n try {\n await fs.unlink(configPath)\n } catch {\n // Ignore if file doesn't exist\n }\n }\n}\n\nasync function generateSitemap(outputDir: string, config: DocGenConfig) {\n console.log('📄 Generating sitemap...')\n \n const baseUrl = config.siteConfig.baseUrl.replace(/\\/$/, '')\n const urls: string[] = []\n \n // Add home page\n urls.push(`${baseUrl}/`)\n \n // Add all skill pages\n for (const file of config.files) {\n const route = getFileRoute(file.path, config.siteConfig.skillsDir)\n if (route !== '/') {\n urls.push(`${baseUrl}${route}`)\n }\n }\n \n const sitemap = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n${urls.map(url => ` <url>\n <loc>${url}</loc>\n <lastmod>${new Date().toISOString().split('T')[0]}</lastmod>\n <changefreq>weekly</changefreq>\n <priority>0.8</priority>\n </url>`).join('\\\\n')}\n</urlset>`\n \n await fs.writeFile(path.join(outputDir, 'sitemap.xml'), sitemap)\n}\n\nasync function generateRobotsTxt(outputDir: string, config: DocGenConfig) {\n console.log('🤖 Generating robots.txt...')\n \n const baseUrl = config.siteConfig.baseUrl.replace(/\\/$/, '')\n const robotsTxt = `User-agent: *\nAllow: /\n\nSitemap: ${baseUrl}/sitemap.xml`\n \n await fs.writeFile(path.join(outputDir, 'robots.txt'), robotsTxt)\n}\n\nasync function copyAssets(outputDir: string) {\n console.log('📁 Copying static assets...')\n \n // Create a simple favicon if it doesn't exist\n const faviconPath = path.join(outputDir, 'favicon.ico')\n \n try {\n await fs.access(faviconPath)\n } catch {\n // Create a simple SVG favicon\n const faviconSvg = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\">\n <rect width=\"32\" height=\"32\" rx=\"4\" fill=\"#3182ce\"/>\n <text x=\"16\" y=\"22\" text-anchor=\"middle\" fill=\"white\" font-family=\"system-ui\" font-size=\"18\" font-weight=\"bold\">D</text>\n</svg>`\n \n await fs.writeFile(path.join(outputDir, 'favicon.svg'), faviconSvg)\n }\n}\n\nfunction getFileRoute(filePath: string, baseDir: string): string {\n // Normalize paths for comparison\n const normalizedFilePath = path.normalize(filePath)\n const normalizedBaseDir = path.normalize(baseDir)\n \n // Check if file is within base directory\n if (!normalizedFilePath.startsWith(normalizedBaseDir)) {\n return '/'\n }\n \n // Get relative path from base directory\n const relativePath = normalizedFilePath.slice(normalizedBaseDir.length)\n const segments = relativePath.split(path.sep).filter(Boolean)\n \n if (segments.length === 0) return '/'\n \n const lastSegment = segments[segments.length - 1]\n if (lastSegment.endsWith('.md')) {\n segments[segments.length - 1] = lastSegment.slice(0, -3)\n }\n \n // SKILL.md and README.md should map to parent directory route\n const finalSegment = segments[segments.length - 1]\n if (finalSegment === 'SKILL' || finalSegment === 'README') {\n segments.pop()\n }\n\n return '/' + segments.join('/')\n}\n\nexport async function buildForProduction(sourceDir: string, outputDir: string): Promise<DocGenConfig> {\n const { scanSkillsDirectory } = await import('../scanner/index.js')\n const { parseSkillsToConfig } = await import('../parser/index.js')\n \n const directories = await scanSkillsDirectory(sourceDir)\n const config = await parseSkillsToConfig(directories, {\n title: 'Documentation',\n description: 'Documentation generated from source directory',\n baseUrl: '/',\n skillsDir: sourceDir,\n outputDir\n })\n \n await buildSite({\n input: sourceDir,\n output: outputDir,\n mode: 'production'\n }, config)\n \n return config\n}","import path from 'path'\nimport fs from 'fs/promises'\nimport sirv from 'sirv'\nimport { createServer } from 'http'\n\ninterface PreviewOptions {\n port: string\n output: string\n}\n\nexport async function previewCommand(options: PreviewOptions) {\n console.log('👀 Starting DocGen preview server...')\n \n const outputDir = path.resolve(process.cwd(), options.output)\n const port = parseInt(options.port, 10)\n \n try {\n // Check if build directory exists\n try {\n await fs.access(outputDir)\n } catch {\n console.error(`❌ Build directory not found: ${outputDir}`)\n console.log('💡 Run \"docgen build\" first to generate the static site')\n process.exit(1)\n }\n \n // Create static file server\n const serve = sirv(outputDir, {\n single: true, // SPA mode\n dev: false,\n setHeaders: (res, pathname) => {\n if (pathname.endsWith('.html') || pathname === '/') {\n res.setHeader('Content-Type', 'text/html; charset=utf-8')\n }\n }\n })\n \n const server = createServer(serve)\n \n server.listen(port, () => {\n console.log(`🎉 Preview server running at http://localhost:${port}`)\n console.log(`📂 Serving files from: ${outputDir}`)\n })\n \n // Handle graceful shutdown\n process.on('SIGINT', () => {\n console.log('\\\\n👋 Shutting down preview server...')\n server.close(() => {\n process.exit(0)\n })\n })\n \n } catch (error) {\n console.error('❌ Failed to start preview server:', error)\n process.exit(1)\n }\n}"],"mappings":";;;;;;;AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AAGjB,eAAsB,oBAAoB,WAA8C;AACtF,MAAI;AACF,UAAM,GAAG,OAAO,SAAS;AAAA,EAC3B,QAAQ;AACN,UAAM,IAAI,MAAM,+BAA+B,SAAS,EAAE;AAAA,EAC5D;AAEA,QAAM,UAAU,MAAM,GAAG,QAAQ,WAAW,EAAE,eAAe,KAAK,CAAC;AACnE,QAAM,cAAgC,CAAC;AAEvC,aAAW,SAAS,SAAS;AAC3B,QAAI,MAAM,YAAY,GAAG;AACvB,YAAM,UAAU,KAAK,KAAK,WAAW,MAAM,IAAI;AAC/C,YAAM,iBAAiB,MAAM,cAAc,SAAS,MAAM,IAAI;AAC9D,kBAAY,KAAK,cAAc;AAAA,IACjC;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAe,cAAc,SAAiB,MAAuC;AACnF,QAAM,UAAU,MAAM,GAAG,QAAQ,SAAS,EAAE,eAAe,KAAK,CAAC;AACjE,QAAM,WAA2C,CAAC;AAClD,MAAI;AAEJ,aAAW,SAAS,SAAS;AAC3B,UAAM,YAAY,KAAK,KAAK,SAAS,MAAM,IAAI;AAE/C,QAAI,MAAM,YAAY,GAAG;AACvB,YAAM,SAAS,MAAM,cAAc,WAAW,MAAM,IAAI;AACxD,eAAS,KAAK,MAAM;AAAA,IACtB,WAAW,MAAM,OAAO,KAAK,MAAM,KAAK,SAAS,KAAK,GAAG;AACvD,YAAM,OAAO,MAAM,kBAAkB,WAAW,MAAM,IAAI;AAE1D,UAAI,MAAM,SAAS,YAAY;AAC7B,oBAAY;AAAA,MACd,OAAO;AACL,iBAAS,KAAK,IAAI;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,eAAe,kBAAkB,UAAkB,UAAsC;AACvF,QAAM,UAAU,MAAM,GAAG,SAAS,UAAU,OAAO;AACnD,QAAM,QAAQ,MAAM,GAAG,KAAK,QAAQ;AAGpC,QAAM,mBAAmB,QAAQ,MAAM,kCAAkC;AACzE,QAAM,cAAmC,CAAC;AAC1C,MAAI,kBAAkB;AAEtB,MAAI,kBAAkB;AAEpB,sBAAkB,QAAQ,MAAM,iBAAiB,CAAC,EAAE,MAAM;AAG1D,UAAM,UAAU,iBAAiB,CAAC,EAAE,MAAM,OAAO;AACjD,eAAW,QAAQ,SAAS;AAC1B,YAAM,aAAa,KAAK,QAAQ,GAAG;AACnC,UAAI,aAAa,GAAG;AAClB,cAAM,MAAM,KAAK,MAAM,GAAG,UAAU,EAAE,KAAK;AAC3C,cAAM,QAAQ,KAAK,MAAM,aAAa,CAAC,EAAE,KAAK,EAAE,QAAQ,gBAAgB,EAAE;AAC1E,YAAI,OAAO,OAAO;AAChB,sBAAY,GAAG,IAAI;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO,YAAY,SAAS,SAAS,QAAQ,OAAO,EAAE;AAAA,IACtD,aAAa,YAAY;AAAA,IACzB,SAAS;AAAA,IACT;AAAA,IACA,cAAc,MAAM,MAAM,QAAQ;AAAA,EACpC;AACF;AAEA,eAAsB,qBACpB,WACA,UACA;AACA,QAAM,WAAW,MAAM,OAAO,UAAU;AAExC,QAAM,UAAU,SAAS,MAAM,WAAW;AAAA,IACxC,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,eAAe;AAAA,EACjB,CAAC;AAED,MAAI;AAEJ,QAAM,eAAe,MAAM;AACzB,iBAAa,aAAa;AAC1B,oBAAgB,WAAW,YAAY;AACrC,UAAI;AACF,cAAM,cAAc,MAAM,oBAAoB,SAAS;AACvD,iBAAS,WAAW;AAAA,MACtB,SAAS,OAAO;AACd,gBAAQ,MAAM,sCAAsC,KAAK;AAAA,MAC3D;AAAA,IACF,GAAG,GAAG;AAAA,EACR;AAEA,UAAQ,GAAG,OAAO,YAAY;AAC9B,UAAQ,GAAG,UAAU,YAAY;AACjC,UAAQ,GAAG,UAAU,YAAY;AACjC,UAAQ,GAAG,UAAU,YAAY;AACjC,UAAQ,GAAG,aAAa,YAAY;AAEpC,SAAO;AACT;AA9HA;AAAA;AAAA;AAAA;AAAA;;;ACCA,OAAO,gBAAgB;AACvB,SAAS,yBAAsC;AAK/C,eAAe,iBAAiB;AAC9B,MAAI,CAAC,aAAa;AAChB,kBAAc,MAAM,kBAAkB;AAAA,MACpC,QAAQ,CAAC,eAAe,cAAc;AAAA,MACtC,OAAO,CAAC,cAAc,cAAc,QAAQ,SAAS,QAAQ,QAAQ,OAAO,OAAO,OAAO,OAAO,UAAU,YAAY,QAAQ,OAAO,MAAM,QAAQ,QAAQ,KAAK,KAAK;AAAA,IACxK,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAYA,eAAsB,oBACpB,aACA,YACuB;AACvB,QAAM,QAAqB,CAAC;AAC5B,QAAM,aAAa,MAAM,mBAAmB,WAAW;AAGvD,kBAAgB,aAAa,KAAK;AAGlC,aAAW,QAAQ,OAAO;AACxB,UAAM,mBAAmB,IAAI;AAAA,EAC/B;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,OAAuC,OAAoB;AAClF,aAAW,QAAQ,OAAO;AACxB,QAAI,aAAa,MAAM;AAErB,YAAM,KAAK,IAAI;AAAA,IACjB,OAAO;AAEL,UAAI,KAAK,WAAW;AAClB,cAAM,KAAK,KAAK,SAAS;AAAA,MAC3B;AACA,sBAAgB,KAAK,UAAU,KAAK;AAAA,IACtC;AAAA,EACF;AACF;AAEA,eAAe,mBAAmB,MAAiB;AACjD,MAAI;AACF,QAAI,UAAU,KAAK;AAGnB,SAAK,QAAQ,KAAK,YAAY,SAAS,KAAK;AAC5C,SAAK,cAAc,KAAK,YAAY,eAAe,KAAK;AAGxD,QAAI,KAAK,YAAY,OAAO;AAE1B,YAAM,UAAU,QAAQ,MAAM,gBAAgB;AAC9C,UAAI,SAAS;AACX,cAAM,SAAS,QAAQ,CAAC,EAAE,KAAK;AAE/B,YAAI,WAAW,KAAK,YAAY,OAAO;AACrC,oBAAU,QAAQ,QAAQ,iBAAiB,EAAE;AAAA,QAC/C;AAAA,MACF;AAAA,IACF;AAGA,QAAI,OAAO,GAAG,OAAO,OAAO;AAG5B,WAAO,MAAM,oBAAoB,IAAI;AACrC,SAAK,YAAY,OAAO;AAGxB,UAAM,WAAW,gBAAgB,OAAO;AACxC,SAAK,YAAY,WAAW;AAAA,EAE9B,SAAS,OAAO;AACd,YAAQ,KAAK,iCAAiC,KAAK,IAAI,KAAK,KAAK;AACjE,QAAI;AACF,WAAK,YAAY,OAAO,GAAG,OAAO,KAAK,OAAO;AAAA,IAChD,SAAS,GAAG;AACV,cAAQ,MAAM,iCAAiC,KAAK,IAAI,KAAK,CAAC;AAAA,IAChE;AAAA,EACF;AACF;AAEA,eAAe,oBAAoB,MAA+B;AAChE,QAAM,KAAK,MAAM,eAAe;AAGhC,QAAM,iBAAiB;AAEvB,QAAM,UAAU,CAAC,GAAG,KAAK,SAAS,cAAc,CAAC;AAEjD,aAAW,SAAS,SAAS;AAC3B,UAAM,CAAC,WAAW,MAAM,WAAW,IAAI;AAEvC,UAAM,OAAO,YACV,QAAQ,SAAS,GAAG,EACpB,QAAQ,SAAS,GAAG,EACpB,QAAQ,UAAU,GAAG,EACrB,QAAQ,WAAW,GAAG,EACtB,QAAQ,UAAU,GAAG;AAExB,QAAI;AACF,YAAM,YAAY,GAAG,mBAAmB,EAAE,SAAS,IAAI,IAAI,OAAO;AAClE,YAAM,cAAc,GAAG,WAAW,MAAM;AAAA,QACtC,MAAM;AAAA,QACN,OAAO;AAAA,MACT,CAAC;AACD,aAAO,KAAK,QAAQ,WAAW,WAAW;AAAA,IAC5C,SAAS,GAAG;AAEV,cAAQ,KAAK,uBAAuB,IAAI,KAAK,CAAC;AAAA,IAChD;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,gBAAgB,SAAiB;AACxC,QAAM,WAAmE,CAAC;AAC1E,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAEhC,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,KAAK,MAAM,oBAAoB;AAC7C,QAAI,OAAO;AACT,YAAM,QAAQ,MAAM,CAAC,EAAE;AACvB,YAAM,OAAO,MAAM,CAAC,EAAE,KAAK;AAC3B,YAAM,SAAS,KAAK,YAAY,EAC7B,QAAQ,eAAe,EAAE,EACzB,QAAQ,SAAS,GAAG,EACpB,KAAK;AAER,eAAS,KAAK,EAAE,OAAO,MAAM,OAAO,CAAC;AAAA,IACvC;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAe,mBAAmB,aAAsD;AACtF,QAAM,aAA2B,CAAC;AAElC,aAAW,OAAO,aAAa;AAC7B,UAAM,UAAsB;AAAA,MAC1B,MAAM,IAAI,WAAW,SAAS,cAAc,IAAI,IAAI;AAAA,MACpD,MAAM,IAAI,YAAY,aAAa,IAAI,SAAS,IAAI;AAAA,IACtD;AAEA,QAAI,IAAI,SAAS,SAAS,GAAG;AAC3B,cAAQ,WAAW,CAAC;AAEpB,iBAAW,SAAS,IAAI,UAAU;AAChC,YAAI,aAAa,OAAO;AAEtB,kBAAQ,SAAS,KAAK;AAAA,YACpB,MAAM,MAAM,SAAS,eAAe,MAAM,IAAI;AAAA,YAC9C,MAAM,aAAa,KAAK;AAAA,UAC1B,CAAC;AAAA,QACH,OAAO;AAEL,gBAAM,SAAS,MAAM,mBAAmB,CAAC,KAAK,CAAC;AAC/C,kBAAQ,SAAS,KAAK,GAAG,MAAM;AAAA,QACjC;AAAA,MACF;AAAA,IACF;AAEA,eAAW,KAAK,OAAO;AAAA,EACzB;AAEA,SAAO;AACT;AAEA,SAAS,aAAa,MAAyB;AAG7C,QAAM,WAAW,KAAK;AAGtB,QAAM,WAAW,CAAC,WAAW,QAAQ,iBAAiB,gBAAgB;AACtE,MAAI,eAAe;AAEnB,aAAW,WAAW,UAAU;AAC9B,UAAM,QAAQ,SAAS,QAAQ,OAAO;AACtC,QAAI,UAAU,IAAI;AAChB,qBAAe,SAAS,MAAM,QAAQ,QAAQ,MAAM;AACpD;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,cAAc;AAEjB,UAAMA,YAAW,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO;AACnD,QAAIA,UAAS,UAAU,GAAG;AACxB,qBAAe,MAAMA,UAAS,MAAM,EAAE,EAAE,KAAK,GAAG;AAAA,IAClD,OAAO;AACL,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,WAAW,aAAa,MAAM,GAAG,EAAE,OAAO,OAAO;AAEvD,MAAI,SAAS,WAAW,EAAG,QAAO;AAGlC,QAAM,cAAc,SAAS,SAAS,SAAS,CAAC;AAChD,MAAI,YAAY,SAAS,KAAK,GAAG;AAC/B,aAAS,SAAS,SAAS,CAAC,IAAI,YAAY,MAAM,GAAG,EAAE;AAAA,EACzD;AAGA,QAAM,eAAe,SAAS,SAAS,SAAS,CAAC;AACjD,MAAI,iBAAiB,WAAW,iBAAiB,UAAU;AACzD,aAAS,IAAI;AAAA,EACf;AAEA,SAAO,MAAM,SAAS,KAAK,GAAG;AAChC;AAEA,SAAS,cAAc,MAAsB;AAC3C,SAAO,KAAK,MAAM,GAAG,EAAE;AAAA,IAAI,UACzB,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC;AAAA,EAC7C,EAAE,KAAK,GAAG;AACZ;AAEA,SAAS,eAAe,MAAsB;AAC5C,QAAM,WAAW,KAAK,QAAQ,OAAO,EAAE;AACvC,SAAO,cAAc,QAAQ;AAC/B;AA1PA,IAKI,aAYE;AAjBN;AAAA;AAAA;AAKA,IAAI,cAAkC;AAYtC,IAAM,KAAK,IAAI,WAAW;AAAA,MACxB,MAAM;AAAA,MACN,SAAS;AAAA,MACT,aAAa;AAAA,MACb,WAAW,SAAU,KAAa,MAAc;AAE9C,eAAO,yCAAyC,QAAQ,MAAM,WAAW,GAAG,MAAM,WAAW,GAAG,CAAC;AAAA,MACnG;AAAA,IACF,CAAC;AAAA;AAAA;;;ACvBD,SAAS,eAAe;;;ACFxB,OAAOC,WAAU;;;ACKjB;AACA;AANA,SAAS,oBAAmC;AAC5C,OAAO,SAAS;AAChB,OAAOC,WAAU;AACjB,SAAS,qBAAqB;AAM9B,IAAM,YAAYA,MAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAC7D,IAAM,cAAcA,MAAK,QAAQ,WAAW,OAAO;AAQnD,eAAsB,oBAAoB,SAAmD;AAC3F,MAAI,gBAAgB,QAAQ;AAE5B,QAAM,SAAS,MAAM,aAAa;AAAA,IAChC,MAAMA,MAAK,QAAQ,aAAa,YAAY;AAAA,IAC5C,QAAQ;AAAA,MACN,MAAM,QAAQ;AAAA,MACd,MAAM;AAAA;AAAA,IACR;AAAA,IACA,WAAW;AAAA;AAAA,IACX,SAAS;AAAA,MACP,IAAI;AAAA;AAAA,MAEJ;AAAA,QACE,MAAM;AAAA,QACN,gBAAgBC,SAAQ;AAEtB,UAAAA,QAAO,YAAY,IAAI,eAAe,CAAC,KAAK,KAAK,SAAS;AACxD,gBAAI,IAAI,WAAW,OAAO;AACxB,kBAAI,UAAU,gBAAgB,kBAAkB;AAChD,kBAAI,IAAI,KAAK,UAAU,aAAa,CAAC;AAAA,YACvC,OAAO;AACL,mBAAK;AAAA,YACP;AAAA,UACF,CAAC;AAGD,iBAAO,MAAM;AACX,YAAAA,QAAO,YAAY,IAAI,CAAC,KAAK,KAAK,SAAS;AAEzC,kBAAI,IAAI,KAAK,WAAW,OAAO,KAAK,IAAI,KAAK,WAAW,IAAI,KAAK,IAAI,KAAK,SAAS,OAAO,GAAG;AAC3F,uBAAO,KAAK;AAAA,cACd;AAEA,kBAAI,IAAI,OAAO,CAAC,IAAI,IAAI,SAAS,GAAG,GAAG;AACrC,oBAAI,MAAM;AAAA,cACZ;AACA,mBAAK;AAAA,YACP,CAAC;AAAA,UACH;AAAA,QACF;AAAA,QACA,oBAAoB;AAAA,UAClB,OAAO;AAAA,UACP,QAAQ,MAAM;AAGZ,kBAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAMrB,mBAAO,KAAK,QAAQ,UAAU,SAAS,YAAY,EAAE;AAAA,UACvD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,SAAS;AAAA,MACP,OAAO;AAAA,QACL,KAAKD,MAAK,QAAQ,aAAa,YAAY;AAAA,MAC7C;AAAA,IACF;AAAA,IACA,QAAQ;AAAA,MACN,mBAAmB,KAAK,UAAU,aAAa;AAAA,IACjD;AAAA,EACF,CAAC;AAGD,QAAM,UAAU,MAAM,qBAAqB,QAAQ,WAAW,OAAO,gBAAgB;AACnF,YAAQ,IAAI,+DAAwD;AAEpE,QAAI;AACF,sBAAgB,MAAM,oBAAoB,aAAa,cAAc,UAAU;AAG/E,aAAO,GAAG,KAAK;AAAA,QACb,MAAM;AAAA,MACR,CAAC;AAED,cAAQ,IAAI,8BAAyB;AAAA,IACvC,SAAS,OAAO;AACd,cAAQ,MAAM,0CAAqC,KAAK;AAGxD,aAAO,GAAG,KAAK;AAAA,QACb,MAAM;AAAA,QACN,KAAK;AAAA,UACH,SAAS,mCAAmC,MAAM,OAAO;AAAA,UACzD,OAAO,MAAM;AAAA,QACf;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAGD,QAAM,OAAO,OAAO;AAGpB,QAAM,aAAa,OAAO,OAAO,OAAO;AAGxC,QAAM,gBAAgB,OAAO,MAAM,KAAK,MAAM;AAC9C,SAAO,QAAQ,YAAY;AACzB,UAAM,SAAS,MAAM;AACrB,WAAO,cAAc;AAAA,EACvB;AAEA,SAAO,EAAE,QAAQ,WAAW;AAC9B;;;AD5HA;AACA;AAOA,eAAsB,WAAW,SAAqB;AACpD,UAAQ,IAAI,iDAA0C;AAEtD,QAAM,YAAYE,MAAK,QAAQ,QAAQ,IAAI,GAAG,QAAQ,GAAG;AACzD,QAAM,OAAO,SAAS,QAAQ,MAAM,EAAE;AAEtC,MAAI;AAEF,YAAQ,IAAI,wCAAiC,SAAS,EAAE;AACxD,UAAM,cAAc,MAAM,oBAAoB,SAAS;AAEvD,YAAQ,IAAI,gBAAW,YAAY,MAAM,cAAc;AAGvD,UAAM,SAAS,MAAM,oBAAoB,aAAa;AAAA,MACpD,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS;AAAA,MACT,WAAW;AAAA,MACX,WAAW;AAAA,IACb,CAAC;AAGD,UAAM,EAAE,QAAQ,WAAW,IAAI,MAAM,oBAAoB;AAAA,MACvD;AAAA,MACA,WAAW;AAAA,MACX;AAAA,IACF,CAAC;AAED,YAAQ,IAAI,oDAA6C,UAAU,EAAE;AAGrE,YAAQ,GAAG,UAAU,YAAY;AAC/B,cAAQ,IAAI,0CAAmC;AAC/C,YAAM,OAAO,MAAM;AACnB,cAAQ,KAAK,CAAC;AAAA,IAChB,CAAC;AAAA,EAEH,SAAS,OAAO;AACd,YAAQ,MAAM,sCAAiC,KAAK;AACpD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;AEpDA,OAAOC,WAAU;;;ACAjB,SAAS,aAAa;AACtB,OAAOC,UAAS;AAChB,OAAOC,WAAU;AACjB,SAAS,iBAAAC,sBAAqB;AAC9B,OAAOC,SAAQ;AAIf,IAAMC,aAAYH,MAAK,QAAQC,eAAc,YAAY,GAAG,CAAC;AAC7D,IAAMG,eAAcJ,MAAK,QAAQG,YAAW,OAAO;AAEnD,eAAsB,UAAU,SAAuB,QAAsB;AAC3E,UAAQ,IAAI,iDAAqC;AAGjD,QAAMD,IAAG,MAAM,QAAQ,QAAQ,EAAE,WAAW,KAAK,CAAC;AAGlD,QAAM,aAAaF,MAAK,KAAK,QAAQ,IAAI,GAAG,kBAAkB;AAC9D,QAAME,IAAG,UAAU,YAAY,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAE9D,MAAI;AAEF,UAAM,MAAM;AAAA,MACV,MAAMF,MAAK,QAAQI,cAAa,YAAY;AAAA,MAC5C,MAAM,OAAO,WAAW;AAAA,MACxB,OAAO;AAAA,QACL,QAAQ,QAAQ;AAAA,QAChB,aAAa;AAAA,QACb,eAAe;AAAA,UACb,OAAO;AAAA,YACL,MAAMJ,MAAK,QAAQI,cAAa,uBAAuB;AAAA,UACzD;AAAA,QACF;AAAA,MACF;AAAA,MACA,SAAS;AAAA,QACPL,KAAI;AAAA;AAAA,QAEJ;AAAA,UACE,MAAM;AAAA,UACN,oBAAoB;AAAA,YAClB,OAAO;AAAA,YACP,QAAQ,MAAM;AAEZ,oBAAM,aAAa,KAAK,UAAU,MAAM;AAExC,oBAAM,cAAc,SAAS,mBAAmB,UAAU,CAAC;AAC3D,oBAAM,eAAe,OAAO,KAAK,aAAa,QAAQ,EAAE,SAAS,QAAQ;AAEzE,oBAAM,eAAe,mFAAmF,YAAY;AACpH,qBAAO,KAAK,QAAQ,WAAW,GAAG,YAAY;AAAA,QAAW;AAAA,YAC3D;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,SAAS;AAAA,QACP,OAAO;AAAA,UACL,KAAKC,MAAK,QAAQI,cAAa,YAAY;AAAA,QAC7C;AAAA,MACF;AAAA,MACA,QAAQ;AAAA,QACN,mBAAmB,KAAK,UAAU,MAAM;AAAA,MAC1C;AAAA,IACF,CAAC;AAGD,UAAM,gBAAgB,QAAQ,QAAQ,MAAM;AAC5C,UAAM,kBAAkB,QAAQ,QAAQ,MAAM;AAC9C,UAAM,WAAW,QAAQ,MAAM;AAE/B,YAAQ,IAAI,sCAAiC;AAAA,EAE/C,UAAE;AAEA,QAAI;AACF,YAAMF,IAAG,OAAO,UAAU;AAAA,IAC5B,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAEA,eAAe,gBAAgB,WAAmB,QAAsB;AACtE,UAAQ,IAAI,iCAA0B;AAEtC,QAAM,UAAU,OAAO,WAAW,QAAQ,QAAQ,OAAO,EAAE;AAC3D,QAAM,OAAiB,CAAC;AAGxB,OAAK,KAAK,GAAG,OAAO,GAAG;AAGvB,aAAW,QAAQ,OAAO,OAAO;AAC/B,UAAM,QAAQG,cAAa,KAAK,MAAM,OAAO,WAAW,SAAS;AACjE,QAAI,UAAU,KAAK;AACjB,WAAK,KAAK,GAAG,OAAO,GAAG,KAAK,EAAE;AAAA,IAChC;AAAA,EACF;AAEA,QAAM,UAAU;AAAA;AAAA,EAEhB,KAAK,IAAI,SAAO;AAAA,WACP,GAAG;AAAA,gBACC,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC;AAAA;AAAA;AAAA,SAG5C,EAAE,KAAK,KAAK,CAAC;AAAA;AAGpB,QAAMH,IAAG,UAAUF,MAAK,KAAK,WAAW,aAAa,GAAG,OAAO;AACjE;AAEA,eAAe,kBAAkB,WAAmB,QAAsB;AACxE,UAAQ,IAAI,oCAA6B;AAEzC,QAAM,UAAU,OAAO,WAAW,QAAQ,QAAQ,OAAO,EAAE;AAC3D,QAAM,YAAY;AAAA;AAAA;AAAA,WAGT,OAAO;AAEhB,QAAME,IAAG,UAAUF,MAAK,KAAK,WAAW,YAAY,GAAG,SAAS;AAClE;AAEA,eAAe,WAAW,WAAmB;AAC3C,UAAQ,IAAI,oCAA6B;AAGzC,QAAM,cAAcA,MAAK,KAAK,WAAW,aAAa;AAEtD,MAAI;AACF,UAAME,IAAG,OAAO,WAAW;AAAA,EAC7B,QAAQ;AAEN,UAAM,aAAa;AAAA;AAAA;AAAA;AAKnB,UAAMA,IAAG,UAAUF,MAAK,KAAK,WAAW,aAAa,GAAG,UAAU;AAAA,EACpE;AACF;AAEA,SAASK,cAAa,UAAkB,SAAyB;AAE/D,QAAM,qBAAqBL,MAAK,UAAU,QAAQ;AAClD,QAAM,oBAAoBA,MAAK,UAAU,OAAO;AAGhD,MAAI,CAAC,mBAAmB,WAAW,iBAAiB,GAAG;AACrD,WAAO;AAAA,EACT;AAGA,QAAM,eAAe,mBAAmB,MAAM,kBAAkB,MAAM;AACtE,QAAM,WAAW,aAAa,MAAMA,MAAK,GAAG,EAAE,OAAO,OAAO;AAE5D,MAAI,SAAS,WAAW,EAAG,QAAO;AAElC,QAAM,cAAc,SAAS,SAAS,SAAS,CAAC;AAChD,MAAI,YAAY,SAAS,KAAK,GAAG;AAC/B,aAAS,SAAS,SAAS,CAAC,IAAI,YAAY,MAAM,GAAG,EAAE;AAAA,EACzD;AAGA,QAAM,eAAe,SAAS,SAAS,SAAS,CAAC;AACjD,MAAI,iBAAiB,WAAW,iBAAiB,UAAU;AACzD,aAAS,IAAI;AAAA,EACf;AAEA,SAAO,MAAM,SAAS,KAAK,GAAG;AAChC;;;ADzKA;AACA;AAOA,eAAsB,aAAa,SAAuB;AACxD,UAAQ,IAAI,wDAA4C;AAExD,QAAM,YAAYM,MAAK,QAAQ,QAAQ,IAAI,GAAG,QAAQ,GAAG;AACzD,QAAM,YAAYA,MAAK,QAAQ,QAAQ,IAAI,GAAG,QAAQ,MAAM;AAE5D,MAAI;AAEF,YAAQ,IAAI,wCAAiC,SAAS,EAAE;AACxD,UAAM,cAAc,MAAM,oBAAoB,SAAS;AAEvD,YAAQ,IAAI,gBAAW,YAAY,MAAM,cAAc;AAGvD,UAAM,SAAS,MAAM,oBAAoB,aAAa;AAAA,MACpD,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS;AAAA,MACT,WAAW;AAAA,MACX;AAAA,IACF,CAAC;AAGD,YAAQ,IAAI,0BAAmB,SAAS,EAAE;AAC1C,UAAM,UAAU;AAAA,MACd,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,MAAM;AAAA,IACR,GAAG,MAAM;AAET,YAAQ,IAAI,sCAAiC;AAC7C,YAAQ,IAAI,iCAA0B,SAAS,EAAE;AAAA,EAEnD,SAAS,OAAO;AACd,YAAQ,MAAM,wBAAmB,KAAK;AACtC,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;AE/CA,OAAOC,WAAU;AACjB,OAAOC,SAAQ;AACf,OAAO,UAAU;AACjB,SAAS,gBAAAC,qBAAoB;AAO7B,eAAsB,eAAe,SAAyB;AAC5D,UAAQ,IAAI,6CAAsC;AAElD,QAAM,YAAYF,MAAK,QAAQ,QAAQ,IAAI,GAAG,QAAQ,MAAM;AAC5D,QAAM,OAAO,SAAS,QAAQ,MAAM,EAAE;AAEtC,MAAI;AAEF,QAAI;AACF,YAAMC,IAAG,OAAO,SAAS;AAAA,IAC3B,QAAQ;AACN,cAAQ,MAAM,qCAAgC,SAAS,EAAE;AACzD,cAAQ,IAAI,gEAAyD;AACrE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,UAAM,QAAQ,KAAK,WAAW;AAAA,MAC5B,QAAQ;AAAA;AAAA,MACR,KAAK;AAAA,MACL,YAAY,CAAC,KAAK,aAAa;AAC7B,YAAI,SAAS,SAAS,OAAO,KAAK,aAAa,KAAK;AAClD,cAAI,UAAU,gBAAgB,0BAA0B;AAAA,QAC1D;AAAA,MACF;AAAA,IACF,CAAC;AAED,UAAM,SAASC,cAAa,KAAK;AAEjC,WAAO,OAAO,MAAM,MAAM;AACxB,cAAQ,IAAI,wDAAiD,IAAI,EAAE;AACnE,cAAQ,IAAI,iCAA0B,SAAS,EAAE;AAAA,IACnD,CAAC;AAGD,YAAQ,GAAG,UAAU,MAAM;AACzB,cAAQ,IAAI,8CAAuC;AACnD,aAAO,MAAM,MAAM;AACjB,gBAAQ,KAAK,CAAC;AAAA,MAChB,CAAC;AAAA,IACH,CAAC;AAAA,EAEH,SAAS,OAAO;AACd,YAAQ,MAAM,0CAAqC,KAAK;AACxD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;ALjDA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,OAAO,EACZ,YAAY,0DAA0D,EACtE,QAAQ,OAAO,EACf,SAAS,eAAe,6CAA6C,QAAQ,EAC7E,OAAO,qBAAqB,6BAA6B,MAAM,EAC/D,OAAO,CAAC,WAAW,YAAY;AAE9B,aAAW,EAAE,KAAK,WAAW,MAAM,QAAQ,KAAK,CAAC;AACnD,CAAC;AAEH,QACG,QAAQ,KAAK,EACb,YAAY,0BAA0B,EACtC,OAAO,qBAAqB,6BAA6B,MAAM,EAC/D,OAAO,yBAAyB,yBAAyB,QAAQ,EACjE,OAAO,UAAU;AAEpB,QACG,QAAQ,OAAO,EACf,YAAY,iCAAiC,EAC7C,OAAO,yBAAyB,yBAAyB,QAAQ,EACjE,OAAO,4BAA4B,oBAAoB,MAAM,EAC7D,OAAO,YAAY;AAEtB,QACG,QAAQ,SAAS,EACjB,YAAY,kCAAkC,EAC9C,OAAO,qBAAqB,iCAAiC,MAAM,EACnE,OAAO,4BAA4B,wBAAwB,MAAM,EACjE,OAAO,cAAc;AAExB,QAAQ,MAAM;","names":["segments","path","path","server","path","path","vue","path","fileURLToPath","fs","__dirname","packageRoot","getFileRoute","path","path","fs","createServer"]}
|
|
1
|
+
{"version":3,"sources":["../src/scanner/index.ts","../src/parser/index.ts","../src/cli/index.ts","../src/cli/commands/dev.ts","../src/builder/vite-dev.ts","../src/utils/port.ts","../src/cli/commands/build.ts","../src/builder/index.ts","../src/cli/commands/preview.ts"],"sourcesContent":["import fs from 'fs/promises'\nimport path from 'path'\nimport { SkillDirectory, SkillFile } from '../types/index.js'\n\nexport async function scanSkillsDirectory(sourceDir: string): Promise<SkillDirectory[]> {\n try {\n await fs.access(sourceDir)\n } catch {\n throw new Error(`Source directory not found: ${sourceDir}`)\n }\n\n const entries = await fs.readdir(sourceDir, { withFileTypes: true })\n const directories: SkillDirectory[] = []\n\n for (const entry of entries) {\n if (entry.isDirectory()) {\n const dirPath = path.join(sourceDir, entry.name)\n const skillDirectory = await scanDirectory(dirPath, entry.name)\n directories.push(skillDirectory)\n }\n }\n\n return directories\n}\n\nasync function scanDirectory(dirPath: string, name: string): Promise<SkillDirectory> {\n const entries = await fs.readdir(dirPath, { withFileTypes: true })\n const children: (SkillDirectory | SkillFile)[] = []\n let skillFile: SkillFile | undefined\n\n for (const entry of entries) {\n const entryPath = path.join(dirPath, entry.name)\n\n if (entry.isDirectory()) {\n const subDir = await scanDirectory(entryPath, entry.name)\n children.push(subDir)\n } else if (entry.isFile() && entry.name.endsWith('.md')) {\n const file = await parseMarkdownFile(entryPath, entry.name)\n \n if (entry.name === 'SKILL.md') {\n skillFile = file\n } else {\n children.push(file)\n }\n }\n }\n\n return {\n path: dirPath,\n name,\n children,\n skillFile\n }\n}\n\nasync function parseMarkdownFile(filePath: string, fileName: string): Promise<SkillFile> {\n const content = await fs.readFile(filePath, 'utf-8')\n const stats = await fs.stat(filePath)\n\n // Basic frontmatter extraction\n const frontmatterMatch = content.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n/)\n const frontmatter: Record<string, any> = {}\n let markdownContent = content\n\n if (frontmatterMatch) {\n // Remove frontmatter from content\n markdownContent = content.slice(frontmatterMatch[0].length)\n \n // Parse frontmatter key-value pairs\n const fmLines = frontmatterMatch[1].split(/\\r?\\n/)\n for (const line of fmLines) {\n const colonIndex = line.indexOf(':')\n if (colonIndex > 0) {\n const key = line.slice(0, colonIndex).trim()\n const value = line.slice(colonIndex + 1).trim().replace(/^[\"']|[\"']$/g, '')\n if (key && value) {\n frontmatter[key] = value\n }\n }\n }\n }\n\n return {\n path: filePath,\n name: fileName,\n title: frontmatter.title || fileName.replace('.md', ''),\n description: frontmatter.description,\n content: markdownContent,\n frontmatter,\n lastModified: stats.mtime.getTime()\n }\n}\n\nexport async function watchSkillsDirectory(\n sourceDir: string, \n callback: (directories: SkillDirectory[]) => void\n) {\n const chokidar = await import('chokidar')\n \n const watcher = chokidar.watch(sourceDir, {\n ignored: /node_modules/,\n persistent: true,\n ignoreInitial: true\n })\n\n let debounceTimer: NodeJS.Timeout\n\n const handleChange = () => {\n clearTimeout(debounceTimer)\n debounceTimer = setTimeout(async () => {\n try {\n const directories = await scanSkillsDirectory(sourceDir)\n callback(directories)\n } catch (error) {\n console.error('Error rescanning source directory:', error)\n }\n }, 300)\n }\n\n watcher.on('add', handleChange)\n watcher.on('change', handleChange)\n watcher.on('unlink', handleChange)\n watcher.on('addDir', handleChange)\n watcher.on('unlinkDir', handleChange)\n\n return watcher\n}","import matter from 'gray-matter'\nimport MarkdownIt from 'markdown-it'\nimport { createHighlighter, Highlighter } from 'shiki'\nimport { SkillDirectory, SkillFile, SiteConfig, DocGenConfig, Navigation } from '../types/index.js'\n\nlet highlighter: Highlighter | null = null\n\nasync function getHighlighter() {\n if (!highlighter) {\n highlighter = await createHighlighter({\n themes: ['github-dark', 'github-light'],\n langs: ['javascript', 'typescript', 'bash', 'shell', 'json', 'html', 'css', 'vue', 'jsx', 'tsx', 'python', 'markdown', 'yaml', 'sql', 'go', 'rust', 'java', 'c', 'cpp']\n })\n }\n return highlighter\n}\n\nconst md = new MarkdownIt({\n html: true,\n linkify: true,\n typographer: true,\n highlight: function (str: string, lang: string) {\n // Synchronous fallback - actual highlighting done in enhanceFileContent\n return `<pre class=\"shiki-pending\" data-lang=\"${lang || 'text'}\"><code>${md.utils.escapeHtml(str)}</code></pre>`\n }\n})\n\nexport async function parseSkillsToConfig(\n directories: SkillDirectory[],\n siteConfig: SiteConfig\n): Promise<DocGenConfig> {\n const files: SkillFile[] = []\n const navigation = await generateNavigation(directories)\n\n // Collect all files for search indexing\n collectAllFiles(directories, files)\n\n // Enhance files with parsed content\n for (const file of files) {\n await enhanceFileContent(file)\n }\n\n return {\n siteConfig,\n navigation,\n files,\n directories\n }\n}\n\nfunction collectAllFiles(items: (SkillDirectory | SkillFile)[], files: SkillFile[]) {\n for (const item of items) {\n if ('content' in item) {\n // It's a SkillFile\n files.push(item)\n } else {\n // It's a SkillDirectory\n if (item.skillFile) {\n files.push(item.skillFile)\n }\n collectAllFiles(item.children, files)\n }\n }\n}\n\nasync function enhanceFileContent(file: SkillFile) {\n try {\n let content = file.content\n\n // Update title and description from frontmatter if available\n file.title = file.frontmatter.title || file.title\n file.description = file.frontmatter.description || file.description\n\n // Remove duplicate h1 if it matches frontmatter title\n if (file.frontmatter.title) {\n // Match first h1 heading in content (may have leading whitespace/newlines)\n const h1Match = content.match(/^\\s*#\\s+(.+)$/m)\n if (h1Match) {\n const h1Text = h1Match[1].trim()\n // If h1 matches title, remove it to avoid duplication\n if (h1Text === file.frontmatter.title) {\n content = content.replace(/^\\s*#\\s+.+\\n*/, '')\n }\n }\n }\n\n // Render markdown to HTML\n let html = md.render(content)\n \n // Apply syntax highlighting with shiki\n html = await highlightCodeBlocks(html)\n file.frontmatter.html = html\n\n // Extract headings for TOC\n const headings = extractHeadings(content)\n file.frontmatter.headings = headings\n\n } catch (error) {\n console.warn(`Failed to enhance content for ${file.path}:`, error)\n try {\n file.frontmatter.html = md.render(file.content)\n } catch (e) {\n console.error(`Failed to render markdown for ${file.path}:`, e)\n }\n }\n}\n\nasync function highlightCodeBlocks(html: string): Promise<string> {\n const hl = await getHighlighter()\n \n // Find all pending shiki code blocks and highlight them\n const codeBlockRegex = /<pre class=\"shiki-pending\" data-lang=\"([^\"]*)\"[^>]*><code>([^]*?)<\\/code><\\/pre>/g\n \n const matches = [...html.matchAll(codeBlockRegex)]\n \n for (const match of matches) {\n const [fullMatch, lang, escapedCode] = match\n // Unescape HTML entities\n const code = escapedCode\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/&/g, '&')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n \n try {\n const validLang = hl.getLoadedLanguages().includes(lang) ? lang : 'text'\n const highlighted = hl.codeToHtml(code, {\n lang: validLang,\n theme: 'github-dark'\n })\n html = html.replace(fullMatch, highlighted)\n } catch (e) {\n // Keep original if highlighting fails\n console.warn(`Failed to highlight ${lang}:`, e)\n }\n }\n \n return html\n}\n\nfunction extractHeadings(content: string) {\n const headings: Array<{ level: number; text: string; anchor: string }> = []\n const lines = content.split('\\n')\n\n for (const line of lines) {\n const match = line.match(/^(#{1,6})\\\\s+(.+)$/)\n if (match) {\n const level = match[1].length\n const text = match[2].trim()\n const anchor = text.toLowerCase()\n .replace(/[^\\\\w\\\\s-]/g, '')\n .replace(/\\\\s+/g, '-')\n .trim()\n\n headings.push({ level, text, anchor })\n }\n }\n\n return headings\n}\n\nasync function generateNavigation(directories: SkillDirectory[]): Promise<Navigation[]> {\n const navigation: Navigation[] = []\n\n for (const dir of directories) {\n const navItem: Navigation = {\n text: dir.skillFile?.title || formatDirName(dir.name),\n link: dir.skillFile ? getFileRoute(dir.skillFile) : undefined\n }\n\n if (dir.children.length > 0) {\n navItem.children = []\n \n for (const child of dir.children) {\n if ('content' in child) {\n // It's a file\n navItem.children.push({\n text: child.title || formatFileName(child.name),\n link: getFileRoute(child)\n })\n } else {\n // It's a subdirectory\n const subNav = await generateNavigation([child])\n navItem.children.push(...subNav)\n }\n }\n }\n\n navigation.push(navItem)\n }\n\n return navigation\n}\n\nfunction getFileRoute(file: SkillFile): string {\n // Import the utility function from client utils\n // For now, use inline implementation\n const filePath = file.path\n \n // Try common patterns\n const patterns = ['my-docs', 'docs', 'documentation', '.claude/skills']\n let relativePath = ''\n \n for (const pattern of patterns) {\n const index = filePath.indexOf(pattern)\n if (index !== -1) {\n relativePath = filePath.slice(index + pattern.length)\n break\n }\n }\n \n if (!relativePath) {\n // Fallback: use last 2 segments\n const segments = filePath.split('/').filter(Boolean)\n if (segments.length >= 2) {\n relativePath = '/' + segments.slice(-2).join('/')\n } else {\n return '/'\n }\n }\n \n const segments = relativePath.split('/').filter(Boolean)\n \n if (segments.length === 0) return '/'\n \n // Remove file extension\n const lastSegment = segments[segments.length - 1]\n if (lastSegment.endsWith('.md')) {\n segments[segments.length - 1] = lastSegment.slice(0, -3)\n }\n \n // SKILL.md and README.md should map to parent directory route\n const finalSegment = segments[segments.length - 1]\n if (finalSegment === 'SKILL' || finalSegment === 'README') {\n segments.pop()\n }\n \n return '/' + segments.join('/')\n}\n\nfunction formatDirName(name: string): string {\n return name.split('-').map(word => \n word.charAt(0).toUpperCase() + word.slice(1)\n ).join(' ')\n}\n\nfunction formatFileName(name: string): string {\n const baseName = name.replace('.md', '')\n return formatDirName(baseName)\n}\n\nexport { md as markdownRenderer }","#!/usr/bin/env node\n\nimport { Command } from 'commander'\nimport { devCommand } from './commands/dev.js'\nimport { buildCommand } from './commands/build.js'\nimport { previewCommand } from './commands/preview.js'\n\nconst program = new Command()\n\nprogram\n .name('docmk')\n .description('CLI tool for generating documentation from any directory')\n .version('1.0.0')\n\nprogram\n .command('dev', { isDefault: true })\n .description('Start development server')\n .option('-p, --port <port>', 'Port to run dev server on', '3000')\n .option('-d, --dir <directory>', 'Source directory path', './docs')\n .action(devCommand)\n\nprogram\n .command('build')\n .description('Build static documentation site')\n .option('-d, --dir <directory>', 'Source directory path', './docs')\n .option('-o, --output <directory>', 'Output directory', 'dist')\n .action(buildCommand)\n\nprogram\n .command('preview')\n .description('Preview built documentation site')\n .option('-p, --port <port>', 'Port to run preview server on', '4173')\n .option('-o, --output <directory>', 'Built site directory', 'dist')\n .action(previewCommand)\n\nprogram.parse()","import path from 'path'\nimport { createViteDevServer } from '../../builder/vite-dev.js'\nimport { scanSkillsDirectory } from '../../scanner/index.js'\nimport { parseSkillsToConfig } from '../../parser/index.js'\nimport { validatePort } from '../../utils/port.js'\n\ninterface DevOptions {\n port: string\n dir: string\n}\n\nexport async function devCommand(options: DevOptions) {\n console.log('🚀 Starting DocGen development server...')\n\n const sourceDir = path.resolve(process.cwd(), options.dir)\n const { port } = validatePort(parseInt(options.port, 10))\n \n try {\n // Check if source directory exists\n console.log(`📁 Scanning source directory: ${sourceDir}`)\n const directories = await scanSkillsDirectory(sourceDir)\n \n console.log(`✅ Found ${directories.length} directories`)\n \n // Parse to config\n const config = await parseSkillsToConfig(directories, {\n title: 'Documentation',\n description: 'Documentation generated from source directory',\n baseUrl: '/',\n skillsDir: sourceDir,\n outputDir: 'dist'\n })\n \n // Start Vite dev server\n const { server, actualPort } = await createViteDevServer({\n port,\n skillsDir: sourceDir,\n config\n })\n\n console.log(`🎉 Dev server running at http://localhost:${actualPort}`)\n \n // Handle graceful shutdown\n process.on('SIGINT', async () => {\n console.log('\\\\n👋 Shutting down dev server...')\n await server.close()\n process.exit(0)\n })\n \n } catch (error) {\n console.error('❌ Failed to start dev server:', error)\n process.exit(1)\n }\n}","import { createServer, ViteDevServer } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\nimport { DocGenConfig } from '../types/index.js'\nimport { scanSkillsDirectory, watchSkillsDirectory } from '../scanner/index.js'\nimport { parseSkillsToConfig } from '../parser/index.js'\n\n// Get the package root directory (where src/client is located)\n// When bundled with tsup, the code runs from dist/index.js, so we only need to go up one level\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\nconst packageRoot = path.resolve(__dirname, '..')\n\ninterface DevServerOptions {\n port: number\n skillsDir: string\n config: DocGenConfig\n}\n\nexport async function createViteDevServer(options: DevServerOptions): Promise<ViteDevServer> {\n let currentConfig = options.config\n\n const server = await createServer({\n root: path.resolve(packageRoot, 'src/client'),\n server: {\n port: options.port,\n host: true // Listen on all addresses\n },\n publicDir: false, // Disable public dir in dev mode\n plugins: [\n vue(),\n // Custom plugin to inject config and handle API routes\n {\n name: 'docgen-dev',\n configureServer(server) {\n // Return a function to be called after all Vite middlewares are set up\n return () => {\n // API endpoint for configuration\n server.middlewares.use('/api/config', (req, res, next) => {\n if (req.method === 'GET') {\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(currentConfig))\n } else {\n next()\n }\n })\n\n // SPA fallback - return index.html for all non-API routes\n // This must be registered AFTER all other middlewares\n server.middlewares.use((req, res, next) => {\n // Skip API routes and vite's own routes\n if (req.url?.startsWith('/api/') || req.url?.startsWith('/@') || req.url?.includes('.')) {\n return next()\n }\n // For all other routes, serve index.html for SPA routing\n req.url = '/index.html'\n next()\n })\n }\n },\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n // Inject lightweight config placeholder\n // Full config will be loaded via API to avoid HTML parsing issues\n const configScript = `\n <script>\n // Config will be loaded from /api/config\n globalThis.__DOCGEN_CONFIG__ = null;\n </script>\n `\n return html.replace('<head>', `<head>${configScript}`)\n }\n }\n }\n ],\n resolve: {\n alias: {\n '@': path.resolve(packageRoot, 'src/client')\n }\n },\n define: {\n __DOCGEN_CONFIG__: JSON.stringify(currentConfig)\n }\n })\n\n // Watch skills directory for changes\n const watcher = await watchSkillsDirectory(options.skillsDir, async (directories) => {\n console.log('📝 Skills directory changed, updating configuration...')\n \n try {\n currentConfig = await parseSkillsToConfig(directories, currentConfig.siteConfig)\n \n // Notify all connected clients to reload\n server.ws.send({\n type: 'full-reload'\n })\n \n console.log('✅ Configuration updated')\n } catch (error) {\n console.error('❌ Failed to update configuration:', error)\n \n // Send error to clients\n server.ws.send({\n type: 'error',\n err: {\n message: `Failed to update configuration: ${error.message}`,\n stack: error.stack\n }\n })\n }\n })\n\n // Start the server\n await server.listen()\n\n // Get actual port (in case requested port was taken)\n const address = server.httpServer.address()\n const actualPort = typeof address === 'object' && address ? address.port : options.port\n\n // Extend server with cleanup method\n const originalClose = server.close.bind(server)\n server.close = async () => {\n await watcher?.close()\n return originalClose()\n }\n\n return { server, actualPort }\n}\n\nexport async function createProductionBuild() {\n // This will be implemented in the builder\n}","/**\n * 浏览器认为不安全的端口列表\n * 参考: https://chromium.googlesource.com/chromium/src/+/refs/heads/main/net/base/port_util.cc\n */\nconst UNSAFE_PORTS = new Set([\n 1, // tcpmux\n 7, // echo\n 9, // discard\n 11, // systat\n 13, // daytime\n 15, // netstat\n 17, // qotd\n 19, // chargen\n 20, // ftp data\n 21, // ftp access\n 22, // ssh\n 23, // telnet\n 25, // smtp\n 37, // time\n 42, // name\n 43, // nicname\n 53, // domain\n 69, // tftp\n 77, // priv-rjs\n 79, // finger\n 87, // ttylink\n 95, // supdup\n 101, // hostriame\n 102, // iso-tsap\n 103, // gppitnp\n 104, // acr-nema\n 109, // pop2\n 110, // pop3\n 111, // sunrpc\n 113, // auth\n 115, // sftp\n 117, // uucp-path\n 119, // nntp\n 123, // NTP\n 135, // loc-srv / epmap\n 137, // netbios\n 139, // netbios\n 143, // imap2\n 161, // snmp\n 179, // BGP\n 389, // ldap\n 427, // SLP (Also used by determine_refmode)\n 465, // smtp+ssl\n 512, // print / exec\n 513, // login\n 514, // shell\n 515, // printer\n 526, // tempo\n 530, // courier\n 531, // chat\n 532, // netnews\n 540, // uucp\n 548, // AFP (Apple Filing Protocol)\n 554, // rtsp\n 556, // remotefs\n 563, // nntp+ssl\n 587, // smtp (rfc6409)\n 601, // syslog-conn (rfc3195)\n 636, // ldap+ssl\n 989, // ftps-data\n 990, // ftps\n 993, // ldap+ssl\n 995, // pop3+ssl\n 1719, // h323gatestat\n 1720, // h323hostcall\n 1723, // pptp\n 2049, // nfs\n 3659, // apple-sasl / PasswordServer\n 4045, // lockd\n 5060, // sip\n 5061, // sips\n 6000, // X11\n 6566, // sane-port\n 6665, // Alternate IRC [Apple addition]\n 6666, // Alternate IRC [Apple addition]\n 6667, // Standard IRC [Apple addition]\n 6668, // Alternate IRC [Apple addition]\n 6669, // Alternate IRC [Apple addition]\n 6697, // IRC + TLS\n 10080, // Amanda\n])\n\n/**\n * 检查端口是否安全(不被浏览器阻止)\n */\nexport function isPortSafe(port: number): boolean {\n return !UNSAFE_PORTS.has(port)\n}\n\n/**\n * 获取一个安全的端口,如果当前端口不安全则返回替代端口\n */\nexport function getSafePort(port: number, fallback = 3000): number {\n return isPortSafe(port) ? port : fallback\n}\n\n/**\n * 验证端口并返回结果,如果不安全则打印警告\n */\nexport function validatePort(port: number): { port: number; wasUnsafe: boolean } {\n if (isPortSafe(port)) {\n return { port, wasUnsafe: false }\n }\n\n // 找一个安全的替代端口\n const safePort = findNextSafePort(port)\n console.warn(`⚠️ 端口 ${port} 被浏览器视为不安全端口,已自动切换到 ${safePort}`)\n console.warn(` 不安全端口会被浏览器阻止访问 (ERR_UNSAFE_PORT)`)\n\n return { port: safePort, wasUnsafe: true }\n}\n\n/**\n * 从给定端口开始寻找下一个安全端口\n */\nexport function findNextSafePort(startPort: number): number {\n let port = startPort\n while (!isPortSafe(port) && port < 65535) {\n port++\n }\n // 如果找不到(不太可能),回退到 3000\n return port < 65535 ? port : 3000\n}\n","import path from 'path'\nimport { buildSite } from '../../builder/index.js'\nimport { scanSkillsDirectory } from '../../scanner/index.js'\nimport { parseSkillsToConfig } from '../../parser/index.js'\n\ninterface BuildOptions {\n dir: string\n output: string\n}\n\nexport async function buildCommand(options: BuildOptions) {\n console.log('🏗️ Building DocGen documentation site...')\n \n const sourceDir = path.resolve(process.cwd(), options.dir)\n const outputDir = path.resolve(process.cwd(), options.output)\n \n try {\n // Scan source directory\n console.log(`📁 Scanning source directory: ${sourceDir}`)\n const directories = await scanSkillsDirectory(sourceDir)\n \n console.log(`✅ Found ${directories.length} directories`)\n \n // Parse to config\n const config = await parseSkillsToConfig(directories, {\n title: 'Documentation',\n description: 'Documentation generated from source directory',\n baseUrl: '/',\n skillsDir: sourceDir,\n outputDir\n })\n \n // Build site\n console.log(`📦 Building to: ${outputDir}`)\n await buildSite({\n input: sourceDir,\n output: outputDir,\n mode: 'production'\n }, config)\n \n console.log('✅ Build completed successfully!')\n console.log(`📂 Built files are in: ${outputDir}`)\n \n } catch (error) {\n console.error('❌ Build failed:', error)\n process.exit(1)\n }\n}","import { build } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\nimport fs from 'fs/promises'\nimport { DocGenConfig, BuildOptions } from '../types/index.js'\n\n// Get the package root directory\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\nconst packageRoot = path.resolve(__dirname, '../..')\n\nexport async function buildSite(options: BuildOptions, config: DocGenConfig) {\n console.log('🏗️ Building documentation site...')\n\n // Ensure output directory exists\n await fs.mkdir(options.output, { recursive: true })\n\n // Write config to a temporary file for the build process\n const configPath = path.join(process.cwd(), 'temp-config.json')\n await fs.writeFile(configPath, JSON.stringify(config, null, 2))\n\n try {\n // Build the client application\n await build({\n root: path.resolve(packageRoot, 'src/client'),\n base: config.siteConfig.baseUrl,\n build: {\n outDir: options.output,\n emptyOutDir: true,\n rollupOptions: {\n input: {\n main: path.resolve(packageRoot, 'src/client/index.html')\n }\n }\n },\n plugins: [\n vue(),\n // Plugin to inject config during build\n {\n name: 'docgen-build',\n transformIndexHtml: {\n order: 'pre',\n handler(html) {\n // Encode config as base64 with proper UTF-8 handling\n const configJson = JSON.stringify(config)\n // Use encodeURIComponent to handle UTF-8 properly before base64\n const utf8Encoded = unescape(encodeURIComponent(configJson))\n const configBase64 = Buffer.from(utf8Encoded, 'binary').toString('base64')\n // Decode: atob -> decodeURIComponent(escape()) to restore UTF-8\n const configScript = `<script>globalThis.__DOCGEN_CONFIG__=JSON.parse(decodeURIComponent(escape(atob(\"${configBase64}\"))));</script>`\n return html.replace('</head>', `${configScript}\\n</head>`)\n }\n }\n }\n ],\n resolve: {\n alias: {\n '@': path.resolve(packageRoot, 'src/client')\n }\n },\n define: {\n __DOCGEN_CONFIG__: JSON.stringify(config)\n }\n })\n \n // Generate additional static files\n await generateSitemap(options.output, config)\n await generateRobotsTxt(options.output, config)\n await copyAssets(options.output)\n \n console.log('✅ Build completed successfully!')\n \n } finally {\n // Clean up temporary config file\n try {\n await fs.unlink(configPath)\n } catch {\n // Ignore if file doesn't exist\n }\n }\n}\n\nasync function generateSitemap(outputDir: string, config: DocGenConfig) {\n console.log('📄 Generating sitemap...')\n \n const baseUrl = config.siteConfig.baseUrl.replace(/\\/$/, '')\n const urls: string[] = []\n \n // Add home page\n urls.push(`${baseUrl}/`)\n \n // Add all skill pages\n for (const file of config.files) {\n const route = getFileRoute(file.path, config.siteConfig.skillsDir)\n if (route !== '/') {\n urls.push(`${baseUrl}${route}`)\n }\n }\n \n const sitemap = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n${urls.map(url => ` <url>\n <loc>${url}</loc>\n <lastmod>${new Date().toISOString().split('T')[0]}</lastmod>\n <changefreq>weekly</changefreq>\n <priority>0.8</priority>\n </url>`).join('\\\\n')}\n</urlset>`\n \n await fs.writeFile(path.join(outputDir, 'sitemap.xml'), sitemap)\n}\n\nasync function generateRobotsTxt(outputDir: string, config: DocGenConfig) {\n console.log('🤖 Generating robots.txt...')\n \n const baseUrl = config.siteConfig.baseUrl.replace(/\\/$/, '')\n const robotsTxt = `User-agent: *\nAllow: /\n\nSitemap: ${baseUrl}/sitemap.xml`\n \n await fs.writeFile(path.join(outputDir, 'robots.txt'), robotsTxt)\n}\n\nasync function copyAssets(outputDir: string) {\n console.log('📁 Copying static assets...')\n \n // Create a simple favicon if it doesn't exist\n const faviconPath = path.join(outputDir, 'favicon.ico')\n \n try {\n await fs.access(faviconPath)\n } catch {\n // Create a simple SVG favicon\n const faviconSvg = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\">\n <rect width=\"32\" height=\"32\" rx=\"4\" fill=\"#3182ce\"/>\n <text x=\"16\" y=\"22\" text-anchor=\"middle\" fill=\"white\" font-family=\"system-ui\" font-size=\"18\" font-weight=\"bold\">D</text>\n</svg>`\n \n await fs.writeFile(path.join(outputDir, 'favicon.svg'), faviconSvg)\n }\n}\n\nfunction getFileRoute(filePath: string, baseDir: string): string {\n // Normalize paths for comparison\n const normalizedFilePath = path.normalize(filePath)\n const normalizedBaseDir = path.normalize(baseDir)\n \n // Check if file is within base directory\n if (!normalizedFilePath.startsWith(normalizedBaseDir)) {\n return '/'\n }\n \n // Get relative path from base directory\n const relativePath = normalizedFilePath.slice(normalizedBaseDir.length)\n const segments = relativePath.split(path.sep).filter(Boolean)\n \n if (segments.length === 0) return '/'\n \n const lastSegment = segments[segments.length - 1]\n if (lastSegment.endsWith('.md')) {\n segments[segments.length - 1] = lastSegment.slice(0, -3)\n }\n \n // SKILL.md and README.md should map to parent directory route\n const finalSegment = segments[segments.length - 1]\n if (finalSegment === 'SKILL' || finalSegment === 'README') {\n segments.pop()\n }\n\n return '/' + segments.join('/')\n}\n\nexport async function buildForProduction(sourceDir: string, outputDir: string): Promise<DocGenConfig> {\n const { scanSkillsDirectory } = await import('../scanner/index.js')\n const { parseSkillsToConfig } = await import('../parser/index.js')\n \n const directories = await scanSkillsDirectory(sourceDir)\n const config = await parseSkillsToConfig(directories, {\n title: 'Documentation',\n description: 'Documentation generated from source directory',\n baseUrl: '/',\n skillsDir: sourceDir,\n outputDir\n })\n \n await buildSite({\n input: sourceDir,\n output: outputDir,\n mode: 'production'\n }, config)\n \n return config\n}","import path from 'path'\nimport fs from 'fs/promises'\nimport sirv from 'sirv'\nimport { createServer } from 'http'\nimport { validatePort } from '../../utils/port.js'\n\ninterface PreviewOptions {\n port: string\n output: string\n}\n\nexport async function previewCommand(options: PreviewOptions) {\n console.log('👀 Starting DocGen preview server...')\n\n const outputDir = path.resolve(process.cwd(), options.output)\n const { port } = validatePort(parseInt(options.port, 10))\n \n try {\n // Check if build directory exists\n try {\n await fs.access(outputDir)\n } catch {\n console.error(`❌ Build directory not found: ${outputDir}`)\n console.log('💡 Run \"docgen build\" first to generate the static site')\n process.exit(1)\n }\n \n // Create static file server\n const serve = sirv(outputDir, {\n single: true, // SPA mode\n dev: false,\n setHeaders: (res, pathname) => {\n if (pathname.endsWith('.html') || pathname === '/') {\n res.setHeader('Content-Type', 'text/html; charset=utf-8')\n }\n }\n })\n \n const server = createServer(serve)\n \n server.listen(port, () => {\n console.log(`🎉 Preview server running at http://localhost:${port}`)\n console.log(`📂 Serving files from: ${outputDir}`)\n })\n \n // Handle graceful shutdown\n process.on('SIGINT', () => {\n console.log('\\\\n👋 Shutting down preview server...')\n server.close(() => {\n process.exit(0)\n })\n })\n \n } catch (error) {\n console.error('❌ Failed to start preview server:', error)\n process.exit(1)\n }\n}"],"mappings":"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","names":["segments","path","path","server","path","path","vue","path","fileURLToPath","fs","__dirname","packageRoot","getFileRoute","path","path","fs","createServer","path","fs","createServer"]}
|
package/package.json
CHANGED
package/src/builder/vite-dev.ts
CHANGED
|
@@ -7,8 +7,9 @@ import { scanSkillsDirectory, watchSkillsDirectory } from '../scanner/index.js'
|
|
|
7
7
|
import { parseSkillsToConfig } from '../parser/index.js'
|
|
8
8
|
|
|
9
9
|
// Get the package root directory (where src/client is located)
|
|
10
|
+
// When bundled with tsup, the code runs from dist/index.js, so we only need to go up one level
|
|
10
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
11
|
-
const packageRoot = path.resolve(__dirname, '
|
|
12
|
+
const packageRoot = path.resolve(__dirname, '..')
|
|
12
13
|
|
|
13
14
|
interface DevServerOptions {
|
|
14
15
|
port: number
|
|
@@ -32,27 +33,27 @@ export async function createViteDevServer(options: DevServerOptions): Promise<Vi
|
|
|
32
33
|
{
|
|
33
34
|
name: 'docgen-dev',
|
|
34
35
|
configureServer(server) {
|
|
35
|
-
//
|
|
36
|
-
server.middlewares.use('/api/config', (req, res, next) => {
|
|
37
|
-
if (req.method === 'GET') {
|
|
38
|
-
res.setHeader('Content-Type', 'application/json')
|
|
39
|
-
res.end(JSON.stringify(currentConfig))
|
|
40
|
-
} else {
|
|
41
|
-
next()
|
|
42
|
-
}
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
// SPA fallback - return index.html for all non-API routes
|
|
36
|
+
// Return a function to be called after all Vite middlewares are set up
|
|
46
37
|
return () => {
|
|
38
|
+
// API endpoint for configuration
|
|
39
|
+
server.middlewares.use('/api/config', (req, res, next) => {
|
|
40
|
+
if (req.method === 'GET') {
|
|
41
|
+
res.setHeader('Content-Type', 'application/json')
|
|
42
|
+
res.end(JSON.stringify(currentConfig))
|
|
43
|
+
} else {
|
|
44
|
+
next()
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// SPA fallback - return index.html for all non-API routes
|
|
49
|
+
// This must be registered AFTER all other middlewares
|
|
47
50
|
server.middlewares.use((req, res, next) => {
|
|
48
51
|
// Skip API routes and vite's own routes
|
|
49
|
-
if (req.url?.startsWith('/api/') || req.url?.startsWith('/@') || req.url?.includes('.
|
|
52
|
+
if (req.url?.startsWith('/api/') || req.url?.startsWith('/@') || req.url?.includes('.')) {
|
|
50
53
|
return next()
|
|
51
54
|
}
|
|
52
55
|
// For all other routes, serve index.html for SPA routing
|
|
53
|
-
|
|
54
|
-
req.url = '/index.html'
|
|
55
|
-
}
|
|
56
|
+
req.url = '/index.html'
|
|
56
57
|
next()
|
|
57
58
|
})
|
|
58
59
|
}
|
|
@@ -114,7 +115,8 @@ export async function createViteDevServer(options: DevServerOptions): Promise<Vi
|
|
|
114
115
|
await server.listen()
|
|
115
116
|
|
|
116
117
|
// Get actual port (in case requested port was taken)
|
|
117
|
-
const
|
|
118
|
+
const address = server.httpServer.address()
|
|
119
|
+
const actualPort = typeof address === 'object' && address ? address.port : options.port
|
|
118
120
|
|
|
119
121
|
// Extend server with cleanup method
|
|
120
122
|
const originalClose = server.close.bind(server)
|
package/src/cli/commands/dev.ts
CHANGED
|
@@ -2,6 +2,7 @@ import path from 'path'
|
|
|
2
2
|
import { createViteDevServer } from '../../builder/vite-dev.js'
|
|
3
3
|
import { scanSkillsDirectory } from '../../scanner/index.js'
|
|
4
4
|
import { parseSkillsToConfig } from '../../parser/index.js'
|
|
5
|
+
import { validatePort } from '../../utils/port.js'
|
|
5
6
|
|
|
6
7
|
interface DevOptions {
|
|
7
8
|
port: string
|
|
@@ -10,9 +11,9 @@ interface DevOptions {
|
|
|
10
11
|
|
|
11
12
|
export async function devCommand(options: DevOptions) {
|
|
12
13
|
console.log('🚀 Starting DocGen development server...')
|
|
13
|
-
|
|
14
|
+
|
|
14
15
|
const sourceDir = path.resolve(process.cwd(), options.dir)
|
|
15
|
-
const port = parseInt(options.port, 10)
|
|
16
|
+
const { port } = validatePort(parseInt(options.port, 10))
|
|
16
17
|
|
|
17
18
|
try {
|
|
18
19
|
// Check if source directory exists
|
|
@@ -2,6 +2,7 @@ import path from 'path'
|
|
|
2
2
|
import fs from 'fs/promises'
|
|
3
3
|
import sirv from 'sirv'
|
|
4
4
|
import { createServer } from 'http'
|
|
5
|
+
import { validatePort } from '../../utils/port.js'
|
|
5
6
|
|
|
6
7
|
interface PreviewOptions {
|
|
7
8
|
port: string
|
|
@@ -10,9 +11,9 @@ interface PreviewOptions {
|
|
|
10
11
|
|
|
11
12
|
export async function previewCommand(options: PreviewOptions) {
|
|
12
13
|
console.log('👀 Starting DocGen preview server...')
|
|
13
|
-
|
|
14
|
+
|
|
14
15
|
const outputDir = path.resolve(process.cwd(), options.output)
|
|
15
|
-
const port = parseInt(options.port, 10)
|
|
16
|
+
const { port } = validatePort(parseInt(options.port, 10))
|
|
16
17
|
|
|
17
18
|
try {
|
|
18
19
|
// Check if build directory exists
|
package/src/cli/index.ts
CHANGED
|
@@ -11,15 +11,9 @@ program
|
|
|
11
11
|
.name('docmk')
|
|
12
12
|
.description('CLI tool for generating documentation from any directory')
|
|
13
13
|
.version('1.0.0')
|
|
14
|
-
.argument('[directory]', 'Source directory path (starts dev server)', './docs')
|
|
15
|
-
.option('-p, --port <port>', 'Port to run dev server on', '3000')
|
|
16
|
-
.action((directory, options) => {
|
|
17
|
-
// Default action: start dev server
|
|
18
|
-
devCommand({ dir: directory, port: options.port })
|
|
19
|
-
})
|
|
20
14
|
|
|
21
15
|
program
|
|
22
|
-
.command('dev')
|
|
16
|
+
.command('dev', { isDefault: true })
|
|
23
17
|
.description('Start development server')
|
|
24
18
|
.option('-p, --port <port>', 'Port to run dev server on', '3000')
|
|
25
19
|
.option('-d, --dir <directory>', 'Source directory path', './docs')
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 浏览器认为不安全的端口列表
|
|
3
|
+
* 参考: https://chromium.googlesource.com/chromium/src/+/refs/heads/main/net/base/port_util.cc
|
|
4
|
+
*/
|
|
5
|
+
const UNSAFE_PORTS = new Set([
|
|
6
|
+
1, // tcpmux
|
|
7
|
+
7, // echo
|
|
8
|
+
9, // discard
|
|
9
|
+
11, // systat
|
|
10
|
+
13, // daytime
|
|
11
|
+
15, // netstat
|
|
12
|
+
17, // qotd
|
|
13
|
+
19, // chargen
|
|
14
|
+
20, // ftp data
|
|
15
|
+
21, // ftp access
|
|
16
|
+
22, // ssh
|
|
17
|
+
23, // telnet
|
|
18
|
+
25, // smtp
|
|
19
|
+
37, // time
|
|
20
|
+
42, // name
|
|
21
|
+
43, // nicname
|
|
22
|
+
53, // domain
|
|
23
|
+
69, // tftp
|
|
24
|
+
77, // priv-rjs
|
|
25
|
+
79, // finger
|
|
26
|
+
87, // ttylink
|
|
27
|
+
95, // supdup
|
|
28
|
+
101, // hostriame
|
|
29
|
+
102, // iso-tsap
|
|
30
|
+
103, // gppitnp
|
|
31
|
+
104, // acr-nema
|
|
32
|
+
109, // pop2
|
|
33
|
+
110, // pop3
|
|
34
|
+
111, // sunrpc
|
|
35
|
+
113, // auth
|
|
36
|
+
115, // sftp
|
|
37
|
+
117, // uucp-path
|
|
38
|
+
119, // nntp
|
|
39
|
+
123, // NTP
|
|
40
|
+
135, // loc-srv / epmap
|
|
41
|
+
137, // netbios
|
|
42
|
+
139, // netbios
|
|
43
|
+
143, // imap2
|
|
44
|
+
161, // snmp
|
|
45
|
+
179, // BGP
|
|
46
|
+
389, // ldap
|
|
47
|
+
427, // SLP (Also used by determine_refmode)
|
|
48
|
+
465, // smtp+ssl
|
|
49
|
+
512, // print / exec
|
|
50
|
+
513, // login
|
|
51
|
+
514, // shell
|
|
52
|
+
515, // printer
|
|
53
|
+
526, // tempo
|
|
54
|
+
530, // courier
|
|
55
|
+
531, // chat
|
|
56
|
+
532, // netnews
|
|
57
|
+
540, // uucp
|
|
58
|
+
548, // AFP (Apple Filing Protocol)
|
|
59
|
+
554, // rtsp
|
|
60
|
+
556, // remotefs
|
|
61
|
+
563, // nntp+ssl
|
|
62
|
+
587, // smtp (rfc6409)
|
|
63
|
+
601, // syslog-conn (rfc3195)
|
|
64
|
+
636, // ldap+ssl
|
|
65
|
+
989, // ftps-data
|
|
66
|
+
990, // ftps
|
|
67
|
+
993, // ldap+ssl
|
|
68
|
+
995, // pop3+ssl
|
|
69
|
+
1719, // h323gatestat
|
|
70
|
+
1720, // h323hostcall
|
|
71
|
+
1723, // pptp
|
|
72
|
+
2049, // nfs
|
|
73
|
+
3659, // apple-sasl / PasswordServer
|
|
74
|
+
4045, // lockd
|
|
75
|
+
5060, // sip
|
|
76
|
+
5061, // sips
|
|
77
|
+
6000, // X11
|
|
78
|
+
6566, // sane-port
|
|
79
|
+
6665, // Alternate IRC [Apple addition]
|
|
80
|
+
6666, // Alternate IRC [Apple addition]
|
|
81
|
+
6667, // Standard IRC [Apple addition]
|
|
82
|
+
6668, // Alternate IRC [Apple addition]
|
|
83
|
+
6669, // Alternate IRC [Apple addition]
|
|
84
|
+
6697, // IRC + TLS
|
|
85
|
+
10080, // Amanda
|
|
86
|
+
])
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 检查端口是否安全(不被浏览器阻止)
|
|
90
|
+
*/
|
|
91
|
+
export function isPortSafe(port: number): boolean {
|
|
92
|
+
return !UNSAFE_PORTS.has(port)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 获取一个安全的端口,如果当前端口不安全则返回替代端口
|
|
97
|
+
*/
|
|
98
|
+
export function getSafePort(port: number, fallback = 3000): number {
|
|
99
|
+
return isPortSafe(port) ? port : fallback
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 验证端口并返回结果,如果不安全则打印警告
|
|
104
|
+
*/
|
|
105
|
+
export function validatePort(port: number): { port: number; wasUnsafe: boolean } {
|
|
106
|
+
if (isPortSafe(port)) {
|
|
107
|
+
return { port, wasUnsafe: false }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 找一个安全的替代端口
|
|
111
|
+
const safePort = findNextSafePort(port)
|
|
112
|
+
console.warn(`⚠️ 端口 ${port} 被浏览器视为不安全端口,已自动切换到 ${safePort}`)
|
|
113
|
+
console.warn(` 不安全端口会被浏览器阻止访问 (ERR_UNSAFE_PORT)`)
|
|
114
|
+
|
|
115
|
+
return { port: safePort, wasUnsafe: true }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 从给定端口开始寻找下一个安全端口
|
|
120
|
+
*/
|
|
121
|
+
export function findNextSafePort(startPort: number): number {
|
|
122
|
+
let port = startPort
|
|
123
|
+
while (!isPortSafe(port) && port < 65535) {
|
|
124
|
+
port++
|
|
125
|
+
}
|
|
126
|
+
// 如果找不到(不太可能),回退到 3000
|
|
127
|
+
return port < 65535 ? port : 3000
|
|
128
|
+
}
|