@knowcode/doc-builder 1.4.9 → 1.4.11

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.
@@ -0,0 +1,497 @@
1
+ const chalk = require('chalk');
2
+ const prompts = require('prompts');
3
+ const { execSync } = require('child_process');
4
+ const fs = require('fs-extra');
5
+ const path = require('path');
6
+
7
+ /**
8
+ * Setup Vercel project for first-time deployment
9
+ */
10
+ async function setupVercelProject(config) {
11
+ console.log(chalk.blue('\nšŸ“‹ Setting up Vercel project...\n'));
12
+
13
+ // Check if Vercel CLI is installed
14
+ try {
15
+ execSync('vercel --version', { stdio: 'ignore' });
16
+ } catch (error) {
17
+ console.log(chalk.red('āŒ Vercel CLI not found!'));
18
+ console.log(chalk.gray('Install it with: npm install -g vercel'));
19
+ process.exit(1);
20
+ }
21
+
22
+ // Project setup questions
23
+ const answers = await prompts([
24
+ {
25
+ type: 'text',
26
+ name: 'projectName',
27
+ message: 'What is your project name?',
28
+ initial: config.siteName.toLowerCase().replace(/[^a-z0-9-]/g, '-') || 'my-docs',
29
+ hint: 'This will be your URL: project-name.vercel.app'
30
+ },
31
+ {
32
+ type: 'select',
33
+ name: 'framework',
34
+ message: 'Which framework preset?',
35
+ choices: [
36
+ { title: 'Other (Static HTML)', value: 'other' },
37
+ { title: 'Next.js', value: 'nextjs' },
38
+ { title: 'Vite', value: 'vite' }
39
+ ],
40
+ initial: 0,
41
+ hint: 'Choose "Other (Static HTML)" for doc-builder'
42
+ },
43
+ {
44
+ type: 'confirm',
45
+ name: 'publicAccess',
46
+ message: 'Make the deployment publicly accessible?',
47
+ initial: true,
48
+ hint: 'Choose Yes for public docs, No for team-only access'
49
+ }
50
+ ]);
51
+
52
+ // Create vercel.json in the output directory
53
+ const outputDir = path.join(process.cwd(), config.outputDir || 'html');
54
+ const vercelConfigPath = path.join(outputDir, 'vercel.json');
55
+
56
+ // Ensure output directory exists
57
+ if (!fs.existsSync(outputDir)) {
58
+ fs.mkdirSync(outputDir, { recursive: true });
59
+ }
60
+
61
+ // Create vercel.json that explicitly overrides build settings
62
+ const vercelConfig = {
63
+ "buildCommand": "",
64
+ "outputDirectory": ".",
65
+ "devCommand": "",
66
+ "installCommand": "",
67
+ "framework": null,
68
+ "cleanUrls": true,
69
+ "trailingSlash": false,
70
+ "headers": [
71
+ {
72
+ "source": "/css/(.*)",
73
+ "headers": [
74
+ {
75
+ "key": "Cache-Control",
76
+ "value": "public, max-age=31536000, immutable"
77
+ }
78
+ ]
79
+ },
80
+ {
81
+ "source": "/js/(.*)",
82
+ "headers": [
83
+ {
84
+ "key": "Cache-Control",
85
+ "value": "public, max-age=31536000, immutable"
86
+ }
87
+ ]
88
+ }
89
+ ]
90
+ };
91
+
92
+ fs.writeJsonSync(vercelConfigPath, vercelConfig, { spaces: 2 });
93
+ console.log(chalk.green(`āœ… Created vercel.json in ${config.outputDir || 'html'} directory`));
94
+
95
+ // Run Vercel setup with prominent instructions
96
+ console.log(chalk.blue('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
97
+ console.log(chalk.blue('šŸ”— Linking to Vercel - IMPORTANT INSTRUCTIONS'));
98
+ console.log(chalk.blue('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
99
+
100
+ console.log(chalk.yellow('āš ļø FOLLOW THESE ANSWERS CAREFULLY:\n'));
101
+
102
+ console.log(chalk.white('1ļøāƒ£ ') + chalk.green('Set up "~/Documents/.../html"?'));
103
+ console.log(chalk.white(' šŸ‘‰ Answer: ') + chalk.yellow.bold('YES') + '\n');
104
+
105
+ console.log(chalk.white('2ļøāƒ£ ') + chalk.green('Which scope should contain your project?'));
106
+ console.log(chalk.white(' šŸ‘‰ Answer: ') + chalk.yellow.bold('Select your account') + ' (usually your username)\n');
107
+
108
+ console.log(chalk.white('3ļøāƒ£ ') + chalk.green('Found project "username/html". Link to it?'));
109
+ console.log(chalk.white(' šŸ‘‰ Answer: ') + chalk.red.bold('NO') + ' (this is NOT your project!)\n');
110
+
111
+ console.log(chalk.white('4ļøāƒ£ ') + chalk.green('Link to different existing project?'));
112
+ console.log(chalk.white(' šŸ‘‰ Answer: ') + chalk.yellow.bold('YES') + ' if you have an existing project');
113
+ console.log(chalk.white(' šŸ‘‰ Answer: ') + chalk.yellow.bold('NO') + ' to create new project\n');
114
+
115
+ console.log(chalk.white('5ļøāƒ£ ') + chalk.green('What\'s the name of your existing project?'));
116
+ console.log(chalk.white(' šŸ‘‰ Answer: ') + chalk.yellow.bold(answers.projectName) + ' (your actual project name)\n');
117
+
118
+ console.log(chalk.red.bold('āš ļø CRITICAL WARNING ABOUT ROOT DIRECTORY:\n'));
119
+ console.log(chalk.bgRed.white.bold(' If Vercel asks about Root Directory or shows it in settings: '));
120
+ console.log(chalk.bgRed.white.bold(' LEAVE IT COMPLETELY EMPTY! DO NOT ENTER "html"! '));
121
+ console.log(chalk.white('\nWe are already in the html folder - setting Root Directory'));
122
+ console.log(chalk.white('to "html" will cause "html/html does not exist" errors!\n'));
123
+
124
+ console.log(chalk.blue('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
125
+
126
+ try {
127
+ // Run vercel link from the output directory
128
+ execSync('vercel link', {
129
+ stdio: 'inherit',
130
+ cwd: outputDir
131
+ });
132
+ } catch (error) {
133
+ console.error(chalk.red('Failed to link Vercel project'));
134
+ process.exit(1);
135
+ }
136
+
137
+ // Important reminders for Vercel settings
138
+ console.log(chalk.red('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
139
+ console.log(chalk.red.bold('🚨 CRITICAL POST-SETUP STEP - DO THIS NOW!'));
140
+ console.log(chalk.red('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
141
+
142
+ console.log(chalk.yellow.bold('Vercel may have set Root Directory incorrectly!\n'));
143
+
144
+ console.log(chalk.white.bold('1. GO TO YOUR PROJECT SETTINGS NOW:'));
145
+ console.log(chalk.cyan(` https://vercel.com/${answers.projectName}/settings\n`));
146
+
147
+ console.log(chalk.white.bold('2. Find "Root Directory" under "Build & Development Settings"\n'));
148
+
149
+ console.log(chalk.white.bold('3. CHECK THE VALUE:'));
150
+ console.log(chalk.green(' āœ… CORRECT: ') + chalk.green.bold('Empty (blank) or "./"'));
151
+ console.log(chalk.red(' āŒ WRONG: ') + chalk.red.bold('"html" or any other value\n'));
152
+
153
+ console.log(chalk.white.bold('4. IF IT SAYS "html":'));
154
+ console.log(chalk.yellow(' • DELETE the value completely'));
155
+ console.log(chalk.yellow(' • Leave it EMPTY'));
156
+ console.log(chalk.yellow(' • Click SAVE\n'));
157
+
158
+ console.log(chalk.bgRed.white.bold(' Failure to do this will cause deployment errors! '));
159
+ console.log();
160
+ console.log(chalk.yellow('šŸ” For Public Access:'));
161
+ console.log(chalk.white('1. Navigate to Project Settings > General'));
162
+ console.log(chalk.white('2. Under "Security", find "Deployment Protection"'));
163
+ console.log(chalk.white('3. Set "Deployment Protection" to ') + chalk.yellow.bold('Disabled'));
164
+ console.log();
165
+ console.log(chalk.cyan('Dashboard URL: https://vercel.com/dashboard'));
166
+ console.log();
167
+
168
+ // Add .vercel to .gitignore if not already there
169
+ const gitignorePath = path.join(process.cwd(), '.gitignore');
170
+ if (fs.existsSync(gitignorePath)) {
171
+ const gitignore = fs.readFileSync(gitignorePath, 'utf8');
172
+ if (!gitignore.includes('.vercel')) {
173
+ fs.appendFileSync(gitignorePath, '\n# Vercel\n.vercel\n');
174
+ console.log(chalk.green('āœ… Added .vercel to .gitignore'));
175
+ }
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Deploy to Vercel
181
+ */
182
+ async function deployToVercel(config, isProd = false) {
183
+ // Ensure the build output exists
184
+ const outputPath = path.join(process.cwd(), config.outputDir || 'html');
185
+ if (!fs.existsSync(outputPath)) {
186
+ throw new Error(`Output directory ${outputPath} does not exist. Run 'doc-builder build' first.`);
187
+ }
188
+
189
+ // Check if CSS files exist
190
+ const cssPath = path.join(outputPath, 'css', 'style.css');
191
+ if (!fs.existsSync(cssPath)) {
192
+ console.log(chalk.yellow('\nāš ļø Warning: CSS files not found in output directory!'));
193
+ console.log(chalk.yellow(' Your documentation may appear without styling.'));
194
+ console.log(chalk.cyan('\nšŸ’” To fix this:'));
195
+ console.log(chalk.white(' 1. Update to latest version: ') + chalk.gray('npm update @knowcode/doc-builder'));
196
+ console.log(chalk.white(' 2. Rebuild your docs: ') + chalk.gray('npx @knowcode/doc-builder build'));
197
+ console.log(chalk.white(' 3. Then deploy again: ') + chalk.gray('npx @knowcode/doc-builder deploy\n'));
198
+ }
199
+
200
+ // Simple deployment message
201
+ console.log(chalk.blue('\nšŸš€ Starting deployment to Vercel...'));
202
+ console.log(chalk.gray('This will take a few seconds...\n'));
203
+
204
+ // Create vercel.json in output directory for deployment
205
+ const vercelConfigPath = path.join(outputPath, 'vercel.json');
206
+ if (!fs.existsSync(vercelConfigPath)) {
207
+ // Create vercel.json that explicitly overrides build settings
208
+ const vercelConfig = {
209
+ "buildCommand": "",
210
+ "outputDirectory": ".",
211
+ "devCommand": "",
212
+ "installCommand": "",
213
+ "framework": null,
214
+ "cleanUrls": true,
215
+ "trailingSlash": false,
216
+ "headers": [
217
+ {
218
+ "source": "/css/(.*)",
219
+ "headers": [
220
+ {
221
+ "key": "Cache-Control",
222
+ "value": "public, max-age=31536000, immutable"
223
+ }
224
+ ]
225
+ },
226
+ {
227
+ "source": "/js/(.*)",
228
+ "headers": [
229
+ {
230
+ "key": "Cache-Control",
231
+ "value": "public, max-age=31536000, immutable"
232
+ }
233
+ ]
234
+ }
235
+ ]
236
+ };
237
+ fs.writeJsonSync(vercelConfigPath, vercelConfig, { spaces: 2 });
238
+ }
239
+
240
+ // Deploy command with explicit build settings
241
+ const deployArgs = [];
242
+
243
+ if (isProd) {
244
+ deployArgs.push('--prod');
245
+ }
246
+
247
+ const deployCmd = `vercel ${deployArgs.join(' ')}`;
248
+
249
+ try {
250
+ // Run deployment from the output directory with real-time output
251
+ const { spawn } = require('child_process');
252
+
253
+ return new Promise((resolve, reject) => {
254
+ const vercelProcess = spawn('vercel', deployArgs, {
255
+ cwd: outputPath,
256
+ env: {
257
+ ...process.env,
258
+ // Force Vercel to skip build
259
+ VERCEL_BUILD_SKIP: '1'
260
+ },
261
+ shell: true
262
+ });
263
+
264
+ let deployUrl = '';
265
+
266
+ // Capture stdout in real-time
267
+ vercelProcess.stdout.on('data', (data) => {
268
+ const output = data.toString();
269
+ process.stdout.write(output); // Show output in real-time
270
+
271
+ // Try to extract URL from output
272
+ const urlMatch = output.match(/https:\/\/[^\s]+/);
273
+ if (urlMatch) {
274
+ deployUrl = urlMatch[0];
275
+ }
276
+ });
277
+
278
+ // Capture stderr
279
+ vercelProcess.stderr.on('data', (data) => {
280
+ process.stderr.write(data.toString());
281
+ });
282
+
283
+ vercelProcess.on('close', (code) => {
284
+ if (code === 0) {
285
+ resolve(deployUrl || 'Check Vercel dashboard');
286
+ } else {
287
+ reject(new Error(`Vercel deployment failed with code ${code}`));
288
+ }
289
+ });
290
+
291
+ vercelProcess.on('error', (err) => {
292
+ reject(new Error(`Failed to start Vercel process: ${err.message}`));
293
+ });
294
+ });
295
+ } catch (error) {
296
+ // Check if this is the common "html/html" path error
297
+ if (error.message && error.message.includes('html/html') && error.message.includes('does not exist')) {
298
+ console.log(chalk.red.bold('\nāŒ ERROR: Vercel has incorrect Root Directory settings!\n'));
299
+ console.log(chalk.yellow('The project is configured with Root Directory = "html"'));
300
+ console.log(chalk.yellow('But we are already deploying FROM the html directory.\n'));
301
+
302
+ console.log(chalk.green.bold('šŸ”§ TO FIX THIS:\n'));
303
+ console.log(chalk.white('1. Go to: ') + chalk.cyan(error.message.match(/https:\/\/vercel\.com\/[^\s]+/)?.[0] || 'https://vercel.com/dashboard'));
304
+ console.log(chalk.white('2. Find "Root Directory" under "Build & Development Settings"'));
305
+ console.log(chalk.white('3. ') + chalk.yellow.bold('DELETE the "html" value (leave it EMPTY)'));
306
+ console.log(chalk.white('4. Click "Save"'));
307
+ console.log(chalk.white('5. Run this command again\n'));
308
+
309
+ console.log(chalk.gray('Alternative: Delete html/.vercel folder and set up fresh'));
310
+
311
+ throw new Error('Root Directory misconfiguration - see instructions above');
312
+ }
313
+
314
+ // Check if this is the buildCommand error
315
+ if (error.message && error.message.includes('buildCommand') && error.message.includes('should be string,null')) {
316
+ console.log(chalk.red.bold('\nāŒ ERROR: Vercel has saved build settings that conflict!\n'));
317
+ console.log(chalk.yellow('Your Vercel project has build settings that need to be cleared.\n'));
318
+
319
+ console.log(chalk.green.bold('šŸ”§ TO FIX THIS:\n'));
320
+ console.log(chalk.white('Option 1 - Clear project settings:'));
321
+ console.log(chalk.cyan('1. Go to your project settings'));
322
+ console.log(chalk.cyan('2. Under "Build & Development Settings"'));
323
+ console.log(chalk.cyan('3. Clear ALL fields (Build Command, Output Directory, etc.)'));
324
+ console.log(chalk.cyan('4. Save and try again\n'));
325
+
326
+ console.log(chalk.white('Option 2 - Reset and start fresh:'));
327
+ console.log(chalk.cyan('1. Run: npx @knowcode/doc-builder reset-vercel'));
328
+ console.log(chalk.cyan('2. Run: npx @knowcode/doc-builder deploy'));
329
+ console.log(chalk.cyan('3. Create a NEW project (don\'t link to existing)\n'));
330
+
331
+ throw new Error('Build settings conflict - see instructions above');
332
+ }
333
+
334
+ throw new Error(`Vercel deployment failed: ${error.message}`);
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Create deployment-specific files
340
+ */
341
+ async function prepareDeployment(config) {
342
+ const outputDir = path.join(process.cwd(), config.outputDir || 'html');
343
+
344
+ // Log version for debugging
345
+ const packageJson = require('../package.json');
346
+ console.log(chalk.blue(`\nšŸ“¦ Preparing deployment with @knowcode/doc-builder v${packageJson.version}`));
347
+
348
+ // Create index.html from README.html if needed
349
+ console.log(chalk.blue('\nšŸ“ Deployment preparation - index.html check:'));
350
+ const indexPath = path.join(outputDir, 'index.html');
351
+ const readmePath = path.join(outputDir, 'README.html');
352
+
353
+ console.log(chalk.gray(` - Output directory: ${outputDir}`));
354
+ console.log(chalk.gray(` - Output dir exists: ${fs.existsSync(outputDir)}`));
355
+
356
+ if (fs.existsSync(outputDir)) {
357
+ const files = fs.readdirSync(outputDir);
358
+ const htmlFiles = files.filter(f => f.endsWith('.html'));
359
+ console.log(chalk.gray(` - Total files: ${files.length}`));
360
+ console.log(chalk.gray(` - HTML files: [${htmlFiles.slice(0, 5).join(', ')}${htmlFiles.length > 5 ? '...' : ''}]`));
361
+ }
362
+
363
+ console.log(chalk.gray(` - Checking index.html: ${fs.existsSync(indexPath) ? 'exists' : 'missing'}`));
364
+ console.log(chalk.gray(` - Index path: ${indexPath}`));
365
+
366
+ // Check if we need to create/replace index.html
367
+ let shouldCreateIndex = false;
368
+
369
+ if (!fs.existsSync(indexPath)) {
370
+ console.log(chalk.yellow(' āš ļø index.html is missing, attempting to create...'));
371
+ shouldCreateIndex = true;
372
+ } else {
373
+ // Check if existing index.html needs replacement
374
+ const indexStats = fs.statSync(indexPath);
375
+ const indexContent = fs.readFileSync(indexPath, 'utf8');
376
+
377
+ if (indexStats.size < 3000 || (indexContent.includes('<title>Documentation</title>') && indexContent.includes('<ul>') && !indexContent.includes('class="navigation"'))) {
378
+ console.log(chalk.yellow(` āš ļø Existing index.html appears to be a directory listing (${indexStats.size} bytes), will replace`));
379
+ shouldCreateIndex = true;
380
+ } else if (!indexContent.includes('@knowcode/doc-builder')) {
381
+ console.log(chalk.yellow(' āš ļø Existing index.html was not created by doc-builder, will replace'));
382
+ shouldCreateIndex = true;
383
+ }
384
+ }
385
+
386
+ if (shouldCreateIndex) {
387
+ console.log(chalk.gray(` - Checking README.html: ${fs.existsSync(readmePath) ? 'exists' : 'missing'}`));
388
+ console.log(chalk.gray(` - README path: ${readmePath}`));
389
+
390
+ if (fs.existsSync(readmePath)) {
391
+ // Copy README.html to index.html for proper root page
392
+ console.log(chalk.blue(' → Copying README.html to index.html...'));
393
+ try {
394
+ fs.copyFileSync(readmePath, indexPath);
395
+ console.log(chalk.green(' āœ… Successfully copied README.html to index.html'));
396
+
397
+ // Verify the copy
398
+ if (fs.existsSync(indexPath)) {
399
+ const readmeStats = fs.statSync(readmePath);
400
+ const indexStats = fs.statSync(indexPath);
401
+ console.log(chalk.gray(` - README.html size: ${readmeStats.size} bytes`));
402
+ console.log(chalk.gray(` - index.html size: ${indexStats.size} bytes`));
403
+
404
+ if (readmeStats.size === indexStats.size) {
405
+ console.log(chalk.green(' āœ… File sizes match - copy successful'));
406
+ } else {
407
+ console.log(chalk.yellow(' āš ļø File sizes do not match!'));
408
+ }
409
+ } else {
410
+ console.log(chalk.red(' āŒ ERROR: index.html was not created after copy!'));
411
+ }
412
+ } catch (error) {
413
+ console.log(chalk.red(` āŒ ERROR copying file: ${error.message}`));
414
+ console.log(chalk.red(` - Error stack: ${error.stack}`));
415
+ }
416
+ } else {
417
+ // If no README.html, find first available HTML file or create informative page
418
+ console.log(chalk.yellow('āš ļø No README.html found, looking for other HTML files...'));
419
+
420
+ // Find first available HTML file
421
+ const htmlFiles = fs.readdirSync(outputDir)
422
+ .filter(file => file.endsWith('.html') && file !== 'index.html' && file !== 'login.html' && file !== 'logout.html')
423
+ .sort();
424
+
425
+ if (htmlFiles.length > 0) {
426
+ // Redirect to first HTML file
427
+ const firstFile = htmlFiles[0];
428
+ console.log(chalk.green(`āœ… Creating index.html redirect to ${firstFile}`));
429
+ const redirectIndex = `<!DOCTYPE html>
430
+ <html>
431
+ <head>
432
+ <meta charset="UTF-8">
433
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
434
+ <meta http-equiv="refresh" content="0; url=${firstFile}">
435
+ <title>${config.siteName || 'Documentation'}</title>
436
+ <link rel="stylesheet" href="/css/style.css">
437
+ <link rel="stylesheet" href="/css/notion-style.css">
438
+ </head>
439
+ <body>
440
+ <div style="text-align: center; margin-top: 50px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;">
441
+ <h1>šŸ“š ${config.siteName || 'Documentation'}</h1>
442
+ <p>Redirecting to documentation...</p>
443
+ <p><a href="${firstFile}" style="color: #0366d6;">Click here if not redirected automatically</a></p>
444
+ </div>
445
+ </body>
446
+ </html>`;
447
+ fs.writeFileSync(indexPath, redirectIndex);
448
+ console.log(chalk.green(`āœ… Created index.html redirect to ${firstFile}`));
449
+ } else {
450
+ // No HTML files at all - this should never happen after build
451
+ console.log(chalk.red('āŒ No HTML files found in output directory!'));
452
+ console.log(chalk.yellow('šŸ“Œ This indicates a build issue. Please run: npx @knowcode/doc-builder build'));
453
+
454
+ // Create emergency fallback page
455
+ const { createDefaultIndexPage } = require('./core-builder');
456
+ const fallbackIndex = await createDefaultIndexPage(outputDir, config, packageJson.version);
457
+ fs.writeFileSync(indexPath, fallbackIndex);
458
+ console.log(chalk.green('āœ… Created fallback index.html with instructions'));
459
+ }
460
+ }
461
+ } else {
462
+ console.log(chalk.gray(' āœ“ index.html already exists and appears valid'));
463
+ const stats = fs.statSync(indexPath);
464
+ console.log(chalk.gray(` - Keeping existing index.html (${stats.size} bytes)`));
465
+ }
466
+
467
+ // Final check - log what files exist
468
+ console.log(chalk.blue('\nšŸ“ Final deployment state:'));
469
+
470
+ // Double-check index.html one more time
471
+ const finalIndexExists = fs.existsSync(indexPath);
472
+ console.log(chalk[finalIndexExists ? 'green' : 'red'](` - index.html: ${finalIndexExists ? 'EXISTS' : 'MISSING'}`));
473
+
474
+ if (finalIndexExists) {
475
+ const stats = fs.statSync(indexPath);
476
+ console.log(chalk.gray(` - index.html size: ${stats.size} bytes`));
477
+ console.log(chalk.gray(` - index.html modified: ${stats.mtime.toISOString()}`));
478
+ }
479
+
480
+ const finalFiles = fs.readdirSync(outputDir)
481
+ .filter(file => file.endsWith('.html'))
482
+ .slice(0, 5); // Show first 5 HTML files
483
+ console.log(chalk.gray(`\n HTML files in ${outputDir}:`));
484
+ finalFiles.forEach(file => {
485
+ const size = fs.statSync(path.join(outputDir, file)).size;
486
+ console.log(chalk.gray(` - ${file} (${size} bytes)`));
487
+ });
488
+ if (fs.readdirSync(outputDir).filter(f => f.endsWith('.html')).length > 5) {
489
+ console.log(chalk.gray(` - ... and ${fs.readdirSync(outputDir).filter(f => f.endsWith('.html')).length - 5} more HTML files`));
490
+ }
491
+ }
492
+
493
+ module.exports = {
494
+ setupVercelProject,
495
+ deployToVercel,
496
+ prepareDeployment
497
+ };
@@ -0,0 +1,96 @@
1
+ const http = require('http');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const chalk = require('chalk');
5
+
6
+ /**
7
+ * Simple development server
8
+ */
9
+ async function startDevServer(config, port = 3000) {
10
+ const outputDir = path.join(process.cwd(), config.outputDir);
11
+
12
+ if (!fs.existsSync(outputDir)) {
13
+ console.log(chalk.yellow('Output directory not found. Building first...'));
14
+ const { build } = require('./builder');
15
+ await build(config);
16
+ }
17
+
18
+ const server = http.createServer((req, res) => {
19
+ let filePath = path.join(outputDir, req.url === '/' ? 'index.html' : req.url);
20
+
21
+ // Add .html extension if not present and not a file with extension
22
+ if (!path.extname(filePath) && !filePath.endsWith('/')) {
23
+ filePath += '.html';
24
+ }
25
+
26
+ // Serve index.html for directories
27
+ if (filePath.endsWith('/')) {
28
+ filePath += 'index.html';
29
+ }
30
+
31
+ fs.readFile(filePath, (err, content) => {
32
+ if (err) {
33
+ if (err.code === 'ENOENT') {
34
+ res.writeHead(404);
35
+ res.end('404 Not Found');
36
+ } else {
37
+ res.writeHead(500);
38
+ res.end(`Server Error: ${err.code}`);
39
+ }
40
+ } else {
41
+ // Determine content type
42
+ const ext = path.extname(filePath);
43
+ let contentType = 'text/html';
44
+ switch (ext) {
45
+ case '.js':
46
+ contentType = 'text/javascript';
47
+ break;
48
+ case '.css':
49
+ contentType = 'text/css';
50
+ break;
51
+ case '.json':
52
+ contentType = 'application/json';
53
+ break;
54
+ case '.png':
55
+ contentType = 'image/png';
56
+ break;
57
+ case '.jpg':
58
+ contentType = 'image/jpg';
59
+ break;
60
+ }
61
+
62
+ res.writeHead(200, { 'Content-Type': contentType });
63
+ res.end(content, 'utf-8');
64
+ }
65
+ });
66
+ });
67
+
68
+ server.listen(port, () => {
69
+ console.log(chalk.green(`\nšŸ“” Development server running at: ${chalk.cyan(`http://localhost:${port}`)}\n`));
70
+ console.log(chalk.gray('Press Ctrl+C to stop'));
71
+ });
72
+
73
+ // Watch for changes (basic implementation)
74
+ if (config.watch !== false) {
75
+ const docsDir = path.join(process.cwd(), config.docsDir);
76
+ console.log(chalk.gray(`Watching for changes in ${config.docsDir}...`));
77
+
78
+ // Simple file watcher - in production, use chokidar
79
+ fs.watch(docsDir, { recursive: true }, async (eventType, filename) => {
80
+ if (filename && filename.endsWith('.md')) {
81
+ console.log(chalk.yellow(`\nšŸ”„ ${filename} changed, rebuilding...`));
82
+ try {
83
+ const { build } = require('./builder');
84
+ await build(config);
85
+ console.log(chalk.green('āœ… Rebuild complete'));
86
+ } catch (error) {
87
+ console.error(chalk.red(`āŒ Rebuild failed: ${error.message}`));
88
+ }
89
+ }
90
+ });
91
+ }
92
+ }
93
+
94
+ module.exports = {
95
+ startDevServer
96
+ };
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@knowcode/doc-builder",
3
+ "version": "1.4.2",
4
+ "description": "Reusable documentation builder for markdown-based sites with Vercel deployment support",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "doc-builder": "scripts/npx-runner.js"
8
+ },
9
+ "scripts": {
10
+ "postinstall": "node scripts/setup.js || true",
11
+ "test": "echo \"Error: no test specified\" && exit 1"
12
+ },
13
+ "keywords": [
14
+ "documentation",
15
+ "markdown",
16
+ "static-site-generator",
17
+ "vercel",
18
+ "notion-style",
19
+ "doc-builder"
20
+ ],
21
+ "author": "KnowCode",
22
+ "license": "MIT",
23
+ "dependencies": {
24
+ "marked": "^15.0.12",
25
+ "commander": "^11.0.0",
26
+ "chalk": "^4.1.2",
27
+ "prompts": "^2.4.2",
28
+ "ora": "5.4.1",
29
+ "fs-extra": "^11.2.0"
30
+ },
31
+ "engines": {
32
+ "node": ">=14.0.0"
33
+ }
34
+ }
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * NPX runner script for @knowcode/doc-builder
5
+ * This script enables zero-configuration usage via npx
6
+ */
7
+
8
+ const { execSync } = require('child_process');
9
+ const path = require('path');
10
+ const fs = require('fs');
11
+
12
+ // Get the command from arguments - no default command
13
+ const [,, ...args] = process.argv;
14
+
15
+ // Path to the actual CLI
16
+ const cliPath = path.join(__dirname, '..', 'cli.js');
17
+
18
+ // Build the command - pass all arguments through
19
+ const fullCommand = `node "${cliPath}" ${args.join(' ')}`;
20
+
21
+ try {
22
+ // Execute the CLI with stdio inherited to preserve colors and interactivity
23
+ execSync(fullCommand, { stdio: 'inherit' });
24
+ } catch (error) {
25
+ // Exit with the same code as the CLI
26
+ process.exit(error.status || 1);
27
+ }