@knowcode/doc-builder 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/README.md +250 -0
- package/assets/css/notion-style.css +1462 -0
- package/assets/css/style.css +1974 -0
- package/assets/js/auth.js +65 -0
- package/assets/js/main.js +1333 -0
- package/cli.js +282 -0
- package/index.js +38 -0
- package/lib/builder.js +79 -0
- package/lib/config.js +255 -0
- package/lib/deploy.js +158 -0
- package/lib/dev-server.js +96 -0
- package/package.json +35 -0
- package/scripts/npx-runner.js +27 -0
- package/scripts/setup.js +56 -0
package/lib/deploy.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
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 || 'my-docs'
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
type: 'select',
|
|
32
|
+
name: 'framework',
|
|
33
|
+
message: 'Which framework preset?',
|
|
34
|
+
choices: [
|
|
35
|
+
{ title: 'Other (Static HTML)', value: 'other' },
|
|
36
|
+
{ title: 'Next.js', value: 'nextjs' },
|
|
37
|
+
{ title: 'Vite', value: 'vite' }
|
|
38
|
+
],
|
|
39
|
+
initial: 0
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
type: 'confirm',
|
|
43
|
+
name: 'publicAccess',
|
|
44
|
+
message: 'Make the deployment publicly accessible?',
|
|
45
|
+
initial: true
|
|
46
|
+
}
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
// Create vercel.json if it doesn't exist
|
|
50
|
+
const vercelConfigPath = path.join(process.cwd(), 'vercel.json');
|
|
51
|
+
if (!fs.existsSync(vercelConfigPath)) {
|
|
52
|
+
const vercelConfig = {
|
|
53
|
+
outputDirectory: config.outputDir || 'html',
|
|
54
|
+
framework: answers.framework === 'other' ? null : answers.framework,
|
|
55
|
+
buildCommand: "npm run build:docs",
|
|
56
|
+
devCommand: "npm run dev:docs",
|
|
57
|
+
installCommand: "npm install",
|
|
58
|
+
public: answers.publicAccess
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
fs.writeJsonSync(vercelConfigPath, vercelConfig, { spaces: 2 });
|
|
62
|
+
console.log(chalk.green('ā
Created vercel.json'));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Run Vercel setup
|
|
66
|
+
console.log(chalk.blue('\nš Linking to Vercel...\n'));
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
execSync('vercel link', { stdio: 'inherit' });
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error(chalk.red('Failed to link Vercel project'));
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Important reminders for Vercel settings
|
|
76
|
+
console.log(chalk.yellow('\nā ļø IMPORTANT: Vercel Project Settings\n'));
|
|
77
|
+
console.log(chalk.white('After deployment, go to your Vercel dashboard and:'));
|
|
78
|
+
console.log(chalk.gray('1. Navigate to Project Settings > General'));
|
|
79
|
+
console.log(chalk.gray('2. Under "Security", find "Deployment Protection"'));
|
|
80
|
+
console.log(chalk.gray('3. Set "Deployment Protection" to "Disabled" for public access'));
|
|
81
|
+
console.log(chalk.gray('4. Or configure authentication if you want to keep it private'));
|
|
82
|
+
console.log();
|
|
83
|
+
console.log(chalk.cyan('Dashboard URL: https://vercel.com/dashboard'));
|
|
84
|
+
console.log();
|
|
85
|
+
|
|
86
|
+
// Add .vercel to .gitignore if not already there
|
|
87
|
+
const gitignorePath = path.join(process.cwd(), '.gitignore');
|
|
88
|
+
if (fs.existsSync(gitignorePath)) {
|
|
89
|
+
const gitignore = fs.readFileSync(gitignorePath, 'utf8');
|
|
90
|
+
if (!gitignore.includes('.vercel')) {
|
|
91
|
+
fs.appendFileSync(gitignorePath, '\n# Vercel\n.vercel\n');
|
|
92
|
+
console.log(chalk.green('ā
Added .vercel to .gitignore'));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Deploy to Vercel
|
|
99
|
+
*/
|
|
100
|
+
async function deployToVercel(config, isProd = false) {
|
|
101
|
+
// Ensure the build output exists
|
|
102
|
+
const outputPath = path.join(process.cwd(), config.outputDir || 'html');
|
|
103
|
+
if (!fs.existsSync(outputPath)) {
|
|
104
|
+
throw new Error(`Output directory ${outputPath} does not exist. Run 'doc-builder build' first.`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Deploy command
|
|
108
|
+
const deployCmd = isProd ? 'vercel --prod' : 'vercel';
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
// Run deployment and capture output
|
|
112
|
+
const output = execSync(deployCmd, {
|
|
113
|
+
cwd: process.cwd(),
|
|
114
|
+
encoding: 'utf8'
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Extract URL from output
|
|
118
|
+
const urlMatch = output.match(/https:\/\/[^\s]+/);
|
|
119
|
+
const deployUrl = urlMatch ? urlMatch[0] : 'Check Vercel dashboard';
|
|
120
|
+
|
|
121
|
+
return deployUrl;
|
|
122
|
+
} catch (error) {
|
|
123
|
+
throw new Error(`Vercel deployment failed: ${error.message}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Create deployment-specific files
|
|
129
|
+
*/
|
|
130
|
+
async function prepareDeployment(config) {
|
|
131
|
+
const outputDir = path.join(process.cwd(), config.outputDir || 'html');
|
|
132
|
+
|
|
133
|
+
// Create a simple index redirect if needed
|
|
134
|
+
const indexPath = path.join(outputDir, 'index.html');
|
|
135
|
+
if (!fs.existsSync(indexPath)) {
|
|
136
|
+
const readmePath = path.join(outputDir, 'README.html');
|
|
137
|
+
if (fs.existsSync(readmePath)) {
|
|
138
|
+
// Create redirect
|
|
139
|
+
const redirectHtml = `<!DOCTYPE html>
|
|
140
|
+
<html>
|
|
141
|
+
<head>
|
|
142
|
+
<meta http-equiv="refresh" content="0; url=README.html">
|
|
143
|
+
<title>Redirecting...</title>
|
|
144
|
+
</head>
|
|
145
|
+
<body>
|
|
146
|
+
<p>Redirecting to <a href="README.html">documentation</a>...</p>
|
|
147
|
+
</body>
|
|
148
|
+
</html>`;
|
|
149
|
+
fs.writeFileSync(indexPath, redirectHtml);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = {
|
|
155
|
+
setupVercelProject,
|
|
156
|
+
deployToVercel,
|
|
157
|
+
prepareDeployment
|
|
158
|
+
};
|
|
@@ -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
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@knowcode/doc-builder",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Reusable documentation builder for markdown-based sites with Vercel deployment support",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"doc-builder": "./cli.js",
|
|
8
|
+
"juno-docs": "./cli.js",
|
|
9
|
+
"@knowcode/doc-builder": "./scripts/npx-runner.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"postinstall": "node scripts/setup.js || true",
|
|
13
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"documentation",
|
|
17
|
+
"markdown",
|
|
18
|
+
"static-site-generator",
|
|
19
|
+
"vercel",
|
|
20
|
+
"juno"
|
|
21
|
+
],
|
|
22
|
+
"author": "KnowCode",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"marked": "^15.0.12",
|
|
26
|
+
"commander": "^11.0.0",
|
|
27
|
+
"chalk": "^4.1.2",
|
|
28
|
+
"prompts": "^2.4.2",
|
|
29
|
+
"ora": "5.4.1",
|
|
30
|
+
"fs-extra": "^11.2.0"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=14.0.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -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
|
|
13
|
+
const [,, command = 'build', ...args] = process.argv;
|
|
14
|
+
|
|
15
|
+
// Path to the actual CLI
|
|
16
|
+
const cliPath = path.join(__dirname, '..', 'cli.js');
|
|
17
|
+
|
|
18
|
+
// Build the command
|
|
19
|
+
const fullCommand = `node "${cliPath}" ${command} ${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
|
+
}
|
package/scripts/setup.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-install setup script for @juno/doc-builder
|
|
3
|
+
* This script runs after npm install to help users get started
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
|
|
10
|
+
// Check if we're in the doc-builder package itself (skip setup)
|
|
11
|
+
const packagePath = path.join(process.cwd(), 'package.json');
|
|
12
|
+
if (fs.existsSync(packagePath)) {
|
|
13
|
+
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
14
|
+
if (pkg.name === '@knowcode/doc-builder') {
|
|
15
|
+
// We're in the doc-builder package itself, skip setup
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
console.log(chalk.cyan('\nš Welcome to @knowcode/doc-builder!\n'));
|
|
21
|
+
|
|
22
|
+
console.log('You can use doc-builder in several ways:\n');
|
|
23
|
+
|
|
24
|
+
console.log(chalk.yellow('1. Using npx (no installation required):'));
|
|
25
|
+
console.log(' npx @knowcode/doc-builder build');
|
|
26
|
+
console.log(' npx @knowcode/doc-builder dev');
|
|
27
|
+
console.log(' npx @knowcode/doc-builder deploy\n');
|
|
28
|
+
|
|
29
|
+
console.log(chalk.yellow('2. After installing as a dependency:'));
|
|
30
|
+
console.log(' doc-builder build');
|
|
31
|
+
console.log(' doc-builder dev');
|
|
32
|
+
console.log(' doc-builder deploy\n');
|
|
33
|
+
|
|
34
|
+
console.log(chalk.yellow('3. In your package.json scripts:'));
|
|
35
|
+
console.log(chalk.gray(' "scripts": {'));
|
|
36
|
+
console.log(chalk.gray(' "docs:build": "doc-builder build",'));
|
|
37
|
+
console.log(chalk.gray(' "docs:dev": "doc-builder dev",'));
|
|
38
|
+
console.log(chalk.gray(' "docs:deploy": "doc-builder deploy --prod"'));
|
|
39
|
+
console.log(chalk.gray(' }\n'));
|
|
40
|
+
|
|
41
|
+
console.log(chalk.yellow('4. Programmatically in your code:'));
|
|
42
|
+
console.log(chalk.gray(' const { build } = require("@knowcode/doc-builder");'));
|
|
43
|
+
console.log(chalk.gray(' await build();\n'));
|
|
44
|
+
|
|
45
|
+
// Check for common doc directories
|
|
46
|
+
const commonDirs = ['docs', 'documentation', 'doc'];
|
|
47
|
+
const foundDir = commonDirs.find(dir => fs.existsSync(path.join(process.cwd(), dir)));
|
|
48
|
+
|
|
49
|
+
if (foundDir) {
|
|
50
|
+
console.log(chalk.green(`ā
Found ${foundDir}/ directory - doc-builder will use this by default\n`));
|
|
51
|
+
} else {
|
|
52
|
+
console.log(chalk.yellow(`ā¹ļø No docs directory found. Create a 'docs' folder with markdown files to get started.\n`));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.log(chalk.gray('For more information, visit: https://github.com/juno-platform/doc-builder'));
|
|
56
|
+
console.log(chalk.gray('To create a config file, run: doc-builder init --config\n'));
|