@knowcode/doc-builder 1.4.25 ā 1.5.1
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/settings.local.json +2 -1
- package/CHANGELOG.md +52 -0
- package/README.md +57 -7
- package/assets/404.html +115 -0
- package/assets/js/main.js +23 -0
- package/cli.js +251 -2
- package/doc-builder.config.js +34 -0
- package/html/404.html +115 -0
- package/html/README.html +71 -5
- package/html/claude-workflow-guide.html +71 -5
- package/html/documentation-index.html +77 -5
- package/html/guides/authentication-guide.html +77 -5
- package/html/guides/documentation-standards.html +77 -5
- package/html/guides/seo-guide.html +557 -0
- package/html/guides/troubleshooting-guide.html +77 -5
- package/html/index.html +71 -5
- package/html/js/main.js +23 -0
- package/html/robots.txt +5 -0
- package/html/sitemap.xml +75 -0
- package/html/vercel-cli-setup-guide.html +452 -0
- package/html/vercel-first-time-setup-guide.html +419 -0
- package/lib/config.js +39 -1
- package/lib/core-builder.js +110 -5
- package/lib/deploy.js +61 -2
- package/lib/seo.js +359 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,58 @@ All notable changes to @knowcode/doc-builder will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.5.1] - 2025-07-21
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Automatic redirect from .md URLs to their corresponding .html files
|
|
12
|
+
- Client-side JavaScript to intercept clicks on .md links and redirect to .html
|
|
13
|
+
- Custom 404.html page with automatic redirect logic for direct .md URL navigation
|
|
14
|
+
- Enhanced vercel.json generation to include 404 page routing
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- Fixed 404 errors when clicking or navigating to .md links
|
|
18
|
+
- Links like `claude-workflow-guide.md` now automatically redirect to `claude-workflow-guide.html`
|
|
19
|
+
|
|
20
|
+
### Background
|
|
21
|
+
- Users reported that links to .md files resulted in 404 errors
|
|
22
|
+
- Implemented client-side solution to handle both link clicks and direct navigation
|
|
23
|
+
- Works seamlessly with Vercel's cleanUrls configuration
|
|
24
|
+
|
|
25
|
+
## [1.5.0] - 2025-07-21
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- Comprehensive SEO features including meta tags, Open Graph, Twitter Cards
|
|
29
|
+
- JSON-LD structured data for better search engine understanding
|
|
30
|
+
- Automatic sitemap.xml and robots.txt generation
|
|
31
|
+
- Interactive `setup-seo` CLI command to configure SEO settings
|
|
32
|
+
- SEO configuration options in doc-builder.config.js
|
|
33
|
+
- Production URL configuration with `set-production-url` command
|
|
34
|
+
- Support for custom production URLs via config file, CLI command, or deployment flag
|
|
35
|
+
|
|
36
|
+
### Improved
|
|
37
|
+
- Better deployment URL detection with multiple fallback methods
|
|
38
|
+
- Enhanced meta tag generation with author, keywords, and canonical URLs
|
|
39
|
+
- Social media preview support with customizable images and descriptions
|
|
40
|
+
|
|
41
|
+
### Documentation
|
|
42
|
+
- Added comprehensive SEO guide explaining all features and configuration
|
|
43
|
+
- Updated troubleshooting guide with npx cache clearing instructions
|
|
44
|
+
|
|
45
|
+
## [1.4.26] - 2025-07-21
|
|
46
|
+
|
|
47
|
+
### Improved
|
|
48
|
+
- Clarified Vercel setup instructions to show both paths for question #5
|
|
49
|
+
- Now clearly indicates what prompt users see based on their answer to question #4
|
|
50
|
+
- Added "If you answered YES/NO to #4" conditional guidance
|
|
51
|
+
- Shows appropriate project name prompts for both existing and new projects
|
|
52
|
+
|
|
53
|
+
### Background
|
|
54
|
+
- Users were confused about what happens after answering NO to "Link to different existing project?"
|
|
55
|
+
- The instructions now clearly show both scenarios:
|
|
56
|
+
- YES: "What's the name of your existing project?"
|
|
57
|
+
- NO: "What is your project name?" (for creating new project)
|
|
58
|
+
- This matches the actual Vercel CLI flow more accurately
|
|
59
|
+
|
|
8
60
|
## [1.4.25] - 2025-07-21
|
|
9
61
|
|
|
10
62
|
### Fixed
|
package/README.md
CHANGED
|
@@ -54,6 +54,7 @@ This aligns perfectly with our mission: beautiful documentation should be access
|
|
|
54
54
|
- š **Dark Mode** - Automatic dark mode support
|
|
55
55
|
- š **Live Reload** - Development server with hot reloading
|
|
56
56
|
- āļø **Vercel Integration** - One-command deployment to Vercel
|
|
57
|
+
- š **SEO Optimized** - Meta tags, Open Graph, Twitter Cards, and structured data
|
|
57
58
|
- š¦ **Self-Contained** - No configuration or setup required
|
|
58
59
|
- š¤ **Claude Code Ready** - Optimized for AI-generated documentation workflows
|
|
59
60
|
|
|
@@ -119,6 +120,9 @@ module.exports = {
|
|
|
119
120
|
siteName: 'My Documentation',
|
|
120
121
|
siteDescription: 'Documentation for my project',
|
|
121
122
|
|
|
123
|
+
// Production URL (optional)
|
|
124
|
+
productionUrl: 'https://my-docs.vercel.app', // Custom URL to display after deployment
|
|
125
|
+
|
|
122
126
|
// Features
|
|
123
127
|
features: {
|
|
124
128
|
authentication: true,
|
|
@@ -145,6 +149,33 @@ npx @knowcode/doc-builder build --preset notion-inspired
|
|
|
145
149
|
|
|
146
150
|
## Commands
|
|
147
151
|
|
|
152
|
+
### set-production-url
|
|
153
|
+
Set a custom production URL to display after deployment:
|
|
154
|
+
```bash
|
|
155
|
+
# Set your custom production URL
|
|
156
|
+
npx @knowcode/doc-builder set-production-url doc-builder-delta.vercel.app
|
|
157
|
+
|
|
158
|
+
# Or with full protocol
|
|
159
|
+
npx @knowcode/doc-builder set-production-url https://my-custom-domain.com
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
This is useful when you have a custom domain or Vercel alias that differs from the auto-detected URL.
|
|
163
|
+
|
|
164
|
+
### setup-seo
|
|
165
|
+
Interactive SEO configuration wizard:
|
|
166
|
+
```bash
|
|
167
|
+
# Configure all SEO settings
|
|
168
|
+
npx @knowcode/doc-builder setup-seo
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
This wizard helps you set up:
|
|
172
|
+
- Site URL and author information
|
|
173
|
+
- Social media meta tags (Open Graph, Twitter Cards)
|
|
174
|
+
- Structured data (JSON-LD)
|
|
175
|
+
- Automatic sitemap and robots.txt generation
|
|
176
|
+
|
|
177
|
+
See the [SEO Guide](docs/guides/seo-guide.md) for complete details.
|
|
178
|
+
|
|
148
179
|
### build
|
|
149
180
|
Build the documentation site to static HTML:
|
|
150
181
|
```bash
|
|
@@ -189,15 +220,15 @@ Deploy documentation to Vercel (requires Vercel CLI):
|
|
|
189
220
|
doc-builder deploy [options]
|
|
190
221
|
|
|
191
222
|
Options:
|
|
192
|
-
-c, --config <path>
|
|
193
|
-
--prod
|
|
194
|
-
--
|
|
195
|
-
--
|
|
223
|
+
-c, --config <path> Path to config file (default: "doc-builder.config.js")
|
|
224
|
+
--no-prod Deploy as preview instead of production
|
|
225
|
+
--force Force deployment without confirmation
|
|
226
|
+
--production-url <url> Override production URL for this deployment
|
|
196
227
|
|
|
197
228
|
Examples:
|
|
198
|
-
doc-builder deploy
|
|
199
|
-
doc-builder deploy --prod
|
|
200
|
-
doc-builder deploy --
|
|
229
|
+
doc-builder deploy # Deploy to production
|
|
230
|
+
doc-builder deploy --no-prod # Deploy as preview only
|
|
231
|
+
doc-builder deploy --production-url my-docs.vercel.app # Use custom URL display
|
|
201
232
|
|
|
202
233
|
First-time setup:
|
|
203
234
|
The tool will guide you through:
|
|
@@ -324,6 +355,25 @@ npx @knowcode/doc-builder@1.4.22
|
|
|
324
355
|
- Ensure Vercel CLI is installed: `npm install -g vercel`
|
|
325
356
|
- Check that the `html/` directory was created by build command
|
|
326
357
|
|
|
358
|
+
**Wrong production URL displayed**
|
|
359
|
+
- The deployment may show a deployment-specific URL instead of your custom domain
|
|
360
|
+
- Solution 1: Set production URL in config:
|
|
361
|
+
```javascript
|
|
362
|
+
// doc-builder.config.js
|
|
363
|
+
module.exports = {
|
|
364
|
+
productionUrl: 'https://my-docs.vercel.app',
|
|
365
|
+
// ... other config
|
|
366
|
+
};
|
|
367
|
+
```
|
|
368
|
+
- Solution 2: Use command to set it:
|
|
369
|
+
```bash
|
|
370
|
+
npx @knowcode/doc-builder set-production-url my-docs.vercel.app
|
|
371
|
+
```
|
|
372
|
+
- Solution 3: Override for a single deployment:
|
|
373
|
+
```bash
|
|
374
|
+
npx @knowcode/doc-builder deploy --production-url my-docs.vercel.app
|
|
375
|
+
```
|
|
376
|
+
|
|
327
377
|
## Using in Other Projects
|
|
328
378
|
|
|
329
379
|
### Option 1: NPM Link (Development)
|
package/assets/404.html
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Page Not Found - Redirecting...</title>
|
|
7
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
8
|
+
<style>
|
|
9
|
+
body {
|
|
10
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
11
|
+
margin: 0;
|
|
12
|
+
padding: 0;
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: center;
|
|
16
|
+
min-height: 100vh;
|
|
17
|
+
background-color: #f7f7f5;
|
|
18
|
+
color: #37352f;
|
|
19
|
+
}
|
|
20
|
+
.container {
|
|
21
|
+
text-align: center;
|
|
22
|
+
padding: 2rem;
|
|
23
|
+
max-width: 600px;
|
|
24
|
+
}
|
|
25
|
+
h1 {
|
|
26
|
+
font-size: 3rem;
|
|
27
|
+
margin: 0 0 1rem 0;
|
|
28
|
+
color: #37352f;
|
|
29
|
+
}
|
|
30
|
+
p {
|
|
31
|
+
font-size: 1.125rem;
|
|
32
|
+
line-height: 1.6;
|
|
33
|
+
color: #6b6b6b;
|
|
34
|
+
margin: 0 0 2rem 0;
|
|
35
|
+
}
|
|
36
|
+
a {
|
|
37
|
+
color: #0366d6;
|
|
38
|
+
text-decoration: none;
|
|
39
|
+
}
|
|
40
|
+
a:hover {
|
|
41
|
+
text-decoration: underline;
|
|
42
|
+
}
|
|
43
|
+
.emoji {
|
|
44
|
+
font-size: 4rem;
|
|
45
|
+
margin-bottom: 1rem;
|
|
46
|
+
}
|
|
47
|
+
.loading {
|
|
48
|
+
display: none;
|
|
49
|
+
color: #0366d6;
|
|
50
|
+
margin-top: 1rem;
|
|
51
|
+
}
|
|
52
|
+
.redirect-message {
|
|
53
|
+
display: none;
|
|
54
|
+
background-color: #e8f4fd;
|
|
55
|
+
border: 1px solid #c3e0f7;
|
|
56
|
+
padding: 1rem;
|
|
57
|
+
border-radius: 8px;
|
|
58
|
+
margin-top: 1rem;
|
|
59
|
+
}
|
|
60
|
+
</style>
|
|
61
|
+
</head>
|
|
62
|
+
<body>
|
|
63
|
+
<div class="container">
|
|
64
|
+
<div class="emoji">š</div>
|
|
65
|
+
<h1>404</h1>
|
|
66
|
+
<p id="message">The page you're looking for doesn't exist.</p>
|
|
67
|
+
<div id="redirect-message" class="redirect-message">
|
|
68
|
+
Redirecting to the correct page...
|
|
69
|
+
</div>
|
|
70
|
+
<p id="loading" class="loading">Redirecting...</p>
|
|
71
|
+
<p>
|
|
72
|
+
<a href="/" id="home-link">Go to Home</a>
|
|
73
|
+
</p>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<script>
|
|
77
|
+
// Check if the URL ends with .md
|
|
78
|
+
const pathname = window.location.pathname;
|
|
79
|
+
|
|
80
|
+
if (pathname.endsWith('.md')) {
|
|
81
|
+
// Convert .md to .html
|
|
82
|
+
const htmlPath = pathname.replace(/\.md$/, '.html');
|
|
83
|
+
|
|
84
|
+
// Show redirect message
|
|
85
|
+
document.getElementById('message').textContent = 'Found a markdown link. Redirecting to the HTML version...';
|
|
86
|
+
document.getElementById('redirect-message').style.display = 'block';
|
|
87
|
+
document.getElementById('loading').style.display = 'block';
|
|
88
|
+
|
|
89
|
+
// Redirect after a brief delay to show the message
|
|
90
|
+
setTimeout(() => {
|
|
91
|
+
window.location.replace(htmlPath);
|
|
92
|
+
}, 500);
|
|
93
|
+
} else {
|
|
94
|
+
// For true 404s, show the standard message
|
|
95
|
+
document.getElementById('message').textContent = "The page you're looking for doesn't exist.";
|
|
96
|
+
|
|
97
|
+
// Also check if we can suggest a similar page
|
|
98
|
+
// Remove common suffixes and try to find a match
|
|
99
|
+
const basePath = pathname
|
|
100
|
+
.replace(/\.(html|htm|php|asp|aspx)$/, '')
|
|
101
|
+
.replace(/\/$/, '');
|
|
102
|
+
|
|
103
|
+
if (basePath && basePath !== pathname) {
|
|
104
|
+
document.getElementById('message').innerHTML =
|
|
105
|
+
`The page you're looking for doesn't exist.<br>
|
|
106
|
+
<small>Did you mean <a href="${basePath}.html">${basePath}.html</a>?</small>`;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Update home link to use the correct base URL
|
|
111
|
+
const baseUrl = window.location.origin;
|
|
112
|
+
document.getElementById('home-link').href = baseUrl;
|
|
113
|
+
</script>
|
|
114
|
+
</body>
|
|
115
|
+
</html>
|
package/assets/js/main.js
CHANGED
|
@@ -1318,8 +1318,31 @@ function initTooltips() {
|
|
|
1318
1318
|
});
|
|
1319
1319
|
}
|
|
1320
1320
|
|
|
1321
|
+
// Handle .md link redirects
|
|
1322
|
+
function initMarkdownLinkRedirects() {
|
|
1323
|
+
// Check if current URL ends with .md and redirect
|
|
1324
|
+
if (window.location.pathname.endsWith('.md')) {
|
|
1325
|
+
const htmlPath = window.location.pathname.replace(/\.md$/, '.html');
|
|
1326
|
+
console.log(`Redirecting from .md to .html: ${htmlPath}`);
|
|
1327
|
+
window.location.replace(htmlPath);
|
|
1328
|
+
return; // Stop execution as we're redirecting
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
// Intercept clicks on .md links
|
|
1332
|
+
document.addEventListener('click', function(e) {
|
|
1333
|
+
const link = e.target.closest('a');
|
|
1334
|
+
if (link && link.href && link.href.endsWith('.md')) {
|
|
1335
|
+
e.preventDefault();
|
|
1336
|
+
const htmlUrl = link.href.replace(/\.md$/, '.html');
|
|
1337
|
+
console.log(`Converting .md link to .html: ${htmlUrl}`);
|
|
1338
|
+
window.location.href = htmlUrl;
|
|
1339
|
+
}
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1321
1343
|
// Initialize on DOM Load
|
|
1322
1344
|
document.addEventListener('DOMContentLoaded', () => {
|
|
1345
|
+
initMarkdownLinkRedirects();
|
|
1323
1346
|
highlightNavigation();
|
|
1324
1347
|
generateTableOfContents();
|
|
1325
1348
|
initSidebarResize();
|
package/cli.js
CHANGED
|
@@ -118,6 +118,67 @@ ${chalk.yellow('Examples:')}
|
|
|
118
118
|
}
|
|
119
119
|
});
|
|
120
120
|
|
|
121
|
+
// Set Production URL command
|
|
122
|
+
program
|
|
123
|
+
.command('set-production-url <url>')
|
|
124
|
+
.description('Set the production URL to display after deployment')
|
|
125
|
+
.option('-c, --config <path>', 'path to config file (default: doc-builder.config.js)')
|
|
126
|
+
.addHelpText('after', `
|
|
127
|
+
${chalk.yellow('Examples:')}
|
|
128
|
+
${chalk.gray('$')} doc-builder set-production-url doc-builder-delta.vercel.app
|
|
129
|
+
${chalk.gray('$')} doc-builder set-production-url https://my-custom-domain.com
|
|
130
|
+
|
|
131
|
+
${chalk.yellow('This URL will be displayed after deployment instead of auto-detected URLs.')}
|
|
132
|
+
`)
|
|
133
|
+
.action(async (url, options) => {
|
|
134
|
+
try {
|
|
135
|
+
const configPath = path.join(process.cwd(), options.config || 'doc-builder.config.js');
|
|
136
|
+
|
|
137
|
+
// Ensure URL has protocol
|
|
138
|
+
if (!url.startsWith('http')) {
|
|
139
|
+
url = 'https://' + url;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (fs.existsSync(configPath)) {
|
|
143
|
+
// Update existing config
|
|
144
|
+
let configContent = fs.readFileSync(configPath, 'utf8');
|
|
145
|
+
|
|
146
|
+
if (configContent.includes('productionUrl:')) {
|
|
147
|
+
// Update existing productionUrl
|
|
148
|
+
configContent = configContent.replace(
|
|
149
|
+
/productionUrl:\s*['"][^'"]*['"]/,
|
|
150
|
+
`productionUrl: '${url}'`
|
|
151
|
+
);
|
|
152
|
+
} else {
|
|
153
|
+
// Add productionUrl to config
|
|
154
|
+
configContent = configContent.replace(
|
|
155
|
+
/module\.exports = {/,
|
|
156
|
+
`module.exports = {\n productionUrl: '${url}',`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
fs.writeFileSync(configPath, configContent);
|
|
161
|
+
console.log(chalk.green(`ā
Production URL set to: ${url}`));
|
|
162
|
+
console.log(chalk.gray(`\nThis URL will be displayed after deployment.`));
|
|
163
|
+
} else {
|
|
164
|
+
console.log(chalk.yellow('ā ļø No config file found. Creating one...'));
|
|
165
|
+
await createDefaultConfig();
|
|
166
|
+
|
|
167
|
+
// Add production URL to newly created config
|
|
168
|
+
let configContent = fs.readFileSync(configPath, 'utf8');
|
|
169
|
+
configContent = configContent.replace(
|
|
170
|
+
/module\.exports = {/,
|
|
171
|
+
`module.exports = {\n productionUrl: '${url}',`
|
|
172
|
+
);
|
|
173
|
+
fs.writeFileSync(configPath, configContent);
|
|
174
|
+
console.log(chalk.green(`ā
Created config with production URL: ${url}`));
|
|
175
|
+
}
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error(chalk.red('Failed to set production URL:'), error.message);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
121
182
|
// Deploy command
|
|
122
183
|
program
|
|
123
184
|
.command('deploy')
|
|
@@ -125,6 +186,7 @@ program
|
|
|
125
186
|
.option('-c, --config <path>', 'path to config file (default: doc-builder.config.js)')
|
|
126
187
|
.option('--no-prod', 'deploy as preview instead of production')
|
|
127
188
|
.option('--force', 'force deployment without confirmation')
|
|
189
|
+
.option('--production-url <url>', 'override production URL for this deployment')
|
|
128
190
|
.addHelpText('after', `
|
|
129
191
|
${chalk.yellow('Examples:')}
|
|
130
192
|
${chalk.gray('$')} doc-builder deploy ${chalk.gray('# Deploy to production')}
|
|
@@ -267,6 +329,13 @@ ${chalk.yellow('Troubleshooting:')}
|
|
|
267
329
|
process.exit(1);
|
|
268
330
|
}
|
|
269
331
|
|
|
332
|
+
// Handle production URL option
|
|
333
|
+
if (options.productionUrl) {
|
|
334
|
+
config.productionUrl = options.productionUrl.startsWith('http')
|
|
335
|
+
? options.productionUrl
|
|
336
|
+
: 'https://' + options.productionUrl;
|
|
337
|
+
}
|
|
338
|
+
|
|
270
339
|
// Always build first
|
|
271
340
|
spinner.stop();
|
|
272
341
|
console.log(chalk.blue('\nš¦ Building documentation first...\n'));
|
|
@@ -329,8 +398,8 @@ ${chalk.yellow('Troubleshooting:')}
|
|
|
329
398
|
productionUrl = result.productionUrl;
|
|
330
399
|
}
|
|
331
400
|
|
|
332
|
-
// Use the production URL if available,
|
|
333
|
-
const displayUrl = productionUrl || deployUrl;
|
|
401
|
+
// Use the configured production URL if available, then detected, then deployment URL
|
|
402
|
+
const displayUrl = config.productionUrl || productionUrl || deployUrl;
|
|
334
403
|
|
|
335
404
|
console.log(chalk.green('\nā
Deployment Complete!\n'));
|
|
336
405
|
|
|
@@ -407,6 +476,186 @@ ${chalk.yellow('When to use:')}
|
|
|
407
476
|
}
|
|
408
477
|
});
|
|
409
478
|
|
|
479
|
+
// Setup SEO command
|
|
480
|
+
program
|
|
481
|
+
.command('setup-seo')
|
|
482
|
+
.description('Configure SEO settings for your documentation')
|
|
483
|
+
.option('-c, --config <path>', 'path to config file (default: doc-builder.config.js)')
|
|
484
|
+
.addHelpText('after', `
|
|
485
|
+
${chalk.yellow('What this does:')}
|
|
486
|
+
⢠Configures meta tags for search engines
|
|
487
|
+
⢠Sets up social media previews (Open Graph, Twitter Cards)
|
|
488
|
+
⢠Enables automatic sitemap.xml generation
|
|
489
|
+
⢠Creates robots.txt for search engines
|
|
490
|
+
⢠Adds structured data (JSON-LD)
|
|
491
|
+
|
|
492
|
+
${chalk.yellow('What you\'ll configure:')}
|
|
493
|
+
⢠Site URL (your production URL)
|
|
494
|
+
⢠Author name and organization
|
|
495
|
+
⢠Twitter handle for social cards
|
|
496
|
+
⢠Default keywords
|
|
497
|
+
⢠Open Graph image
|
|
498
|
+
|
|
499
|
+
${chalk.yellow('After setup:')}
|
|
500
|
+
⢠Run ${chalk.cyan('npx @knowcode/doc-builder build')} to generate with SEO
|
|
501
|
+
⢠Check meta tags in generated HTML files
|
|
502
|
+
⢠Submit sitemap.xml to search engines
|
|
503
|
+
`)
|
|
504
|
+
.action(async (options) => {
|
|
505
|
+
try {
|
|
506
|
+
const configPath = path.join(process.cwd(), options.config || 'doc-builder.config.js');
|
|
507
|
+
let config = {};
|
|
508
|
+
|
|
509
|
+
// Load existing config if it exists
|
|
510
|
+
if (fs.existsSync(configPath)) {
|
|
511
|
+
try {
|
|
512
|
+
delete require.cache[require.resolve(configPath)];
|
|
513
|
+
config = require(configPath);
|
|
514
|
+
} catch (e) {
|
|
515
|
+
console.log(chalk.yellow('ā ļø Could not load existing config, starting fresh'));
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
console.log(chalk.blue('\nš SEO Setup for @knowcode/doc-builder\n'));
|
|
520
|
+
console.log(chalk.gray('This wizard will help you configure SEO settings for better search engine visibility.\n'));
|
|
521
|
+
|
|
522
|
+
// Interactive prompts
|
|
523
|
+
const answers = await prompts([
|
|
524
|
+
{
|
|
525
|
+
type: 'text',
|
|
526
|
+
name: 'siteUrl',
|
|
527
|
+
message: 'What is your site\'s URL?',
|
|
528
|
+
initial: config.seo?.siteUrl || config.productionUrl || 'https://my-docs.vercel.app',
|
|
529
|
+
validate: value => {
|
|
530
|
+
try {
|
|
531
|
+
new URL(value);
|
|
532
|
+
return true;
|
|
533
|
+
} catch {
|
|
534
|
+
return 'Please enter a valid URL (e.g., https://example.com)';
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
type: 'text',
|
|
540
|
+
name: 'author',
|
|
541
|
+
message: 'Author name?',
|
|
542
|
+
initial: config.seo?.author || ''
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
type: 'text',
|
|
546
|
+
name: 'twitterHandle',
|
|
547
|
+
message: 'Twitter handle?',
|
|
548
|
+
initial: config.seo?.twitterHandle || '',
|
|
549
|
+
format: value => {
|
|
550
|
+
if (!value) return '';
|
|
551
|
+
return value.startsWith('@') ? value : '@' + value;
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
type: 'text',
|
|
556
|
+
name: 'language',
|
|
557
|
+
message: 'Site language?',
|
|
558
|
+
initial: config.seo?.language || 'en-US'
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
type: 'text',
|
|
562
|
+
name: 'organizationName',
|
|
563
|
+
message: 'Organization name (optional)?',
|
|
564
|
+
initial: config.seo?.organization?.name || ''
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
type: prev => prev ? 'text' : null,
|
|
568
|
+
name: 'organizationUrl',
|
|
569
|
+
message: 'Organization URL?',
|
|
570
|
+
initial: config.seo?.organization?.url || ''
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
type: 'text',
|
|
574
|
+
name: 'ogImage',
|
|
575
|
+
message: 'Default Open Graph image URL/path?',
|
|
576
|
+
initial: config.seo?.ogImage || '/og-default.png',
|
|
577
|
+
hint: 'Recommended: 1200x630px PNG or JPG'
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
type: 'text',
|
|
581
|
+
name: 'keywords',
|
|
582
|
+
message: 'Site keywords (comma-separated)?',
|
|
583
|
+
initial: Array.isArray(config.seo?.keywords) ? config.seo.keywords.join(', ') : 'documentation, guide, api'
|
|
584
|
+
},
|
|
585
|
+
{
|
|
586
|
+
type: 'confirm',
|
|
587
|
+
name: 'generateSitemap',
|
|
588
|
+
message: 'Generate sitemap.xml?',
|
|
589
|
+
initial: config.seo?.generateSitemap !== false
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
type: 'confirm',
|
|
593
|
+
name: 'generateRobotsTxt',
|
|
594
|
+
message: 'Generate robots.txt?',
|
|
595
|
+
initial: config.seo?.generateRobotsTxt !== false
|
|
596
|
+
}
|
|
597
|
+
]);
|
|
598
|
+
|
|
599
|
+
// Build SEO config
|
|
600
|
+
const seoConfig = {
|
|
601
|
+
enabled: true,
|
|
602
|
+
siteUrl: answers.siteUrl,
|
|
603
|
+
author: answers.author,
|
|
604
|
+
twitterHandle: answers.twitterHandle,
|
|
605
|
+
language: answers.language,
|
|
606
|
+
keywords: answers.keywords.split(',').map(k => k.trim()).filter(k => k),
|
|
607
|
+
generateSitemap: answers.generateSitemap,
|
|
608
|
+
generateRobotsTxt: answers.generateRobotsTxt,
|
|
609
|
+
ogImage: answers.ogImage
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
// Add organization if provided
|
|
613
|
+
if (answers.organizationName) {
|
|
614
|
+
seoConfig.organization = {
|
|
615
|
+
name: answers.organizationName,
|
|
616
|
+
url: answers.organizationUrl || answers.siteUrl
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// Update config
|
|
621
|
+
config.seo = seoConfig;
|
|
622
|
+
|
|
623
|
+
// Also update productionUrl if not set
|
|
624
|
+
if (!config.productionUrl && answers.siteUrl) {
|
|
625
|
+
config.productionUrl = answers.siteUrl;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Write config file
|
|
629
|
+
const configContent = `module.exports = ${JSON.stringify(config, null, 2)};\n`;
|
|
630
|
+
fs.writeFileSync(configPath, configContent);
|
|
631
|
+
|
|
632
|
+
console.log(chalk.green('\nā
SEO configuration saved to ' + path.basename(configPath)));
|
|
633
|
+
|
|
634
|
+
console.log(chalk.blue('\nYour documentation will now include:'));
|
|
635
|
+
console.log(chalk.gray('⢠Meta tags for search engines'));
|
|
636
|
+
console.log(chalk.gray('⢠Open Graph tags for social media previews'));
|
|
637
|
+
console.log(chalk.gray('⢠Twitter Card tags for Twitter sharing'));
|
|
638
|
+
console.log(chalk.gray('⢠JSON-LD structured data'));
|
|
639
|
+
if (answers.generateSitemap) {
|
|
640
|
+
console.log(chalk.gray('⢠Automatic sitemap.xml generation'));
|
|
641
|
+
}
|
|
642
|
+
if (answers.generateRobotsTxt) {
|
|
643
|
+
console.log(chalk.gray('⢠robots.txt for crawler instructions'));
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
console.log(chalk.yellow('\nš” Tips:'));
|
|
647
|
+
if (answers.ogImage) {
|
|
648
|
+
console.log(chalk.gray(`- Add an image at ${answers.ogImage} (1200x630px) for social previews`));
|
|
649
|
+
}
|
|
650
|
+
console.log(chalk.gray('- Run \'npx @knowcode/doc-builder build\' to generate with SEO'));
|
|
651
|
+
console.log(chalk.gray('- Check your SEO at: https://metatags.io'));
|
|
652
|
+
|
|
653
|
+
} catch (error) {
|
|
654
|
+
console.error(chalk.red('Failed to configure SEO:'), error.message);
|
|
655
|
+
process.exit(1);
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
|
|
410
659
|
// Init command
|
|
411
660
|
program
|
|
412
661
|
.command('init')
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
"siteName": "Doc Builder",
|
|
3
|
+
"siteDescription": "Beautiful documentation with the least effort possible",
|
|
4
|
+
"docsDir": "docs",
|
|
5
|
+
"outputDir": "html",
|
|
6
|
+
"productionUrl": "https://doc-builder-delta.vercel.app",
|
|
7
|
+
"features": {
|
|
8
|
+
"authentication": false,
|
|
9
|
+
"changelog": true,
|
|
10
|
+
"mermaid": true,
|
|
11
|
+
"darkMode": true
|
|
12
|
+
},
|
|
13
|
+
"seo": {
|
|
14
|
+
"enabled": true,
|
|
15
|
+
"siteUrl": "https://doc-builder-delta.vercel.app",
|
|
16
|
+
"author": "Lindsay Smith",
|
|
17
|
+
"twitterHandle": "@planbbackups",
|
|
18
|
+
"language": "en-US",
|
|
19
|
+
"keywords": [
|
|
20
|
+
"documentation",
|
|
21
|
+
"markdown",
|
|
22
|
+
"static site generator",
|
|
23
|
+
"vercel",
|
|
24
|
+
"notion-style"
|
|
25
|
+
],
|
|
26
|
+
"generateSitemap": true,
|
|
27
|
+
"generateRobotsTxt": true,
|
|
28
|
+
"ogImage": "/og-default.png",
|
|
29
|
+
"organization": {
|
|
30
|
+
"name": "Knowcode Ltd",
|
|
31
|
+
"url": "https://knowcode.tech"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|