@mgks/docmd 0.1.1 โ†’ 0.1.3

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.
@@ -506,8 +506,10 @@
506
506
  text-align: right;
507
507
  opacity: 0.9;
508
508
  font-weight: 500;
509
-
510
509
  }
510
+ .branding-footer svg {
511
+ color: rgb(251, 58, 58);
512
+ }
511
513
 
512
514
  .page-footer a {
513
515
  color: var(--link-color);
@@ -2,11 +2,12 @@
2
2
  const fs = require('fs-extra');
3
3
  const path = require('path');
4
4
  const { loadConfig } = require('../core/config-loader');
5
- const { processMarkdownFile } = require('../core/file-processor');
5
+ const { processMarkdownFile, findMarkdownFiles } = require('../core/file-processor');
6
6
  const { generateHtmlPage, generateNavigationHtml } = require('../core/html-generator');
7
7
  const { renderIcon, clearWarnedIcons } = require('../core/icon-renderer'); // Update import
8
8
  const { generateSitemap } = require('../plugins/sitemap'); // Import our sitemap plugin
9
9
  const { version } = require('../../package.json'); // Import package version
10
+ const matter = require('gray-matter'); // Use gray-matter instead of front-matter
10
11
 
11
12
  // Debug function to log navigation information
12
13
  function logNavigationPaths(pagePath, navPath, normalizedPath) {
@@ -28,16 +29,36 @@ const ASSET_VERSIONS = {
28
29
  // Add other assets here with their versions
29
30
  };
30
31
 
31
- async function buildSite(configPath, options = { isDev: false, preserve: false }) {
32
+ /**
33
+ * Format paths for display to make them relative to CWD
34
+ * @param {string} absolutePath - The absolute path to format
35
+ * @param {string} cwd - Current working directory
36
+ * @returns {string} - Formatted relative path
37
+ */
38
+ function formatPathForDisplay(absolutePath, cwd) {
39
+ // Get the relative path from CWD
40
+ const relativePath = path.relative(cwd, absolutePath);
41
+
42
+ // If it's not a subdirectory, prefix with ./ for clarity
43
+ if (!relativePath.startsWith('..') && !path.isAbsolute(relativePath)) {
44
+ return `./${relativePath}`;
45
+ }
46
+
47
+ // Return the relative path
48
+ return relativePath;
49
+ }
50
+
51
+ async function buildSite(configPath, options = { isDev: false, preserve: false, noDoubleProcessing: false }) {
32
52
  clearWarnedIcons(); // Clear warnings at the start of every build
33
53
 
34
54
  const config = await loadConfig(configPath);
35
55
  const CWD = process.cwd();
36
56
  const SRC_DIR = path.resolve(CWD, config.srcDir);
37
57
  const OUTPUT_DIR = path.resolve(CWD, config.outputDir);
58
+ const USER_ASSETS_DIR = path.resolve(CWD, 'assets'); // User's custom assets directory
38
59
 
39
60
  if (!await fs.pathExists(SRC_DIR)) {
40
- throw new Error(`Source directory not found: ${SRC_DIR}`);
61
+ throw new Error(`Source directory not found: ${formatPathForDisplay(SRC_DIR, CWD)}`);
41
62
  }
42
63
 
43
64
  // Create output directory if it doesn't exist
@@ -51,12 +72,39 @@ async function buildSite(configPath, options = { isDev: false, preserve: false }
51
72
  await fs.remove(file);
52
73
  }
53
74
  if (!options.isDev) {
54
- console.log(`๐Ÿงน Cleaned HTML files from output directory: ${OUTPUT_DIR}`);
75
+ console.log(`๐Ÿงน Cleaned HTML files from output directory: ${formatPathForDisplay(OUTPUT_DIR, CWD)}`);
55
76
  }
56
77
  }
57
78
 
58
79
  // Track preserved files for summary report
59
80
  const preservedFiles = [];
81
+ const userAssetsCopied = [];
82
+
83
+ // Copy user assets from root assets/ directory if it exists
84
+ if (await fs.pathExists(USER_ASSETS_DIR)) {
85
+ const assetsDestDir = path.join(OUTPUT_DIR, 'assets');
86
+ await fs.ensureDir(assetsDestDir);
87
+
88
+ if (!options.isDev) {
89
+ console.log(`๐Ÿ“‚ Copying user assets from ${formatPathForDisplay(USER_ASSETS_DIR, CWD)} to ${formatPathForDisplay(assetsDestDir, CWD)}...`);
90
+ }
91
+
92
+ const userAssetFiles = await getAllFiles(USER_ASSETS_DIR);
93
+
94
+ for (const srcFile of userAssetFiles) {
95
+ const relativePath = path.relative(USER_ASSETS_DIR, srcFile);
96
+ const destFile = path.join(assetsDestDir, relativePath);
97
+
98
+ // Ensure directory exists
99
+ await fs.ensureDir(path.dirname(destFile));
100
+ await fs.copyFile(srcFile, destFile);
101
+ userAssetsCopied.push(relativePath);
102
+ }
103
+
104
+ if (!options.isDev && userAssetsCopied.length > 0) {
105
+ console.log(`๐Ÿ“ฆ Copied ${userAssetsCopied.length} user assets`);
106
+ }
107
+ }
60
108
 
61
109
  // Copy assets
62
110
  const assetsSrcDir = path.join(__dirname, '..', 'assets');
@@ -64,7 +112,7 @@ async function buildSite(configPath, options = { isDev: false, preserve: false }
64
112
 
65
113
  if (await fs.pathExists(assetsSrcDir)) {
66
114
  if (!options.isDev) {
67
- console.log(`๐Ÿ“‚ Copying assets to ${assetsDestDir}...`);
115
+ console.log(`๐Ÿ“‚ Copying docmd assets to ${formatPathForDisplay(assetsDestDir, CWD)}...`);
68
116
  }
69
117
 
70
118
  // Create destination directory if it doesn't exist
@@ -81,10 +129,13 @@ async function buildSite(configPath, options = { isDev: false, preserve: false }
81
129
  // Check if destination file already exists
82
130
  const fileExists = await fs.pathExists(destFile);
83
131
 
84
- if (fileExists && options.preserve) {
132
+ // Skip if the file exists and either:
133
+ // 1. The preserve flag is set, OR
134
+ // 2. The file was copied from user assets (user assets take precedence)
135
+ if (fileExists && (options.preserve || userAssetsCopied.includes(relativePath))) {
85
136
  // Skip file and add to preserved list
86
137
  preservedFiles.push(relativePath);
87
- if (!options.isDev) {
138
+ if (!options.isDev && options.preserve) {
88
139
  console.log(` Preserving existing file: ${relativePath}`);
89
140
  }
90
141
  } else {
@@ -94,7 +145,7 @@ async function buildSite(configPath, options = { isDev: false, preserve: false }
94
145
  }
95
146
  }
96
147
  } else {
97
- console.warn(`โš ๏ธ Assets source directory not found: ${assetsSrcDir}`);
148
+ console.warn(`โš ๏ธ Assets source directory not found: ${formatPathForDisplay(assetsSrcDir, CWD)}`);
98
149
  }
99
150
 
100
151
  // Check for Highlight.js themes
@@ -108,8 +159,8 @@ async function buildSite(configPath, options = { isDev: false, preserve: false }
108
159
  // For 'docmd dev', show only once per session if not already shown.
109
160
  if (!options.isDev || (options.isDev && !highlightWarningShown)) {
110
161
  console.warn(`โš ๏ธ Highlight.js themes not found in assets. Please ensure these files exist:
111
- - ${lightThemePath}
112
- - ${darkThemePath}
162
+ - ${path.relative(CWD, lightThemePath)}
163
+ - ${path.relative(CWD, darkThemePath)}
113
164
  Syntax highlighting may not work correctly.`);
114
165
  if (options.isDev) {
115
166
  highlightWarningShown = true; // Mark as shown for this dev session
@@ -117,217 +168,242 @@ async function buildSite(configPath, options = { isDev: false, preserve: false }
117
168
  }
118
169
  }
119
170
 
171
+ // Array to collect information about all processed pages for sitemap
172
+ const processedPages = [];
120
173
 
174
+ // Find all Markdown files in the source directory
121
175
  const markdownFiles = await findMarkdownFiles(SRC_DIR);
122
- if (markdownFiles.length === 0) {
123
- console.warn(`โš ๏ธ No Markdown files found in ${SRC_DIR}. Nothing to build.`);
124
- return;
125
- }
126
176
  if (!options.isDev) {
127
177
  console.log(`๐Ÿ“„ Found ${markdownFiles.length} markdown files.`);
128
178
  }
129
-
130
- // Array to collect information about all processed pages for sitemap
131
- const processedPages = [];
132
179
 
133
- // Extract a flattened navigation array for prev/next links
134
- const flatNavigation = [];
180
+ // Process each Markdown file
181
+ const processedFiles = new Set(); // Track processed files to avoid double processing
135
182
 
136
- // Helper function to create a normalized path for navigation matching
137
- function createNormalizedPath(item) {
138
- if (!item.path) return null;
139
- return item.path.startsWith('/') ? item.path : '/' + item.path;
140
- }
141
-
142
- function extractNavigationItems(items, parentPath = '') {
143
- if (!items || !Array.isArray(items)) return;
144
-
145
- for (const item of items) {
146
- if (item.external) continue; // Skip external links
183
+ for (const filePath of markdownFiles) {
184
+ try {
185
+ const fileContent = await fs.readFile(filePath, 'utf8');
186
+ const { data: frontmatter, content } = matter(fileContent);
147
187
 
148
- // Only include items with paths (not section headers without links)
149
- if (item.path) {
150
- // Normalize path - ensure leading slash
151
- let normalizedPath = createNormalizedPath(item);
152
-
153
- // For parent items with children, ensure path ends with / (folders)
154
- // This helps with matching in the navigation template
155
- if (item.children && item.children.length > 0) {
156
- // If path from config doesn't end with slash, add it
157
- if (!item.path.endsWith('/') && !normalizedPath.endsWith('/')) {
158
- normalizedPath += '/';
159
- }
160
- }
161
-
162
- flatNavigation.push({
163
- title: item.title,
164
- path: normalizedPath,
165
- fullPath: item.path, // Original path as defined in config
166
- isParent: item.children && item.children.length > 0 // Mark if it's a parent with children
167
- });
188
+ // Skip this file if it's already been processed and noDoubleProcessing is true
189
+ const relativePath = path.relative(SRC_DIR, filePath);
190
+ if (options.noDoubleProcessing && processedFiles.has(relativePath)) {
191
+ continue;
168
192
  }
193
+ processedFiles.add(relativePath);
194
+
195
+ // Pretty URL handling - properly handle index.md files in subfolders
196
+ let outputHtmlPath;
197
+ const fileName = path.basename(relativePath);
198
+ const isIndexFile = fileName === 'index.md';
169
199
 
170
- // Process children (depth first to maintain document outline order)
171
- if (item.children && Array.isArray(item.children)) {
172
- extractNavigationItems(item.children, item.path || parentPath);
200
+ if (isIndexFile) {
201
+ // For any index.md file (in root or subfolder), convert to index.html in the same folder
202
+ const dirPath = path.dirname(relativePath);
203
+ outputHtmlPath = path.join(dirPath, 'index.html');
204
+ } else {
205
+ // For non-index files, create a folder with index.html
206
+ outputHtmlPath = relativePath.replace(/\.md$/, '/index.html');
173
207
  }
174
- }
175
- }
176
-
177
- // Extract navigation items into flat array
178
- extractNavigationItems(config.navigation);
179
208
 
180
- for (const mdFilePath of markdownFiles) {
181
- const relativeMdPath = path.relative(SRC_DIR, mdFilePath);
182
-
183
- // Pretty URL handling - properly handle index.md files in subfolders
184
- let outputHtmlPath;
185
- const fileName = path.basename(relativeMdPath);
186
- const isIndexFile = fileName === 'index.md';
187
-
188
- if (isIndexFile) {
189
- // For any index.md file (in root or subfolder), convert to index.html in the same folder
190
- const dirPath = path.dirname(relativeMdPath);
191
- outputHtmlPath = path.join(dirPath, 'index.html');
192
- } else {
193
- // For non-index files, create a folder with index.html
194
- outputHtmlPath = relativeMdPath.replace(/\.md$/, '/index.html');
195
- }
209
+ const finalOutputHtmlPath = path.join(OUTPUT_DIR, outputHtmlPath);
196
210
 
197
- const finalOutputHtmlPath = path.join(OUTPUT_DIR, outputHtmlPath);
211
+ const depth = outputHtmlPath.split(path.sep).length - 1;
212
+ const relativePathToRoot = depth > 0 ? '../'.repeat(depth) : './';
198
213
 
199
- const depth = outputHtmlPath.split(path.sep).length - 1;
200
- const relativePathToRoot = depth > 0 ? '../'.repeat(depth) : './';
214
+ const { frontmatter: pageFrontmatter, htmlContent, headings } = await processMarkdownFile(filePath, { isDev: options.isDev });
215
+
216
+ // Special handling for no-style pages
217
+ let finalHtmlContent = htmlContent;
218
+ if (pageFrontmatter.noStyle === true) {
219
+ // For no-style pages, ensure the HTML content is not escaped
220
+ // This is critical for the landing page and custom pages
221
+ finalHtmlContent = htmlContent;
222
+
223
+ // Log a message for debugging - but only for non-dev mode or verbose logging
224
+ if (!options.isDev) {
225
+ console.log(`๐Ÿ“„ Processing no-style page: ${path.relative(CWD, filePath)}`);
226
+ }
227
+ }
201
228
 
202
- const { frontmatter, htmlContent, headings } = await processMarkdownFile(mdFilePath);
203
-
204
- // Get the URL path for navigation
205
- let currentPagePathForNav;
206
- let normalizedPath;
207
-
208
- if (isIndexFile) {
209
- // For index.md files, the nav path should be the directory itself with trailing slash
210
- const dirPath = path.dirname(relativeMdPath);
211
- if (dirPath === '.') {
212
- // Root index.md
213
- currentPagePathForNav = 'index.html';
214
- normalizedPath = '/';
229
+ // Get the URL path for navigation
230
+ let currentPagePathForNav;
231
+ let normalizedPath;
232
+
233
+ if (isIndexFile) {
234
+ // For index.md files, the nav path should be the directory itself with trailing slash
235
+ const dirPath = path.dirname(relativePath);
236
+ if (dirPath === '.') {
237
+ // Root index.md
238
+ currentPagePathForNav = 'index.html';
239
+ normalizedPath = '/';
240
+ } else {
241
+ // Subfolder index.md - simple format: directory-name/
242
+ currentPagePathForNav = dirPath + '/';
243
+ normalizedPath = '/' + dirPath;
244
+ }
215
245
  } else {
216
- // Subfolder index.md - simple format: directory-name/
217
- currentPagePathForNav = dirPath + '/';
218
- normalizedPath = '/' + dirPath;
219
- }
220
- } else {
221
- // For non-index files, the path should be the file name with trailing slash
222
- const pathWithoutExt = relativeMdPath.replace(/\.md$/, '');
223
- currentPagePathForNav = pathWithoutExt + '/';
224
- normalizedPath = '/' + pathWithoutExt;
225
- }
226
-
227
- // Convert Windows backslashes to forward slashes for web paths
228
- currentPagePathForNav = currentPagePathForNav.replace(/\\/g, '/');
229
-
230
- // Log navigation paths for debugging
231
- // Uncomment this line when debugging:
232
- // logNavigationPaths(mdFilePath, currentPagePathForNav, normalizedPath);
233
-
234
- const navigationHtml = await generateNavigationHtml(
235
- config.navigation,
236
- currentPagePathForNav,
237
- relativePathToRoot,
238
- config
239
- );
240
-
241
- // Find current page in navigation for prev/next links
242
- let prevPage = null;
243
- let nextPage = null;
244
- let currentPageIndex = -1;
245
-
246
- // Find the current page in flatNavigation
247
- currentPageIndex = flatNavigation.findIndex(item => {
248
- // Direct path match
249
- if (item.path === normalizedPath) {
250
- return true;
246
+ // For non-index files, the path should be the file name with trailing slash
247
+ const pathWithoutExt = relativePath.replace(/\.md$/, '');
248
+ currentPagePathForNav = pathWithoutExt + '/';
249
+ normalizedPath = '/' + pathWithoutExt;
251
250
  }
252
251
 
253
- // Special handling for parent folders
254
- if (isIndexFile && item.path.endsWith('/')) {
255
- // Remove trailing slash for comparison
256
- const itemPathWithoutSlash = item.path.slice(0, -1);
257
- return itemPathWithoutSlash === normalizedPath;
252
+ // Convert Windows backslashes to forward slashes for web paths
253
+ currentPagePathForNav = currentPagePathForNav.replace(/\\/g, '/');
254
+
255
+ // Log navigation paths for debugging
256
+ // Uncomment this line when debugging:
257
+ // logNavigationPaths(filePath, currentPagePathForNav, normalizedPath);
258
+
259
+ const navigationHtml = await generateNavigationHtml(
260
+ config.navigation,
261
+ currentPagePathForNav,
262
+ relativePathToRoot,
263
+ config
264
+ );
265
+
266
+ // Find current page in navigation for prev/next links
267
+ let prevPage = null;
268
+ let nextPage = null;
269
+ let currentPageIndex = -1;
270
+
271
+ // Extract a flattened navigation array for prev/next links
272
+ const flatNavigation = [];
273
+
274
+ // Helper function to create a normalized path for navigation matching
275
+ function createNormalizedPath(item) {
276
+ if (!item.path) return null;
277
+ return item.path.startsWith('/') ? item.path : '/' + item.path;
258
278
  }
259
279
 
260
- return false;
261
- });
262
-
263
- if (currentPageIndex >= 0) {
264
- // Get previous and next pages if they exist
265
- if (currentPageIndex > 0) {
266
- prevPage = flatNavigation[currentPageIndex - 1];
280
+ function extractNavigationItems(items, parentPath = '') {
281
+ if (!items || !Array.isArray(items)) return;
282
+
283
+ for (const item of items) {
284
+ if (item.external) continue; // Skip external links
285
+
286
+ // Only include items with paths (not section headers without links)
287
+ if (item.path) {
288
+ // Normalize path - ensure leading slash
289
+ let normalizedPath = createNormalizedPath(item);
290
+
291
+ // For parent items with children, ensure path ends with / (folders)
292
+ // This helps with matching in the navigation template
293
+ if (item.children && item.children.length > 0) {
294
+ // If path from config doesn't end with slash, add it
295
+ if (!item.path.endsWith('/') && !normalizedPath.endsWith('/')) {
296
+ normalizedPath += '/';
297
+ }
298
+ }
299
+
300
+ flatNavigation.push({
301
+ title: item.title,
302
+ path: normalizedPath,
303
+ fullPath: item.path, // Original path as defined in config
304
+ isParent: item.children && item.children.length > 0 // Mark if it's a parent with children
305
+ });
306
+ }
307
+
308
+ // Process children (depth first to maintain document outline order)
309
+ if (item.children && Array.isArray(item.children)) {
310
+ extractNavigationItems(item.children, item.path || parentPath);
311
+ }
312
+ }
267
313
  }
268
314
 
269
- if (currentPageIndex < flatNavigation.length - 1) {
270
- nextPage = flatNavigation[currentPageIndex + 1];
315
+ // Extract navigation items into flat array
316
+ extractNavigationItems(config.navigation);
317
+
318
+ // Find the current page in flatNavigation
319
+ currentPageIndex = flatNavigation.findIndex(item => {
320
+ // Direct path match
321
+ if (item.path === normalizedPath) {
322
+ return true;
323
+ }
324
+
325
+ // Special handling for parent folders
326
+ if (isIndexFile && item.path.endsWith('/')) {
327
+ // Remove trailing slash for comparison
328
+ const itemPathWithoutSlash = item.path.slice(0, -1);
329
+ return itemPathWithoutSlash === normalizedPath;
330
+ }
331
+
332
+ return false;
333
+ });
334
+
335
+ if (currentPageIndex >= 0) {
336
+ // Get previous and next pages if they exist
337
+ if (currentPageIndex > 0) {
338
+ prevPage = flatNavigation[currentPageIndex - 1];
339
+ }
340
+
341
+ if (currentPageIndex < flatNavigation.length - 1) {
342
+ nextPage = flatNavigation[currentPageIndex + 1];
343
+ }
271
344
  }
272
- }
273
-
274
- // Convert page paths to proper URLs for links
275
- if (prevPage) {
276
- // Format the previous page URL, avoiding double slashes
277
- if (prevPage.path === '/') {
278
- prevPage.url = relativePathToRoot + 'index.html';
279
- } else {
280
- // Remove leading slash and ensure clean path
281
- const cleanPath = prevPage.path.substring(1).replace(/\/+$/, '');
282
- prevPage.url = relativePathToRoot + cleanPath + '/';
345
+
346
+ // Convert page paths to proper URLs for links
347
+ if (prevPage) {
348
+ // Format the previous page URL, avoiding double slashes
349
+ if (prevPage.path === '/') {
350
+ prevPage.url = relativePathToRoot + 'index.html';
351
+ } else {
352
+ // Remove leading slash and ensure clean path
353
+ const cleanPath = prevPage.path.substring(1).replace(/\/+$/, '');
354
+ prevPage.url = relativePathToRoot + cleanPath + '/';
355
+ }
283
356
  }
284
- }
285
-
286
- if (nextPage) {
287
- // Format the next page URL, avoiding double slashes
288
- if (nextPage.path === '/') {
289
- nextPage.url = relativePathToRoot + 'index.html';
290
- } else {
291
- // Remove leading slash and ensure clean path
292
- const cleanPath = nextPage.path.substring(1).replace(/\/+$/, '');
293
- nextPage.url = relativePathToRoot + cleanPath + '/';
357
+
358
+ if (nextPage) {
359
+ // Format the next page URL, avoiding double slashes
360
+ if (nextPage.path === '/') {
361
+ nextPage.url = relativePathToRoot + 'index.html';
362
+ } else {
363
+ // Remove leading slash and ensure clean path
364
+ const cleanPath = nextPage.path.substring(1).replace(/\/+$/, '');
365
+ nextPage.url = relativePathToRoot + cleanPath + '/';
366
+ }
294
367
  }
295
- }
296
368
 
297
- const pageDataForTemplate = {
298
- content: htmlContent,
299
- pageTitle: frontmatter.title || 'Untitled',
300
- siteTitle: config.siteTitle,
301
- navigationHtml,
302
- relativePathToRoot: relativePathToRoot,
303
- config: config, // Pass full config
304
- frontmatter: frontmatter,
305
- outputPath: outputHtmlPath, // Relative path from outputDir root
306
- prettyUrl: true, // Flag to indicate we're using pretty URLs
307
- prevPage: prevPage, // Previous page in navigation
308
- nextPage: nextPage, // Next page in navigation
309
- currentPagePath: normalizedPath, // Pass the normalized path for active state detection
310
- headings: headings || [], // Pass headings for TOC
311
- };
312
-
313
- const pageHtml = await generateHtmlPage(pageDataForTemplate);
314
-
315
- await fs.ensureDir(path.dirname(finalOutputHtmlPath));
316
- await fs.writeFile(finalOutputHtmlPath, pageHtml);
317
-
318
- // Add to processed pages for sitemap
319
- processedPages.push({
320
- outputPath: isIndexFile
321
- ? (path.dirname(relativeMdPath) === '.' ? 'index.html' : path.dirname(relativeMdPath) + '/')
322
- : outputHtmlPath.replace(/\\/g, '/').replace(/\/index\.html$/, '/'),
323
- frontmatter: frontmatter
324
- });
369
+ const pageDataForTemplate = {
370
+ content: finalHtmlContent,
371
+ pageTitle: pageFrontmatter.title || 'Untitled',
372
+ siteTitle: config.siteTitle,
373
+ navigationHtml,
374
+ relativePathToRoot: relativePathToRoot,
375
+ config: config, // Pass full config
376
+ frontmatter: pageFrontmatter,
377
+ outputPath: outputHtmlPath, // Relative path from outputDir root
378
+ prettyUrl: true, // Flag to indicate we're using pretty URLs
379
+ prevPage: prevPage, // Previous page in navigation
380
+ nextPage: nextPage, // Next page in navigation
381
+ currentPagePath: normalizedPath, // Pass the normalized path for active state detection
382
+ headings: headings || [], // Pass headings for TOC
383
+ };
384
+
385
+ const pageHtml = await generateHtmlPage(pageDataForTemplate);
386
+
387
+ await fs.ensureDir(path.dirname(finalOutputHtmlPath));
388
+ await fs.writeFile(finalOutputHtmlPath, pageHtml);
389
+
390
+ // Add to processed pages for sitemap
391
+ const processedPage = {
392
+ outputPath: isIndexFile
393
+ ? (path.dirname(relativePath) === '.' ? 'index.html' : path.dirname(relativePath) + '/')
394
+ : outputHtmlPath.replace(/\\/g, '/').replace(/\/index\.html$/, '/'),
395
+ frontmatter: pageFrontmatter
396
+ };
397
+ processedPages.push(processedPage);
398
+ } catch (error) {
399
+ console.error(`Error processing file ${path.relative(CWD, filePath)}:`, error);
400
+ }
325
401
  }
326
402
 
327
403
  // Generate sitemap if enabled in config
328
404
  if (config.plugins?.sitemap !== false) {
329
405
  try {
330
- await generateSitemap(config, processedPages, OUTPUT_DIR);
406
+ await generateSitemap(config, processedPages, OUTPUT_DIR, { isDev: options.isDev });
331
407
  } catch (error) {
332
408
  console.error(`โŒ Error generating sitemap: ${error.message}`);
333
409
  }
@@ -339,6 +415,22 @@ async function buildSite(configPath, options = { isDev: false, preserve: false }
339
415
  preservedFiles.forEach(file => console.log(` - assets/${file}`));
340
416
  console.log(`\nTo update these files in future builds, run without the --preserve flag.`);
341
417
  }
418
+
419
+ if (userAssetsCopied.length > 0 && !options.isDev) {
420
+ console.log(`\n๐Ÿ“‹ User Assets: ${userAssetsCopied.length} files were copied from your assets/ directory:`);
421
+ if (userAssetsCopied.length <= 10) {
422
+ userAssetsCopied.forEach(file => console.log(` - assets/${file}`));
423
+ } else {
424
+ userAssetsCopied.slice(0, 5).forEach(file => console.log(` - assets/${file}`));
425
+ console.log(` - ... and ${userAssetsCopied.length - 5} more files`);
426
+ }
427
+ }
428
+
429
+ return {
430
+ config,
431
+ processedPages,
432
+ markdownFiles,
433
+ };
342
434
  }
343
435
 
344
436
  // Helper function to find HTML files and sitemap.xml to clean up
@@ -383,19 +475,4 @@ async function getAllFiles(dir) {
383
475
  return files;
384
476
  }
385
477
 
386
- // findMarkdownFiles function remains the same
387
- async function findMarkdownFiles(dir) {
388
- let files = [];
389
- const items = await fs.readdir(dir, { withFileTypes: true });
390
- for (const item of items) {
391
- const fullPath = path.join(dir, item.name);
392
- if (item.isDirectory()) {
393
- files = files.concat(await findMarkdownFiles(fullPath));
394
- } else if (item.isFile() && (item.name.endsWith('.md') || item.name.endsWith('.markdown'))) {
395
- files.push(fullPath);
396
- }
397
- }
398
- return files;
399
- }
400
-
401
478
  module.exports = { buildSite };