@bookshop/hugo-engine 3.17.1 → 3.18.0-alpha.1
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/full-hugo-renderer/hugo_renderer.wasm.gz +0 -0
- package/lib/engine.js +155 -20
- package/package.json +2 -2
|
Binary file
|
package/lib/engine.js
CHANGED
|
@@ -3,6 +3,65 @@ import { gunzipSync } from 'fflate';
|
|
|
3
3
|
import translateTextTemplate from './translateTextTemplate.js';
|
|
4
4
|
import { IdentifierParser } from './hugoIdentifierParser.js';
|
|
5
5
|
|
|
6
|
+
const verboseLog = (message, ...args) => {
|
|
7
|
+
if (typeof window !== 'undefined' && window?.bookshopLiveVerbose) {
|
|
8
|
+
console.log(message, ...args);
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const UNIFIED_LAYOUT = [
|
|
13
|
+
`{{ if .Params.components }}`,
|
|
14
|
+
`{{ range $i, $c := .Params.components }}`,
|
|
15
|
+
`<script type="bookshop/batch" data-batch="{{ $.Params.batch_id }}" data-id="{{ $i }}" data-pos="start"></script>`,
|
|
16
|
+
`{{ if eq $c.type "component" }}`,
|
|
17
|
+
`{{ partial "bookshop" (slice $c.name $c.props) }}`,
|
|
18
|
+
`{{ else }}`,
|
|
19
|
+
`{{ partial "bookshop_partial" (slice $c.name $c.props) }}`,
|
|
20
|
+
`{{ end }}`,
|
|
21
|
+
`<script type="bookshop/batch" data-batch="{{ $.Params.batch_id }}" data-id="{{ $i }}" data-pos="end"></script>`,
|
|
22
|
+
`{{ end }}`,
|
|
23
|
+
`{{ else if .Params.bookshop_name }}`,
|
|
24
|
+
`{{ if eq .Params.bookshop_type "shared" }}`,
|
|
25
|
+
`{{ partial "bookshop_partial" (slice .Params.bookshop_name .Params.component) }}`,
|
|
26
|
+
`{{ else }}`,
|
|
27
|
+
`{{ partial "bookshop" (slice .Params.bookshop_name .Params.component) }}`,
|
|
28
|
+
`{{ end }}`,
|
|
29
|
+
`{{ end }}`
|
|
30
|
+
].join('');
|
|
31
|
+
|
|
32
|
+
const ensureUnifiedLayoutInstalled = () => {
|
|
33
|
+
if (typeof window === 'undefined') return {};
|
|
34
|
+
if (window.__bookshop_unified_layout_installed) return {};
|
|
35
|
+
|
|
36
|
+
window.__bookshop_unified_layout_installed = true;
|
|
37
|
+
verboseLog('[hugo-engine] Installing unified static layout');
|
|
38
|
+
return { "layouts/index.html": UNIFIED_LAYOUT };
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Build Hugo with retry logic using componentQuack for error recovery.
|
|
43
|
+
* @param {Engine} engine - The engine instance for componentQuack access
|
|
44
|
+
* @param {string} context - Description for logging (e.g., "rendering" or "batch rendering")
|
|
45
|
+
* @returns {Promise<string|null>} - The build error if unrecoverable, or null on success
|
|
46
|
+
*/
|
|
47
|
+
const buildHugoWithRetry = async (engine, context = "rendering") => {
|
|
48
|
+
window.hugo_wasm_logging = [];
|
|
49
|
+
let render_attempts = 1;
|
|
50
|
+
let buildError = window.buildHugo();
|
|
51
|
+
while (buildError && render_attempts < 5) {
|
|
52
|
+
console.warn(`Hit a build error when ${context} Hugo:\n${window.hugo_wasm_logging.map(l => ` ${l}`).join('\n')}`);
|
|
53
|
+
if (await engine.componentQuack(buildError, window.hugo_wasm_logging) === null) {
|
|
54
|
+
// Can't find a template to overwrite and re-render
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
// Try render again with the problem template stubbed out
|
|
58
|
+
window.hugo_wasm_logging = [];
|
|
59
|
+
buildError = window.buildHugo();
|
|
60
|
+
render_attempts += 1;
|
|
61
|
+
}
|
|
62
|
+
return buildError;
|
|
63
|
+
};
|
|
64
|
+
|
|
6
65
|
const sleep = (ms = 0) => {
|
|
7
66
|
return new Promise(r => setTimeout(r, ms));
|
|
8
67
|
}
|
|
@@ -292,17 +351,19 @@ export class Engine {
|
|
|
292
351
|
};
|
|
293
352
|
|
|
294
353
|
let writeFiles = {};
|
|
354
|
+
let componentType = null;
|
|
295
355
|
|
|
296
356
|
if (this.hasComponent(name)) {
|
|
297
|
-
|
|
357
|
+
componentType = "component";
|
|
298
358
|
} else if (this.hasShared(name)) {
|
|
299
|
-
|
|
359
|
+
componentType = "shared";
|
|
300
360
|
} else {
|
|
301
361
|
console.warn(`[hugo-engine] No component found for ${name}`);
|
|
302
362
|
return "";
|
|
303
363
|
}
|
|
304
|
-
|
|
305
|
-
|
|
364
|
+
|
|
365
|
+
Object.assign(writeFiles, ensureUnifiedLayoutInstalled());
|
|
366
|
+
logger?.log?.(`Going to render ${name}`);
|
|
306
367
|
|
|
307
368
|
if (!globals || typeof globals !== "object") globals = {};
|
|
308
369
|
props = {
|
|
@@ -313,25 +374,13 @@ export class Engine {
|
|
|
313
374
|
// If we have assigned a root scope we need to pass that in as the context
|
|
314
375
|
if (props["."]) props = props["."];
|
|
315
376
|
writeFiles["content/_index.md"] = JSON.stringify({
|
|
377
|
+
bookshop_name: name,
|
|
378
|
+
bookshop_type: componentType,
|
|
316
379
|
component: props
|
|
317
380
|
}, null, 2) + "\n";
|
|
318
381
|
window.writeHugoFiles(JSON.stringify(writeFiles));
|
|
319
382
|
|
|
320
|
-
|
|
321
|
-
let render_attempts = 1;
|
|
322
|
-
let buildError = window.buildHugo();
|
|
323
|
-
while (buildError && render_attempts < 5) {
|
|
324
|
-
console.warn(`Hit a build error when rendering Hugo:\n${window.hugo_wasm_logging.map(l => ` ${l}`).join('\n')}`);
|
|
325
|
-
if (this.componentQuack(buildError, window.hugo_wasm_logging) === null) {
|
|
326
|
-
// Can't find a template to overwrite and re-render
|
|
327
|
-
break;
|
|
328
|
-
}
|
|
329
|
-
// Try render again with the problem template stubbed out
|
|
330
|
-
window.hugo_wasm_logging = [];
|
|
331
|
-
buildError = window.buildHugo();
|
|
332
|
-
render_attempts += 1;
|
|
333
|
-
}
|
|
334
|
-
|
|
383
|
+
const buildError = await buildHugoWithRetry(this, "rendering");
|
|
335
384
|
if (buildError) {
|
|
336
385
|
console.error(buildError);
|
|
337
386
|
return;
|
|
@@ -342,7 +391,87 @@ export class Engine {
|
|
|
342
391
|
]));
|
|
343
392
|
|
|
344
393
|
target.innerHTML = output["public/index.html"];
|
|
345
|
-
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
async renderBatch(components, logger) {
|
|
397
|
+
if (!components || components.length === 0) return;
|
|
398
|
+
|
|
399
|
+
if (components.length === 1) {
|
|
400
|
+
const c = components[0];
|
|
401
|
+
return this.render(c.target, c.name, c.props, c.globals, logger);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
while (!window.buildHugo) {
|
|
405
|
+
logger?.log?.(`Waiting for the Hugo WASM to be available...`);
|
|
406
|
+
await sleep(100);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
verboseLog(`[hugo-engine] Batch rendering ${components.length} components`);
|
|
410
|
+
|
|
411
|
+
// Generate a unique batch ID to prevent marker collisions with component content
|
|
412
|
+
const batchId = `b${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;
|
|
413
|
+
|
|
414
|
+
const componentData = components.map((c, index) => {
|
|
415
|
+
const isComponent = this.hasComponent(c.name);
|
|
416
|
+
const isShared = this.hasShared(c.name);
|
|
417
|
+
|
|
418
|
+
if (!isComponent && !isShared) {
|
|
419
|
+
console.warn(`[hugo-engine] No component found for ${c.name}`);
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
let props = { ...(c.globals || {}), ...(c.props || {}), env_bookshop_live: true };
|
|
424
|
+
if (props["."]) props = props["."];
|
|
425
|
+
|
|
426
|
+
return {
|
|
427
|
+
index,
|
|
428
|
+
name: c.name,
|
|
429
|
+
type: isComponent ? 'component' : 'shared',
|
|
430
|
+
props
|
|
431
|
+
};
|
|
432
|
+
}).filter(Boolean);
|
|
433
|
+
|
|
434
|
+
if (componentData.length === 0) return;
|
|
435
|
+
|
|
436
|
+
let writeFiles = {
|
|
437
|
+
"content/_index.md": JSON.stringify({
|
|
438
|
+
batch_id: batchId,
|
|
439
|
+
components: componentData.map(c => ({ name: c.name, type: c.type, props: c.props }))
|
|
440
|
+
}, null, 2) + "\n",
|
|
441
|
+
...ensureUnifiedLayoutInstalled()
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
window.writeHugoFiles(JSON.stringify(writeFiles));
|
|
445
|
+
|
|
446
|
+
const buildError = await buildHugoWithRetry(this, "batch rendering");
|
|
447
|
+
if (buildError) {
|
|
448
|
+
console.error(`Batch render error: ${buildError}`);
|
|
449
|
+
verboseLog('[hugo-engine] Falling back to individual renders');
|
|
450
|
+
for (const c of components) {
|
|
451
|
+
await this.render(c.target, c.name, c.props, c.globals, logger);
|
|
452
|
+
}
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const output = window.readHugoFiles(JSON.stringify(["public/index.html"]));
|
|
457
|
+
const html = output["public/index.html"];
|
|
458
|
+
|
|
459
|
+
for (let i = 0; i < componentData.length; i++) {
|
|
460
|
+
const originalIndex = componentData[i].index;
|
|
461
|
+
const startMarker = `<script type="bookshop/batch" data-batch="${batchId}" data-id="${i}" data-pos="start"></script>`;
|
|
462
|
+
const endMarker = `<script type="bookshop/batch" data-batch="${batchId}" data-id="${i}" data-pos="end"></script>`;
|
|
463
|
+
const startIdx = html.indexOf(startMarker);
|
|
464
|
+
const endIdx = html.indexOf(endMarker);
|
|
465
|
+
|
|
466
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
467
|
+
const componentHtml = html.substring(startIdx + startMarker.length, endIdx);
|
|
468
|
+
components[originalIndex].target.innerHTML = componentHtml;
|
|
469
|
+
} else {
|
|
470
|
+
verboseLog(`[hugo-engine] Could not find markers for component ${i}, falling back to individual render`);
|
|
471
|
+
const original = components[originalIndex];
|
|
472
|
+
await this.render(original.target, original.name, original.props, original.globals, logger);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
346
475
|
}
|
|
347
476
|
|
|
348
477
|
async eval(str, props = [{}], logger) {
|
|
@@ -433,6 +562,12 @@ export class Engine {
|
|
|
433
562
|
return null;
|
|
434
563
|
}
|
|
435
564
|
|
|
565
|
+
// This is the SLOW PATH - we're hitting Hugo WASM because no short-circuit worked
|
|
566
|
+
verboseLog(`[hugo-engine] eval requires Hugo WASM: "${str.substring(0, 80)}${str.length > 80 ? '...' : ''}"`);
|
|
567
|
+
|
|
568
|
+
// Reset the unified layout flag since we're about to overwrite the layout
|
|
569
|
+
window.__bookshop_unified_layout_installed = false;
|
|
570
|
+
|
|
436
571
|
window.writeHugoFiles(JSON.stringify({
|
|
437
572
|
"layouts/index.html": eval_str,
|
|
438
573
|
"content/_index.md": JSON.stringify({ props: props_obj, full_props: full_props }, null, 2)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bookshop/hugo-engine",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.18.0-alpha.1",
|
|
4
4
|
"description": "Bookshop frontend Hugo renderer",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"esbuild": "^0.19.3",
|
|
32
32
|
"fflate": "^0.7.3",
|
|
33
33
|
"liquidjs": "10.17.0",
|
|
34
|
-
"@bookshop/helpers": "3.
|
|
34
|
+
"@bookshop/helpers": "3.18.0-alpha.1"
|
|
35
35
|
},
|
|
36
36
|
"engines": {
|
|
37
37
|
"node": ">=14.16"
|