docmk 1.0.0
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/.claude/skills/pdf/SKILL.md +89 -0
- package/.claude/skills/web-scraping/SKILL.md +78 -0
- package/CLAUDE.md +90 -0
- package/bin/docmk.js +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +636 -0
- package/dist/index.js.map +1 -0
- package/final-site/assets/main-B4orIFxK.css +1 -0
- package/final-site/assets/main-CSoKXua6.js +25 -0
- package/final-site/favicon.svg +4 -0
- package/final-site/index.html +26 -0
- package/final-site/robots.txt +4 -0
- package/final-site/sitemap.xml +14 -0
- package/my-docs/api/README.md +152 -0
- package/my-docs/api/advanced.md +260 -0
- package/my-docs/getting-started/README.md +24 -0
- package/my-docs/tutorials/README.md +272 -0
- package/my-docs/tutorials/customization.md +492 -0
- package/package.json +59 -0
- package/postcss.config.js +6 -0
- package/site/assets/main-BZUsYUCF.css +1 -0
- package/site/assets/main-q6laQtCD.js +114 -0
- package/site/favicon.svg +4 -0
- package/site/index.html +23 -0
- package/site/robots.txt +4 -0
- package/site/sitemap.xml +34 -0
- package/site-output/assets/main-B4orIFxK.css +1 -0
- package/site-output/assets/main-CSoKXua6.js +25 -0
- package/site-output/favicon.svg +4 -0
- package/site-output/index.html +26 -0
- package/site-output/robots.txt +4 -0
- package/site-output/sitemap.xml +14 -0
- package/src/builder/index.ts +189 -0
- package/src/builder/vite-dev.ts +117 -0
- package/src/cli/commands/build.ts +48 -0
- package/src/cli/commands/dev.ts +53 -0
- package/src/cli/commands/preview.ts +57 -0
- package/src/cli/index.ts +42 -0
- package/src/client/App.vue +15 -0
- package/src/client/components/SearchBox.vue +204 -0
- package/src/client/components/Sidebar.vue +18 -0
- package/src/client/components/SidebarItem.vue +108 -0
- package/src/client/index.html +21 -0
- package/src/client/layouts/AppLayout.vue +99 -0
- package/src/client/lib/utils.ts +6 -0
- package/src/client/main.ts +42 -0
- package/src/client/pages/Home.vue +279 -0
- package/src/client/pages/SkillPage.vue +565 -0
- package/src/client/router.ts +16 -0
- package/src/client/styles/global.css +92 -0
- package/src/client/utils/routes.ts +69 -0
- package/src/parser/index.ts +253 -0
- package/src/scanner/index.ts +127 -0
- package/src/types/index.ts +45 -0
- package/tailwind.config.js +65 -0
- package/test-build/assets/main-C2ARPC0e.css +1 -0
- package/test-build/assets/main-CHIQpV3B.js +25 -0
- package/test-build/favicon.svg +4 -0
- package/test-build/index.html +47 -0
- package/test-build/robots.txt +4 -0
- package/test-build/sitemap.xml +19 -0
- package/test-dist/assets/main-B4orIFxK.css +1 -0
- package/test-dist/assets/main-CSoKXua6.js +25 -0
- package/test-dist/favicon.svg +4 -0
- package/test-dist/index.html +26 -0
- package/test-dist/robots.txt +4 -0
- package/test-dist/sitemap.xml +14 -0
- package/tsconfig.json +30 -0
- package/tsup.config.ts +13 -0
- package/vite.config.ts +21 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { build } from 'vite'
|
|
2
|
+
import vue from '@vitejs/plugin-vue'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import fs from 'fs/promises'
|
|
5
|
+
import { DocGenConfig, BuildOptions } from '../types/index.js'
|
|
6
|
+
|
|
7
|
+
export async function buildSite(options: BuildOptions, config: DocGenConfig) {
|
|
8
|
+
console.log('šļø Building documentation site...')
|
|
9
|
+
|
|
10
|
+
// Ensure output directory exists
|
|
11
|
+
await fs.mkdir(options.output, { recursive: true })
|
|
12
|
+
|
|
13
|
+
// Write config to a temporary file for the build process
|
|
14
|
+
const configPath = path.join(process.cwd(), 'temp-config.json')
|
|
15
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2))
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
// Build the client application
|
|
19
|
+
await build({
|
|
20
|
+
root: path.resolve(process.cwd(), 'src/client'),
|
|
21
|
+
base: config.siteConfig.baseUrl,
|
|
22
|
+
build: {
|
|
23
|
+
outDir: options.output,
|
|
24
|
+
emptyOutDir: true,
|
|
25
|
+
rollupOptions: {
|
|
26
|
+
input: {
|
|
27
|
+
main: path.resolve(process.cwd(), 'src/client/index.html')
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
plugins: [
|
|
32
|
+
vue(),
|
|
33
|
+
// Plugin to inject config during build
|
|
34
|
+
{
|
|
35
|
+
name: 'docgen-build',
|
|
36
|
+
transformIndexHtml: {
|
|
37
|
+
order: 'pre',
|
|
38
|
+
handler(html) {
|
|
39
|
+
// Encode config as base64 with proper UTF-8 handling
|
|
40
|
+
const configJson = JSON.stringify(config)
|
|
41
|
+
// Use encodeURIComponent to handle UTF-8 properly before base64
|
|
42
|
+
const utf8Encoded = unescape(encodeURIComponent(configJson))
|
|
43
|
+
const configBase64 = Buffer.from(utf8Encoded, 'binary').toString('base64')
|
|
44
|
+
// Decode: atob -> decodeURIComponent(escape()) to restore UTF-8
|
|
45
|
+
const configScript = `<script>globalThis.__DOCGEN_CONFIG__=JSON.parse(decodeURIComponent(escape(atob("${configBase64}"))));</script>`
|
|
46
|
+
return html.replace('</head>', `${configScript}\n</head>`)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
resolve: {
|
|
52
|
+
alias: {
|
|
53
|
+
'@': path.resolve(process.cwd(), 'src/client')
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
define: {
|
|
57
|
+
__DOCGEN_CONFIG__: JSON.stringify(config)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
// Generate additional static files
|
|
62
|
+
await generateSitemap(options.output, config)
|
|
63
|
+
await generateRobotsTxt(options.output, config)
|
|
64
|
+
await copyAssets(options.output)
|
|
65
|
+
|
|
66
|
+
console.log('ā
Build completed successfully!')
|
|
67
|
+
|
|
68
|
+
} finally {
|
|
69
|
+
// Clean up temporary config file
|
|
70
|
+
try {
|
|
71
|
+
await fs.unlink(configPath)
|
|
72
|
+
} catch {
|
|
73
|
+
// Ignore if file doesn't exist
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function generateSitemap(outputDir: string, config: DocGenConfig) {
|
|
79
|
+
console.log('š Generating sitemap...')
|
|
80
|
+
|
|
81
|
+
const baseUrl = config.siteConfig.baseUrl.replace(/\/$/, '')
|
|
82
|
+
const urls: string[] = []
|
|
83
|
+
|
|
84
|
+
// Add home page
|
|
85
|
+
urls.push(`${baseUrl}/`)
|
|
86
|
+
|
|
87
|
+
// Add all skill pages
|
|
88
|
+
for (const file of config.files) {
|
|
89
|
+
const route = getFileRoute(file.path, config.siteConfig.skillsDir)
|
|
90
|
+
if (route !== '/') {
|
|
91
|
+
urls.push(`${baseUrl}${route}`)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
|
|
96
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
97
|
+
${urls.map(url => ` <url>
|
|
98
|
+
<loc>${url}</loc>
|
|
99
|
+
<lastmod>${new Date().toISOString().split('T')[0]}</lastmod>
|
|
100
|
+
<changefreq>weekly</changefreq>
|
|
101
|
+
<priority>0.8</priority>
|
|
102
|
+
</url>`).join('\\n')}
|
|
103
|
+
</urlset>`
|
|
104
|
+
|
|
105
|
+
await fs.writeFile(path.join(outputDir, 'sitemap.xml'), sitemap)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function generateRobotsTxt(outputDir: string, config: DocGenConfig) {
|
|
109
|
+
console.log('š¤ Generating robots.txt...')
|
|
110
|
+
|
|
111
|
+
const baseUrl = config.siteConfig.baseUrl.replace(/\/$/, '')
|
|
112
|
+
const robotsTxt = `User-agent: *
|
|
113
|
+
Allow: /
|
|
114
|
+
|
|
115
|
+
Sitemap: ${baseUrl}/sitemap.xml`
|
|
116
|
+
|
|
117
|
+
await fs.writeFile(path.join(outputDir, 'robots.txt'), robotsTxt)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function copyAssets(outputDir: string) {
|
|
121
|
+
console.log('š Copying static assets...')
|
|
122
|
+
|
|
123
|
+
// Create a simple favicon if it doesn't exist
|
|
124
|
+
const faviconPath = path.join(outputDir, 'favicon.ico')
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
await fs.access(faviconPath)
|
|
128
|
+
} catch {
|
|
129
|
+
// Create a simple SVG favicon
|
|
130
|
+
const faviconSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
131
|
+
<rect width="32" height="32" rx="4" fill="#3182ce"/>
|
|
132
|
+
<text x="16" y="22" text-anchor="middle" fill="white" font-family="system-ui" font-size="18" font-weight="bold">D</text>
|
|
133
|
+
</svg>`
|
|
134
|
+
|
|
135
|
+
await fs.writeFile(path.join(outputDir, 'favicon.svg'), faviconSvg)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function getFileRoute(filePath: string, baseDir: string): string {
|
|
140
|
+
// Normalize paths for comparison
|
|
141
|
+
const normalizedFilePath = path.normalize(filePath)
|
|
142
|
+
const normalizedBaseDir = path.normalize(baseDir)
|
|
143
|
+
|
|
144
|
+
// Check if file is within base directory
|
|
145
|
+
if (!normalizedFilePath.startsWith(normalizedBaseDir)) {
|
|
146
|
+
return '/'
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Get relative path from base directory
|
|
150
|
+
const relativePath = normalizedFilePath.slice(normalizedBaseDir.length)
|
|
151
|
+
const segments = relativePath.split(path.sep).filter(Boolean)
|
|
152
|
+
|
|
153
|
+
if (segments.length === 0) return '/'
|
|
154
|
+
|
|
155
|
+
const lastSegment = segments[segments.length - 1]
|
|
156
|
+
if (lastSegment.endsWith('.md')) {
|
|
157
|
+
segments[segments.length - 1] = lastSegment.slice(0, -3)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// SKILL.md and README.md should map to parent directory route
|
|
161
|
+
const finalSegment = segments[segments.length - 1]
|
|
162
|
+
if (finalSegment === 'SKILL' || finalSegment === 'README') {
|
|
163
|
+
segments.pop()
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return '/' + segments.join('/')
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export async function buildForProduction(sourceDir: string, outputDir: string): Promise<DocGenConfig> {
|
|
170
|
+
const { scanSkillsDirectory } = await import('../scanner/index.js')
|
|
171
|
+
const { parseSkillsToConfig } = await import('../parser/index.js')
|
|
172
|
+
|
|
173
|
+
const directories = await scanSkillsDirectory(sourceDir)
|
|
174
|
+
const config = await parseSkillsToConfig(directories, {
|
|
175
|
+
title: 'Documentation',
|
|
176
|
+
description: 'Documentation generated from source directory',
|
|
177
|
+
baseUrl: '/',
|
|
178
|
+
skillsDir: sourceDir,
|
|
179
|
+
outputDir
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
await buildSite({
|
|
183
|
+
input: sourceDir,
|
|
184
|
+
output: outputDir,
|
|
185
|
+
mode: 'production'
|
|
186
|
+
}, config)
|
|
187
|
+
|
|
188
|
+
return config
|
|
189
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { createServer, ViteDevServer } from 'vite'
|
|
2
|
+
import vue from '@vitejs/plugin-vue'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import { DocGenConfig } from '../types/index.js'
|
|
5
|
+
import { scanSkillsDirectory, watchSkillsDirectory } from '../scanner/index.js'
|
|
6
|
+
import { parseSkillsToConfig } from '../parser/index.js'
|
|
7
|
+
|
|
8
|
+
interface DevServerOptions {
|
|
9
|
+
port: number
|
|
10
|
+
skillsDir: string
|
|
11
|
+
config: DocGenConfig
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function createViteDevServer(options: DevServerOptions): Promise<ViteDevServer> {
|
|
15
|
+
let currentConfig = options.config
|
|
16
|
+
|
|
17
|
+
const server = await createServer({
|
|
18
|
+
root: path.resolve(process.cwd(), 'src/client'),
|
|
19
|
+
server: {
|
|
20
|
+
port: options.port,
|
|
21
|
+
host: 'localhost'
|
|
22
|
+
},
|
|
23
|
+
plugins: [
|
|
24
|
+
vue(),
|
|
25
|
+
// Custom plugin to inject config and handle API routes
|
|
26
|
+
{
|
|
27
|
+
name: 'docgen-dev',
|
|
28
|
+
configureServer(server) {
|
|
29
|
+
// API endpoint for configuration
|
|
30
|
+
server.middlewares.use('/api/config', (req, res, next) => {
|
|
31
|
+
if (req.method === 'GET') {
|
|
32
|
+
res.setHeader('Content-Type', 'application/json')
|
|
33
|
+
res.end(JSON.stringify(currentConfig))
|
|
34
|
+
} else {
|
|
35
|
+
next()
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// Inject config into HTML
|
|
40
|
+
server.middlewares.use((req, res, next) => {
|
|
41
|
+
if (req.url === '/' || req.url === '/index.html') {
|
|
42
|
+
// Let Vite handle the HTML transformation
|
|
43
|
+
next()
|
|
44
|
+
} else {
|
|
45
|
+
next()
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
},
|
|
49
|
+
transformIndexHtml: {
|
|
50
|
+
order: 'pre',
|
|
51
|
+
handler(html) {
|
|
52
|
+
// Inject lightweight config placeholder
|
|
53
|
+
// Full config will be loaded via API to avoid HTML parsing issues
|
|
54
|
+
const configScript = `
|
|
55
|
+
<script>
|
|
56
|
+
// Config will be loaded from /api/config
|
|
57
|
+
globalThis.__DOCGEN_CONFIG__ = null;
|
|
58
|
+
</script>
|
|
59
|
+
`
|
|
60
|
+
return html.replace('<head>', `<head>${configScript}`)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
resolve: {
|
|
66
|
+
alias: {
|
|
67
|
+
'@': path.resolve(process.cwd(), 'src/client')
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
define: {
|
|
71
|
+
__DOCGEN_CONFIG__: JSON.stringify(currentConfig)
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// Watch skills directory for changes
|
|
76
|
+
const watcher = await watchSkillsDirectory(options.skillsDir, async (directories) => {
|
|
77
|
+
console.log('š Skills directory changed, updating configuration...')
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
currentConfig = await parseSkillsToConfig(directories, currentConfig.siteConfig)
|
|
81
|
+
|
|
82
|
+
// Notify all connected clients to reload
|
|
83
|
+
server.ws.send({
|
|
84
|
+
type: 'full-reload'
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
console.log('ā
Configuration updated')
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error('ā Failed to update configuration:', error)
|
|
90
|
+
|
|
91
|
+
// Send error to clients
|
|
92
|
+
server.ws.send({
|
|
93
|
+
type: 'error',
|
|
94
|
+
err: {
|
|
95
|
+
message: `Failed to update configuration: ${error.message}`,
|
|
96
|
+
stack: error.stack
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
// Start the server
|
|
103
|
+
await server.listen()
|
|
104
|
+
|
|
105
|
+
// Extend server with cleanup method
|
|
106
|
+
const originalClose = server.close.bind(server)
|
|
107
|
+
server.close = async () => {
|
|
108
|
+
await watcher?.close()
|
|
109
|
+
return originalClose()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return server
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function createProductionBuild() {
|
|
116
|
+
// This will be implemented in the builder
|
|
117
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { buildSite } from '../../builder/index.js'
|
|
3
|
+
import { scanSkillsDirectory } from '../../scanner/index.js'
|
|
4
|
+
import { parseSkillsToConfig } from '../../parser/index.js'
|
|
5
|
+
|
|
6
|
+
interface BuildOptions {
|
|
7
|
+
dir: string
|
|
8
|
+
output: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function buildCommand(options: BuildOptions) {
|
|
12
|
+
console.log('šļø Building DocGen documentation site...')
|
|
13
|
+
|
|
14
|
+
const sourceDir = path.resolve(process.cwd(), options.dir)
|
|
15
|
+
const outputDir = path.resolve(process.cwd(), options.output)
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
// Scan source directory
|
|
19
|
+
console.log(`š Scanning source directory: ${sourceDir}`)
|
|
20
|
+
const directories = await scanSkillsDirectory(sourceDir)
|
|
21
|
+
|
|
22
|
+
console.log(`ā
Found ${directories.length} directories`)
|
|
23
|
+
|
|
24
|
+
// Parse to config
|
|
25
|
+
const config = await parseSkillsToConfig(directories, {
|
|
26
|
+
title: 'Documentation',
|
|
27
|
+
description: 'Documentation generated from source directory',
|
|
28
|
+
baseUrl: '/',
|
|
29
|
+
skillsDir: sourceDir,
|
|
30
|
+
outputDir
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
// Build site
|
|
34
|
+
console.log(`š¦ Building to: ${outputDir}`)
|
|
35
|
+
await buildSite({
|
|
36
|
+
input: sourceDir,
|
|
37
|
+
output: outputDir,
|
|
38
|
+
mode: 'production'
|
|
39
|
+
}, config)
|
|
40
|
+
|
|
41
|
+
console.log('ā
Build completed successfully!')
|
|
42
|
+
console.log(`š Built files are in: ${outputDir}`)
|
|
43
|
+
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('ā Build failed:', error)
|
|
46
|
+
process.exit(1)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { createViteDevServer } from '../../builder/vite-dev.js'
|
|
3
|
+
import { scanSkillsDirectory } from '../../scanner/index.js'
|
|
4
|
+
import { parseSkillsToConfig } from '../../parser/index.js'
|
|
5
|
+
|
|
6
|
+
interface DevOptions {
|
|
7
|
+
port: string
|
|
8
|
+
dir: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function devCommand(options: DevOptions) {
|
|
12
|
+
console.log('š Starting DocGen development server...')
|
|
13
|
+
|
|
14
|
+
const sourceDir = path.resolve(process.cwd(), options.dir)
|
|
15
|
+
const port = parseInt(options.port, 10)
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
// Check if source directory exists
|
|
19
|
+
console.log(`š Scanning source directory: ${sourceDir}`)
|
|
20
|
+
const directories = await scanSkillsDirectory(sourceDir)
|
|
21
|
+
|
|
22
|
+
console.log(`ā
Found ${directories.length} directories`)
|
|
23
|
+
|
|
24
|
+
// Parse to config
|
|
25
|
+
const config = await parseSkillsToConfig(directories, {
|
|
26
|
+
title: 'Documentation',
|
|
27
|
+
description: 'Documentation generated from source directory',
|
|
28
|
+
baseUrl: '/',
|
|
29
|
+
skillsDir: sourceDir,
|
|
30
|
+
outputDir: 'dist'
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
// Start Vite dev server
|
|
34
|
+
const server = await createViteDevServer({
|
|
35
|
+
port,
|
|
36
|
+
skillsDir: sourceDir,
|
|
37
|
+
config
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
console.log(`š Dev server running at http://localhost:${port}`)
|
|
41
|
+
|
|
42
|
+
// Handle graceful shutdown
|
|
43
|
+
process.on('SIGINT', async () => {
|
|
44
|
+
console.log('\\nš Shutting down dev server...')
|
|
45
|
+
await server.close()
|
|
46
|
+
process.exit(0)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('ā Failed to start dev server:', error)
|
|
51
|
+
process.exit(1)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import fs from 'fs/promises'
|
|
3
|
+
import sirv from 'sirv'
|
|
4
|
+
import { createServer } from 'http'
|
|
5
|
+
|
|
6
|
+
interface PreviewOptions {
|
|
7
|
+
port: string
|
|
8
|
+
output: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function previewCommand(options: PreviewOptions) {
|
|
12
|
+
console.log('š Starting DocGen preview server...')
|
|
13
|
+
|
|
14
|
+
const outputDir = path.resolve(process.cwd(), options.output)
|
|
15
|
+
const port = parseInt(options.port, 10)
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
// Check if build directory exists
|
|
19
|
+
try {
|
|
20
|
+
await fs.access(outputDir)
|
|
21
|
+
} catch {
|
|
22
|
+
console.error(`ā Build directory not found: ${outputDir}`)
|
|
23
|
+
console.log('š” Run "docgen build" first to generate the static site')
|
|
24
|
+
process.exit(1)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Create static file server
|
|
28
|
+
const serve = sirv(outputDir, {
|
|
29
|
+
single: true, // SPA mode
|
|
30
|
+
dev: false,
|
|
31
|
+
setHeaders: (res, pathname) => {
|
|
32
|
+
if (pathname.endsWith('.html') || pathname === '/') {
|
|
33
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const server = createServer(serve)
|
|
39
|
+
|
|
40
|
+
server.listen(port, () => {
|
|
41
|
+
console.log(`š Preview server running at http://localhost:${port}`)
|
|
42
|
+
console.log(`š Serving files from: ${outputDir}`)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// Handle graceful shutdown
|
|
46
|
+
process.on('SIGINT', () => {
|
|
47
|
+
console.log('\\nš Shutting down preview server...')
|
|
48
|
+
server.close(() => {
|
|
49
|
+
process.exit(0)
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error('ā Failed to start preview server:', error)
|
|
55
|
+
process.exit(1)
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander'
|
|
4
|
+
import { devCommand } from './commands/dev.js'
|
|
5
|
+
import { buildCommand } from './commands/build.js'
|
|
6
|
+
import { previewCommand } from './commands/preview.js'
|
|
7
|
+
|
|
8
|
+
const program = new Command()
|
|
9
|
+
|
|
10
|
+
program
|
|
11
|
+
.name('docmk')
|
|
12
|
+
.description('CLI tool for generating documentation from any directory')
|
|
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
|
+
|
|
21
|
+
program
|
|
22
|
+
.command('dev')
|
|
23
|
+
.description('Start development server')
|
|
24
|
+
.option('-p, --port <port>', 'Port to run dev server on', '3000')
|
|
25
|
+
.option('-d, --dir <directory>', 'Source directory path', './docs')
|
|
26
|
+
.action(devCommand)
|
|
27
|
+
|
|
28
|
+
program
|
|
29
|
+
.command('build')
|
|
30
|
+
.description('Build static documentation site')
|
|
31
|
+
.option('-d, --dir <directory>', 'Source directory path', './docs')
|
|
32
|
+
.option('-o, --output <directory>', 'Output directory', 'dist')
|
|
33
|
+
.action(buildCommand)
|
|
34
|
+
|
|
35
|
+
program
|
|
36
|
+
.command('preview')
|
|
37
|
+
.description('Preview built documentation site')
|
|
38
|
+
.option('-p, --port <port>', 'Port to run preview server on', '4173')
|
|
39
|
+
.option('-o, --output <directory>', 'Built site directory', 'dist')
|
|
40
|
+
.action(previewCommand)
|
|
41
|
+
|
|
42
|
+
program.parse()
|