@mgks/docmd 0.3.3 → 0.3.5

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.
@@ -6,156 +6,93 @@ const WebSocket = require('ws');
6
6
  const chokidar = require('chokidar');
7
7
  const path = require('path');
8
8
  const fs = require('fs-extra');
9
- const { buildSite } = require('./build'); // Re-use the build logic
9
+ const chalk = require('chalk');
10
+ const os = require('os');
11
+ const { buildSite } = require('./build');
10
12
  const { loadConfig } = require('../core/config-loader');
11
13
 
12
- /**
13
- * Format paths for display to make them relative to CWD
14
- * @param {string} absolutePath - The absolute path to format
15
- * @param {string} cwd - Current working directory
16
- * @returns {string} - Formatted relative path
17
- */
18
14
  function formatPathForDisplay(absolutePath, cwd) {
19
- // Get the relative path from CWD
20
15
  const relativePath = path.relative(cwd, absolutePath);
21
-
22
- // If it's not a subdirectory, prefix with ./ for clarity
23
16
  if (!relativePath.startsWith('..') && !path.isAbsolute(relativePath)) {
24
17
  return `./${relativePath}`;
25
18
  }
26
-
27
- // Return the relative path
28
19
  return relativePath;
29
20
  }
30
21
 
22
+ function getNetworkIp() {
23
+ const interfaces = os.networkInterfaces();
24
+ for (const name of Object.keys(interfaces)) {
25
+ for (const iface of interfaces[name]) {
26
+ if (iface.family === 'IPv4' && !iface.internal) {
27
+ return iface.address;
28
+ }
29
+ }
30
+ }
31
+ return null;
32
+ }
33
+
31
34
  async function startDevServer(configPathOption, options = { preserve: false, port: undefined }) {
32
- let config = await loadConfig(configPathOption); // Load initial config
33
- const CWD = process.cwd(); // Current Working Directory where user runs `docmd dev`
35
+ let config = await loadConfig(configPathOption);
36
+ const CWD = process.cwd();
37
+
38
+ // Config Fallback for Watcher
39
+ let actualConfigPath = path.resolve(CWD, configPathOption);
40
+ if (configPathOption === 'docmd.config.js' && !await fs.pathExists(actualConfigPath)) {
41
+ const legacyPath = path.resolve(CWD, 'config.js');
42
+ if (await fs.pathExists(legacyPath)) {
43
+ actualConfigPath = legacyPath;
44
+ }
45
+ }
34
46
 
35
- // Function to resolve paths based on current config
36
47
  const resolveConfigPaths = (currentConfig) => {
37
48
  return {
38
49
  outputDir: path.resolve(CWD, currentConfig.outputDir),
39
50
  srcDirToWatch: path.resolve(CWD, currentConfig.srcDir),
40
- configFileToWatch: path.resolve(CWD, configPathOption), // Path to the config file itself
41
- userAssetsDir: path.resolve(CWD, 'assets'), // User's assets directory
51
+ configFileToWatch: actualConfigPath,
52
+ userAssetsDir: path.resolve(CWD, 'assets'),
42
53
  };
43
54
  };
44
55
 
45
56
  let paths = resolveConfigPaths(config);
46
-
47
- // docmd's internal templates and assets (for live dev of docmd itself)
48
- const DOCMD_COMMANDS_DIR = path.resolve(__dirname, '..', 'commands');
49
- const DOCMD_CORE_DIR = path.resolve(__dirname, '..', 'core');
50
- const DOCMD_PLUGINS_DIR = path.resolve(__dirname, '..', 'plugins');
51
- const DOCMD_TEMPLATES_DIR = path.resolve(__dirname, '..', 'templates');
52
- const DOCMD_ASSETS_DIR = path.resolve(__dirname, '..', 'assets');
53
-
57
+ const DOCMD_ROOT = path.resolve(__dirname, '..');
58
+
54
59
  const app = express();
55
60
  const server = http.createServer(app);
56
61
  const wss = new WebSocket.Server({ server });
57
62
 
58
- let wsClients = new Set();
59
- wss.on('connection', (ws) => {
60
- wsClients.add(ws);
61
- // console.log('Client connected to WebSocket. Total clients:', wsClients.size);
62
- ws.on('close', () => {
63
- wsClients.delete(ws);
64
- // console.log('Client disconnected. Total clients:', wsClients.size);
65
- });
66
- ws.on('error', (error) => {
67
- console.error('WebSocket error on client:', error);
68
- });
69
- });
70
- wss.on('error', (error) => {
71
- console.error('WebSocket Server error:', error);
72
- });
73
-
74
63
  function broadcastReload() {
75
- wsClients.forEach(client => {
64
+ wss.clients.forEach((client) => {
76
65
  if (client.readyState === WebSocket.OPEN) {
77
- try {
78
- client.send('reload');
79
- } catch (error) {
80
- console.error('Error sending reload command to client:', error);
81
- }
66
+ client.send('reload');
82
67
  }
83
68
  });
84
69
  }
85
70
 
86
- // Inject live reload script into HTML responses
71
+ // Inject live reload script
87
72
  app.use((req, res, next) => {
88
- if (req.path.endsWith('.html') || !req.path.includes('.')) {
73
+ if (req.path.endsWith('.html') || req.path === '/' || !req.path.includes('.')) {
89
74
  const originalSend = res.send;
90
75
  res.send = function(body) {
91
76
  if (typeof body === 'string' && body.includes('</body>')) {
92
77
  const liveReloadScript = `
93
78
  <script>
94
79
  (function() {
95
- // More robust WebSocket connection with automatic reconnection
96
80
  let socket;
97
- let reconnectAttempts = 0;
98
- const maxReconnectAttempts = 5;
99
- const reconnectDelay = 1000; // Start with 1 second delay
100
-
81
+ let reconnectTimer;
101
82
  function connect() {
102
- socket = new WebSocket(\`ws://\${window.location.host}\`);
103
-
104
- socket.onmessage = function(event) {
105
- if (event.data === 'reload') {
106
- console.log('Received reload signal. Refreshing page...');
107
- window.location.reload();
108
- }
109
- };
110
-
83
+ socket = new WebSocket('ws://' + window.location.host);
111
84
  socket.onopen = function() {
112
- console.log('Live reload connected.');
113
- reconnectAttempts = 0; // Reset reconnect counter on successful connection
85
+ console.log('⚔ docmd live reload connected');
86
+ if (reconnectTimer) clearInterval(reconnectTimer);
114
87
  };
115
-
116
- socket.onclose = function() {
117
- if (reconnectAttempts < maxReconnectAttempts) {
118
- reconnectAttempts++;
119
- const delay = reconnectDelay * Math.pow(1.5, reconnectAttempts - 1); // Exponential backoff
120
- console.log(\`Live reload disconnected. Reconnecting in \${delay/1000} seconds...\`);
121
- setTimeout(connect, delay);
122
- } else {
123
- console.log('Live reload disconnected. Max reconnect attempts reached.');
124
- }
88
+ socket.onmessage = function(event) {
89
+ if (event.data === 'reload') window.location.reload();
125
90
  };
126
-
127
- socket.onerror = function(error) {
128
- console.error('WebSocket error:', error);
91
+ socket.onclose = function() {
92
+ reconnectTimer = setTimeout(connect, 1000);
129
93
  };
130
94
  }
131
-
132
- // Initial connection
133
95
  connect();
134
-
135
- // Backup reload mechanism using polling for browsers with WebSocket issues
136
- let lastModified = new Date().getTime();
137
- const pollInterval = 2000; // Poll every 2 seconds
138
-
139
- function checkForChanges() {
140
- fetch(window.location.href, { method: 'HEAD', cache: 'no-store' })
141
- .then(response => {
142
- const serverLastModified = new Date(response.headers.get('Last-Modified')).getTime();
143
- if (serverLastModified > lastModified) {
144
- console.log('Change detected via polling. Refreshing page...');
145
- window.location.reload();
146
- }
147
- lastModified = serverLastModified;
148
- })
149
- .catch(error => console.error('Error checking for changes:', error));
150
- }
151
-
152
- // Only use polling as a fallback if WebSocket fails
153
- setTimeout(() => {
154
- if (socket.readyState !== WebSocket.OPEN) {
155
- console.log('WebSocket not connected. Falling back to polling.');
156
- setInterval(checkForChanges, pollInterval);
157
- }
158
- }, 5000);
159
96
  })();
160
97
  </script>
161
98
  `;
@@ -167,148 +104,122 @@ async function startDevServer(configPathOption, options = { preserve: false, por
167
104
  next();
168
105
  });
169
106
 
170
- // Add Last-Modified header to all responses for polling fallback
171
- app.use((req, res, next) => {
172
- res.setHeader('Last-Modified', new Date().toUTCString());
173
- next();
174
- });
175
-
176
- // Serve static files from the output directory
177
- // This middleware needs to be dynamic if outputDir changes
178
107
  let staticMiddleware = express.static(paths.outputDir);
179
108
  app.use((req, res, next) => staticMiddleware(req, res, next));
180
109
 
181
- // Initial build
182
- console.log('šŸš€ Performing initial build for dev server...');
110
+ // --- 1. Initial Build ---
111
+ console.log(chalk.blue('šŸš€ Performing initial build...'));
183
112
  try {
184
- await buildSite(configPathOption, { isDev: true, preserve: options.preserve, noDoubleProcessing: true }); // Use the original config path option
185
- console.log('āœ… Initial build complete.');
113
+ await buildSite(configPathOption, { isDev: true, preserve: options.preserve, noDoubleProcessing: true });
186
114
  } catch (error) {
187
- console.error('āŒ Initial build failed:', error.message, error.stack);
188
- // Optionally, don't start server if initial build fails, or serve a specific error page.
115
+ console.error(chalk.red('āŒ Initial build failed:'), error.message);
189
116
  }
190
117
 
191
- // Check if user assets directory exists
118
+ // --- 2. Setup Watcher & Logs ---
192
119
  const userAssetsDirExists = await fs.pathExists(paths.userAssetsDir);
193
-
194
- // Watch for changes
195
- const watchedPaths = [
196
- paths.srcDirToWatch,
197
- paths.configFileToWatch,
198
- ];
199
-
200
- // Add user assets directory to watched paths if it exists
201
- if (userAssetsDirExists) {
202
- watchedPaths.push(paths.userAssetsDir);
203
- }
204
-
205
- // Add internal paths for docmd development (not shown to end users)
206
- const internalPaths = [DOCMD_TEMPLATES_DIR, DOCMD_ASSETS_DIR, DOCMD_COMMANDS_DIR, DOCMD_CORE_DIR, DOCMD_PLUGINS_DIR];
120
+ const watchedPaths = [paths.srcDirToWatch, paths.configFileToWatch];
121
+ if (userAssetsDirExists) watchedPaths.push(paths.userAssetsDir);
207
122
 
208
- // Only in development environments, we might want to watch internal files too
209
123
  if (process.env.DOCMD_DEV === 'true') {
210
- watchedPaths.push(...internalPaths);
124
+ watchedPaths.push(
125
+ path.join(DOCMD_ROOT, 'templates'),
126
+ path.join(DOCMD_ROOT, 'assets'),
127
+ path.join(DOCMD_ROOT, 'core'),
128
+ path.join(DOCMD_ROOT, 'plugins')
129
+ );
211
130
  }
212
131
 
213
- console.log(`šŸ‘€ Watching for changes in:`);
214
- console.log(` - Source: ${formatPathForDisplay(paths.srcDirToWatch, CWD)}`);
215
- console.log(` - Config: ${formatPathForDisplay(paths.configFileToWatch, CWD)}`);
132
+ // LOGS: Explicitly print what we are watching
133
+ console.log(chalk.dim('\nšŸ‘€ Watching for changes in:'));
134
+ console.log(chalk.dim(` - Source: ${chalk.cyan(formatPathForDisplay(paths.srcDirToWatch, CWD))}`));
135
+ console.log(chalk.dim(` - Config: ${chalk.cyan(formatPathForDisplay(paths.configFileToWatch, CWD))}`));
216
136
  if (userAssetsDirExists) {
217
- console.log(` - Assets: ${formatPathForDisplay(paths.userAssetsDir, CWD)}`);
137
+ console.log(chalk.dim(` - Assets: ${chalk.cyan(formatPathForDisplay(paths.userAssetsDir, CWD))}`));
218
138
  }
219
139
  if (process.env.DOCMD_DEV === 'true') {
220
- console.log(` - docmd Templates: ${formatPathForDisplay(DOCMD_TEMPLATES_DIR, CWD)} (internal)`);
221
- console.log(` - docmd Assets: ${formatPathForDisplay(DOCMD_ASSETS_DIR, CWD)} (internal)`);
140
+ console.log(chalk.dim(` - docmd Internal: ${chalk.magenta(formatPathForDisplay(DOCMD_ROOT, CWD))}`));
222
141
  }
142
+ console.log('');
223
143
 
224
144
  const watcher = chokidar.watch(watchedPaths, {
225
- ignored: /(^|[\/\\])\../, // ignore dotfiles
145
+ ignored: /(^|[\/\\])\../,
226
146
  persistent: true,
227
- ignoreInitial: true, // Don't trigger for initial scan
228
- awaitWriteFinish: { // Helps with rapid saves or large file writes
229
- stabilityThreshold: 100,
230
- pollInterval: 100
231
- }
147
+ ignoreInitial: true,
148
+ awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 100 }
232
149
  });
233
150
 
234
151
  watcher.on('all', async (event, filePath) => {
235
152
  const relativeFilePath = path.relative(CWD, filePath);
236
- console.log(`šŸ”„ Detected ${event} in ${relativeFilePath}. Rebuilding...`);
153
+ process.stdout.write(chalk.dim(`↻ Change in ${relativeFilePath}... `));
154
+
237
155
  try {
238
156
  if (filePath === paths.configFileToWatch) {
239
- console.log('Config file changed. Reloading configuration...');
240
- config = await loadConfig(configPathOption); // Reload config
157
+ config = await loadConfig(configPathOption);
241
158
  const newPaths = resolveConfigPaths(config);
242
-
243
- // Update watcher if srcDir changed - Chokidar doesn't easily support dynamic path changes after init.
244
- // For simplicity, we might need to restart the watcher or inform user to restart dev server if srcDir/outputDir change.
245
- // For now, we'll at least update the static server path.
246
159
  if (newPaths.outputDir !== paths.outputDir) {
247
- console.log(`Output directory changed from ${formatPathForDisplay(paths.outputDir, CWD)} to ${formatPathForDisplay(newPaths.outputDir, CWD)}. Updating static server.`);
248
160
  staticMiddleware = express.static(newPaths.outputDir);
249
161
  }
250
- // If srcDirToWatch changes, chokidar won't automatically pick it up.
251
- // A full dev server restart would be more robust for such config changes.
252
- // For now, the old srcDir will still be watched.
253
- paths = newPaths; // Update paths for next build reference
162
+ paths = newPaths;
254
163
  }
255
164
 
256
- await buildSite(configPathOption, { isDev: true, preserve: options.preserve, noDoubleProcessing: true }); // Re-build using the potentially updated config path
165
+ await buildSite(configPathOption, { isDev: true, preserve: options.preserve, noDoubleProcessing: true });
257
166
  broadcastReload();
258
- console.log('āœ… Rebuild complete.');
167
+ process.stdout.write(chalk.green('Done.\n'));
168
+
259
169
  } catch (error) {
260
- console.error('āŒ Rebuild failed:', error.message, error.stack);
170
+ console.error(chalk.red('\nāŒ Rebuild failed:'), error.message);
261
171
  }
262
172
  });
263
173
 
264
- watcher.on('error', error => console.error(`Watcher error: ${error}`));
265
-
266
- // Try different ports if the default port is in use
267
174
  const PORT = options.port || process.env.PORT || 3000;
268
175
  const MAX_PORT_ATTEMPTS = 10;
269
- let currentPort = parseInt(PORT, 10);
270
176
 
271
- // Function to try starting the server on different ports
272
177
  function tryStartServer(port, attempt = 1) {
273
- server.listen(port)
178
+ // 0.0.0.0 allows network access
179
+ server.listen(port, '0.0.0.0')
274
180
  .on('listening', async () => {
275
- // Check if index.html exists after initial build
276
181
  const indexHtmlPath = path.join(paths.outputDir, 'index.html');
182
+ const networkIp = getNetworkIp();
183
+
184
+ // Use 127.0.0.1 explicitly
185
+ const localUrl = `http://127.0.0.1:${port}`;
186
+ const networkUrl = networkIp ? `http://${networkIp}:${port}` : null;
187
+
188
+ const border = chalk.gray('────────────────────────────────────────');
189
+ console.log(border);
190
+ console.log(` ${chalk.bold.green('SERVER RUNNING')} ${chalk.dim(`(v${require('../../package.json').version})`)}`);
191
+ console.log('');
192
+ console.log(` ${chalk.bold('Local:')} ${chalk.cyan(localUrl)}`);
193
+ if (networkUrl) {
194
+ console.log(` ${chalk.bold('Network:')} ${chalk.cyan(networkUrl)}`);
195
+ }
196
+ console.log('');
197
+ console.log(` ${chalk.dim('Serving:')} ${formatPathForDisplay(paths.outputDir, CWD)}`);
198
+ console.log(border);
199
+ console.log('');
200
+
277
201
  if (!await fs.pathExists(indexHtmlPath)) {
278
- console.warn(`āš ļø Warning: ${formatPathForDisplay(indexHtmlPath, CWD)} not found after initial build.
279
- The dev server is running, but you might see a 404 for the root page.
280
- Ensure your '${config.srcDir}' directory contains an 'index.md' or your navigation points to existing files.`);
202
+ console.warn(chalk.yellow(`āš ļø Warning: Root index.html not found.`));
281
203
  }
282
- console.log(`šŸŽ‰ Dev server started at http://localhost:${port}`);
283
- console.log(`Serving content from: ${formatPathForDisplay(paths.outputDir, CWD)}`);
284
- console.log(`Live reload is active. Browser will refresh automatically when files change.`);
285
204
  })
286
205
  .on('error', (err) => {
287
206
  if (err.code === 'EADDRINUSE' && attempt < MAX_PORT_ATTEMPTS) {
288
- console.log(`Port ${port} is in use, trying port ${port + 1}...`);
289
- server.close();
290
207
  tryStartServer(port + 1, attempt + 1);
291
208
  } else {
292
- console.error(`Failed to start server: ${err.message}`);
209
+ console.error(chalk.red(`Failed to start server: ${err.message}`));
293
210
  process.exit(1);
294
211
  }
295
212
  });
296
213
  }
297
214
 
298
- // Start the server with port fallback
299
- tryStartServer(currentPort);
215
+ tryStartServer(parseInt(PORT, 10));
300
216
 
301
- // Graceful shutdown
302
217
  process.on('SIGINT', () => {
303
- console.log('\nšŸ›‘ Shutting down dev server...');
218
+ console.log(chalk.yellow('\nšŸ›‘ Shutting down...'));
304
219
  watcher.close();
305
- wss.close(() => {
306
- server.close(() => {
307
- console.log('Server closed.');
308
- process.exit(0);
309
- });
310
- });
220
+ process.exit(0);
311
221
  });
312
222
  }
313
223
 
224
+ // Ensure this export is here!
314
225
  module.exports = { startDevServer };
@@ -14,10 +14,10 @@ module.exports = {
14
14
 
15
15
  // Logo Configuration
16
16
  logo: {
17
- light: '/assets/images/docmd-logo-light.png', // Path relative to outputDir root
18
- dark: '/assets/images/docmd-logo-dark.png', // Path relative to outputDir root
17
+ light: 'assets/images/docmd-logo-light.png', // Path relative to outputDir root
18
+ dark: 'assets/images/docmd-logo-dark.png', // Path relative to outputDir root
19
19
  alt: 'docmd logo', // Alt text for the logo
20
- href: '/', // Link for the logo, defaults to site root
20
+ href: './', // Link for the logo, defaults to site root
21
21
  },
22
22
 
23
23
  // Directory Configuration
@@ -44,14 +44,14 @@ module.exports = {
44
44
  positionMode: 'top', // 'top' or 'bottom' for the theme toggle
45
45
  codeHighlight: true, // Enable/disable codeblock highlighting and import of highlight.js
46
46
  customCss: [ // Array of paths to custom CSS files
47
- // '/assets/css/custom.css', // Custom TOC styles
47
+ // 'assets/css/custom.css', // Custom TOC styles
48
48
  ]
49
49
  },
50
50
 
51
51
  // Custom JavaScript Files
52
52
  customJs: [ // Array of paths to custom JS files, loaded at end of body
53
- // '/assets/js/custom-script.js', // Paths relative to outputDir root
54
- '/assets/js/docmd-image-lightbox.js', // Image lightbox functionality
53
+ // 'assets/js/custom-script.js', // Paths relative to outputDir root
54
+ 'assets/js/docmd-image-lightbox.js', // Image lightbox functionality
55
55
  ],
56
56
 
57
57
  // Content Processing
@@ -71,7 +71,7 @@ module.exports = {
71
71
  // siteName: 'docmd Documentation', // Optional, defaults to config.siteTitle
72
72
  // Default image for og:image if not specified in page frontmatter
73
73
  // Path relative to outputDir root
74
- defaultImage: '/assets/images/docmd-preview.png',
74
+ defaultImage: 'assets/images/docmd-preview.png',
75
75
  },
76
76
  twitter: { // For Twitter Cards
77
77
  cardType: 'summary_large_image', // 'summary', 'summary_large_image'
@@ -139,7 +139,7 @@ module.exports = {
139
139
 
140
140
  // Favicon Configuration
141
141
  // Path relative to outputDir root
142
- favicon: '/assets/favicon.ico',
142
+ favicon: 'assets/favicon.ico',
143
143
  };
144
144
  `;
145
145
 
@@ -5,12 +5,32 @@ const fs = require('fs-extra');
5
5
  const { validateConfig } = require('./config-validator');
6
6
 
7
7
  async function loadConfig(configPath) {
8
- const absoluteConfigPath = path.resolve(process.cwd(), configPath);
8
+ const cwd = process.cwd();
9
+ let absoluteConfigPath = path.resolve(cwd, configPath);
10
+
11
+ // 1. Check if the requested config file exists
9
12
  if (!await fs.pathExists(absoluteConfigPath)) {
10
- throw new Error(`Configuration file not found at: ${absoluteConfigPath}\nRun "docmd init" to create one.`);
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
+ }
11
30
  }
31
+
12
32
  try {
13
- // Clear require cache to always get the freshest config
33
+ // Clear require cache to always get the freshest config (important for dev mode reloading)
14
34
  delete require.cache[require.resolve(absoluteConfigPath)];
15
35
  const config = require(absoluteConfigPath);
16
36
 
@@ -34,12 +34,17 @@ function formatPathForDisplay(absolutePath) {
34
34
 
35
35
  async function processMarkdownFile(filePath, md, config) {
36
36
  const rawContent = await fs.readFile(filePath, 'utf8');
37
+ return processMarkdownContent(rawContent, md, config, filePath);
38
+ }
39
+
40
+ // Pure logic, no file reading (Used by Live Editor)
41
+ function processMarkdownContent(rawContent, md, config, filePath = 'memory') {
37
42
  let frontmatter, markdownContent;
38
43
 
39
44
  try {
40
45
  ({ data: frontmatter, content: markdownContent } = matter(rawContent));
41
46
  } catch (e) {
42
- console.error(`āŒ Error parsing frontmatter in ${formatPathForDisplay(filePath)}:`);
47
+ console.error(`āŒ Error parsing frontmatter in ${filePath === 'memory' ? 'content' : formatPathForDisplay(filePath)}:`);
43
48
  console.error(` ${e.message}`);
44
49
  return null;
45
50
  }
@@ -70,7 +75,7 @@ async function processMarkdownFile(filePath, md, config) {
70
75
 
71
76
  return { frontmatter, htmlContent, headings, searchData };
72
77
  }
73
-
78
+
74
79
  async function findMarkdownFiles(dir) {
75
80
  let files = [];
76
81
  const items = await fs.readdir(dir, { withFileTypes: true });
@@ -87,6 +92,7 @@ async function findMarkdownFiles(dir) {
87
92
 
88
93
  module.exports = {
89
94
  processMarkdownFile,
95
+ processMarkdownContent,
90
96
  createMarkdownItInstance,
91
97
  extractHeadingsFromHtml,
92
98
  findMarkdownFiles