@mgks/docmd 0.2.7 → 0.2.9
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/CONTRIBUTING.md +129 -0
- package/.github/workflows/publish.yml +3 -1
- package/README.md +119 -82
- package/config.js +13 -1
- package/docs/configuration.md +29 -1
- package/docs/content/containers/changelogs.md +128 -0
- package/docs/content/containers/collapsible.md +89 -0
- package/docs/content/markdown-syntax.md +32 -0
- package/docs/contributing.md +8 -0
- package/package.json +3 -2
- package/src/assets/css/docmd-main.css +507 -299
- package/src/assets/css/docmd-theme-retro.css +860 -1
- package/src/assets/css/docmd-theme-ruby.css +621 -1
- package/src/assets/css/docmd-theme-sky.css +606 -1
- package/src/assets/js/docmd-image-lightbox.js +13 -13
- package/src/assets/js/docmd-main.js +23 -23
- package/src/assets/js/docmd-mermaid.js +28 -29
- package/src/commands/build.js +79 -40
- package/src/commands/init.js +10 -0
- package/src/core/file-processor.js +49 -777
- package/src/core/html-generator.js +23 -0
- package/src/core/markdown/containers.js +94 -0
- package/src/core/markdown/renderers.js +90 -0
- package/src/core/markdown/rules.js +355 -0
- package/src/core/markdown/setup.js +124 -0
- package/src/templates/layout.ejs +15 -0
|
@@ -20,7 +20,7 @@ function initializeCollapsibleNav() {
|
|
|
20
20
|
const submenu = item.querySelector('.submenu');
|
|
21
21
|
|
|
22
22
|
if (!navId || !anchor || !submenu) return;
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
const isParentActive = item.classList.contains('active-parent');
|
|
25
25
|
// Default to expanded if it's a parent of the active page, otherwise check stored state.
|
|
26
26
|
let isExpanded = isParentActive || (navStates[navId] === true);
|
|
@@ -51,19 +51,19 @@ function initializeCollapsibleNav() {
|
|
|
51
51
|
}
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
-
/* anchor.addEventListener('click', (e) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
/* anchor.addEventListener('click', (e) => {
|
|
55
|
+
// If the click target is the icon, ALWAYS prevent navigation and toggle.
|
|
56
|
+
if (e.target.closest('.collapse-icon')) {
|
|
57
|
+
e.preventDefault();
|
|
58
|
+
toggleSubmenu(item.getAttribute('aria-expanded') !== 'true');
|
|
59
|
+
}
|
|
60
|
+
// If the link is just a placeholder, also prevent navigation and toggle.
|
|
61
|
+
else if (anchor.getAttribute('href') === '#') {
|
|
62
|
+
e.preventDefault();
|
|
63
|
+
toggleSubmenu(item.getAttribute('aria-expanded') !== 'true');
|
|
64
|
+
}
|
|
65
|
+
// Otherwise, let the click proceed to navigate to the link.
|
|
66
|
+
});*/
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -101,7 +101,7 @@ function setupThemeToggleListener() {
|
|
|
101
101
|
document.documentElement.setAttribute('data-theme', theme);
|
|
102
102
|
document.body.setAttribute('data-theme', theme);
|
|
103
103
|
localStorage.setItem('docmd-theme', theme);
|
|
104
|
-
|
|
104
|
+
|
|
105
105
|
// Switch highlight.js theme
|
|
106
106
|
const highlightThemeLink = document.getElementById('highlight-theme');
|
|
107
107
|
if (highlightThemeLink) {
|
|
@@ -131,7 +131,7 @@ function initializeSidebarToggle() {
|
|
|
131
131
|
|
|
132
132
|
const defaultConfigCollapsed = body.dataset.defaultCollapsed === 'true';
|
|
133
133
|
let isCollapsed = localStorage.getItem('docmd-sidebar-collapsed');
|
|
134
|
-
|
|
134
|
+
|
|
135
135
|
if (isCollapsed === null) {
|
|
136
136
|
isCollapsed = defaultConfigCollapsed;
|
|
137
137
|
} else {
|
|
@@ -161,8 +161,8 @@ function initializeTabs() {
|
|
|
161
161
|
tabPanes.forEach(pane => pane.classList.remove('active'));
|
|
162
162
|
|
|
163
163
|
navItem.classList.add('active');
|
|
164
|
-
if(tabPanes[index]) {
|
|
165
|
-
|
|
164
|
+
if (tabPanes[index]) {
|
|
165
|
+
tabPanes[index].classList.add('active');
|
|
166
166
|
}
|
|
167
167
|
});
|
|
168
168
|
});
|
|
@@ -186,16 +186,16 @@ function initializeCopyCodeButtons() {
|
|
|
186
186
|
const wrapper = document.createElement('div');
|
|
187
187
|
wrapper.style.position = 'relative';
|
|
188
188
|
wrapper.style.display = 'block';
|
|
189
|
-
|
|
189
|
+
|
|
190
190
|
// Insert the wrapper before the pre element
|
|
191
191
|
preElement.parentNode.insertBefore(wrapper, preElement);
|
|
192
|
-
|
|
192
|
+
|
|
193
193
|
// Move the pre element into the wrapper
|
|
194
194
|
wrapper.appendChild(preElement);
|
|
195
|
-
|
|
195
|
+
|
|
196
196
|
// Remove the relative positioning from pre since wrapper handles it
|
|
197
197
|
preElement.style.position = 'static';
|
|
198
|
-
|
|
198
|
+
|
|
199
199
|
const copyButton = document.createElement('button');
|
|
200
200
|
copyButton.className = 'copy-code-button';
|
|
201
201
|
copyButton.innerHTML = copyIconSvg;
|
|
@@ -224,7 +224,7 @@ function syncBodyTheme() {
|
|
|
224
224
|
if (currentTheme && document.body) {
|
|
225
225
|
document.body.setAttribute('data-theme', currentTheme);
|
|
226
226
|
}
|
|
227
|
-
|
|
227
|
+
|
|
228
228
|
// Also ensure highlight CSS matches the current theme
|
|
229
229
|
const highlightThemeLink = document.getElementById('highlight-theme');
|
|
230
230
|
if (highlightThemeLink && currentTheme) {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// Source file from the docmd project — https://github.com/mgks/docmd
|
|
2
2
|
// Mermaid diagram integration with theme support
|
|
3
3
|
|
|
4
|
-
(function() {
|
|
4
|
+
(function () {
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
7
7
|
// Configuration for mermaid based on current theme
|
|
8
8
|
function getMermaidConfig(theme) {
|
|
9
9
|
const isDark = theme === 'dark';
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
return {
|
|
12
12
|
startOnLoad: false,
|
|
13
13
|
theme: isDark ? 'dark' : 'default',
|
|
@@ -33,16 +33,16 @@
|
|
|
33
33
|
|
|
34
34
|
const currentTheme = document.body.getAttribute('data-theme') || 'light';
|
|
35
35
|
const config = getMermaidConfig(currentTheme);
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
mermaid.initialize(config);
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
// Render all mermaid diagrams
|
|
40
40
|
renderMermaidDiagrams();
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// Store for diagram codes
|
|
44
44
|
const diagramStore = new Map();
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
// Render all mermaid diagrams on the page
|
|
47
47
|
function renderMermaidDiagrams() {
|
|
48
48
|
if (typeof mermaid === 'undefined') {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
const mermaidElements = document.querySelectorAll('pre.mermaid');
|
|
53
|
-
|
|
53
|
+
|
|
54
54
|
mermaidElements.forEach((element, index) => {
|
|
55
55
|
// Skip if already rendered
|
|
56
56
|
if (element.getAttribute('data-processed') === 'true') {
|
|
@@ -60,22 +60,22 @@
|
|
|
60
60
|
try {
|
|
61
61
|
// Get the diagram code
|
|
62
62
|
const code = element.textContent;
|
|
63
|
-
|
|
63
|
+
|
|
64
64
|
// Create a unique ID for this diagram
|
|
65
65
|
const id = `mermaid-diagram-${index}-${Date.now()}`;
|
|
66
|
-
|
|
66
|
+
|
|
67
67
|
// Store the original code for re-rendering
|
|
68
68
|
diagramStore.set(id, code);
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
// Create a container div
|
|
71
71
|
const container = document.createElement('div');
|
|
72
72
|
container.className = 'mermaid-container';
|
|
73
73
|
container.setAttribute('data-mermaid-id', id);
|
|
74
74
|
container.setAttribute('data-processed', 'true');
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
// Replace the pre element with the container
|
|
77
77
|
element.parentNode.replaceChild(container, element);
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
// Render the diagram
|
|
80
80
|
renderSingleDiagram(container, id, code);
|
|
81
81
|
} catch (error) {
|
|
@@ -83,17 +83,17 @@
|
|
|
83
83
|
}
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
|
-
|
|
86
|
+
|
|
87
87
|
// Render a single diagram
|
|
88
88
|
function renderSingleDiagram(container, id, code) {
|
|
89
89
|
if (typeof mermaid === 'undefined') {
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
92
|
-
|
|
92
|
+
|
|
93
93
|
// Process the code to handle theme overrides
|
|
94
94
|
const currentTheme = document.body.getAttribute('data-theme') || 'light';
|
|
95
95
|
const processedCode = processThemeInCode(code, currentTheme);
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
// Render the diagram
|
|
98
98
|
mermaid.render(id, processedCode).then(result => {
|
|
99
99
|
container.innerHTML = result.svg;
|
|
@@ -102,21 +102,21 @@
|
|
|
102
102
|
container.innerHTML = `<pre class="mermaid-error">Error rendering diagram: ${error.message}</pre>`;
|
|
103
103
|
});
|
|
104
104
|
}
|
|
105
|
-
|
|
105
|
+
|
|
106
106
|
// Process mermaid code to inject or override theme
|
|
107
107
|
function processThemeInCode(code, theme) {
|
|
108
108
|
const isDark = theme === 'dark';
|
|
109
109
|
const targetTheme = isDark ? 'dark' : 'default';
|
|
110
|
-
|
|
110
|
+
|
|
111
111
|
// Check if code has %%{init: config - match the entire init block including nested objects
|
|
112
112
|
const initRegex = /%%\{init:\s*\{.*?\}\s*\}%%/s;
|
|
113
113
|
const match = code.match(initRegex);
|
|
114
|
-
|
|
114
|
+
|
|
115
115
|
if (match) {
|
|
116
116
|
// Code has init config, replace only the theme property
|
|
117
117
|
const initBlock = match[0];
|
|
118
118
|
let updatedBlock = initBlock;
|
|
119
|
-
|
|
119
|
+
|
|
120
120
|
// Try to replace theme property
|
|
121
121
|
if (initBlock.includes("'theme'")) {
|
|
122
122
|
updatedBlock = initBlock.replace(/'theme'\s*:\s*'[^']*'/, `'theme':'${targetTheme}'`);
|
|
@@ -126,10 +126,10 @@
|
|
|
126
126
|
// Add theme to the config - insert after the first {
|
|
127
127
|
updatedBlock = initBlock.replace(/%%\{init:\s*\{/, `%%{init: {'theme':'${targetTheme}',`);
|
|
128
128
|
}
|
|
129
|
-
|
|
129
|
+
|
|
130
130
|
return code.replace(initRegex, updatedBlock);
|
|
131
131
|
}
|
|
132
|
-
|
|
132
|
+
|
|
133
133
|
// No init config, code will use global mermaid config
|
|
134
134
|
return code;
|
|
135
135
|
}
|
|
@@ -142,21 +142,21 @@
|
|
|
142
142
|
|
|
143
143
|
const currentTheme = document.body.getAttribute('data-theme') || 'light';
|
|
144
144
|
const config = getMermaidConfig(currentTheme);
|
|
145
|
-
|
|
145
|
+
|
|
146
146
|
// Re-initialize mermaid with new theme
|
|
147
147
|
mermaid.initialize(config);
|
|
148
|
-
|
|
148
|
+
|
|
149
149
|
// Find all rendered diagrams and re-render them
|
|
150
150
|
const containers = document.querySelectorAll('.mermaid-container[data-processed="true"]');
|
|
151
|
-
|
|
151
|
+
|
|
152
152
|
containers.forEach((container) => {
|
|
153
153
|
const mermaidId = container.getAttribute('data-mermaid-id');
|
|
154
154
|
const code = diagramStore.get(mermaidId);
|
|
155
|
-
|
|
155
|
+
|
|
156
156
|
if (code) {
|
|
157
157
|
// Create a new unique ID for re-rendering
|
|
158
158
|
const newId = `${mermaidId}-${Date.now()}`;
|
|
159
|
-
|
|
159
|
+
|
|
160
160
|
// Clear the container and re-render
|
|
161
161
|
container.innerHTML = '';
|
|
162
162
|
renderSingleDiagram(container, newId, code);
|
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
|
|
183
183
|
// Initialize when DOM is ready
|
|
184
184
|
if (document.readyState === 'loading') {
|
|
185
|
-
document.addEventListener('DOMContentLoaded', function() {
|
|
185
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
186
186
|
initializeMermaid();
|
|
187
187
|
setupThemeObserver();
|
|
188
188
|
});
|
|
@@ -192,12 +192,11 @@
|
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
// Handle tab switches - render mermaid in newly visible tabs
|
|
195
|
-
document.addEventListener('click', function(e) {
|
|
195
|
+
document.addEventListener('click', function (e) {
|
|
196
196
|
if (e.target.classList.contains('docmd-tabs-nav-item')) {
|
|
197
197
|
// Wait a bit for tab content to be visible
|
|
198
198
|
setTimeout(renderMermaidDiagrams, 100);
|
|
199
199
|
}
|
|
200
200
|
});
|
|
201
201
|
|
|
202
|
-
})();
|
|
203
|
-
|
|
202
|
+
})();
|
package/src/commands/build.js
CHANGED
|
@@ -12,6 +12,8 @@ const { version } = require('../../package.json');
|
|
|
12
12
|
const matter = require('gray-matter');
|
|
13
13
|
const MarkdownIt = require('markdown-it');
|
|
14
14
|
const hljs = require('highlight.js');
|
|
15
|
+
const CleanCSS = require('clean-css');
|
|
16
|
+
const esbuild = require('esbuild');
|
|
15
17
|
|
|
16
18
|
// Debug function to log navigation information
|
|
17
19
|
function logNavigationPaths(pagePath, navPath, normalizedPath) {
|
|
@@ -42,12 +44,12 @@ const ASSET_VERSIONS = {
|
|
|
42
44
|
function formatPathForDisplay(absolutePath, cwd) {
|
|
43
45
|
// Get the relative path from CWD
|
|
44
46
|
const relativePath = path.relative(cwd, absolutePath);
|
|
45
|
-
|
|
47
|
+
|
|
46
48
|
// If it's not a subdirectory, prefix with ./ for clarity
|
|
47
49
|
if (!relativePath.startsWith('..') && !path.isAbsolute(relativePath)) {
|
|
48
50
|
return `./${relativePath}`;
|
|
49
51
|
}
|
|
50
|
-
|
|
52
|
+
|
|
51
53
|
// Return the relative path
|
|
52
54
|
return relativePath;
|
|
53
55
|
}
|
|
@@ -61,6 +63,7 @@ async function buildSite(configPath, options = { isDev: false, preserve: false,
|
|
|
61
63
|
const OUTPUT_DIR = path.resolve(CWD, config.outputDir);
|
|
62
64
|
const USER_ASSETS_DIR = path.resolve(CWD, 'assets');
|
|
63
65
|
const md = createMarkdownItInstance(config);
|
|
66
|
+
const shouldMinify = !options.isDev && config.minify !== false;
|
|
64
67
|
|
|
65
68
|
if (!await fs.pathExists(SRC_DIR)) {
|
|
66
69
|
throw new Error(`Source directory not found: ${formatPathForDisplay(SRC_DIR, CWD)}`);
|
|
@@ -85,27 +88,70 @@ async function buildSite(configPath, options = { isDev: false, preserve: false,
|
|
|
85
88
|
const preservedFiles = [];
|
|
86
89
|
const userAssetsCopied = [];
|
|
87
90
|
|
|
91
|
+
// Function to process and copy a single asset
|
|
92
|
+
const processAndCopyAsset = async (srcPath, destPath) => {
|
|
93
|
+
const ext = path.extname(srcPath).toLowerCase();
|
|
94
|
+
|
|
95
|
+
if (process.env.DOCMD_DEBUG) {
|
|
96
|
+
console.log(`[Asset Debug] Processing: ${path.basename(srcPath)} | Minify: ${shouldMinify} | Ext: ${ext}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (shouldMinify && ext === '.css') {
|
|
100
|
+
try {
|
|
101
|
+
const content = await fs.readFile(srcPath, 'utf8');
|
|
102
|
+
const output = new CleanCSS({}).minify(content);
|
|
103
|
+
if (output.errors.length > 0) {
|
|
104
|
+
console.warn(`⚠️ CSS Minification error for ${path.basename(srcPath)}, using original.`, output.errors);
|
|
105
|
+
await fs.copyFile(srcPath, destPath);
|
|
106
|
+
} else {
|
|
107
|
+
await fs.writeFile(destPath, output.styles);
|
|
108
|
+
}
|
|
109
|
+
} catch (e) {
|
|
110
|
+
console.warn(`⚠️ CSS processing failed: ${e.message}`);
|
|
111
|
+
await fs.copyFile(srcPath, destPath);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else if (shouldMinify && ext === '.js') {
|
|
115
|
+
try {
|
|
116
|
+
const content = await fs.readFile(srcPath, 'utf8');
|
|
117
|
+
// Simple minification transform
|
|
118
|
+
const result = await esbuild.transform(content, {
|
|
119
|
+
minify: true,
|
|
120
|
+
loader: 'js',
|
|
121
|
+
target: 'es2015' // Ensure compatibility
|
|
122
|
+
});
|
|
123
|
+
await fs.writeFile(destPath, result.code);
|
|
124
|
+
} catch (e) {
|
|
125
|
+
console.warn(`⚠️ JS Minification failed: ${e.message}`);
|
|
126
|
+
await fs.copyFile(srcPath, destPath);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Images, fonts, or dev mode -> standard copy
|
|
131
|
+
await fs.copyFile(srcPath, destPath);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
88
135
|
// Copy user assets from root assets/ directory if it exists
|
|
89
136
|
if (await fs.pathExists(USER_ASSETS_DIR)) {
|
|
90
137
|
const assetsDestDir = path.join(OUTPUT_DIR, 'assets');
|
|
91
138
|
await fs.ensureDir(assetsDestDir);
|
|
92
|
-
|
|
139
|
+
|
|
93
140
|
if (!options.isDev) {
|
|
94
141
|
console.log(`📂 Copying user assets from ${formatPathForDisplay(USER_ASSETS_DIR, CWD)} to ${formatPathForDisplay(assetsDestDir, CWD)}...`);
|
|
95
142
|
}
|
|
96
|
-
|
|
143
|
+
|
|
97
144
|
const userAssetFiles = await getAllFiles(USER_ASSETS_DIR);
|
|
98
|
-
|
|
99
145
|
for (const srcFile of userAssetFiles) {
|
|
100
146
|
const relativePath = path.relative(USER_ASSETS_DIR, srcFile);
|
|
101
147
|
const destFile = path.join(assetsDestDir, relativePath);
|
|
102
|
-
|
|
103
|
-
// Ensure directory exists
|
|
148
|
+
|
|
104
149
|
await fs.ensureDir(path.dirname(destFile));
|
|
105
|
-
await
|
|
150
|
+
await processAndCopyAsset(srcFile, destFile);
|
|
151
|
+
|
|
106
152
|
userAssetsCopied.push(relativePath);
|
|
107
153
|
}
|
|
108
|
-
|
|
154
|
+
|
|
109
155
|
if (!options.isDev && userAssetsCopied.length > 0) {
|
|
110
156
|
console.log(`📦 Copied ${userAssetsCopied.length} user assets`);
|
|
111
157
|
}
|
|
@@ -114,41 +160,34 @@ async function buildSite(configPath, options = { isDev: false, preserve: false,
|
|
|
114
160
|
// Copy assets
|
|
115
161
|
const assetsSrcDir = path.join(__dirname, '..', 'assets');
|
|
116
162
|
const assetsDestDir = path.join(OUTPUT_DIR, 'assets');
|
|
117
|
-
|
|
163
|
+
|
|
118
164
|
if (await fs.pathExists(assetsSrcDir)) {
|
|
119
165
|
if (!options.isDev) {
|
|
120
166
|
console.log(`📂 Copying docmd assets to ${formatPathForDisplay(assetsDestDir, CWD)}...`);
|
|
121
167
|
}
|
|
122
|
-
|
|
168
|
+
|
|
123
169
|
// Create destination directory if it doesn't exist
|
|
124
170
|
await fs.ensureDir(assetsDestDir);
|
|
125
|
-
|
|
171
|
+
|
|
126
172
|
// Get all files from source directory recursively
|
|
127
173
|
const assetFiles = await getAllFiles(assetsSrcDir);
|
|
128
|
-
|
|
129
|
-
// Copy each file individually, checking for existing files if preserve flag is set
|
|
130
174
|
for (const srcFile of assetFiles) {
|
|
131
175
|
const relativePath = path.relative(assetsSrcDir, srcFile);
|
|
132
176
|
const destFile = path.join(assetsDestDir, relativePath);
|
|
133
|
-
|
|
134
|
-
// Check if destination file already exists
|
|
135
177
|
const fileExists = await fs.pathExists(destFile);
|
|
136
|
-
|
|
137
|
-
// Skip if the file exists and either:
|
|
138
|
-
// 1. The preserve flag is set, OR
|
|
139
|
-
// 2. The file was copied from user assets (user assets take precedence)
|
|
178
|
+
|
|
140
179
|
if (fileExists && (options.preserve || userAssetsCopied.includes(relativePath))) {
|
|
141
|
-
// Skip file and add to preserved list
|
|
142
180
|
preservedFiles.push(relativePath);
|
|
143
181
|
if (!options.isDev && options.preserve) {
|
|
144
182
|
console.log(` Preserving existing file: ${relativePath}`);
|
|
145
183
|
}
|
|
184
|
+
|
|
146
185
|
} else {
|
|
147
|
-
// Copy file (either it doesn't exist or we're not preserving)
|
|
148
186
|
await fs.ensureDir(path.dirname(destFile));
|
|
149
|
-
await
|
|
187
|
+
await processAndCopyAsset(srcFile, destFile);
|
|
150
188
|
}
|
|
151
189
|
}
|
|
190
|
+
|
|
152
191
|
} else {
|
|
153
192
|
console.warn(`⚠️ Assets source directory not found: ${formatPathForDisplay(assetsSrcDir, CWD)}`);
|
|
154
193
|
}
|
|
@@ -189,7 +228,7 @@ async function buildSite(configPath, options = { isDev: false, preserve: false,
|
|
|
189
228
|
for (const filePath of markdownFiles) {
|
|
190
229
|
try {
|
|
191
230
|
const relativePath = path.relative(SRC_DIR, filePath);
|
|
192
|
-
|
|
231
|
+
|
|
193
232
|
// Skip file if already processed in this dev build cycle
|
|
194
233
|
if (options.noDoubleProcessing && processedFiles.has(relativePath)) {
|
|
195
234
|
continue;
|
|
@@ -203,12 +242,12 @@ async function buildSite(configPath, options = { isDev: false, preserve: false,
|
|
|
203
242
|
if (!processedData) {
|
|
204
243
|
continue;
|
|
205
244
|
}
|
|
206
|
-
|
|
245
|
+
|
|
207
246
|
// Destructure the valid data
|
|
208
247
|
const { frontmatter: pageFrontmatter, htmlContent, headings } = processedData;
|
|
209
|
-
|
|
248
|
+
|
|
210
249
|
const isIndexFile = path.basename(relativePath) === 'index.md';
|
|
211
|
-
|
|
250
|
+
|
|
212
251
|
let outputHtmlPath;
|
|
213
252
|
if (isIndexFile) {
|
|
214
253
|
const dirPath = path.dirname(relativePath);
|
|
@@ -221,9 +260,9 @@ async function buildSite(configPath, options = { isDev: false, preserve: false,
|
|
|
221
260
|
|
|
222
261
|
let relativePathToRoot = path.relative(path.dirname(finalOutputHtmlPath), OUTPUT_DIR);
|
|
223
262
|
if (relativePathToRoot === '') {
|
|
224
|
-
|
|
263
|
+
relativePathToRoot = './';
|
|
225
264
|
} else {
|
|
226
|
-
|
|
265
|
+
relativePathToRoot = relativePathToRoot.replace(/\\/g, '/') + '/';
|
|
227
266
|
}
|
|
228
267
|
|
|
229
268
|
let normalizedPath = path.relative(SRC_DIR, filePath).replace(/\\/g, '/');
|
|
@@ -259,7 +298,7 @@ async function buildSite(configPath, options = { isDev: false, preserve: false,
|
|
|
259
298
|
const cleanPath = prevPage.path.substring(1);
|
|
260
299
|
prevPage.url = relativePathToRoot + cleanPath;
|
|
261
300
|
}
|
|
262
|
-
|
|
301
|
+
|
|
263
302
|
if (nextPage) {
|
|
264
303
|
const cleanPath = nextPage.path.substring(1);
|
|
265
304
|
nextPage.url = relativePathToRoot + cleanPath;
|
|
@@ -286,9 +325,9 @@ async function buildSite(configPath, options = { isDev: false, preserve: false,
|
|
|
286
325
|
await fs.ensureDir(path.dirname(finalOutputHtmlPath));
|
|
287
326
|
await fs.writeFile(finalOutputHtmlPath, pageHtml);
|
|
288
327
|
|
|
289
|
-
const sitemapOutputPath = isIndexFile
|
|
290
|
-
|
|
291
|
-
|
|
328
|
+
const sitemapOutputPath = isIndexFile
|
|
329
|
+
? (path.dirname(relativePath) === '.' ? '' : path.dirname(relativePath) + '/')
|
|
330
|
+
: relativePath.replace(/\.md$/, '/');
|
|
292
331
|
|
|
293
332
|
processedPages.push({
|
|
294
333
|
outputPath: sitemapOutputPath.replace(/\\/g, '/'),
|
|
@@ -314,7 +353,7 @@ async function buildSite(configPath, options = { isDev: false, preserve: false,
|
|
|
314
353
|
preservedFiles.forEach(file => console.log(` - assets/${file}`));
|
|
315
354
|
console.log(`\nTo update these files in future builds, run without the --preserve flag.`);
|
|
316
355
|
}
|
|
317
|
-
|
|
356
|
+
|
|
318
357
|
if (userAssetsCopied.length > 0 && !options.isDev) {
|
|
319
358
|
console.log(`\n📋 User Assets: ${userAssetsCopied.length} files were copied from your assets/ directory:`);
|
|
320
359
|
if (userAssetsCopied.length <= 10) {
|
|
@@ -336,10 +375,10 @@ async function buildSite(configPath, options = { isDev: false, preserve: false,
|
|
|
336
375
|
async function findFilesToCleanup(dir) {
|
|
337
376
|
const filesToRemove = [];
|
|
338
377
|
const items = await fs.readdir(dir, { withFileTypes: true });
|
|
339
|
-
|
|
378
|
+
|
|
340
379
|
for (const item of items) {
|
|
341
380
|
const fullPath = path.join(dir, item.name);
|
|
342
|
-
|
|
381
|
+
|
|
343
382
|
if (item.isDirectory()) {
|
|
344
383
|
// Don't delete the assets directory
|
|
345
384
|
if (item.name !== 'assets') {
|
|
@@ -347,13 +386,13 @@ async function findFilesToCleanup(dir) {
|
|
|
347
386
|
filesToRemove.push(...subDirFiles);
|
|
348
387
|
}
|
|
349
388
|
} else if (
|
|
350
|
-
item.name.endsWith('.html') ||
|
|
389
|
+
item.name.endsWith('.html') ||
|
|
351
390
|
item.name === 'sitemap.xml'
|
|
352
391
|
) {
|
|
353
392
|
filesToRemove.push(fullPath);
|
|
354
393
|
}
|
|
355
394
|
}
|
|
356
|
-
|
|
395
|
+
|
|
357
396
|
return filesToRemove;
|
|
358
397
|
}
|
|
359
398
|
|
|
@@ -361,7 +400,7 @@ async function findFilesToCleanup(dir) {
|
|
|
361
400
|
async function getAllFiles(dir) {
|
|
362
401
|
const files = [];
|
|
363
402
|
const items = await fs.readdir(dir, { withFileTypes: true });
|
|
364
|
-
|
|
403
|
+
|
|
365
404
|
for (const item of items) {
|
|
366
405
|
const fullPath = path.join(dir, item.name);
|
|
367
406
|
if (item.isDirectory()) {
|
|
@@ -370,7 +409,7 @@ async function getAllFiles(dir) {
|
|
|
370
409
|
files.push(fullPath);
|
|
371
410
|
}
|
|
372
411
|
}
|
|
373
|
-
|
|
412
|
+
|
|
374
413
|
return files;
|
|
375
414
|
}
|
|
376
415
|
|
package/src/commands/init.js
CHANGED
|
@@ -23,6 +23,7 @@ module.exports = {
|
|
|
23
23
|
// Directory Configuration
|
|
24
24
|
srcDir: 'docs', // Source directory for Markdown files
|
|
25
25
|
outputDir: 'site', // Directory for generated static site
|
|
26
|
+
minify: true, // Enable/disable HTML/CSS/JS minification
|
|
26
27
|
|
|
27
28
|
// Sidebar Configuration
|
|
28
29
|
sidebar: {
|
|
@@ -88,6 +89,15 @@ module.exports = {
|
|
|
88
89
|
// Add other future plugin configurations here by their key
|
|
89
90
|
},
|
|
90
91
|
|
|
92
|
+
// "Edit this page" Link Configuration
|
|
93
|
+
editLink: {
|
|
94
|
+
enabled: false,
|
|
95
|
+
// The URL to the folder containing your docs in the git repo
|
|
96
|
+
// Note: It usually ends with /edit/main/docs or /blob/main/docs
|
|
97
|
+
baseUrl: 'https://github.com/mgks/docmd/edit/main/docs',
|
|
98
|
+
text: 'Edit this page on GitHub'
|
|
99
|
+
},
|
|
100
|
+
|
|
91
101
|
// Navigation Structure (Sidebar)
|
|
92
102
|
// Icons are kebab-case names from Lucide Icons (https://lucide.dev/)
|
|
93
103
|
navigation: [
|