@mgks/docmd 0.3.9 → 0.4.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.
Files changed (47) hide show
  1. package/README.md +15 -160
  2. package/bin/docmd.js +6 -69
  3. package/package.json +6 -79
  4. package/bin/postinstall.js +0 -14
  5. package/src/assets/css/docmd-highlight-dark.css +0 -86
  6. package/src/assets/css/docmd-highlight-light.css +0 -86
  7. package/src/assets/css/docmd-main.css +0 -1736
  8. package/src/assets/css/docmd-theme-retro.css +0 -867
  9. package/src/assets/css/docmd-theme-ruby.css +0 -629
  10. package/src/assets/css/docmd-theme-sky.css +0 -617
  11. package/src/assets/favicon.ico +0 -0
  12. package/src/assets/images/docmd-logo-dark.png +0 -0
  13. package/src/assets/images/docmd-logo-light.png +0 -0
  14. package/src/assets/js/docmd-image-lightbox.js +0 -74
  15. package/src/assets/js/docmd-main.js +0 -260
  16. package/src/assets/js/docmd-mermaid.js +0 -205
  17. package/src/assets/js/docmd-search.js +0 -218
  18. package/src/commands/build.js +0 -237
  19. package/src/commands/dev.js +0 -352
  20. package/src/commands/init.js +0 -277
  21. package/src/commands/live.js +0 -145
  22. package/src/core/asset-manager.js +0 -72
  23. package/src/core/config-loader.js +0 -58
  24. package/src/core/config-validator.js +0 -80
  25. package/src/core/file-processor.js +0 -103
  26. package/src/core/fs-utils.js +0 -40
  27. package/src/core/html-generator.js +0 -184
  28. package/src/core/icon-renderer.js +0 -106
  29. package/src/core/logger.js +0 -21
  30. package/src/core/markdown/containers.js +0 -94
  31. package/src/core/markdown/renderers.js +0 -90
  32. package/src/core/markdown/rules.js +0 -402
  33. package/src/core/markdown/setup.js +0 -113
  34. package/src/core/navigation-helper.js +0 -74
  35. package/src/index.js +0 -12
  36. package/src/live/core.js +0 -67
  37. package/src/live/index.html +0 -216
  38. package/src/live/live.css +0 -256
  39. package/src/live/shims.js +0 -1
  40. package/src/plugins/analytics.js +0 -48
  41. package/src/plugins/seo.js +0 -107
  42. package/src/plugins/sitemap.js +0 -127
  43. package/src/templates/layout.ejs +0 -187
  44. package/src/templates/navigation.ejs +0 -87
  45. package/src/templates/no-style.ejs +0 -166
  46. package/src/templates/partials/theme-init.js +0 -30
  47. package/src/templates/toc.ejs +0 -38
@@ -1,277 +0,0 @@
1
- // Source file from the docmd project — https://github.com/docmd-io/docmd
2
-
3
- const fs = require('../core/fs-utils');
4
- const path = require('path');
5
- const readline = require('readline');
6
- const { version } = require('../../package.json');
7
-
8
- const defaultConfigContent = `// docmd.config.js
9
- module.exports = {
10
- // --- Core Metadata ---
11
- siteTitle: 'My Documentation',
12
- siteUrl: '', // e.g. https://mysite.com (Critical for SEO/Sitemap)
13
-
14
- // --- Branding ---
15
- logo: {
16
- light: 'assets/images/docmd-logo-dark.png',
17
- dark: 'assets/images/docmd-logo-light.png',
18
- alt: 'Logo',
19
- href: './',
20
- },
21
- favicon: 'assets/favicon.ico',
22
-
23
- // --- Source & Output ---
24
- srcDir: 'docs',
25
- outputDir: 'site',
26
-
27
- // --- Theme & Layout ---
28
- theme: {
29
- name: 'sky', // Options: 'default', 'sky', 'ruby', 'retro'
30
- defaultMode: 'system', // 'light', 'dark', or 'system'
31
- enableModeToggle: true, // Show mode toggle button
32
- positionMode: 'top', // 'top' or 'bottom'
33
- codeHighlight: true, // Enable Highlight.js
34
- customCss: [], // e.g. ['assets/css/custom.css']
35
- },
36
-
37
- // --- Features ---
38
- search: true, // Built-in offline search
39
- minify: true, // Minify HTML/CSS/JS in build
40
- autoTitleFromH1: true, // Auto-generate page title from first H1
41
- copyCode: true, // Show "copy" button on code blocks
42
- pageNavigation: true, // Prev/Next buttons at bottom
43
-
44
- // --- Navigation (Sidebar) ---
45
- navigation: [
46
- { title: 'Introduction', path: '/', icon: 'home' },
47
- {
48
- title: 'Guide',
49
- icon: 'book-open',
50
- collapsible: true,
51
- children: [
52
- { title: 'Getting Started', path: 'https://docs.docmd.io/getting-started/installation', icon: 'rocket', external: true },
53
- { title: 'Configuration', path: 'https://docs.docmd.io/configuration', icon: 'settings', external: true },
54
- ],
55
- },
56
- { title: 'Live Editor', path: 'https://live.docmd.io', icon: 'pencil-ruler', external: true },
57
- { title: 'GitHub', path: 'https://github.com/docmd-io/docmd', icon: 'github', external: true },
58
- ],
59
-
60
- // --- Plugins ---
61
- plugins: {
62
- seo: {
63
- defaultDescription: 'Documentation built with docmd.',
64
- openGraph: {
65
- defaultImage: '', // e.g. 'assets/images/og-image.png'
66
- },
67
- twitter: {
68
- cardType: 'summary_large_image',
69
- }
70
- },
71
- analytics: {
72
- googleV4: {
73
- measurementId: 'G-X9WTDL262N' // Replace with your Google Analytics Measurement ID
74
- }
75
- },
76
- sitemap: {
77
- defaultChangefreq: 'weekly', // e.g. 'daily', 'weekly', 'monthly'
78
- defaultPriority: 0.8 // Priority between 0.0 and 1.0
79
- }
80
- },
81
-
82
- // --- Footer ---
83
- footer: '© ' + new Date().getFullYear() + ' My Project. Built with [docmd](https://docmd.io).',
84
-
85
- // --- Edit Link ---
86
- editLink: {
87
- enabled: false,
88
- baseUrl: 'https://github.com/USERNAME/REPO/edit/main/docs',
89
- text: 'Edit this page'
90
- }
91
- };
92
- `;
93
-
94
- const defaultIndexMdContent = `---
95
- title: "Welcome"
96
- description: "Welcome to your new documentation site."
97
- ---
98
-
99
- # Welcome to Your Docs
100
-
101
- Congratulations! You have successfully initialized a new **docmd** project.
102
-
103
- ## 🚀 Quick Start
104
-
105
- You are currently viewing the content of \`docs/index.md\`.
106
-
107
- \`\`\`bash
108
- npm start # Start the dev server
109
- docmd build # Build for production
110
- \`\`\`
111
-
112
- ## ✨ Features Demo
113
-
114
- docmd comes with built-in components to make your documentation beautiful.
115
-
116
- ::: callout tip
117
- **Try this:** Edit this file and save it. The browser will live reload instantly!
118
- :::
119
-
120
- ### Container Examples
121
-
122
- ::: card Flexible Structure
123
- **Organize your way.**
124
- Create Markdown files in the \`docs/\` folder and map them in \`docmd.config.js\`.
125
- :::
126
-
127
- ::: tabs
128
- == tab "Simple"
129
- This is a simple tab content.
130
-
131
- == tab "Nested"
132
- ::: callout info
133
- You can even nest other components inside tabs!
134
- :::
135
- :::
136
-
137
- ## 📚 Next Steps
138
-
139
- * [Check the Official Documentation](https://docs.docmd.io)
140
- * [Customize your Theme](https://docs.docmd.io/theming)
141
- * [Deploy to GitHub Pages](https://docs.docmd.io/deployment)
142
- `;
143
-
144
- const defaultPackageJson = {
145
- name: "my-docs",
146
- version: "0.0.1",
147
- private: true,
148
- scripts: {
149
- "dev": "docmd dev",
150
- "build": "docmd build",
151
- "preview": "npx serve site"
152
- },
153
- dependencies: {
154
- "@mgks/docmd": `^${version}`
155
- }
156
- };
157
-
158
- async function initProject() {
159
- const baseDir = process.cwd();
160
- const packageJsonFile = path.join(baseDir, 'package.json');
161
- const configFile = path.join(baseDir, 'docmd.config.js');
162
- const docsDir = path.join(baseDir, 'docs');
163
- const indexMdFile = path.join(docsDir, 'index.md');
164
- const assetsDir = path.join(baseDir, 'assets');
165
- const assetsCssDir = path.join(assetsDir, 'css');
166
- const assetsJsDir = path.join(assetsDir, 'js');
167
- const assetsImagesDir = path.join(assetsDir, 'images');
168
-
169
- const existingFiles = [];
170
- const dirExists = {
171
- docs: false,
172
- assets: false
173
- };
174
-
175
- // Check if package.json exists
176
- if (!await fs.pathExists(packageJsonFile)) {
177
- await fs.writeJson(packageJsonFile, defaultPackageJson, { spaces: 2 });
178
- console.log('📦 Created `package.json` (Deployment Ready)');
179
- } else {
180
- console.log('⏭️ Skipped existing `package.json`');
181
- }
182
-
183
- // Check each file individually
184
- if (await fs.pathExists(configFile)) {
185
- existingFiles.push('docmd.config.js');
186
- }
187
-
188
- // Check for the legacy config.js
189
- const oldConfigFile = path.join(baseDir, 'config.js');
190
- if (await fs.pathExists(oldConfigFile)) {
191
- existingFiles.push('config.js');
192
- }
193
-
194
- // Check if docs directory exists
195
- if (await fs.pathExists(docsDir)) {
196
- dirExists.docs = true;
197
- if (await fs.pathExists(indexMdFile)) {
198
- existingFiles.push('docs/index.md');
199
- }
200
- }
201
-
202
- // Check if assets directory exists
203
- if (await fs.pathExists(assetsDir)) {
204
- dirExists.assets = true;
205
- }
206
-
207
- // Determine if we should override existing files
208
- let shouldOverride = false;
209
- if (existingFiles.length > 0) {
210
- console.warn('⚠️ The following files already exist:');
211
- existingFiles.forEach(file => console.warn(` - ${file}`));
212
-
213
- const rl = readline.createInterface({
214
- input: process.stdin,
215
- output: process.stdout
216
- });
217
-
218
- const answer = await new Promise(resolve => {
219
- rl.question('Do you want to override these files? (y/N): ', resolve);
220
- });
221
-
222
- rl.close();
223
-
224
- shouldOverride = answer.toLowerCase() === 'y';
225
-
226
- if (!shouldOverride) {
227
- console.log('⏭️ Skipping existing files. Will only create new files.');
228
- }
229
- }
230
-
231
- // Create docs directory if it doesn't exist
232
- if (!dirExists.docs) {
233
- await fs.ensureDir(docsDir);
234
- console.log('📁 Created `docs/` directory');
235
- } else {
236
- console.log('📁 Using existing `docs/` directory');
237
- }
238
-
239
- // Create assets directory structure if it doesn't exist
240
- if (!dirExists.assets) {
241
- await fs.ensureDir(assetsDir);
242
- await fs.ensureDir(assetsCssDir);
243
- await fs.ensureDir(assetsJsDir);
244
- await fs.ensureDir(assetsImagesDir);
245
- console.log('📁 Created `assets/` directory with css, js, and images subdirectories');
246
- } else {
247
- console.log('📁 Using existing `assets/` directory');
248
-
249
- if (!await fs.pathExists(assetsCssDir)) await fs.ensureDir(assetsCssDir);
250
- if (!await fs.pathExists(assetsJsDir)) await fs.ensureDir(assetsJsDir);
251
- if (!await fs.pathExists(assetsImagesDir)) await fs.ensureDir(assetsImagesDir);
252
- }
253
-
254
- // Write config file if it doesn't exist or user confirmed override
255
- if (!await fs.pathExists(configFile) || shouldOverride) {
256
- await fs.writeFile(configFile, defaultConfigContent, 'utf8');
257
- console.log(`📄 ${shouldOverride ? 'Updated' : 'Created'} \`docmd.config.js\``);
258
- } else {
259
- console.log('⏭️ Skipped existing `docmd.config.js`');
260
- }
261
-
262
- // Write index.md file if it doesn't exist or user confirmed override
263
- if (!await fs.pathExists(indexMdFile) || shouldOverride) {
264
- await fs.writeFile(indexMdFile, defaultIndexMdContent, 'utf8');
265
- console.log('📄 Created `docs/index.md`');
266
- } else if (shouldOverride) {
267
- await fs.writeFile(indexMdFile, defaultIndexMdContent, 'utf8');
268
- console.log('📄 Updated `docs/index.md`');
269
- } else {
270
- console.log('⏭️ Skipped existing `docs/index.md`');
271
- }
272
-
273
- console.log('✅ Project initialization complete!');
274
- console.log('👉 Run `npm install` to setup dependencies.');
275
- }
276
-
277
- module.exports = { initProject };
@@ -1,145 +0,0 @@
1
- // Source file from the docmd project — https://github.com/docmd-io/docmd
2
-
3
- const fs = require('../core/fs-utils'); // Native wrapper
4
- const path = require('path');
5
- const esbuild = require('esbuild');
6
- const { processAssets } = require('../core/asset-manager');
7
-
8
- async function build() {
9
- console.log('📦 Building @docmd/live core...');
10
-
11
- // Resolve paths relative to src/commands/live.js
12
- const SRC_DIR = path.resolve(__dirname, '..'); // Points to /src
13
- const LIVE_SRC_DIR = path.join(SRC_DIR, 'live');
14
- const DIST_DIR = path.resolve(__dirname, '../../dist'); // Points to /dist in root
15
-
16
- // 1. Clean/Create dist
17
- if (await fs.exists(DIST_DIR)) {
18
- await fs.remove(DIST_DIR);
19
- }
20
- await fs.ensureDir(DIST_DIR);
21
-
22
- // 2. Generate Shim for Buffer (Browser compatibility)
23
- const shimPath = path.join(LIVE_SRC_DIR, 'shims.js');
24
- await fs.writeFile(shimPath, `import { Buffer } from 'buffer'; globalThis.Buffer = Buffer;`);
25
-
26
- // 3. Define the Virtual Template Plugin
27
- // This reads EJS files from disk and bundles them as a JSON object string
28
- const templatePlugin = {
29
- name: 'docmd-templates',
30
- setup(build) {
31
- build.onResolve({ filter: /^virtual:docmd-templates$/ }, args => ({
32
- path: args.path,
33
- namespace: 'docmd-templates-ns',
34
- }));
35
-
36
- build.onLoad({ filter: /.*/, namespace: 'docmd-templates-ns' }, async () => {
37
- const templatesDir = path.join(SRC_DIR, 'templates');
38
- const files = await fs.readdir(templatesDir);
39
- const templates = {};
40
-
41
- for (const file of files) {
42
- if (file.endsWith('.ejs')) {
43
- const content = await fs.readFile(path.join(templatesDir, file), 'utf8');
44
- templates[file] = content;
45
- }
46
- }
47
-
48
- return {
49
- contents: `module.exports = ${JSON.stringify(templates)};`,
50
- loader: 'js',
51
- };
52
- });
53
- },
54
- };
55
-
56
- // 4. Define Node Modules Shim Plugin
57
- // Stubs out fs/path requires so the browser bundle doesn't crash
58
- const nodeShimPlugin = {
59
- name: 'node-deps-shim',
60
- setup(build) {
61
- build.onResolve({ filter: /^(node:)?path$/ }, args => ({ path: args.path, namespace: 'path-shim' }));
62
- build.onLoad({ filter: /.*/, namespace: 'path-shim' }, () => ({
63
- contents: `
64
- module.exports = {
65
- join: (...args) => args.filter(Boolean).join('/'),
66
- resolve: (...args) => '/' + args.filter(Boolean).join('/'),
67
- basename: (p) => p ? p.split(/[\\\\/]/).pop() : '',
68
- dirname: (p) => p ? p.split(/[\\\\/]/).slice(0, -1).join('/') || '.' : '.',
69
- extname: (p) => { if (!p) return ''; const parts = p.split('.'); return parts.length > 1 ? '.' + parts.pop() : ''; },
70
- isAbsolute: (p) => p.startsWith('/'),
71
- normalize: (p) => p,
72
- sep: '/'
73
- };
74
- `,
75
- loader: 'js'
76
- }));
77
-
78
- build.onResolve({ filter: /^(node:)?fs(\/promises)?|fs-extra$/ }, args => ({ path: args.path, namespace: 'fs-shim' }));
79
- build.onLoad({ filter: /.*/, namespace: 'fs-shim' }, () => ({
80
- contents: `
81
- module.exports = {
82
- existsSync: () => false,
83
- readFileSync: () => '',
84
- statSync: () => ({ isFile: () => true, isDirectory: () => false }),
85
- constants: { F_OK: 0, R_OK: 4 },
86
- promises: {}
87
- };
88
- `,
89
- loader: 'js'
90
- }));
91
- }
92
- };
93
-
94
- // 5. Bundle JS
95
- console.log('⚡ Bundling & Compressing JS...');
96
- try {
97
- await esbuild.build({
98
- entryPoints: [path.join(LIVE_SRC_DIR, 'core.js')],
99
- bundle: true,
100
- outfile: path.join(DIST_DIR, 'docmd-live.js'),
101
- platform: 'browser',
102
- format: 'iife',
103
- globalName: 'docmd',
104
- minify: true,
105
- define: { 'process.env.NODE_ENV': '"production"' },
106
- inject: [shimPath],
107
- plugins: [templatePlugin, nodeShimPlugin]
108
- });
109
-
110
- // 6. Copy & Minify Static Assets using Universal Manager
111
- console.log('📂 Processing static assets...');
112
- const assetsSrc = path.join(SRC_DIR, 'assets');
113
- const assetsDest = path.join(DIST_DIR, 'assets');
114
-
115
- if (await fs.exists(assetsSrc)) {
116
- await processAssets(assetsSrc, assetsDest, { minify: true });
117
- }
118
-
119
- // 7. Copy HTML Wrapper & Minify Live CSS
120
- await fs.copy(path.join(LIVE_SRC_DIR, 'index.html'), path.join(DIST_DIR, 'index.html'));
121
-
122
- const liveCss = await fs.readFile(path.join(LIVE_SRC_DIR, 'live.css'), 'utf8');
123
- const minifiedLiveCss = await esbuild.transform(liveCss, { loader: 'css', minify: true });
124
- await fs.writeFile(path.join(DIST_DIR, 'live.css'), minifiedLiveCss.code);
125
-
126
- // 8. Copy Favicon to Root
127
- const internalFavicon = path.join(assetsSrc, 'favicon.ico');
128
- if (await fs.exists(internalFavicon)) {
129
- await fs.copy(internalFavicon, path.join(DIST_DIR, 'favicon.ico'));
130
- }
131
-
132
- console.log('✅ Live Editor build complete!');
133
-
134
- } catch (e) {
135
- console.error('❌ Build failed:', e);
136
- process.exit(1);
137
- }
138
- }
139
-
140
- // Allow direct execution (via scripts/failsafe.js)
141
- if (require.main === module) {
142
- build();
143
- }
144
-
145
- module.exports = { build };
@@ -1,72 +0,0 @@
1
- // Source file from the docmd project — https://github.com/docmd-io/docmd
2
-
3
- const path = require('path');
4
- const fs = require('./fs-utils');
5
- const esbuild = require('esbuild');
6
-
7
- const DOCMD_HEADER = `/*! Source file from the docmd project — https://github.com/docmd-io/docmd */\n\n`;
8
-
9
- /**
10
- * Recursively copies assets from src to dest.
11
- * Minifies CSS/JS files and adds docmd header.
12
- *
13
- * @param {string} srcDir - Source directory
14
- * @param {string} destDir - Destination directory
15
- * @param {object} options - { minify: boolean }
16
- */
17
- async function processAssets(srcDir, destDir, options = { minify: true }) {
18
- // Ensure source exists
19
- if (!await fs.exists(srcDir)) return;
20
-
21
- // Ensure dest exists
22
- await fs.ensureDir(destDir);
23
-
24
- const entries = await fs.readdir(srcDir, { withFileTypes: true });
25
-
26
- for (const entry of entries) {
27
- const srcPath = path.join(srcDir, entry.name);
28
- const destPath = path.join(destDir, entry.name);
29
-
30
- if (entry.isDirectory()) {
31
- await processAssets(srcPath, destPath, options);
32
- } else {
33
- const ext = path.extname(entry.name).toLowerCase();
34
-
35
- // Handle CSS & JS: Minify + docmd Header
36
- if (ext === '.css' || ext === '.js') {
37
- try {
38
- const content = await fs.readFile(srcPath, 'utf8');
39
-
40
- if (options.minify) {
41
- // Minify using esbuild
42
- const result = await esbuild.transform(content, {
43
- loader: ext.slice(1), // 'css' or 'js'
44
- minify: true,
45
- banner: ext === '.css' || ext === '.js' ? DOCMD_HEADER : ''
46
- });
47
- await fs.writeFile(destPath, result.code);
48
- } else {
49
- // Just add banner if not minifying (Dev mode)
50
- const contentWithBanner = `${DOCMD_HEADER}\n${content}`;
51
- await fs.writeFile(destPath, contentWithBanner);
52
- }
53
- } catch (e) {
54
- console.warn(`⚠️ Processing failed for ${entry.name}, copying original.`);
55
- await fs.copy(srcPath, destPath);
56
- }
57
- }
58
- // Handle HTML Files (if any in assets): HTML Comment Header
59
- else if (ext === '.html') {
60
- const content = await fs.readFile(srcPath, 'utf8');
61
- const htmlBanner = `<!-- Generated by docmd - https://docmd.io -->\n\n`;
62
- await fs.writeFile(destPath, htmlBanner + content);
63
- }
64
- // Everything else (Images, Fonts): Direct Copy
65
- else {
66
- await fs.copy(srcPath, destPath);
67
- }
68
- }
69
- }
70
- }
71
-
72
- module.exports = { processAssets, DOCMD_HEADER };
@@ -1,58 +0,0 @@
1
- // Source file from the docmd project — https://github.com/docmd-io/docmd
2
-
3
- const path = require('path');
4
- const fs = require('./fs-utils');
5
- const { validateConfig } = require('./config-validator');
6
-
7
- async function loadConfig(configPath) {
8
- const cwd = process.cwd();
9
- let absoluteConfigPath = path.resolve(cwd, configPath);
10
-
11
- // 1. Check if the requested config file exists
12
- if (!await fs.pathExists(absoluteConfigPath)) {
13
- // 2. Fallback Logic:
14
- // If the user didn't specify a custom path (i.e., using default 'docmd.config.js')
15
- // AND 'docmd.config.js' is missing...
16
- // Check if legacy 'config.js' exists.
17
- if (configPath === 'docmd.config.js') {
18
- const legacyPath = path.resolve(cwd, 'config.js');
19
- if (await fs.pathExists(legacyPath)) {
20
- // console.log('⚠️ Using legacy config.js. Please rename to docmd.config.js'); // Optional warning
21
- absoluteConfigPath = legacyPath;
22
- } else {
23
- // Neither exists
24
- throw new Error(`Configuration file not found at: ${absoluteConfigPath}\nRun "docmd init" to create one.`);
25
- }
26
- } else {
27
- // User specified a custom path that doesn't exist
28
- throw new Error(`Configuration file not found at: ${absoluteConfigPath}`);
29
- }
30
- }
31
-
32
- try {
33
- // Clear require cache to always get the freshest config (important for dev mode reloading)
34
- delete require.cache[require.resolve(absoluteConfigPath)];
35
- const config = require(absoluteConfigPath);
36
-
37
- // Validate configuration call
38
- validateConfig(config);
39
-
40
- // Basic validation and defaults
41
- config.base = config.base || '/';
42
- config.srcDir = config.srcDir || 'docs';
43
- config.outputDir = config.outputDir || 'site';
44
- config.theme = config.theme || {};
45
- config.theme.defaultMode = config.theme.defaultMode || 'light';
46
- config.navigation = config.navigation || [{ title: 'Home', path: '/' }];
47
- config.pageNavigation = config.pageNavigation ?? true;
48
-
49
- return config;
50
- } catch (e) {
51
- if (e.message === 'Invalid configuration file.') {
52
- throw e;
53
- }
54
- throw new Error(`Error parsing config file: ${e.message}`);
55
- }
56
- }
57
-
58
- module.exports = { loadConfig };
@@ -1,80 +0,0 @@
1
- // Source file from the docmd project — https://github.com/docmd-io/docmd
2
-
3
- const chalk = require('chalk');
4
-
5
- // Known configuration keys for typo detection
6
- const KNOWN_KEYS = [
7
- 'siteTitle', 'siteUrl', 'srcDir', 'outputDir', 'logo',
8
- 'sidebar', 'theme', 'customJs', 'autoTitleFromH1',
9
- 'copyCode', 'plugins', 'navigation', 'footer', 'sponsor', 'favicon',
10
- 'search', 'minify', 'editLink', 'pageNavigation'
11
- ];
12
-
13
- // Common typos mapping
14
- const TYPO_MAPPING = {
15
- 'site_title': 'siteTitle',
16
- 'sitetitle': 'siteTitle',
17
- 'baseUrl': 'siteUrl',
18
- 'source': 'srcDir',
19
- 'out': 'outputDir',
20
- 'customCSS': 'theme.customCss',
21
- 'customcss': 'theme.customCss',
22
- 'customJS': 'customJs',
23
- 'customjs': 'customJs',
24
- 'nav': 'navigation',
25
- 'menu': 'navigation'
26
- };
27
-
28
- function validateConfig(config) {
29
- const errors = [];
30
- const warnings = [];
31
-
32
- // 1. Required Fields
33
- if (!config.siteTitle) {
34
- errors.push('Missing required property: "siteTitle"');
35
- }
36
-
37
- // 2. Type Checking
38
- if (config.navigation && !Array.isArray(config.navigation)) {
39
- errors.push('"navigation" must be an Array');
40
- }
41
-
42
- if (config.customJs && !Array.isArray(config.customJs)) {
43
- errors.push('"customJs" must be an Array of strings');
44
- }
45
-
46
- if (config.theme) {
47
- if (config.theme.customCss && !Array.isArray(config.theme.customCss)) {
48
- errors.push('"theme.customCss" must be an Array of strings');
49
- }
50
- }
51
-
52
- // 3. Typos and Unknown Keys (Top Level)
53
- Object.keys(config).forEach(key => {
54
- if (TYPO_MAPPING[key]) {
55
- warnings.push(`Found unknown property "${key}". Did you mean "${TYPO_MAPPING[key]}"?`);
56
- }
57
- });
58
-
59
- // 4. Theme specific typos
60
- if (config.theme) {
61
- if (config.theme.customCSS) {
62
- warnings.push('Found "theme.customCSS". Did you mean "theme.customCss"?');
63
- }
64
- }
65
-
66
- // Output results
67
- if (warnings.length > 0) {
68
- console.log(chalk.yellow('\n⚠️ Configuration Warnings:'));
69
- warnings.forEach(w => console.log(chalk.yellow(` - ${w}`)));
70
- }
71
-
72
- if (errors.length > 0) {
73
- console.log(chalk.red('\n❌ Configuration Errors:'));
74
- errors.forEach(e => console.log(chalk.red(` - ${e}`)));
75
- console.log('');
76
- throw new Error('Invalid configuration file.');
77
- }
78
- }
79
-
80
- module.exports = { validateConfig };