@mgks/docmd 0.1.2 → 0.1.4
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/.github/workflows/deploy-docmd.yml +2 -2
- package/README.md +3 -1
- package/assets/css/welcome.css +378 -0
- package/assets/images/preview-dark-1.png +0 -0
- package/assets/images/preview-dark-2.png +0 -0
- package/assets/images/preview-dark-3.png +0 -0
- package/assets/images/preview-light-1.png +0 -0
- package/assets/images/preview-light-2.png +0 -0
- package/assets/images/preview-light-3.png +0 -0
- package/config.js +8 -3
- package/docs/cli-commands.md +1 -2
- package/docs/configuration.md +34 -22
- package/docs/content/frontmatter.md +2 -2
- package/docs/content/index.md +5 -4
- package/docs/content/markdown-syntax.md +4 -4
- package/docs/content/no-style-example.md +110 -0
- package/docs/content/no-style-pages.md +202 -0
- package/docs/contributing.md +7 -0
- package/docs/deployment.md +22 -31
- package/docs/getting-started/basic-usage.md +3 -2
- package/docs/getting-started/index.md +3 -3
- package/docs/getting-started/installation.md +1 -1
- package/docs/index.md +137 -53
- package/docs/overview.md +56 -0
- package/docs/plugins/sitemap.md +1 -1
- package/docs/theming/assets-management.md +1 -1
- package/docs/theming/available-themes.md +29 -51
- package/package.json +1 -1
- package/src/assets/css/docmd-main.css +2 -1
- package/src/assets/css/docmd-theme-ruby.css +606 -0
- package/src/commands/build.js +239 -203
- package/src/commands/dev.js +75 -30
- package/src/commands/init.js +2 -0
- package/src/core/file-processor.js +67 -5
- package/src/core/html-generator.js +16 -3
- package/src/plugins/sitemap.js +15 -1
- package/src/templates/layout.ejs +1 -1
- package/src/templates/no-style.ejs +159 -0
package/src/commands/dev.js
CHANGED
|
@@ -8,6 +8,25 @@ const fs = require('fs-extra');
|
|
|
8
8
|
const { buildSite } = require('./build'); // Re-use the build logic
|
|
9
9
|
const { loadConfig } = require('../core/config-loader');
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Format paths for display to make them relative to CWD
|
|
13
|
+
* @param {string} absolutePath - The absolute path to format
|
|
14
|
+
* @param {string} cwd - Current working directory
|
|
15
|
+
* @returns {string} - Formatted relative path
|
|
16
|
+
*/
|
|
17
|
+
function formatPathForDisplay(absolutePath, cwd) {
|
|
18
|
+
// Get the relative path from CWD
|
|
19
|
+
const relativePath = path.relative(cwd, absolutePath);
|
|
20
|
+
|
|
21
|
+
// If it's not a subdirectory, prefix with ./ for clarity
|
|
22
|
+
if (!relativePath.startsWith('..') && !path.isAbsolute(relativePath)) {
|
|
23
|
+
return `./${relativePath}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Return the relative path
|
|
27
|
+
return relativePath;
|
|
28
|
+
}
|
|
29
|
+
|
|
11
30
|
async function startDevServer(configPathOption, options = { preserve: false }) {
|
|
12
31
|
let config = await loadConfig(configPathOption); // Load initial config
|
|
13
32
|
const CWD = process.cwd(); // Current Working Directory where user runs `docmd dev`
|
|
@@ -25,6 +44,9 @@ async function startDevServer(configPathOption, options = { preserve: false }) {
|
|
|
25
44
|
let paths = resolveConfigPaths(config);
|
|
26
45
|
|
|
27
46
|
// docmd's internal templates and assets (for live dev of docmd itself)
|
|
47
|
+
const DOCMD_COMMANDS_DIR = path.resolve(__dirname, '..', 'commands');
|
|
48
|
+
const DOCMD_CORE_DIR = path.resolve(__dirname, '..', 'core');
|
|
49
|
+
const DOCMD_PLUGINS_DIR = path.resolve(__dirname, '..', 'plugins');
|
|
28
50
|
const DOCMD_TEMPLATES_DIR = path.resolve(__dirname, '..', 'templates');
|
|
29
51
|
const DOCMD_ASSETS_DIR = path.resolve(__dirname, '..', 'assets');
|
|
30
52
|
|
|
@@ -48,22 +70,24 @@ async function startDevServer(configPathOption, options = { preserve: false }) {
|
|
|
48
70
|
console.error('WebSocket Server error:', error);
|
|
49
71
|
});
|
|
50
72
|
|
|
51
|
-
|
|
52
73
|
function broadcastReload() {
|
|
53
|
-
// console.log('Broadcasting reload to', wsClients.size, 'clients');
|
|
54
74
|
wsClients.forEach(client => {
|
|
55
75
|
if (client.readyState === WebSocket.OPEN) {
|
|
56
|
-
|
|
76
|
+
try {
|
|
77
|
+
client.send('reload');
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error('Error sending reload command to client:', error);
|
|
80
|
+
}
|
|
57
81
|
}
|
|
58
82
|
});
|
|
59
83
|
}
|
|
60
84
|
|
|
61
|
-
// Inject live reload script into HTML
|
|
85
|
+
// Inject live reload script into HTML responses
|
|
62
86
|
app.use((req, res, next) => {
|
|
63
|
-
if (req.path.endsWith('.html')) {
|
|
87
|
+
if (req.path.endsWith('.html') || !req.path.includes('.')) {
|
|
64
88
|
const originalSend = res.send;
|
|
65
|
-
res.send = function
|
|
66
|
-
if (typeof body === 'string') {
|
|
89
|
+
res.send = function(body) {
|
|
90
|
+
if (typeof body === 'string' && body.includes('</body>')) {
|
|
67
91
|
const liveReloadScript = `
|
|
68
92
|
<script>
|
|
69
93
|
(function() {
|
|
@@ -156,7 +180,7 @@ async function startDevServer(configPathOption, options = { preserve: false }) {
|
|
|
156
180
|
// Initial build
|
|
157
181
|
console.log('🚀 Performing initial build for dev server...');
|
|
158
182
|
try {
|
|
159
|
-
await buildSite(configPathOption, { isDev: true, preserve: options.preserve }); // Use the original config path option
|
|
183
|
+
await buildSite(configPathOption, { isDev: true, preserve: options.preserve, noDoubleProcessing: true }); // Use the original config path option
|
|
160
184
|
console.log('✅ Initial build complete.');
|
|
161
185
|
} catch (error) {
|
|
162
186
|
console.error('❌ Initial build failed:', error.message, error.stack);
|
|
@@ -178,22 +202,22 @@ async function startDevServer(configPathOption, options = { preserve: false }) {
|
|
|
178
202
|
}
|
|
179
203
|
|
|
180
204
|
// Add internal paths for docmd development (not shown to end users)
|
|
181
|
-
const internalPaths = [DOCMD_TEMPLATES_DIR, DOCMD_ASSETS_DIR];
|
|
205
|
+
const internalPaths = [DOCMD_TEMPLATES_DIR, DOCMD_ASSETS_DIR, DOCMD_COMMANDS_DIR, DOCMD_CORE_DIR, DOCMD_PLUGINS_DIR];
|
|
182
206
|
|
|
183
207
|
// Only in development environments, we might want to watch internal files too
|
|
184
208
|
if (process.env.DOCMD_DEV === 'true') {
|
|
185
209
|
watchedPaths.push(...internalPaths);
|
|
186
210
|
}
|
|
187
|
-
|
|
211
|
+
|
|
188
212
|
console.log(`👀 Watching for changes in:`);
|
|
189
|
-
console.log(` - Source: ${paths.srcDirToWatch}`);
|
|
190
|
-
console.log(` - Config: ${paths.configFileToWatch}`);
|
|
213
|
+
console.log(` - Source: ${formatPathForDisplay(paths.srcDirToWatch, CWD)}`);
|
|
214
|
+
console.log(` - Config: ${formatPathForDisplay(paths.configFileToWatch, CWD)}`);
|
|
191
215
|
if (userAssetsDirExists) {
|
|
192
|
-
console.log(` - Assets: ${paths.userAssetsDir}`);
|
|
216
|
+
console.log(` - Assets: ${formatPathForDisplay(paths.userAssetsDir, CWD)}`);
|
|
193
217
|
}
|
|
194
218
|
if (process.env.DOCMD_DEV === 'true') {
|
|
195
|
-
console.log(` - docmd Templates: ${DOCMD_TEMPLATES_DIR} (internal)`);
|
|
196
|
-
console.log(` - docmd Assets: ${DOCMD_ASSETS_DIR} (internal)`);
|
|
219
|
+
console.log(` - docmd Templates: ${formatPathForDisplay(DOCMD_TEMPLATES_DIR, CWD)} (internal)`);
|
|
220
|
+
console.log(` - docmd Assets: ${formatPathForDisplay(DOCMD_ASSETS_DIR, CWD)} (internal)`);
|
|
197
221
|
}
|
|
198
222
|
|
|
199
223
|
const watcher = chokidar.watch(watchedPaths, {
|
|
@@ -219,7 +243,7 @@ async function startDevServer(configPathOption, options = { preserve: false }) {
|
|
|
219
243
|
// For simplicity, we might need to restart the watcher or inform user to restart dev server if srcDir/outputDir change.
|
|
220
244
|
// For now, we'll at least update the static server path.
|
|
221
245
|
if (newPaths.outputDir !== paths.outputDir) {
|
|
222
|
-
console.log(`Output directory changed from ${paths.outputDir} to ${newPaths.outputDir}. Updating static server.`);
|
|
246
|
+
console.log(`Output directory changed from ${formatPathForDisplay(paths.outputDir, CWD)} to ${formatPathForDisplay(newPaths.outputDir, CWD)}. Updating static server.`);
|
|
223
247
|
staticMiddleware = express.static(newPaths.outputDir);
|
|
224
248
|
}
|
|
225
249
|
// If srcDirToWatch changes, chokidar won't automatically pick it up.
|
|
@@ -228,9 +252,9 @@ async function startDevServer(configPathOption, options = { preserve: false }) {
|
|
|
228
252
|
paths = newPaths; // Update paths for next build reference
|
|
229
253
|
}
|
|
230
254
|
|
|
231
|
-
await buildSite(configPathOption, { isDev: true, preserve: options.preserve }); // Re-build using the potentially updated config path
|
|
255
|
+
await buildSite(configPathOption, { isDev: true, preserve: options.preserve, noDoubleProcessing: true }); // Re-build using the potentially updated config path
|
|
232
256
|
broadcastReload();
|
|
233
|
-
console.log('✅ Rebuild complete.
|
|
257
|
+
console.log('✅ Rebuild complete.');
|
|
234
258
|
} catch (error) {
|
|
235
259
|
console.error('❌ Rebuild failed:', error.message, error.stack);
|
|
236
260
|
}
|
|
@@ -238,19 +262,40 @@ async function startDevServer(configPathOption, options = { preserve: false }) {
|
|
|
238
262
|
|
|
239
263
|
watcher.on('error', error => console.error(`Watcher error: ${error}`));
|
|
240
264
|
|
|
265
|
+
// Try different ports if the default port is in use
|
|
241
266
|
const PORT = process.env.PORT || 3000;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
267
|
+
const MAX_PORT_ATTEMPTS = 10;
|
|
268
|
+
let currentPort = PORT;
|
|
269
|
+
|
|
270
|
+
// Function to try starting the server on different ports
|
|
271
|
+
function tryStartServer(port, attempt = 1) {
|
|
272
|
+
server.listen(port)
|
|
273
|
+
.on('listening', async () => {
|
|
274
|
+
// Check if index.html exists after initial build
|
|
275
|
+
const indexHtmlPath = path.join(paths.outputDir, 'index.html');
|
|
276
|
+
if (!await fs.pathExists(indexHtmlPath)) {
|
|
277
|
+
console.warn(`⚠️ Warning: ${formatPathForDisplay(indexHtmlPath, CWD)} not found after initial build.
|
|
278
|
+
The dev server is running, but you might see a 404 for the root page.
|
|
279
|
+
Ensure your '${config.srcDir}' directory contains an 'index.md' or your navigation points to existing files.`);
|
|
280
|
+
}
|
|
281
|
+
console.log(`🎉 Dev server started at http://localhost:${port}`);
|
|
282
|
+
console.log(`Serving content from: ${formatPathForDisplay(paths.outputDir, CWD)}`);
|
|
283
|
+
console.log(`Live reload is active. Browser will refresh automatically when files change.`);
|
|
284
|
+
})
|
|
285
|
+
.on('error', (err) => {
|
|
286
|
+
if (err.code === 'EADDRINUSE' && attempt < MAX_PORT_ATTEMPTS) {
|
|
287
|
+
console.log(`Port ${port} is in use, trying port ${port + 1}...`);
|
|
288
|
+
server.close();
|
|
289
|
+
tryStartServer(port + 1, attempt + 1);
|
|
290
|
+
} else {
|
|
291
|
+
console.error(`Failed to start server: ${err.message}`);
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Start the server with port fallback
|
|
298
|
+
tryStartServer(currentPort);
|
|
254
299
|
|
|
255
300
|
// Graceful shutdown
|
|
256
301
|
process.on('SIGINT', () => {
|
package/src/commands/init.js
CHANGED
|
@@ -35,6 +35,7 @@ module.exports = {
|
|
|
35
35
|
// Custom JavaScript Files
|
|
36
36
|
customJs: [ // Array of paths to custom JS files, loaded at end of body
|
|
37
37
|
// '/assets/js/custom-script.js', // Paths relative to outputDir root
|
|
38
|
+
'/assets/js/docmd-image-lightbox.js', // Image lightbox functionality
|
|
38
39
|
],
|
|
39
40
|
|
|
40
41
|
// Plugins Configuration
|
|
@@ -85,6 +86,7 @@ module.exports = {
|
|
|
85
86
|
{ title: 'Documentation', path: 'https://docmd.mgks.dev', icon: 'scroll', external: true },
|
|
86
87
|
{ title: 'Installation', path: 'https://docmd.mgks.dev/getting-started/installation', icon: 'download', external: true },
|
|
87
88
|
{ title: 'Basic Usage', path: 'https://docmd.mgks.dev/getting-started/basic-usage', icon: 'play', external: true },
|
|
89
|
+
{ title: 'Content', path: 'https://docmd.mgks.dev/content', icon: 'layout-template', external: true },
|
|
88
90
|
],
|
|
89
91
|
},
|
|
90
92
|
// External links:
|
|
@@ -5,6 +5,21 @@ const matter = require('gray-matter');
|
|
|
5
5
|
const hljs = require('highlight.js');
|
|
6
6
|
const container = require('markdown-it-container');
|
|
7
7
|
const attrs = require('markdown-it-attrs');
|
|
8
|
+
const path = require('path'); // Add path module for findMarkdownFiles
|
|
9
|
+
|
|
10
|
+
// Function to format paths for display (relative to CWD)
|
|
11
|
+
function formatPathForDisplay(absolutePath) {
|
|
12
|
+
const CWD = process.cwd();
|
|
13
|
+
const relativePath = path.relative(CWD, absolutePath);
|
|
14
|
+
|
|
15
|
+
// If it's not a subdirectory, prefix with ./ for clarity
|
|
16
|
+
if (!relativePath.startsWith('..') && !path.isAbsolute(relativePath)) {
|
|
17
|
+
return `./${relativePath}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Return the relative path
|
|
21
|
+
return relativePath;
|
|
22
|
+
}
|
|
8
23
|
|
|
9
24
|
const md = new MarkdownIt({
|
|
10
25
|
html: true,
|
|
@@ -335,7 +350,7 @@ function extractHeadingsFromHtml(htmlContent) {
|
|
|
335
350
|
return headings;
|
|
336
351
|
}
|
|
337
352
|
|
|
338
|
-
async function processMarkdownFile(filePath) {
|
|
353
|
+
async function processMarkdownFile(filePath, options = { isDev: false }) {
|
|
339
354
|
const rawContent = await fs.readFile(filePath, 'utf8');
|
|
340
355
|
let frontmatter, markdownContent;
|
|
341
356
|
|
|
@@ -346,21 +361,43 @@ async function processMarkdownFile(filePath) {
|
|
|
346
361
|
} catch (e) {
|
|
347
362
|
if (e.name === 'YAMLException') {
|
|
348
363
|
// Provide more specific error for YAML parsing issues
|
|
349
|
-
const errorMessage = `Error parsing YAML frontmatter in ${filePath}: ${e.reason || e.message}${e.mark ? ` at line ${e.mark.line + 1}, column ${e.mark.column + 1}` : ''}. Please check the syntax.`;
|
|
364
|
+
const errorMessage = `Error parsing YAML frontmatter in ${formatPathForDisplay(filePath)}: ${e.reason || e.message}${e.mark ? ` at line ${e.mark.line + 1}, column ${e.mark.column + 1}` : ''}. Please check the syntax.`;
|
|
350
365
|
console.error(`❌ ${errorMessage}`);
|
|
351
366
|
throw new Error(errorMessage); // Propagate error to stop build/dev
|
|
352
367
|
}
|
|
353
368
|
// For other errors from gray-matter or unknown errors
|
|
354
|
-
console.error(`❌ Error processing frontmatter in ${filePath}: ${e.message}`);
|
|
369
|
+
console.error(`❌ Error processing frontmatter in ${formatPathForDisplay(filePath)}: ${e.message}`);
|
|
355
370
|
throw e;
|
|
356
371
|
}
|
|
357
372
|
|
|
358
373
|
if (!frontmatter.title) {
|
|
359
|
-
console.warn(`⚠️ Warning: Markdown file ${filePath} is missing a 'title' in its frontmatter. Using filename as fallback.`);
|
|
374
|
+
console.warn(`⚠️ Warning: Markdown file ${formatPathForDisplay(filePath)} is missing a 'title' in its frontmatter. Using filename as fallback.`);
|
|
360
375
|
// Fallback title, or you could make it an error
|
|
361
376
|
// frontmatter.title = path.basename(filePath, path.extname(filePath));
|
|
362
377
|
}
|
|
363
378
|
|
|
379
|
+
// Special handling for no-style pages with HTML content
|
|
380
|
+
if (frontmatter.noStyle === true) {
|
|
381
|
+
// Only log when not in dev mode to reduce console output during dev
|
|
382
|
+
if (!options.isDev) {
|
|
383
|
+
console.log(`📄 Processing no-style page: ${formatPathForDisplay(filePath)} - Using raw HTML content`);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// For no-style pages, we'll use the raw content directly
|
|
387
|
+
// No markdown processing, no HTML escaping
|
|
388
|
+
const htmlContent = markdownContent;
|
|
389
|
+
|
|
390
|
+
// Extract headings for table of contents (if needed)
|
|
391
|
+
const headings = extractHeadingsFromHtml(htmlContent);
|
|
392
|
+
|
|
393
|
+
return {
|
|
394
|
+
frontmatter,
|
|
395
|
+
htmlContent,
|
|
396
|
+
headings,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Regular processing for standard pages
|
|
364
401
|
// Check if this is a documentation example showing how to use containers
|
|
365
402
|
const isContainerDocumentation = markdownContent.includes('containerName [optionalTitleOrType]') ||
|
|
366
403
|
markdownContent.includes('## Callouts') ||
|
|
@@ -413,4 +450,29 @@ async function processMarkdownFile(filePath) {
|
|
|
413
450
|
};
|
|
414
451
|
}
|
|
415
452
|
|
|
416
|
-
|
|
453
|
+
// Add findMarkdownFiles function
|
|
454
|
+
/**
|
|
455
|
+
* Recursively finds all Markdown files in a directory and its subdirectories
|
|
456
|
+
* @param {string} dir - Directory to search in
|
|
457
|
+
* @returns {Promise<string[]>} - Array of file paths
|
|
458
|
+
*/
|
|
459
|
+
async function findMarkdownFiles(dir) {
|
|
460
|
+
let files = [];
|
|
461
|
+
const items = await fs.readdir(dir, { withFileTypes: true });
|
|
462
|
+
for (const item of items) {
|
|
463
|
+
const fullPath = path.join(dir, item.name);
|
|
464
|
+
if (item.isDirectory()) {
|
|
465
|
+
files = files.concat(await findMarkdownFiles(fullPath));
|
|
466
|
+
} else if (item.isFile() && (item.name.endsWith('.md') || item.name.endsWith('.markdown'))) {
|
|
467
|
+
files.push(fullPath);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return files;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
module.exports = {
|
|
474
|
+
processMarkdownFile,
|
|
475
|
+
mdInstance: md,
|
|
476
|
+
extractHeadingsFromHtml,
|
|
477
|
+
findMarkdownFiles // Export the findMarkdownFiles function
|
|
478
|
+
};
|
|
@@ -18,7 +18,7 @@ async function processPluginHooks(config, pageData, relativePathToRoot) {
|
|
|
18
18
|
// 1. Favicon (built-in handling)
|
|
19
19
|
if (config.favicon) {
|
|
20
20
|
const faviconPath = config.favicon.startsWith('/') ? config.favicon.substring(1) : config.favicon;
|
|
21
|
-
faviconLinkHtml =
|
|
21
|
+
faviconLinkHtml = `<link rel="shortcut icon" href="${relativePathToRoot}${faviconPath}" type="image/x-icon">\n`;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// 2. Theme CSS (built-in handling for theme.name)
|
|
@@ -75,9 +75,21 @@ async function generateHtmlPage(templateData) {
|
|
|
75
75
|
footerHtml = mdInstance.renderInline(config.footer);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
// Determine which template to use based on frontmatter
|
|
79
|
+
let templateName = 'layout.ejs';
|
|
80
|
+
if (frontmatter.noStyle === true) {
|
|
81
|
+
templateName = 'no-style.ejs';
|
|
82
|
+
|
|
83
|
+
// For no-style pages, ensure we're passing the raw HTML content
|
|
84
|
+
// without any additional processing or escaping
|
|
85
|
+
if (content.includes('<') || content.includes('>')) {
|
|
86
|
+
console.warn(`⚠️ Warning: HTML content in no-style page appears to be escaped. This may cause rendering issues.`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const layoutTemplatePath = path.join(__dirname, '..', 'templates', templateName);
|
|
79
91
|
if (!await fs.pathExists(layoutTemplatePath)) {
|
|
80
|
-
throw new Error(`
|
|
92
|
+
throw new Error(`Template not found: ${layoutTemplatePath}`);
|
|
81
93
|
}
|
|
82
94
|
const layoutTemplate = await fs.readFile(layoutTemplatePath, 'utf8');
|
|
83
95
|
|
|
@@ -105,6 +117,7 @@ async function generateHtmlPage(templateData) {
|
|
|
105
117
|
currentPagePath, // Pass the current page path for active state detection
|
|
106
118
|
headings: headings || [], // Pass headings for TOC, default to empty array if not provided
|
|
107
119
|
isActivePage, // Flag to determine if TOC should be shown
|
|
120
|
+
frontmatter, // Pass the entire frontmatter for no-style template
|
|
108
121
|
...pluginOutputs, // Spread all plugin generated HTML strings
|
|
109
122
|
};
|
|
110
123
|
|
package/src/plugins/sitemap.js
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
const fs = require('fs-extra');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
|
|
4
|
+
// Function to format paths for display (relative to CWD)
|
|
5
|
+
function formatPathForDisplay(absolutePath) {
|
|
6
|
+
const CWD = process.cwd();
|
|
7
|
+
const relativePath = path.relative(CWD, absolutePath);
|
|
8
|
+
|
|
9
|
+
// If it's not a subdirectory, prefix with ./ for clarity
|
|
10
|
+
if (!relativePath.startsWith('..') && !path.isAbsolute(relativePath)) {
|
|
11
|
+
return `./${relativePath}`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Return the relative path
|
|
15
|
+
return relativePath;
|
|
16
|
+
}
|
|
17
|
+
|
|
4
18
|
/**
|
|
5
19
|
* Generate sitemap.xml in the output directory root
|
|
6
20
|
* @param {Object} config - The full configuration object
|
|
@@ -100,7 +114,7 @@ async function generateSitemap(config, pages, outputDir, options = { isDev: fals
|
|
|
100
114
|
|
|
101
115
|
// Only show sitemap generation message in production mode or if DOCMD_DEV is true
|
|
102
116
|
if (!options.isDev || process.env.DOCMD_DEV === 'true') {
|
|
103
|
-
console.log(`✅ Generated sitemap at ${sitemapPath}`);
|
|
117
|
+
console.log(`✅ Generated sitemap at ${formatPathForDisplay(sitemapPath)}`);
|
|
104
118
|
}
|
|
105
119
|
}
|
|
106
120
|
|
package/src/templates/layout.ejs
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
<%- metaTagsHtml || '' %> <%# SEO Plugin Meta Tags %>
|
|
8
8
|
|
|
9
|
-
<title><%= pageTitle %>
|
|
9
|
+
<title><%= pageTitle %> : <%= siteTitle %></title>
|
|
10
10
|
<% if (description && !(metaTagsHtml && metaTagsHtml.includes('name="description"'))) { %>
|
|
11
11
|
<meta name="description" content="<%= description %>">
|
|
12
12
|
<% } %>
|
|
@@ -0,0 +1,159 @@
|
|
|
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
|
+
|
|
7
|
+
<% if (frontmatter.components?.meta !== false) { %>
|
|
8
|
+
<%- metaTagsHtml || '' %>
|
|
9
|
+
<title><%= pageTitle %><% if (frontmatter.components?.siteTitle !== false) { %> | <%= siteTitle %><% } %></title>
|
|
10
|
+
<% if (description && !(metaTagsHtml && metaTagsHtml.includes('name="description"'))) { %>
|
|
11
|
+
<meta name="description" content="<%= description %>">
|
|
12
|
+
<% } %>
|
|
13
|
+
<% } %>
|
|
14
|
+
|
|
15
|
+
<% if (frontmatter.components?.favicon !== false) { %>
|
|
16
|
+
<%- faviconLinkHtml || '' %>
|
|
17
|
+
<% } %>
|
|
18
|
+
|
|
19
|
+
<% if (frontmatter.components?.css !== false) { %>
|
|
20
|
+
<link rel="stylesheet" href="<%= relativePathToRoot %>assets/css/docmd-main.css">
|
|
21
|
+
<% if (frontmatter.components?.highlight !== false) { %>
|
|
22
|
+
<link rel="stylesheet" href="<%= relativePathToRoot %>assets/css/docmd-highlight-<%= defaultMode === 'dark' ? 'dark' : 'light' %>.css" id="highlight-theme">
|
|
23
|
+
<% } %>
|
|
24
|
+
<% } %>
|
|
25
|
+
|
|
26
|
+
<% if (frontmatter.components?.theme !== false) { %>
|
|
27
|
+
<%- themeCssLinkHtml || '' %>
|
|
28
|
+
<% } %>
|
|
29
|
+
|
|
30
|
+
<% if (frontmatter.components?.customCss !== false && customCssFiles && customCssFiles.length > 0) { %>
|
|
31
|
+
<% customCssFiles.forEach(cssFile => { %>
|
|
32
|
+
<link rel="stylesheet" href="<%= relativePathToRoot %><%- cssFile.startsWith('/') ? cssFile.substring(1) : cssFile %>">
|
|
33
|
+
<% }); %>
|
|
34
|
+
<% } %>
|
|
35
|
+
|
|
36
|
+
<% if (frontmatter.components?.pluginStyles !== false) { %>
|
|
37
|
+
<%- pluginStylesHtml || '' %>
|
|
38
|
+
<% } %>
|
|
39
|
+
|
|
40
|
+
<% if (frontmatter.components?.pluginHeadScripts !== false) { %>
|
|
41
|
+
<%- pluginHeadScriptsHtml || '' %>
|
|
42
|
+
<% } %>
|
|
43
|
+
|
|
44
|
+
<% if (frontmatter.customHead) { %>
|
|
45
|
+
<%- frontmatter.customHead %>
|
|
46
|
+
<% } %>
|
|
47
|
+
</head>
|
|
48
|
+
<body<% if (frontmatter.components?.theme !== false) { %> data-theme="<%= defaultMode %>"<% } %><% if (frontmatter.bodyClass) { %> class="<%= frontmatter.bodyClass %>"<% } %>>
|
|
49
|
+
<% if (frontmatter.components?.layout === true || frontmatter.components?.layout === 'full') { %>
|
|
50
|
+
<div class="main-content-wrapper">
|
|
51
|
+
<% if (frontmatter.components?.header !== false) { %>
|
|
52
|
+
<header class="page-header">
|
|
53
|
+
<% if (frontmatter.components?.pageTitle !== false) { %>
|
|
54
|
+
<h1><%= pageTitle %></h1>
|
|
55
|
+
<% } %>
|
|
56
|
+
</header>
|
|
57
|
+
<% } %>
|
|
58
|
+
<main class="content-area">
|
|
59
|
+
<div class="content-layout">
|
|
60
|
+
<div class="main-content">
|
|
61
|
+
<%- content %>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</main>
|
|
65
|
+
<% if (frontmatter.components?.footer !== false) { %>
|
|
66
|
+
<footer class="page-footer">
|
|
67
|
+
<div class="footer-content">
|
|
68
|
+
<div class="user-footer">
|
|
69
|
+
<%- footerHtml || '' %>
|
|
70
|
+
</div>
|
|
71
|
+
<% if (frontmatter.components?.branding !== false) { %>
|
|
72
|
+
<div class="branding-footer">
|
|
73
|
+
Build with <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"></path><path d="M12 5 9.04 7.96a2.17 2.17 0 0 0 0 3.08c.82.82 2.13.85 3 .07l2.07-1.9a2.82 2.82 0 0 1 3.79 0l2.96 2.66"></path><path d="m18 15-2-2"></path><path d="m15 18-2-2"></path></svg> <a href="https://docmd.mgks.dev" target="_blank" rel="noopener">docmd.</a>
|
|
74
|
+
</div>
|
|
75
|
+
<% } %>
|
|
76
|
+
</div>
|
|
77
|
+
</footer>
|
|
78
|
+
<% } %>
|
|
79
|
+
</div>
|
|
80
|
+
<% } else if (frontmatter.components?.sidebar === true) { %>
|
|
81
|
+
<aside class="sidebar">
|
|
82
|
+
<% if (frontmatter.components?.logo !== false && logo && logo.light && logo.dark) { %>
|
|
83
|
+
<div class="sidebar-header">
|
|
84
|
+
<a href="<%= logo.href || (relativePathToRoot + 'index.html') %>" class="logo-link">
|
|
85
|
+
<img src="<%= relativePathToRoot %><%- logo.light.startsWith('/') ? logo.light.substring(1) : logo.light %>" alt="<%= logo.alt || siteTitle %>" class="logo-light" <% if (logo.height) { %>style="height: <%= logo.height %>;"<% } %>>
|
|
86
|
+
<img src="<%= relativePathToRoot %><%- logo.dark.startsWith('/') ? logo.dark.substring(1) : logo.dark %>" alt="<%= logo.alt || siteTitle %>" class="logo-dark" <% if (logo.height) { %>style="height: <%= logo.height %>;"<% } %>>
|
|
87
|
+
</a>
|
|
88
|
+
</div>
|
|
89
|
+
<% } %>
|
|
90
|
+
<% if (frontmatter.components?.navigation !== false) { %>
|
|
91
|
+
<%- navigationHtml %>
|
|
92
|
+
<% } %>
|
|
93
|
+
<% if (frontmatter.components?.themeToggle !== false && theme && theme.enableModeToggle) { %>
|
|
94
|
+
<button id="theme-toggle-button" aria-label="Toggle theme" class="theme-toggle-button">
|
|
95
|
+
<%- renderIcon('sun', { class: 'icon-sun' }) %>
|
|
96
|
+
<%- renderIcon('moon', { class: 'icon-moon' }) %>
|
|
97
|
+
</button>
|
|
98
|
+
<% } %>
|
|
99
|
+
</aside>
|
|
100
|
+
<div class="main-content-wrapper">
|
|
101
|
+
<% if (frontmatter.components?.header !== false) { %>
|
|
102
|
+
<header class="page-header">
|
|
103
|
+
<% if (frontmatter.components?.pageTitle !== false) { %>
|
|
104
|
+
<h1><%= pageTitle %></h1>
|
|
105
|
+
<% } %>
|
|
106
|
+
</header>
|
|
107
|
+
<% } %>
|
|
108
|
+
<main class="content-area">
|
|
109
|
+
<div class="content-layout">
|
|
110
|
+
<div class="main-content">
|
|
111
|
+
<%- content %>
|
|
112
|
+
</div>
|
|
113
|
+
<% if (frontmatter.components?.toc !== false && headings && headings.length > 0) { %>
|
|
114
|
+
<div class="toc-sidebar">
|
|
115
|
+
<%- include('toc', { content, headings, navigationHtml, isActivePage }) %>
|
|
116
|
+
</div>
|
|
117
|
+
<% } %>
|
|
118
|
+
</div>
|
|
119
|
+
</main>
|
|
120
|
+
<% if (frontmatter.components?.footer !== false) { %>
|
|
121
|
+
<footer class="page-footer">
|
|
122
|
+
<div class="footer-content">
|
|
123
|
+
<div class="user-footer">
|
|
124
|
+
<%- footerHtml || '' %>
|
|
125
|
+
</div>
|
|
126
|
+
<% if (frontmatter.components?.branding !== false) { %>
|
|
127
|
+
<div class="branding-footer">
|
|
128
|
+
Build with <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"></path><path d="M12 5 9.04 7.96a2.17 2.17 0 0 0 0 3.08c.82.82 2.13.85 3 .07l2.07-1.9a2.82 2.82 0 0 1 3.79 0l2.96 2.66"></path><path d="m18 15-2-2"></path><path d="m15 18-2-2"></path></svg> <a href="https://docmd.mgks.dev" target="_blank" rel="noopener">docmd.</a>
|
|
129
|
+
</div>
|
|
130
|
+
<% } %>
|
|
131
|
+
</div>
|
|
132
|
+
</footer>
|
|
133
|
+
<% } %>
|
|
134
|
+
</div>
|
|
135
|
+
<% } else { %>
|
|
136
|
+
<%- content %>
|
|
137
|
+
<% } %>
|
|
138
|
+
|
|
139
|
+
<% if (frontmatter.components?.scripts !== false) { %>
|
|
140
|
+
<% if (frontmatter.components?.themeToggle !== false) { %>
|
|
141
|
+
<script src="<%= relativePathToRoot %>assets/js/docmd-theme-toggle.js"></script>
|
|
142
|
+
<% } %>
|
|
143
|
+
|
|
144
|
+
<% if (frontmatter.components?.customJs !== false && customJsFiles && customJsFiles.length > 0) { %>
|
|
145
|
+
<% customJsFiles.forEach(jsFile => { %>
|
|
146
|
+
<script src="<%= relativePathToRoot %><%- jsFile.startsWith('/') ? jsFile.substring(1) : jsFile %>"></script>
|
|
147
|
+
<% }); %>
|
|
148
|
+
<% } %>
|
|
149
|
+
|
|
150
|
+
<% if (frontmatter.components?.pluginBodyScripts !== false) { %>
|
|
151
|
+
<%- pluginBodyScriptsHtml || '' %>
|
|
152
|
+
<% } %>
|
|
153
|
+
<% } %>
|
|
154
|
+
|
|
155
|
+
<% if (frontmatter.customScripts) { %>
|
|
156
|
+
<%- frontmatter.customScripts %>
|
|
157
|
+
<% } %>
|
|
158
|
+
</body>
|
|
159
|
+
</html>
|