@bookshop/hugo-engine 3.5.0 → 3.5.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/.nyc_output/processinfo/{0abf820b-2028-4355-a51f-2ae7ceb14fc6.json → 0576b405-4d6f-416a-a012-1cd6714d2de3.json} +1 -1
- package/.nyc_output/processinfo/{0c4785c3-d657-4de0-97e2-a5c8bed13440.json → 1186c40f-ab1b-4fc3-8c8f-905478755bdd.json} +1 -1
- package/.nyc_output/processinfo/2fdbc94b-8ef1-4abe-8419-3b1f5ae13538.json +1 -0
- package/.nyc_output/processinfo/{5ab837f9-47fd-4613-be9e-8cad7f893cb6.json → 9afa86b4-7892-4e9a-b022-45b2ba35af2a.json} +1 -1
- package/.nyc_output/processinfo/{6671a456-fd5a-43f9-8289-01bfd323ba6a.json → e261f702-c88f-4b27-aebb-f0ddcd3d512e.json} +1 -1
- package/.nyc_output/processinfo/index.json +1 -1
- package/full-hugo-renderer/hugo_renderer.wasm +0 -0
- package/full-hugo-renderer/hugo_renderer.wasm.gz +0 -0
- package/lib/engine.js +47 -16
- package/lib/engine.test.js +79 -2
- package/lib/engine__test__.js +445 -0
- package/package.json +4 -3
- package/prep__test__.js +5 -0
- package/.nyc_output/processinfo/5217b0dd-26fb-4b8d-af21-def9fd98ee74.json +0 -1
- /package/.nyc_output/{0abf820b-2028-4355-a51f-2ae7ceb14fc6.json → 0576b405-4d6f-416a-a012-1cd6714d2de3.json} +0 -0
- /package/.nyc_output/{0c4785c3-d657-4de0-97e2-a5c8bed13440.json → 1186c40f-ab1b-4fc3-8c8f-905478755bdd.json} +0 -0
- /package/.nyc_output/{5217b0dd-26fb-4b8d-af21-def9fd98ee74.json → 2fdbc94b-8ef1-4abe-8419-3b1f5ae13538.json} +0 -0
- /package/.nyc_output/{5ab837f9-47fd-4613-be9e-8cad7f893cb6.json → 9afa86b4-7892-4e9a-b022-45b2ba35af2a.json} +0 -0
- /package/.nyc_output/{6671a456-fd5a-43f9-8289-01bfd323ba6a.json → e261f702-c88f-4b27-aebb-f0ddcd3d512e.json} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"parent":"
|
|
1
|
+
{"parent":"2fdbc94b-8ef1-4abe-8419-3b1f5ae13538","pid":5527,"argv":["/opt/hostedtoolcache/node/16.20.0/x64/bin/node","/home/runner/work/bookshop/bookshop/javascript-modules/node_modules/ava/lib/worker/subprocess.js"],"execArgv":[],"cwd":"/home/runner/work/bookshop/bookshop/javascript-modules/engines/hugo-engine","time":1683882586943,"ppid":5494,"coverageFilename":"/home/runner/work/bookshop/bookshop/javascript-modules/engines/hugo-engine/.nyc_output/0576b405-4d6f-416a-a012-1cd6714d2de3.json","externalId":"","uuid":"0576b405-4d6f-416a-a012-1cd6714d2de3","files":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"parent":"
|
|
1
|
+
{"parent":"2fdbc94b-8ef1-4abe-8419-3b1f5ae13538","pid":5511,"argv":["/opt/hostedtoolcache/node/16.20.0/x64/bin/node","/home/runner/work/bookshop/bookshop/javascript-modules/node_modules/ava/lib/worker/subprocess.js"],"execArgv":[],"cwd":"/home/runner/work/bookshop/bookshop/javascript-modules/engines/hugo-engine","time":1683882586504,"ppid":5494,"coverageFilename":"/home/runner/work/bookshop/bookshop/javascript-modules/engines/hugo-engine/.nyc_output/1186c40f-ab1b-4fc3-8c8f-905478755bdd.json","externalId":"","uuid":"1186c40f-ab1b-4fc3-8c8f-905478755bdd","files":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"parent":null,"pid":5494,"argv":["/opt/hostedtoolcache/node/16.20.0/x64/bin/node","/home/runner/work/bookshop/bookshop/javascript-modules/node_modules/ava/cli.js","-v"],"execArgv":[],"cwd":"/home/runner/work/bookshop/bookshop/javascript-modules/engines/hugo-engine","time":1683882585987,"ppid":5483,"coverageFilename":"/home/runner/work/bookshop/bookshop/javascript-modules/engines/hugo-engine/.nyc_output/2fdbc94b-8ef1-4abe-8419-3b1f5ae13538.json","externalId":"","uuid":"2fdbc94b-8ef1-4abe-8419-3b1f5ae13538","files":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"parent":"
|
|
1
|
+
{"parent":"2fdbc94b-8ef1-4abe-8419-3b1f5ae13538","pid":5505,"argv":["/opt/hostedtoolcache/node/16.20.0/x64/bin/node","/home/runner/work/bookshop/bookshop/javascript-modules/node_modules/ava/lib/worker/subprocess.js"],"execArgv":[],"cwd":"/home/runner/work/bookshop/bookshop/javascript-modules/engines/hugo-engine","time":1683882586497,"ppid":5494,"coverageFilename":"/home/runner/work/bookshop/bookshop/javascript-modules/engines/hugo-engine/.nyc_output/9afa86b4-7892-4e9a-b022-45b2ba35af2a.json","externalId":"","uuid":"9afa86b4-7892-4e9a-b022-45b2ba35af2a","files":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"parent":"
|
|
1
|
+
{"parent":"2fdbc94b-8ef1-4abe-8419-3b1f5ae13538","pid":5534,"argv":["/opt/hostedtoolcache/node/16.20.0/x64/bin/node","/home/runner/work/bookshop/bookshop/javascript-modules/node_modules/ava/lib/worker/subprocess.js"],"execArgv":[],"cwd":"/home/runner/work/bookshop/bookshop/javascript-modules/engines/hugo-engine","time":1683882586987,"ppid":5494,"coverageFilename":"/home/runner/work/bookshop/bookshop/javascript-modules/engines/hugo-engine/.nyc_output/e261f702-c88f-4b27-aebb-f0ddcd3d512e.json","externalId":"","uuid":"e261f702-c88f-4b27-aebb-f0ddcd3d512e","files":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"processes":{"
|
|
1
|
+
{"processes":{"0576b405-4d6f-416a-a012-1cd6714d2de3":{"parent":"2fdbc94b-8ef1-4abe-8419-3b1f5ae13538","children":[]},"1186c40f-ab1b-4fc3-8c8f-905478755bdd":{"parent":"2fdbc94b-8ef1-4abe-8419-3b1f5ae13538","children":[]},"2fdbc94b-8ef1-4abe-8419-3b1f5ae13538":{"parent":null,"children":["0576b405-4d6f-416a-a012-1cd6714d2de3","1186c40f-ab1b-4fc3-8c8f-905478755bdd","9afa86b4-7892-4e9a-b022-45b2ba35af2a","e261f702-c88f-4b27-aebb-f0ddcd3d512e"]},"9afa86b4-7892-4e9a-b022-45b2ba35af2a":{"parent":"2fdbc94b-8ef1-4abe-8419-3b1f5ae13538","children":[]},"e261f702-c88f-4b27-aebb-f0ddcd3d512e":{"parent":"2fdbc94b-8ef1-4abe-8419-3b1f5ae13538","children":[]}},"files":{},"externalIds":{}}
|
|
Binary file
|
|
Binary file
|
package/lib/engine.js
CHANGED
|
@@ -2,7 +2,6 @@ import compressedHugoWasm from "../full-hugo-renderer/hugo_renderer.wasm.gz";
|
|
|
2
2
|
import { gunzipSync } from 'fflate';
|
|
3
3
|
import translateTextTemplate from './translateTextTemplate.js';
|
|
4
4
|
import { IdentifierParser } from './hugoIdentifierParser.js';
|
|
5
|
-
import { version } from '../package.json';
|
|
6
5
|
|
|
7
6
|
const sleep = (ms = 0) => {
|
|
8
7
|
return new Promise(r => setTimeout(r, ms));
|
|
@@ -42,9 +41,12 @@ export class Engine {
|
|
|
42
41
|
this.key = 'hugo';
|
|
43
42
|
this.name = options.name;
|
|
44
43
|
this.files = options.files;
|
|
45
|
-
this.origin = document.currentScript?.src || `/bookshop.js`;
|
|
44
|
+
this.origin = (typeof document === 'undefined' ? '' : document.currentScript?.src) || `/bookshop.js`;
|
|
45
|
+
this.synthetic = options.synthetic ?? false;
|
|
46
46
|
|
|
47
|
-
this.
|
|
47
|
+
if (!this.synthetic) {
|
|
48
|
+
this.initializeHugo();
|
|
49
|
+
}
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
async initializeHugo() {
|
|
@@ -79,6 +81,20 @@ export class Engine {
|
|
|
79
81
|
|
|
80
82
|
const componentSuccess = window["writeHugoFiles"](JSON.stringify(mappedFiles));
|
|
81
83
|
const templateSuccess = window["writeHugoFiles"](JSON.stringify(templates));
|
|
84
|
+
|
|
85
|
+
// BIG OL' TODO: Writing these files ahead of render() seems to be load-bearing,
|
|
86
|
+
// which doesn't yet make sense to me.
|
|
87
|
+
// If these files are created for the first time in render(), the site doesn't
|
|
88
|
+
// appear to build (need to extract some logs from hugo to determine why).
|
|
89
|
+
// Perplexingly, writing then building in eval() appears to have been working
|
|
90
|
+
// fine in the same case.
|
|
91
|
+
// My suspicion would be something around our changeEvents() handling
|
|
92
|
+
// in the Hugo builder, but I don't know why that wouldn't affect eval()... 🤷♂️
|
|
93
|
+
// For now, the load-bearing stubs will live in this Hugo initialization.
|
|
94
|
+
window.writeHugoFiles(JSON.stringify({
|
|
95
|
+
"layouts/index.html": "Uninitialized Layout",
|
|
96
|
+
"content/_index.md": "{ \”initialized\": false }\n"
|
|
97
|
+
}));
|
|
82
98
|
}
|
|
83
99
|
|
|
84
100
|
async initializeLocalCompressedHugo() {
|
|
@@ -210,9 +226,9 @@ export class Engine {
|
|
|
210
226
|
const component_regex = /execute of template failed: template: ([^:]+):\d/ig;
|
|
211
227
|
let file_stack = [...error_string.matchAll(component_regex)].map(([, file]) => `layouts/${file}`);
|
|
212
228
|
if (file_stack.length) {
|
|
213
|
-
const deepest_errored_component = file_stack[file_stack.length-1];
|
|
229
|
+
const deepest_errored_component = file_stack[file_stack.length - 1];
|
|
214
230
|
const error_chunks = error_string.split("execute of template failed:");
|
|
215
|
-
const error_msg = error_chunks[error_chunks.length-1] ?? "template error";
|
|
231
|
+
const error_msg = error_chunks[error_chunks.length - 1] ?? "template error";
|
|
216
232
|
window.writeHugoFiles(JSON.stringify({
|
|
217
233
|
[deepest_errored_component]: [
|
|
218
234
|
`<div style="padding: 10px; background-color: lightcoral; color: black; font-weight: bold;">`,
|
|
@@ -232,7 +248,7 @@ export class Engine {
|
|
|
232
248
|
return `layouts/partials/bookshop/components/${file}/${filename}.hugo.html`;
|
|
233
249
|
});
|
|
234
250
|
if (file_stack.length) {
|
|
235
|
-
const deepest_errored_component = file_stack[file_stack.length-1];
|
|
251
|
+
const deepest_errored_component = file_stack[file_stack.length - 1];
|
|
236
252
|
window.writeHugoFiles(JSON.stringify({
|
|
237
253
|
[deepest_errored_component]: [
|
|
238
254
|
`<div class="bookshop_error" style="padding: 10px; background-color: lightcoral; color: black; font-weight: bold;">`,
|
|
@@ -308,9 +324,25 @@ export class Engine {
|
|
|
308
324
|
}
|
|
309
325
|
|
|
310
326
|
async eval(str, props = [{}], logger) {
|
|
311
|
-
|
|
327
|
+
if (!this.synthetic) {
|
|
328
|
+
while (!window.buildHugo) await sleep(10);
|
|
329
|
+
}
|
|
312
330
|
let props_obj = props.reduce((a, b) => { return { ...a, ...b } });
|
|
313
331
|
let full_props = props_obj;
|
|
332
|
+
str = str.trim();
|
|
333
|
+
|
|
334
|
+
// We can return early if we're evaluating a literal string or digit
|
|
335
|
+
if (/^".*"$|^`.*`$|^\d+(\.\d+)?$|^true$|^false$/.test(str)) {
|
|
336
|
+
logger?.log?.(`Unwrapping the string ${str}`);
|
|
337
|
+
try {
|
|
338
|
+
if (/^`.*`$/.test(str)) {
|
|
339
|
+
return JSON.parse(str.replace(/^`|`$/g, '"'));
|
|
340
|
+
}
|
|
341
|
+
return JSON.parse(str);
|
|
342
|
+
} catch (e) {
|
|
343
|
+
logger?.log?.(`Was not valid JSON for some reason. Moving on...`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
314
346
|
|
|
315
347
|
// We're capable of looking up a simple variable
|
|
316
348
|
// (and it's hard to pass to wasm since we store variables on the context)
|
|
@@ -336,17 +368,11 @@ export class Engine {
|
|
|
336
368
|
const process = async (obj) => {
|
|
337
369
|
for (const [k, v] of Object.entries(obj)) {
|
|
338
370
|
if (typeof v === "string") {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
logger?.log?.(`Unwrapping the string ${k}: ${v}`);
|
|
342
|
-
obj[k] = JSON.parse(v);
|
|
343
|
-
} else {
|
|
344
|
-
// No inner quotes means it needs to be evaled again
|
|
345
|
-
logger?.log?.(`Evaluating the inner ${k}: ${v}`);
|
|
346
|
-
obj[k] = await this.eval(v, [props_obj], logger.nested());
|
|
347
|
-
}
|
|
371
|
+
logger?.log?.(`Evaluating the inner ${k}: ${v}`);
|
|
372
|
+
obj[k] = await this.eval(v, [props_obj], logger?.nested?.());
|
|
348
373
|
} else if (typeof v === "object") {
|
|
349
374
|
logger?.log?.(`Processing the inner object ${k}`);
|
|
375
|
+
process(v);
|
|
350
376
|
}
|
|
351
377
|
}
|
|
352
378
|
}
|
|
@@ -380,6 +406,11 @@ export class Engine {
|
|
|
380
406
|
}
|
|
381
407
|
}).join('');
|
|
382
408
|
const eval_str = `${variable_decl}{{ with .Params.props }}${assignments}{{ jsonify (${str}) }}{{ end }}`;
|
|
409
|
+
|
|
410
|
+
if (this.synthetic) {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
|
|
383
414
|
window.writeHugoFiles(JSON.stringify({
|
|
384
415
|
"layouts/index.html": eval_str,
|
|
385
416
|
"content/_index.md": JSON.stringify({ props: props_obj, full_props: full_props }, null, 2)
|
package/lib/engine.test.js
CHANGED
|
@@ -1,5 +1,82 @@
|
|
|
1
1
|
import test from 'ava';
|
|
2
|
+
import { Engine } from './engine__test__.js';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
let engine = null, logger = null;
|
|
5
|
+
test.before(async t => {
|
|
6
|
+
engine = new Engine({ synthetic: true });
|
|
7
|
+
logger = {
|
|
8
|
+
log: (str) => console.log(str),
|
|
9
|
+
nested: () => logger,
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("eval value", async t => {
|
|
14
|
+
const val = await engine.eval(`.test`, [{ test: 'bookshop' }]);
|
|
15
|
+
t.is(val, 'bookshop');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("eval variable", async t => {
|
|
19
|
+
const val = await engine.eval(`$var`, [{ "$var": 'string' }, { test: 'bookshop' }]);
|
|
20
|
+
t.is(val, 'string');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("eval literals", async t => {
|
|
24
|
+
let val = await engine.eval(`"test"`, [{}]);
|
|
25
|
+
t.is(val, 'test');
|
|
26
|
+
|
|
27
|
+
val = await engine.eval("`test`", [{}]);
|
|
28
|
+
t.is(val, 'test');
|
|
29
|
+
|
|
30
|
+
val = await engine.eval("4", [{}]);
|
|
31
|
+
t.is(val, 4);
|
|
32
|
+
|
|
33
|
+
val = await engine.eval("99.9", [{}]);
|
|
34
|
+
t.is(val, 99.9);
|
|
35
|
+
|
|
36
|
+
val = await engine.eval("true", [{}]);
|
|
37
|
+
t.is(val, true);
|
|
38
|
+
|
|
39
|
+
val = await engine.eval("false", [{}]);
|
|
40
|
+
t.is(val, false);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("eval through dot scope", async t => {
|
|
44
|
+
const val_later = await engine.eval(`.test`, [{ test: 'bookshop' }, { ".": { test: 'inner_bookshop' } }]);
|
|
45
|
+
t.is(val_later, 'inner_bookshop');
|
|
46
|
+
|
|
47
|
+
const val_earlier = await engine.eval(`.test`, [{ ".": { test: 'inner_bookshop' } }, { test: 'bookshop' }]);
|
|
48
|
+
t.is(val_earlier, 'inner_bookshop');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("access dot scope", async t => {
|
|
52
|
+
const dot = await engine.eval(`.`, [{ test: 'bookshop' }]);
|
|
53
|
+
t.deepEqual(dot, { test: 'bookshop' });
|
|
54
|
+
|
|
55
|
+
const dot_dot = await engine.eval(`.`, [{ test: 'bookshop' }, { ".": { test: 'inner_bookshop' } }]);
|
|
56
|
+
t.deepEqual(dot_dot, { test: 'inner_bookshop' });
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("eval literal dict", async t => {
|
|
60
|
+
const dot = await engine.eval(`(dict "zanzibar" "gin")`, [{}]);
|
|
61
|
+
t.deepEqual(dot, { zanzibar: 'gin' });
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("eval dict with scope access", async t => {
|
|
65
|
+
const dot = await engine.eval(`(dict "zanzibar" .live)`, [{ live: "editing" }]);
|
|
66
|
+
t.deepEqual(dot, { zanzibar: 'editing' });
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("eval dict with variable access", async t => {
|
|
70
|
+
const dot = await engine.eval(`(dict "zanzibar" $live)`, [{ "$live": "editing" }]);
|
|
71
|
+
t.deepEqual(dot, { zanzibar: 'editing' });
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("eval complex dict", async t => {
|
|
75
|
+
const dot = await engine.eval(`(dict "zanzibar" (slice .edit .live) "and" "a_literal")`, [{ live: "editing" }, { edit: "live" }]);
|
|
76
|
+
t.deepEqual(dot, { zanzibar: ['live', 'editing'], and: 'a_literal' });
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("eval array index", async t => {
|
|
80
|
+
const dot = await engine.eval(`(index .live 1)`, [{ live: ["editing", "lively"] }]);
|
|
81
|
+
t.deepEqual(dot, "lively");
|
|
5
82
|
});
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
// import compressedHugoWasm from "../full-hugo-renderer/hugo_renderer.wasm.gz";
|
|
2
|
+
import { gunzipSync } from 'fflate';
|
|
3
|
+
import translateTextTemplate from './translateTextTemplate.js';
|
|
4
|
+
import { IdentifierParser } from './hugoIdentifierParser.js';
|
|
5
|
+
|
|
6
|
+
const sleep = (ms = 0) => {
|
|
7
|
+
return new Promise(r => setTimeout(r, ms));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const dig = (obj, path) => {
|
|
11
|
+
if (typeof path === 'string') path = path.replace(/\[(\d+)]/g, '.$1').split('.');
|
|
12
|
+
obj = obj[path.shift()];
|
|
13
|
+
if (obj && path.length) return dig(obj, path);
|
|
14
|
+
return obj;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let mapHugoVariablesToParams = (obj, path) => {
|
|
18
|
+
let resolved = {};
|
|
19
|
+
for (let [k, v] of Object.entries(obj)) {
|
|
20
|
+
if (k.startsWith('$')) {
|
|
21
|
+
const remapped = `__bksh_${k.substring(1)}`;
|
|
22
|
+
obj[remapped] = v;
|
|
23
|
+
resolved[k] = `${path}.${remapped}`;
|
|
24
|
+
} else {
|
|
25
|
+
if (v && typeof v === 'object' && !Array.isArray(v)) {
|
|
26
|
+
resolved = { ...resolved, ...mapHugoVariablesToParams(v, `${path}.${k}`) }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return resolved
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class Engine {
|
|
34
|
+
constructor(options) {
|
|
35
|
+
options = {
|
|
36
|
+
name: "Hugo",
|
|
37
|
+
files: {},
|
|
38
|
+
...options,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
this.key = 'hugo';
|
|
42
|
+
this.name = options.name;
|
|
43
|
+
this.files = options.files;
|
|
44
|
+
this.origin = (typeof document === 'undefined' ? '' : document.currentScript?.src) || `/bookshop.js`;
|
|
45
|
+
this.synthetic = options.synthetic ?? false;
|
|
46
|
+
|
|
47
|
+
if (!this.synthetic) {
|
|
48
|
+
this.initializeHugo();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async initializeHugo() {
|
|
53
|
+
const templates = {
|
|
54
|
+
"layouts/partials/bookshop.html": (await import("../bookshop-hugo-templates/bookshop.html")).default,
|
|
55
|
+
"layouts/partials/bookshop_scss.html": (await import("../bookshop-hugo-templates/bookshop_scss.html")).default,
|
|
56
|
+
"layouts/partials/bookshop_partial.html": (await import("../bookshop-hugo-templates/bookshop_partial.html")).default,
|
|
57
|
+
"layouts/partials/bookshop_component_browser.html": (await import("../bookshop-hugo-templates/bookshop_component_browser.html")).default,
|
|
58
|
+
"layouts/partials/bookshop_bindings.html": (await import("../bookshop-hugo-templates/bookshop_bindings.html")).default,
|
|
59
|
+
"layouts/partials/_bookshop/helpers/component.html": (await import("../bookshop-hugo-templates/helpers/component.html")).default,
|
|
60
|
+
"layouts/partials/_bookshop/helpers/component_key.html": (await import("../bookshop-hugo-templates/helpers/component_key.html")).default,
|
|
61
|
+
"layouts/partials/_bookshop/helpers/partial.html": (await import("../bookshop-hugo-templates/helpers/partial.html")).default,
|
|
62
|
+
"layouts/partials/_bookshop/helpers/partial_key.html": (await import("../bookshop-hugo-templates/helpers/partial_key.html")).default,
|
|
63
|
+
"layouts/partials/_bookshop/errors/bad_bookshop_tag.html": (await import("../bookshop-hugo-templates/errors/bad_bookshop_tag.html")).default,
|
|
64
|
+
"layouts/partials/_bookshop/errors/err.html": (await import("../bookshop-hugo-templates/errors/err.html")).default,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
templates["config.toml"] = "params.env_bookshop_live = true";
|
|
68
|
+
|
|
69
|
+
// When this script is run locally, the hugo wasm is loaded as binary rather than output as a file.
|
|
70
|
+
if (compressedHugoWasm?.constructor === Uint8Array) {
|
|
71
|
+
await this.initializeInlineHugo();
|
|
72
|
+
} else {
|
|
73
|
+
await this.initializeLocalCompressedHugo();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// TODO: Tidy
|
|
77
|
+
const mappedFiles = {};
|
|
78
|
+
for (const file of Object.entries(this.files)) {
|
|
79
|
+
mappedFiles[`layouts/partials/bookshop/${file[0]}`] = translateTextTemplate(file[1], {});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const componentSuccess = window["writeHugoFiles"](JSON.stringify(mappedFiles));
|
|
83
|
+
const templateSuccess = window["writeHugoFiles"](JSON.stringify(templates));
|
|
84
|
+
|
|
85
|
+
// BIG OL' TODO: Writing these files ahead of render() seems to be load-bearing,
|
|
86
|
+
// which doesn't yet make sense to me.
|
|
87
|
+
// If these files are created for the first time in render(), the site doesn't
|
|
88
|
+
// appear to build (need to extract some logs from hugo to determine why).
|
|
89
|
+
// Perplexingly, writing then building in eval() appears to have been working
|
|
90
|
+
// fine in the same case.
|
|
91
|
+
// My suspicion would be something around our changeEvents() handling
|
|
92
|
+
// in the Hugo builder, but I don't know why that wouldn't affect eval()... 🤷♂️
|
|
93
|
+
// For now, the load-bearing stubs will live in this Hugo initialization.
|
|
94
|
+
window.writeHugoFiles(JSON.stringify({
|
|
95
|
+
"layouts/index.html": "Uninitialized Layout",
|
|
96
|
+
"content/_index.md": "{ \”initialized\": false }\n"
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async initializeLocalCompressedHugo() {
|
|
101
|
+
try {
|
|
102
|
+
|
|
103
|
+
let prefetched = {};
|
|
104
|
+
if (typeof window !== "undefined" && window?.CloudCannon && window?.CloudCannon?.prefetchedFiles) {
|
|
105
|
+
prefetched = await window.CloudCannon.prefetchedFiles?.();
|
|
106
|
+
this.availablePrefetchKeys = Object.keys(prefetched);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const go = new Go();
|
|
110
|
+
const compressedWasmOrigin = this.origin.replace(/\/[^\.\/]+\.(min\.)?js/, compressedHugoWasm.replace(/^\./, ''));
|
|
111
|
+
const compressedWasmPath = (new URL(compressedWasmOrigin)).pathname;
|
|
112
|
+
|
|
113
|
+
let compressedBuffer;
|
|
114
|
+
if (prefetched[compressedWasmPath]) {
|
|
115
|
+
compressedBuffer = await prefetched[compressedWasmPath]?.arrayBuffer();
|
|
116
|
+
this.loadedFrom = "prefetch";
|
|
117
|
+
} else {
|
|
118
|
+
const compressedResponse = await fetch(compressedWasmOrigin);
|
|
119
|
+
compressedBuffer = await compressedResponse.arrayBuffer();
|
|
120
|
+
this.loadedFrom = "network";
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const renderer = gunzipSync(new Uint8Array(compressedBuffer));
|
|
124
|
+
|
|
125
|
+
// Check the magic word at the start of the buffer as a way to verify the CDN download worked.
|
|
126
|
+
const isWasm = ([...renderer.slice(0, 4)]).map(g => g.toString(16).padStart(2, '0')).join('') === "0061736d";
|
|
127
|
+
if (!isWasm) throw "Not WASM";
|
|
128
|
+
|
|
129
|
+
const compressedResult = await WebAssembly.instantiate(renderer, go.importObject);
|
|
130
|
+
go.run(compressedResult.instance);
|
|
131
|
+
} catch (e) {
|
|
132
|
+
console.error("Couldn't load the local compressed Hugo WASM");
|
|
133
|
+
console.error(e);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async initializeInlineHugo() {
|
|
138
|
+
const go = new Go();
|
|
139
|
+
const buffer = compressedHugoWasm.buffer;
|
|
140
|
+
const renderer = gunzipSync(new Uint8Array(buffer));
|
|
141
|
+
const result = await WebAssembly.instantiate(renderer, go.importObject);
|
|
142
|
+
go.run(result.instance);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
getSharedKey(name) {
|
|
146
|
+
return `shared/hugo/${name}.hugo.html`;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
getShared(name) {
|
|
150
|
+
const key = this.getSharedKey(name);
|
|
151
|
+
return this.files?.[key];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
getComponentKey(name) {
|
|
155
|
+
const base = name.split("/").reverse()[0];
|
|
156
|
+
return `components/${name}/${base}.hugo.html`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
getComponent(name) {
|
|
160
|
+
const key = this.getComponentKey(name);
|
|
161
|
+
return this.files?.[key];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
hasComponent(name) {
|
|
165
|
+
const key = this.getComponentKey(name);
|
|
166
|
+
return !!this.files?.[key];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
hasShared(name) {
|
|
170
|
+
const key = this.getSharedKey(name);
|
|
171
|
+
return !!this.files?.[key];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
resolveComponentType(name) {
|
|
175
|
+
if (this.hasComponent(name)) return 'component';
|
|
176
|
+
if (this.hasShared(name)) return 'shared';
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
transformData(data) {
|
|
181
|
+
return {
|
|
182
|
+
Params: data
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async storeMeta(meta = {}) {
|
|
187
|
+
while (!window.writeHugoFiles) {
|
|
188
|
+
await sleep(100);
|
|
189
|
+
};
|
|
190
|
+
window.writeHugoFiles(JSON.stringify({
|
|
191
|
+
"config.toml": [
|
|
192
|
+
meta.baseurl ? `baseURL = ${meta.baseurl}` : "",
|
|
193
|
+
meta.copyright ? `copyright = ${meta.copyright}` : "",
|
|
194
|
+
meta.title ? `title = ${meta.title}` : "",
|
|
195
|
+
"params.env_bookshop_live = true"
|
|
196
|
+
].join('\n')
|
|
197
|
+
}));
|
|
198
|
+
const err = window.initHugoConfig();
|
|
199
|
+
if (err) {
|
|
200
|
+
console.error(err);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// window.loadHugoBookshopMeta(JSON.stringify(meta));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async storeInfo(info = {}) {
|
|
207
|
+
while (!window.writeHugoFiles) {
|
|
208
|
+
await sleep(100);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const data = info?.data;
|
|
212
|
+
if (!data || typeof data !== "object") return;
|
|
213
|
+
const files = {};
|
|
214
|
+
for (const [file, contents] of Object.entries(data)) {
|
|
215
|
+
files[`data/${file}.json`] = JSON.stringify(contents);
|
|
216
|
+
}
|
|
217
|
+
window.writeHugoFiles(JSON.stringify(files));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Tries to parse an error message and patch up the components
|
|
222
|
+
* in a rudimentary way before a rebuild.
|
|
223
|
+
*/
|
|
224
|
+
async componentQuack(error_string = "", log_messages = []) {
|
|
225
|
+
try {
|
|
226
|
+
const component_regex = /execute of template failed: template: ([^:]+):\d/ig;
|
|
227
|
+
let file_stack = [...error_string.matchAll(component_regex)].map(([, file]) => `layouts/${file}`);
|
|
228
|
+
if (file_stack.length) {
|
|
229
|
+
const deepest_errored_component = file_stack[file_stack.length - 1];
|
|
230
|
+
const error_chunks = error_string.split("execute of template failed:");
|
|
231
|
+
const error_msg = error_chunks[error_chunks.length - 1] ?? "template error";
|
|
232
|
+
window.writeHugoFiles(JSON.stringify({
|
|
233
|
+
[deepest_errored_component]: [
|
|
234
|
+
`<div style="padding: 10px; background-color: lightcoral; color: black; font-weight: bold;">`,
|
|
235
|
+
`Failed to render ${deepest_errored_component}. <br/>`,
|
|
236
|
+
`<pre style="margin-top: 10px; background-color: lightcoral; border: solid 1px black; white-space: pre-line;">`,
|
|
237
|
+
`<code style="font-family: monospace; color: black;">${error_msg.replace(/</, '<')}</code></pre>`,
|
|
238
|
+
`</div>`
|
|
239
|
+
].join('\n')
|
|
240
|
+
}));
|
|
241
|
+
return deepest_errored_component;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const error_logs = log_messages.filter(log => log.startsWith("ERROR")).join("\n");
|
|
245
|
+
const missing_regex = /Component "([^"]+)" does not exist/ig;
|
|
246
|
+
file_stack = [...error_logs.matchAll(missing_regex)].map(([, file]) => {
|
|
247
|
+
let filename = file.split('/').pop();
|
|
248
|
+
return `layouts/partials/bookshop/components/${file}/${filename}.hugo.html`;
|
|
249
|
+
});
|
|
250
|
+
if (file_stack.length) {
|
|
251
|
+
const deepest_errored_component = file_stack[file_stack.length - 1];
|
|
252
|
+
window.writeHugoFiles(JSON.stringify({
|
|
253
|
+
[deepest_errored_component]: [
|
|
254
|
+
`<div class="bookshop_error" style="padding: 10px; background-color: lightcoral; color: black; font-weight: bold;">`,
|
|
255
|
+
`Failed to find component ${deepest_errored_component}`,
|
|
256
|
+
`</div>`
|
|
257
|
+
].join('\n')
|
|
258
|
+
}));
|
|
259
|
+
return deepest_errored_component;
|
|
260
|
+
}
|
|
261
|
+
} catch (e) {
|
|
262
|
+
console.error(`ComponentQuack failed to patch things up: ${e}`);
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async render(target, name, props, globals, logger) {
|
|
268
|
+
while (!window.buildHugo) {
|
|
269
|
+
logger?.log?.(`Waiting for the Hugo WASM to be available...`);
|
|
270
|
+
await sleep(100);
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
let writeFiles = {};
|
|
274
|
+
|
|
275
|
+
if (this.hasComponent(name)) {
|
|
276
|
+
writeFiles["layouts/index.html"] = `{{ partial "bookshop" (slice "${name}" .Params.component) }}`
|
|
277
|
+
} else if (this.hasShared(name)) {
|
|
278
|
+
writeFiles["layouts/index.html"] = `{{ partial "bookshop_partial" (slice "${name}" .Params.component) }}`
|
|
279
|
+
} else {
|
|
280
|
+
console.warn(`[hugo-engine] No component found for ${name}`);
|
|
281
|
+
return "";
|
|
282
|
+
}
|
|
283
|
+
logger?.log?.(`Going to render ${name}, with layout:`);
|
|
284
|
+
logger?.log?.(writeFiles["layouts/index.html"]);
|
|
285
|
+
|
|
286
|
+
if (!globals || typeof globals !== "object") globals = {};
|
|
287
|
+
props = {
|
|
288
|
+
...globals, ...props,
|
|
289
|
+
env_bookshop_live: true
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// If we have assigned a root scope we need to pass that in as the context
|
|
293
|
+
if (props["."]) props = props["."];
|
|
294
|
+
writeFiles["content/_index.md"] = JSON.stringify({
|
|
295
|
+
component: props
|
|
296
|
+
}, null, 2) + "\n";
|
|
297
|
+
window.writeHugoFiles(JSON.stringify(writeFiles));
|
|
298
|
+
|
|
299
|
+
window.hugo_wasm_logging = [];
|
|
300
|
+
let render_attempts = 1;
|
|
301
|
+
let buildError = window.buildHugo();
|
|
302
|
+
while (buildError && render_attempts < 5) {
|
|
303
|
+
if (this.componentQuack(buildError, window.hugo_wasm_logging) === null) {
|
|
304
|
+
// Can't find a template to overwrite and re-render
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
// Try render again with the problem template stubbed out
|
|
308
|
+
window.hugo_wasm_logging = [];
|
|
309
|
+
buildError = window.buildHugo();
|
|
310
|
+
render_attempts += 1;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (buildError) {
|
|
314
|
+
console.error(buildError);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const output = window.readHugoFiles(JSON.stringify([
|
|
319
|
+
"public/index.html"
|
|
320
|
+
]));
|
|
321
|
+
|
|
322
|
+
target.innerHTML = output["public/index.html"];
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async eval(str, props = [{}], logger) {
|
|
327
|
+
if (!this.synthetic) {
|
|
328
|
+
while (!window.buildHugo) await sleep(10);
|
|
329
|
+
}
|
|
330
|
+
let props_obj = props.reduce((a, b) => { return { ...a, ...b } });
|
|
331
|
+
let full_props = props_obj;
|
|
332
|
+
str = str.trim();
|
|
333
|
+
|
|
334
|
+
// We can return early if we're evaluating a literal string or digit
|
|
335
|
+
if (/^".*"$|^`.*`$|^\d+(\.\d+)?$|^true$|^false$/.test(str)) {
|
|
336
|
+
logger?.log?.(`Unwrapping the string ${str}`);
|
|
337
|
+
try {
|
|
338
|
+
if (/^`.*`$/.test(str)) {
|
|
339
|
+
return JSON.parse(str.replace(/^`|`$/g, '"'));
|
|
340
|
+
}
|
|
341
|
+
return JSON.parse(str);
|
|
342
|
+
} catch (e) {
|
|
343
|
+
logger?.log?.(`Was not valid JSON for some reason. Moving on...`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// We're capable of looking up a simple variable
|
|
348
|
+
// (and it's hard to pass to wasm since we store variables on the context)
|
|
349
|
+
if (/^\$/.test(str)) {
|
|
350
|
+
logger?.log?.(`Trying to short circuit to return a variable`);
|
|
351
|
+
if (props_obj[str]) return props_obj[str];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// If we have assigned a root scope we need to pass that in as the context
|
|
355
|
+
if (props_obj["."]) {
|
|
356
|
+
logger?.log?.(`Nesting the props object into its dot scope`);
|
|
357
|
+
props_obj = props_obj["."];
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (str === ".") {
|
|
361
|
+
logger?.log?.(`Short circuiting dot notation to return the scope`);
|
|
362
|
+
return props_obj;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
let normalized = this.normalize(str);
|
|
366
|
+
if (typeof normalized === 'object') {
|
|
367
|
+
logger?.log?.(`Digging into the object ${JSON.stringify(normalized)}`);
|
|
368
|
+
const process = async (obj) => {
|
|
369
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
370
|
+
if (typeof v === "string") {
|
|
371
|
+
logger?.log?.(`Evaluating the inner ${k}: ${v}`);
|
|
372
|
+
obj[k] = await this.eval(v, [props_obj], logger?.nested?.());
|
|
373
|
+
} else if (typeof v === "object") {
|
|
374
|
+
logger?.log?.(`Processing the inner object ${k}`);
|
|
375
|
+
process(v);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
await process(normalized);
|
|
380
|
+
return normalized;
|
|
381
|
+
}
|
|
382
|
+
if (/^\w+(\.\w+)*$/.test(normalized)) {
|
|
383
|
+
logger?.log?.(`Trying to short circuit to return the dot notation ${normalized}`);
|
|
384
|
+
// We're capable of looking up a simple dot notation access
|
|
385
|
+
const result = dig(props_obj, normalized);
|
|
386
|
+
if (result !== undefined) return result
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Rewrite array.0 into index array 0
|
|
390
|
+
str = str.replace(/(.*)\.(\d+)$/, (_, obj, index) => {
|
|
391
|
+
return `index (${obj}) ${index}`;
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
const variable_pairs = mapHugoVariablesToParams(full_props, ".Params.full_props");
|
|
395
|
+
const variable_decl = Object.entries(variable_pairs).map(([k, v]) => {
|
|
396
|
+
return `{{ ${k} := ${v} }}`
|
|
397
|
+
}).join('');
|
|
398
|
+
|
|
399
|
+
const assignments = Object.entries(props_obj).filter(([key]) => key.startsWith('$')).map(([key, value]) => {
|
|
400
|
+
if (Array.isArray(value)) {
|
|
401
|
+
return `{{ ${key} := index ( \`{"a": ${JSON.stringify(value)}}\` | transform.Unmarshal ) "a" }}`
|
|
402
|
+
} else if (typeof value === 'object') {
|
|
403
|
+
return `{{ ${key} := \`${JSON.stringify(value)}\` | transform.Unmarshal }}`
|
|
404
|
+
} else {
|
|
405
|
+
return `{{ ${key} := ${JSON.stringify(value)} }}`
|
|
406
|
+
}
|
|
407
|
+
}).join('');
|
|
408
|
+
const eval_str = `${variable_decl}{{ with .Params.props }}${assignments}{{ jsonify (${str}) }}{{ end }}`;
|
|
409
|
+
|
|
410
|
+
if (this.synthetic) {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
window.writeHugoFiles(JSON.stringify({
|
|
415
|
+
"layouts/index.html": eval_str,
|
|
416
|
+
"content/_index.md": JSON.stringify({ props: props_obj, full_props: full_props }, null, 2)
|
|
417
|
+
}));
|
|
418
|
+
|
|
419
|
+
const buildError = window.buildHugo();
|
|
420
|
+
if (buildError) {
|
|
421
|
+
console.warn(buildError);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const output = window.readHugoFiles(JSON.stringify([
|
|
426
|
+
"public/index.html"
|
|
427
|
+
]))["public/index.html"];
|
|
428
|
+
|
|
429
|
+
try {
|
|
430
|
+
return JSON.parse(output);
|
|
431
|
+
} catch (e) {
|
|
432
|
+
logger?.log?.(`Error evaluating \`${str}\` in the Hugo engine`);
|
|
433
|
+
logger?.log?.(output);
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
normalize(str) {
|
|
439
|
+
return (new IdentifierParser(str)).build();
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
loader() {
|
|
443
|
+
// esbuild loader if required
|
|
444
|
+
}
|
|
445
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bookshop/hugo-engine",
|
|
3
3
|
"packageManager": "yarn@3.0.0",
|
|
4
|
-
"version": "3.5.
|
|
4
|
+
"version": "3.5.1",
|
|
5
5
|
"description": "Bookshop frontend Hugo renderer",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
"*.js",
|
|
15
15
|
"lib/*.js",
|
|
16
16
|
"!**/*.test.js",
|
|
17
|
+
"!**/*__test__.js",
|
|
17
18
|
"full-hugo-renderer/hugo_renderer.wasm.gz",
|
|
18
19
|
"bookshop-hugo-templates/**/*"
|
|
19
20
|
],
|
|
20
21
|
"scripts": {
|
|
21
|
-
"test": "nyc ava -v"
|
|
22
|
+
"test": "node prep__test__.js && nyc ava -v"
|
|
22
23
|
},
|
|
23
24
|
"author": "@bglw",
|
|
24
25
|
"license": "MIT",
|
|
@@ -30,7 +31,7 @@
|
|
|
30
31
|
"nyc": "^15.1.0"
|
|
31
32
|
},
|
|
32
33
|
"dependencies": {
|
|
33
|
-
"@bookshop/helpers": "3.5.
|
|
34
|
+
"@bookshop/helpers": "3.5.1",
|
|
34
35
|
"esbuild": "^0.13.10",
|
|
35
36
|
"fflate": "^0.7.3",
|
|
36
37
|
"liquidjs": "9.28.0"
|
package/prep__test__.js
ADDED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"parent":null,"pid":5417,"argv":["/opt/hostedtoolcache/node/16.20.0/x64/bin/node","/home/runner/work/bookshop/bookshop/javascript-modules/node_modules/ava/cli.js","-v"],"execArgv":[],"cwd":"/home/runner/work/bookshop/bookshop/javascript-modules/engines/hugo-engine","time":1683759481967,"ppid":5406,"coverageFilename":"/home/runner/work/bookshop/bookshop/javascript-modules/engines/hugo-engine/.nyc_output/5217b0dd-26fb-4b8d-af21-def9fd98ee74.json","externalId":"","uuid":"5217b0dd-26fb-4b8d-af21-def9fd98ee74","files":[]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|