@rettangoli/sites 0.2.0-rc3 → 0.2.0-rc5
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/package.json +2 -2
- package/src/cli/build.js +10 -16
- package/src/cli/watch.js +80 -42
- package/src/createSiteBuilder.js +11 -11
- package/src/utils/loadSiteConfig.js +36 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rettangoli/sites",
|
|
3
|
-
"version": "0.2.0-
|
|
3
|
+
"version": "0.2.0-rc5",
|
|
4
4
|
"description": "Generate static sites using Markdown and YAML. Straightforward, zero-complexity. Complete toolkit for landing pages, blogs, documentation, admin dashboards, and more.git remote add origin git@github.com:yuusoft-org/sitic.git",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Luciano Hanyon Wu",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"templates"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"jempl": "^0.
|
|
20
|
+
"jempl": "^0.3.1-rc1",
|
|
21
21
|
"js-yaml": "^4.1.0",
|
|
22
22
|
"markdown-it": "^14.1.0",
|
|
23
23
|
"playwright": "^1.55.0",
|
package/src/cli/build.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
2
|
import { createSiteBuilder } from '../createSiteBuilder.js';
|
|
3
|
+
import { loadSiteConfig } from '../utils/loadSiteConfig.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Build the static site
|
|
@@ -10,27 +10,21 @@ import { createSiteBuilder } from '../createSiteBuilder.js';
|
|
|
10
10
|
* @param {boolean} options.quiet - Suppress build output logs
|
|
11
11
|
*/
|
|
12
12
|
export const buildSite = async (options = {}) => {
|
|
13
|
-
const { rootDir = process.cwd(), mdRender, quiet = false } = options;
|
|
14
|
-
|
|
15
|
-
//
|
|
13
|
+
const { rootDir = process.cwd(), mdRender, functions, quiet = false } = options;
|
|
14
|
+
|
|
15
|
+
// Load config file if needed
|
|
16
16
|
let config = {};
|
|
17
|
-
if (!mdRender) {
|
|
18
|
-
|
|
19
|
-
const configPath = path.join(rootDir, 'sites.config.js');
|
|
20
|
-
const configModule = await import(configPath);
|
|
21
|
-
config = configModule.default || {};
|
|
22
|
-
} catch (e) {
|
|
23
|
-
// Config file is optional, continue without it
|
|
24
|
-
}
|
|
17
|
+
if (!mdRender || !functions) {
|
|
18
|
+
config = await loadSiteConfig(rootDir);
|
|
25
19
|
}
|
|
26
20
|
|
|
27
|
-
const build = createSiteBuilder({
|
|
28
|
-
fs,
|
|
21
|
+
const build = createSiteBuilder({
|
|
22
|
+
fs,
|
|
29
23
|
rootDir,
|
|
30
24
|
mdRender: mdRender || config.mdRender,
|
|
31
|
-
functions: config.functions || {},
|
|
25
|
+
functions: functions || config.functions || {},
|
|
32
26
|
quiet
|
|
33
27
|
});
|
|
34
|
-
|
|
28
|
+
|
|
35
29
|
build();
|
|
36
30
|
};
|
package/src/cli/watch.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import fs, { watch, existsSync } from 'node:fs';
|
|
2
2
|
import http from 'node:http';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
4
5
|
import { WebSocketServer } from 'ws';
|
|
5
6
|
import { buildSite } from './build.js';
|
|
6
7
|
import ScreenshotCapture from './screenshot.js';
|
|
8
|
+
import { loadSiteConfig } from '../utils/loadSiteConfig.js';
|
|
7
9
|
|
|
8
10
|
// Client script to inject into HTML pages
|
|
9
11
|
const CLIENT_SCRIPT = `
|
|
@@ -74,16 +76,16 @@ class DevServer {
|
|
|
74
76
|
// Create WebSocket server
|
|
75
77
|
this.wss = new WebSocketServer({ server: this.httpServer });
|
|
76
78
|
console.log('WebSocket server created');
|
|
77
|
-
|
|
79
|
+
|
|
78
80
|
this.wss.on('connection', (ws) => {
|
|
79
81
|
console.log('✅ Client connected. Total clients:', this.clients.size + 1);
|
|
80
82
|
this.clients.add(ws);
|
|
81
|
-
|
|
83
|
+
|
|
82
84
|
ws.on('close', () => {
|
|
83
85
|
console.log('❌ Client disconnected. Remaining clients:', this.clients.size - 1);
|
|
84
86
|
this.clients.delete(ws);
|
|
85
87
|
});
|
|
86
|
-
|
|
88
|
+
|
|
87
89
|
ws.on('error', (err) => {
|
|
88
90
|
console.error('❌ WebSocket error:', err);
|
|
89
91
|
this.clients.delete(ws);
|
|
@@ -103,21 +105,21 @@ class DevServer {
|
|
|
103
105
|
const urlParts = req.url.split('?');
|
|
104
106
|
let urlPath = urlParts[0];
|
|
105
107
|
const queryString = urlParts[1] || '';
|
|
106
|
-
|
|
108
|
+
|
|
107
109
|
// Check if this is a screenshot request
|
|
108
110
|
const isScreenshotRequest = queryString.includes('screenshot=true');
|
|
109
|
-
|
|
111
|
+
|
|
110
112
|
// Default to index.html for root
|
|
111
113
|
if (urlPath === '/') {
|
|
112
114
|
urlPath = '/index.html';
|
|
113
115
|
}
|
|
114
|
-
|
|
116
|
+
|
|
115
117
|
// Handle trailing slash - remove it for processing
|
|
116
118
|
const hasTrailingSlash = urlPath.endsWith('/') && urlPath !== '/';
|
|
117
119
|
if (hasTrailingSlash) {
|
|
118
120
|
urlPath = urlPath.slice(0, -1);
|
|
119
121
|
}
|
|
120
|
-
|
|
122
|
+
|
|
121
123
|
// Handle paths without extensions
|
|
122
124
|
if (!path.extname(urlPath)) {
|
|
123
125
|
// First try as .html file
|
|
@@ -132,16 +134,16 @@ class DevServer {
|
|
|
132
134
|
}
|
|
133
135
|
}
|
|
134
136
|
}
|
|
135
|
-
|
|
137
|
+
|
|
136
138
|
const filePath = path.join(this.siteDir, urlPath);
|
|
137
|
-
|
|
139
|
+
|
|
138
140
|
// Check if file exists
|
|
139
141
|
if (!existsSync(filePath)) {
|
|
140
142
|
res.writeHead(404);
|
|
141
143
|
res.end('404 Not Found');
|
|
142
144
|
return;
|
|
143
145
|
}
|
|
144
|
-
|
|
146
|
+
|
|
145
147
|
// Check if it's a directory
|
|
146
148
|
const stats = fs.statSync(filePath);
|
|
147
149
|
if (stats.isDirectory()) {
|
|
@@ -155,18 +157,18 @@ class DevServer {
|
|
|
155
157
|
return;
|
|
156
158
|
}
|
|
157
159
|
}
|
|
158
|
-
|
|
160
|
+
|
|
159
161
|
// Serve the file
|
|
160
162
|
this.serveFile(filePath, res, isScreenshotRequest);
|
|
161
163
|
}
|
|
162
|
-
|
|
164
|
+
|
|
163
165
|
serveFile(filePath, res, skipWebSocket = false) {
|
|
164
166
|
const ext = path.extname(filePath);
|
|
165
167
|
const contentType = this.getContentType(ext);
|
|
166
|
-
|
|
168
|
+
|
|
167
169
|
try {
|
|
168
170
|
let content = fs.readFileSync(filePath);
|
|
169
|
-
|
|
171
|
+
|
|
170
172
|
// Inject client script into HTML files (unless it's a screenshot request)
|
|
171
173
|
if (ext === '.html' && !skipWebSocket) {
|
|
172
174
|
content = content.toString();
|
|
@@ -179,7 +181,7 @@ class DevServer {
|
|
|
179
181
|
content = content + CLIENT_SCRIPT;
|
|
180
182
|
}
|
|
181
183
|
}
|
|
182
|
-
|
|
184
|
+
|
|
183
185
|
res.writeHead(200, { 'Content-Type': contentType });
|
|
184
186
|
res.end(content);
|
|
185
187
|
} catch (err) {
|
|
@@ -206,12 +208,12 @@ class DevServer {
|
|
|
206
208
|
|
|
207
209
|
reloadAll() {
|
|
208
210
|
console.log('📤 Sending reload message to', this.clients.size, 'clients');
|
|
209
|
-
|
|
211
|
+
|
|
210
212
|
// Send a simple reload command to all clients
|
|
211
213
|
const message = JSON.stringify({
|
|
212
214
|
type: 'reload-current'
|
|
213
215
|
});
|
|
214
|
-
|
|
216
|
+
|
|
215
217
|
let sentCount = 0;
|
|
216
218
|
this.clients.forEach(client => {
|
|
217
219
|
if (client.readyState === 1) { // WebSocket.OPEN
|
|
@@ -219,7 +221,7 @@ class DevServer {
|
|
|
219
221
|
sentCount++;
|
|
220
222
|
}
|
|
221
223
|
});
|
|
222
|
-
|
|
224
|
+
|
|
223
225
|
console.log('✅ Reload message sent to', sentCount, 'clients');
|
|
224
226
|
}
|
|
225
227
|
|
|
@@ -233,29 +235,38 @@ class DevServer {
|
|
|
233
235
|
const setupWatcher = (directory, options, server, screenshotCapture) => {
|
|
234
236
|
let debounceTimer = null;
|
|
235
237
|
let pendingFiles = new Set();
|
|
236
|
-
|
|
238
|
+
|
|
237
239
|
const processChanges = async () => {
|
|
238
240
|
const files = [...pendingFiles];
|
|
239
241
|
pendingFiles.clear();
|
|
240
|
-
|
|
242
|
+
|
|
241
243
|
console.log('Rebuilding site...');
|
|
242
244
|
try {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
+
// Always reload config on rebuild to pick up function changes
|
|
246
|
+
const config = await loadSiteConfig(options.rootDir, true, true);
|
|
245
247
|
|
|
248
|
+
const currentOptions = {
|
|
249
|
+
...options,
|
|
250
|
+
mdRender: config.mdRender || options.mdRender,
|
|
251
|
+
functions: config.functions || options.functions || {}
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
await buildSite({ ...currentOptions, quiet: true });
|
|
255
|
+
console.log('Rebuild complete');
|
|
256
|
+
|
|
246
257
|
// Just reload all clients - they'll reload their current page
|
|
247
258
|
console.log('🔄 Reloading all connected clients');
|
|
248
259
|
server.reloadAll();
|
|
249
|
-
|
|
260
|
+
|
|
250
261
|
// If screenshots are enabled and pages were changed, capture screenshots
|
|
251
|
-
const pageFiles = files.filter(file =>
|
|
262
|
+
const pageFiles = files.filter(file =>
|
|
252
263
|
(file.includes('pages/') || file.startsWith('pages/')) &&
|
|
253
264
|
(file.endsWith('.md') || file.endsWith('.yaml') || file.endsWith('.yml'))
|
|
254
265
|
);
|
|
255
266
|
if (screenshotCapture && pageFiles.length > 0) {
|
|
256
267
|
// Wait a bit for the server to be ready with new content
|
|
257
268
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
258
|
-
|
|
269
|
+
|
|
259
270
|
console.log('📸 Capturing screenshots for changed pages...');
|
|
260
271
|
// Capture screenshots for changed pages
|
|
261
272
|
for (const file of pageFiles) {
|
|
@@ -264,12 +275,12 @@ const setupWatcher = (directory, options, server, screenshotCapture) => {
|
|
|
264
275
|
await screenshotCapture.capturePageScreenshot(file);
|
|
265
276
|
}
|
|
266
277
|
}
|
|
267
|
-
|
|
278
|
+
|
|
268
279
|
} catch (error) {
|
|
269
280
|
console.error('Error during rebuild:', error);
|
|
270
281
|
}
|
|
271
282
|
};
|
|
272
|
-
|
|
283
|
+
|
|
273
284
|
watch(
|
|
274
285
|
directory,
|
|
275
286
|
{ recursive: true },
|
|
@@ -279,35 +290,35 @@ const setupWatcher = (directory, options, server, screenshotCapture) => {
|
|
|
279
290
|
if (filename.endsWith('~') || filename.startsWith('.') || filename.includes('/.')) {
|
|
280
291
|
return;
|
|
281
292
|
}
|
|
282
|
-
|
|
293
|
+
|
|
283
294
|
// Skip _site directory if it somehow gets included
|
|
284
295
|
if (filename.includes('_site/')) {
|
|
285
296
|
return;
|
|
286
297
|
}
|
|
287
|
-
|
|
298
|
+
|
|
288
299
|
// For static directory, only rebuild for content files, not binary files
|
|
289
300
|
const isStaticDir = directory.endsWith('/static') || directory.includes('/static/');
|
|
290
301
|
if (isStaticDir) {
|
|
291
302
|
const ext = path.extname(filename).toLowerCase();
|
|
292
303
|
const binaryExts = ['.png', '.jpg', '.jpeg', '.gif', '.ico', '.pdf', '.zip', '.woff', '.woff2', '.ttf', '.eot'];
|
|
293
|
-
|
|
304
|
+
|
|
294
305
|
if (binaryExts.includes(ext)) {
|
|
295
306
|
// For binary files in static, just copy them without full rebuild
|
|
296
307
|
console.log(`📁 Static file changed: ${directory}/${filename} (skipping rebuild)`);
|
|
297
308
|
return;
|
|
298
309
|
}
|
|
299
310
|
}
|
|
300
|
-
|
|
311
|
+
|
|
301
312
|
console.log(`Detected ${event} in ${filename}`);
|
|
302
|
-
|
|
313
|
+
|
|
303
314
|
// Add to pending files for rebuild
|
|
304
315
|
const fullPath = path.join(directory, filename);
|
|
305
316
|
pendingFiles.add(fullPath);
|
|
306
|
-
|
|
317
|
+
|
|
307
318
|
if (debounceTimer) {
|
|
308
319
|
clearTimeout(debounceTimer);
|
|
309
320
|
}
|
|
310
|
-
|
|
321
|
+
|
|
311
322
|
debounceTimer = setTimeout(processChanges, 10);
|
|
312
323
|
}
|
|
313
324
|
},
|
|
@@ -316,15 +327,38 @@ const setupWatcher = (directory, options, server, screenshotCapture) => {
|
|
|
316
327
|
|
|
317
328
|
// Main watch function
|
|
318
329
|
const watchSite = async (options = {}) => {
|
|
319
|
-
const {
|
|
330
|
+
const {
|
|
320
331
|
port = 3001,
|
|
321
|
-
rootDir =
|
|
332
|
+
rootDir = process.cwd(),
|
|
322
333
|
screenshots = false
|
|
323
334
|
} = options;
|
|
324
335
|
|
|
325
|
-
//
|
|
336
|
+
// Load config file
|
|
337
|
+
console.log(`📁 Current working directory: ${process.cwd()}`);
|
|
338
|
+
console.log(`📁 rootDir parameter: ${rootDir}`);
|
|
339
|
+
const config = await loadSiteConfig(rootDir, false);
|
|
340
|
+
|
|
341
|
+
if (Object.keys(config).length > 0) {
|
|
342
|
+
console.log('✅ Loaded sites.config.js');
|
|
343
|
+
if (config.mdRender) {
|
|
344
|
+
console.log('✅ Custom mdRender function found');
|
|
345
|
+
} else {
|
|
346
|
+
console.log('ℹ️ No custom mdRender function in config');
|
|
347
|
+
}
|
|
348
|
+
if (config.functions) {
|
|
349
|
+
console.log(`✅ Found ${Object.keys(config.functions).length} custom function(s)`);
|
|
350
|
+
}
|
|
351
|
+
} else {
|
|
352
|
+
console.log('ℹ️ No sites.config.js found, using defaults');
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Do initial build with config
|
|
326
356
|
console.log('Starting initial build...');
|
|
327
|
-
await buildSite({
|
|
357
|
+
await buildSite({
|
|
358
|
+
rootDir,
|
|
359
|
+
mdRender: config.mdRender,
|
|
360
|
+
functions: config.functions || {}
|
|
361
|
+
});
|
|
328
362
|
console.log('Initial build complete');
|
|
329
363
|
|
|
330
364
|
// Start custom dev server
|
|
@@ -341,12 +375,16 @@ const watchSite = async (options = {}) => {
|
|
|
341
375
|
|
|
342
376
|
// Watch all relevant directories
|
|
343
377
|
const dirsToWatch = ['data', 'templates', 'partials', 'pages'];
|
|
344
|
-
|
|
378
|
+
|
|
345
379
|
dirsToWatch.forEach(dir => {
|
|
346
380
|
const dirPath = path.join(rootDir, dir);
|
|
347
381
|
if (existsSync(dirPath)) {
|
|
348
382
|
console.log(`👁️ Watching: ${dir}/`);
|
|
349
|
-
setupWatcher(dirPath, {
|
|
383
|
+
setupWatcher(dirPath, {
|
|
384
|
+
rootDir,
|
|
385
|
+
mdRender: config.mdRender,
|
|
386
|
+
functions: config.functions || {}
|
|
387
|
+
}, server, screenshotCapture);
|
|
350
388
|
}
|
|
351
389
|
});
|
|
352
390
|
|
|
@@ -359,7 +397,7 @@ const watchSite = async (options = {}) => {
|
|
|
359
397
|
server.close();
|
|
360
398
|
process.exit();
|
|
361
399
|
});
|
|
362
|
-
|
|
400
|
+
|
|
363
401
|
process.on('SIGTERM', async () => {
|
|
364
402
|
if (screenshotCapture) {
|
|
365
403
|
await screenshotCapture.close();
|
|
@@ -369,4 +407,4 @@ const watchSite = async (options = {}) => {
|
|
|
369
407
|
});
|
|
370
408
|
};
|
|
371
409
|
|
|
372
|
-
export default watchSite;
|
|
410
|
+
export default watchSite;
|
package/src/createSiteBuilder.js
CHANGED
|
@@ -120,7 +120,7 @@ export function createSiteBuilder({ fs, rootDir = '.', mdRender, functions = {},
|
|
|
120
120
|
} else if (item.isFile() && (item.name.endsWith('.yaml') || item.name.endsWith('.md'))) {
|
|
121
121
|
// Extract frontmatter
|
|
122
122
|
const frontmatter = extractFrontmatter(itemPath);
|
|
123
|
-
|
|
123
|
+
|
|
124
124
|
// Calculate URL
|
|
125
125
|
const outputFileName = item.name.replace(/\.(yaml|md)$/, '.html');
|
|
126
126
|
const outputRelativePath = basePath ? path.join(basePath, outputFileName) : outputFileName;
|
|
@@ -130,7 +130,7 @@ export function createSiteBuilder({ fs, rootDir = '.', mdRender, functions = {},
|
|
|
130
130
|
if (frontmatter.tags) {
|
|
131
131
|
// Normalize tags to array
|
|
132
132
|
const tags = Array.isArray(frontmatter.tags) ? frontmatter.tags : [frontmatter.tags];
|
|
133
|
-
|
|
133
|
+
|
|
134
134
|
// Add to collections
|
|
135
135
|
tags.forEach(tag => {
|
|
136
136
|
if (typeof tag === 'string' && tag.trim()) {
|
|
@@ -296,25 +296,25 @@ export function createSiteBuilder({ fs, rootDir = '.', mdRender, functions = {},
|
|
|
296
296
|
function copyStaticFiles() {
|
|
297
297
|
const staticDir = path.join(rootDir, 'static');
|
|
298
298
|
const outputDir = path.join(rootDir, '_site');
|
|
299
|
-
|
|
299
|
+
|
|
300
300
|
if (!fs.existsSync(staticDir)) {
|
|
301
301
|
return;
|
|
302
302
|
}
|
|
303
|
-
|
|
303
|
+
|
|
304
304
|
// Ensure output directory exists
|
|
305
305
|
if (!fs.existsSync(outputDir)) {
|
|
306
306
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
307
307
|
}
|
|
308
|
-
|
|
308
|
+
|
|
309
309
|
function copyRecursive(src, dest) {
|
|
310
310
|
const stats = fs.statSync(src);
|
|
311
|
-
|
|
311
|
+
|
|
312
312
|
if (stats.isDirectory()) {
|
|
313
313
|
// Create directory if it doesn't exist
|
|
314
314
|
if (!fs.existsSync(dest)) {
|
|
315
315
|
fs.mkdirSync(dest, { recursive: true });
|
|
316
316
|
}
|
|
317
|
-
|
|
317
|
+
|
|
318
318
|
// Copy all items in directory
|
|
319
319
|
const items = fs.readdirSync(src);
|
|
320
320
|
items.forEach(item => {
|
|
@@ -326,7 +326,7 @@ export function createSiteBuilder({ fs, rootDir = '.', mdRender, functions = {},
|
|
|
326
326
|
if (!quiet) console.log(` -> Copied ${src} to ${dest}`);
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
|
-
|
|
329
|
+
|
|
330
330
|
if (!quiet) console.log('Copying static files...');
|
|
331
331
|
const items = fs.readdirSync(staticDir);
|
|
332
332
|
items.forEach(item => {
|
|
@@ -338,13 +338,13 @@ export function createSiteBuilder({ fs, rootDir = '.', mdRender, functions = {},
|
|
|
338
338
|
|
|
339
339
|
// Start build process
|
|
340
340
|
if (!quiet) console.log('Starting build process...');
|
|
341
|
-
|
|
341
|
+
|
|
342
342
|
// Copy static files first (they can be overwritten by pages)
|
|
343
343
|
copyStaticFiles();
|
|
344
|
-
|
|
344
|
+
|
|
345
345
|
// Process all pages (can overwrite static files)
|
|
346
346
|
processAllPages('');
|
|
347
|
-
|
|
347
|
+
|
|
348
348
|
if (!quiet) console.log('Build complete!');
|
|
349
349
|
};
|
|
350
350
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { pathToFileURL } from 'url';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Load the sites.config.js file from a given directory
|
|
6
|
+
* @param {string} rootDir - The root directory to look for sites.config.js
|
|
7
|
+
* @param {boolean} throwOnError - Whether to throw on errors other than file not found
|
|
8
|
+
* @param {boolean} bustCache - Whether to bypass module cache (for reloading)
|
|
9
|
+
* @returns {Promise<Object>} The loaded config object or empty object if not found
|
|
10
|
+
*/
|
|
11
|
+
export async function loadSiteConfig(rootDir, throwOnError = true, bustCache = false) {
|
|
12
|
+
try {
|
|
13
|
+
const configPath = path.join(rootDir, 'sites.config.js');
|
|
14
|
+
let importUrl = pathToFileURL(configPath).href;
|
|
15
|
+
|
|
16
|
+
// Add timestamp to force reload if cache busting is requested
|
|
17
|
+
if (bustCache) {
|
|
18
|
+
importUrl += `?t=${Date.now()}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const configModule = await import(importUrl);
|
|
22
|
+
return configModule.default || {};
|
|
23
|
+
} catch (e) {
|
|
24
|
+
// Only ignore file not found errors
|
|
25
|
+
if (e.code === 'ENOENT') {
|
|
26
|
+
// Config file is optional, return empty config
|
|
27
|
+
return {};
|
|
28
|
+
} else if (throwOnError) {
|
|
29
|
+
// Re-throw any other errors (syntax errors, module not found, etc.)
|
|
30
|
+
throw e;
|
|
31
|
+
} else {
|
|
32
|
+
console.error('Error loading sites.config.js:', e);
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|