@bookshop/hugo-engine 2.4.2 → 2.6.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.
Files changed (25) hide show
  1. package/.nyc_output/{05c20625-d974-4cff-8627-ffd39eb87964.json → 51df4d42-a45e-4cd8-8872-46e9147299bf.json} +0 -0
  2. package/.nyc_output/{1c8dd4f5-9b2f-4218-9c39-615aa7a79f1a.json → 8ff4b2d0-5848-4ba6-9e8b-455ac526027c.json} +0 -0
  3. package/.nyc_output/{55d11bd3-216d-42c2-970e-55360a3aa1f7.json → 970a6ddf-4bc1-4741-8913-f05ce8e287c6.json} +0 -0
  4. package/.nyc_output/{bb3f1e57-7efe-4f35-852c-6971836aa288.json → ac095d2d-5d4a-449e-82ae-0e0f0dd32b01.json} +0 -0
  5. package/.nyc_output/{e6440686-4e4c-487d-9abc-bb15eb8663e1.json → f5e4f4d9-24b3-4cb7-a369-95e27032d6ba.json} +0 -0
  6. package/.nyc_output/processinfo/{1c8dd4f5-9b2f-4218-9c39-615aa7a79f1a.json → 51df4d42-a45e-4cd8-8872-46e9147299bf.json} +1 -1
  7. package/.nyc_output/processinfo/{55d11bd3-216d-42c2-970e-55360a3aa1f7.json → 8ff4b2d0-5848-4ba6-9e8b-455ac526027c.json} +1 -1
  8. package/.nyc_output/processinfo/{bb3f1e57-7efe-4f35-852c-6971836aa288.json → 970a6ddf-4bc1-4741-8913-f05ce8e287c6.json} +1 -1
  9. package/.nyc_output/processinfo/ac095d2d-5d4a-449e-82ae-0e0f0dd32b01.json +1 -0
  10. package/.nyc_output/processinfo/{e6440686-4e4c-487d-9abc-bb15eb8663e1.json → f5e4f4d9-24b3-4cb7-a369-95e27032d6ba.json} +1 -1
  11. package/.nyc_output/processinfo/index.json +1 -1
  12. package/hugo-renderer/helpers/bookshop_modified_content.go +28 -0
  13. package/hugo-renderer/hugo_renderer.wasm +0 -0
  14. package/hugo-renderer/tpl/bookshop_library/bookshop_components.go +17 -2
  15. package/hugo-renderer/tpl/transform/bookshop_unmarshal.go +39 -0
  16. package/hugo-renderer/tpl/transform/init.go +7 -0
  17. package/hugo-renderer/tpl/transform/transform.go +3 -0
  18. package/lib/builder.js +2 -2
  19. package/lib/engine.js +26 -5
  20. package/lib/hugoIdentifierParser.js +1 -1
  21. package/lib/hugoIdentifierParser.test.js +19 -0
  22. package/lib/translateTextTemplate.js +52 -28
  23. package/lib/translateTextTemplate.test.js +57 -1
  24. package/package.json +2 -2
  25. package/.nyc_output/processinfo/05c20625-d974-4cff-8627-ffd39eb87964.json +0 -1
@@ -1 +1 @@
1
- {"parent":"05c20625-d974-4cff-8627-ffd39eb87964","pid":94691,"argv":["/Users/liambigelow/.nvm/versions/node/v14.16.0/bin/node","/Users/liambigelow/Projects/CC/bookshop/javascript-modules/node_modules/ava/lib/worker/subprocess.js"],"execArgv":[],"cwd":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine","time":1643783907793,"ppid":94690,"coverageFilename":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine/.nyc_output/1c8dd4f5-9b2f-4218-9c39-615aa7a79f1a.json","externalId":"","uuid":"1c8dd4f5-9b2f-4218-9c39-615aa7a79f1a","files":[]}
1
+ {"parent":"ac095d2d-5d4a-449e-82ae-0e0f0dd32b01","pid":32141,"argv":["/Users/liambigelow/.nvm/versions/node/v14.16.0/bin/node","/Users/liambigelow/Projects/CC/bookshop/javascript-modules/node_modules/ava/lib/worker/subprocess.js"],"execArgv":[],"cwd":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine","time":1646349109338,"ppid":32139,"coverageFilename":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine/.nyc_output/51df4d42-a45e-4cd8-8872-46e9147299bf.json","externalId":"","uuid":"51df4d42-a45e-4cd8-8872-46e9147299bf","files":[]}
@@ -1 +1 @@
1
- {"parent":"05c20625-d974-4cff-8627-ffd39eb87964","pid":94693,"argv":["/Users/liambigelow/.nvm/versions/node/v14.16.0/bin/node","/Users/liambigelow/Projects/CC/bookshop/javascript-modules/node_modules/ava/lib/worker/subprocess.js"],"execArgv":[],"cwd":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine","time":1643783907804,"ppid":94690,"coverageFilename":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine/.nyc_output/55d11bd3-216d-42c2-970e-55360a3aa1f7.json","externalId":"","uuid":"55d11bd3-216d-42c2-970e-55360a3aa1f7","files":[]}
1
+ {"parent":"ac095d2d-5d4a-449e-82ae-0e0f0dd32b01","pid":32140,"argv":["/Users/liambigelow/.nvm/versions/node/v14.16.0/bin/node","/Users/liambigelow/Projects/CC/bookshop/javascript-modules/node_modules/ava/lib/worker/subprocess.js"],"execArgv":[],"cwd":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine","time":1646349109337,"ppid":32139,"coverageFilename":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine/.nyc_output/8ff4b2d0-5848-4ba6-9e8b-455ac526027c.json","externalId":"","uuid":"8ff4b2d0-5848-4ba6-9e8b-455ac526027c","files":[]}
@@ -1 +1 @@
1
- {"parent":"05c20625-d974-4cff-8627-ffd39eb87964","pid":94692,"argv":["/Users/liambigelow/.nvm/versions/node/v14.16.0/bin/node","/Users/liambigelow/Projects/CC/bookshop/javascript-modules/node_modules/ava/lib/worker/subprocess.js"],"execArgv":[],"cwd":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine","time":1643783907805,"ppid":94690,"coverageFilename":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine/.nyc_output/bb3f1e57-7efe-4f35-852c-6971836aa288.json","externalId":"","uuid":"bb3f1e57-7efe-4f35-852c-6971836aa288","files":[]}
1
+ {"parent":"ac095d2d-5d4a-449e-82ae-0e0f0dd32b01","pid":32143,"argv":["/Users/liambigelow/.nvm/versions/node/v14.16.0/bin/node","/Users/liambigelow/Projects/CC/bookshop/javascript-modules/node_modules/ava/lib/worker/subprocess.js"],"execArgv":[],"cwd":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine","time":1646349109350,"ppid":32139,"coverageFilename":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine/.nyc_output/970a6ddf-4bc1-4741-8913-f05ce8e287c6.json","externalId":"","uuid":"970a6ddf-4bc1-4741-8913-f05ce8e287c6","files":[]}
@@ -0,0 +1 @@
1
+ {"parent":null,"pid":32139,"argv":["/Users/liambigelow/.nvm/versions/node/v14.16.0/bin/node","/Users/liambigelow/Projects/CC/bookshop/javascript-modules/node_modules/ava/cli.js","-v"],"execArgv":[],"cwd":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine","time":1646349108851,"ppid":32138,"coverageFilename":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine/.nyc_output/ac095d2d-5d4a-449e-82ae-0e0f0dd32b01.json","externalId":"","uuid":"ac095d2d-5d4a-449e-82ae-0e0f0dd32b01","files":[]}
@@ -1 +1 @@
1
- {"parent":"05c20625-d974-4cff-8627-ffd39eb87964","pid":94694,"argv":["/Users/liambigelow/.nvm/versions/node/v14.16.0/bin/node","/Users/liambigelow/Projects/CC/bookshop/javascript-modules/node_modules/ava/lib/worker/subprocess.js"],"execArgv":[],"cwd":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine","time":1643783907813,"ppid":94690,"coverageFilename":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine/.nyc_output/e6440686-4e4c-487d-9abc-bb15eb8663e1.json","externalId":"","uuid":"e6440686-4e4c-487d-9abc-bb15eb8663e1","files":[]}
1
+ {"parent":"ac095d2d-5d4a-449e-82ae-0e0f0dd32b01","pid":32142,"argv":["/Users/liambigelow/.nvm/versions/node/v14.16.0/bin/node","/Users/liambigelow/Projects/CC/bookshop/javascript-modules/node_modules/ava/lib/worker/subprocess.js"],"execArgv":[],"cwd":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine","time":1646349109342,"ppid":32139,"coverageFilename":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine/.nyc_output/f5e4f4d9-24b3-4cb7-a369-95e27032d6ba.json","externalId":"","uuid":"f5e4f4d9-24b3-4cb7-a369-95e27032d6ba","files":[]}
@@ -1 +1 @@
1
- {"processes":{"05c20625-d974-4cff-8627-ffd39eb87964":{"parent":null,"children":["1c8dd4f5-9b2f-4218-9c39-615aa7a79f1a","55d11bd3-216d-42c2-970e-55360a3aa1f7","bb3f1e57-7efe-4f35-852c-6971836aa288","e6440686-4e4c-487d-9abc-bb15eb8663e1"]},"1c8dd4f5-9b2f-4218-9c39-615aa7a79f1a":{"parent":"05c20625-d974-4cff-8627-ffd39eb87964","children":[]},"55d11bd3-216d-42c2-970e-55360a3aa1f7":{"parent":"05c20625-d974-4cff-8627-ffd39eb87964","children":[]},"bb3f1e57-7efe-4f35-852c-6971836aa288":{"parent":"05c20625-d974-4cff-8627-ffd39eb87964","children":[]},"e6440686-4e4c-487d-9abc-bb15eb8663e1":{"parent":"05c20625-d974-4cff-8627-ffd39eb87964","children":[]}},"files":{},"externalIds":{}}
1
+ {"processes":{"51df4d42-a45e-4cd8-8872-46e9147299bf":{"parent":"ac095d2d-5d4a-449e-82ae-0e0f0dd32b01","children":[]},"8ff4b2d0-5848-4ba6-9e8b-455ac526027c":{"parent":"ac095d2d-5d4a-449e-82ae-0e0f0dd32b01","children":[]},"970a6ddf-4bc1-4741-8913-f05ce8e287c6":{"parent":"ac095d2d-5d4a-449e-82ae-0e0f0dd32b01","children":[]},"ac095d2d-5d4a-449e-82ae-0e0f0dd32b01":{"parent":null,"children":["51df4d42-a45e-4cd8-8872-46e9147299bf","8ff4b2d0-5848-4ba6-9e8b-455ac526027c","970a6ddf-4bc1-4741-8913-f05ce8e287c6","f5e4f4d9-24b3-4cb7-a369-95e27032d6ba"]},"f5e4f4d9-24b3-4cb7-a369-95e27032d6ba":{"parent":"ac095d2d-5d4a-449e-82ae-0e0f0dd32b01","children":[]}},"files":{},"externalIds":{}}
@@ -18,6 +18,7 @@
18
18
  package helpers
19
19
 
20
20
  import (
21
+ "bytes"
21
22
  "html/template"
22
23
  "strings"
23
24
  "unicode"
@@ -25,6 +26,13 @@ import (
25
26
  bp "github.com/gohugoio/hugo/bufferpool"
26
27
  )
27
28
 
29
+ var (
30
+ openingPTag = []byte("<p>")
31
+ closingPTag = []byte("</p>")
32
+ paragraphIndicator = []byte("<p")
33
+ closingIndicator = []byte("</")
34
+ )
35
+
28
36
  var stripHTMLReplacer = strings.NewReplacer("\n", " ", "</p>", "\n", "<br>", "\n", "<br />", "\n")
29
37
 
30
38
  // StripHTML accepts a string, strips out all HTML tags and returns it.
@@ -68,3 +76,23 @@ func StripHTML(s string) string {
68
76
  func BytesToHTML(b []byte) template.HTML {
69
77
  return template.HTML(string(b))
70
78
  }
79
+
80
+ // TrimShortHTML removes the <p>/</p> tags from HTML input in the situation
81
+ // where said tags are the only <p> tags in the input and enclose the content
82
+ // of the input (whitespace excluded).
83
+ // Bookshop: Changed this to _not_ be a method of *ContentSpec
84
+ func TrimShortHTML(input []byte) []byte {
85
+ firstOpeningP := bytes.Index(input, paragraphIndicator)
86
+ lastOpeningP := bytes.LastIndex(input, paragraphIndicator)
87
+
88
+ lastClosingP := bytes.LastIndex(input, closingPTag)
89
+ lastClosing := bytes.LastIndex(input, closingIndicator)
90
+
91
+ if firstOpeningP == lastOpeningP && lastClosingP == lastClosing {
92
+ input = bytes.TrimSpace(input)
93
+ input = bytes.TrimPrefix(input, openingPTag)
94
+ input = bytes.TrimSuffix(input, closingPTag)
95
+ input = bytes.TrimSpace(input)
96
+ }
97
+ return input
98
+ }
Binary file
@@ -86,14 +86,24 @@ func UnwrapBookshopComponent(context interface{}) (string, interface{}) {
86
86
  key := fmt.Sprintf("components/%s/%s.hugo.html", component.(string), component.(string))
87
87
  return key, context
88
88
  }
89
+ return "err_no_bookshop_name_key", context
89
90
  }
90
91
  if componentData, ok := context.([]interface{}); ok {
91
92
  if component, ok := componentData[0].(string); ok {
92
93
  key := fmt.Sprintf("components/%s/%s.hugo.html", component, component)
93
94
  return key, componentData[1]
94
95
  }
96
+ return "err_slice_zero_not_string", context
95
97
  }
96
- return "err_no_bookshop_name", context
98
+ if componentData, ok := context.([]string); ok {
99
+ key := fmt.Sprintf("components/%s/%s.hugo.html", componentData[0], componentData[0])
100
+ return key, componentData[1]
101
+ }
102
+ if componentName, ok := context.(string); ok {
103
+ key := fmt.Sprintf("components/%s/%s.hugo.html", componentName, componentName)
104
+ return key, nil
105
+ }
106
+ return "err_not_map_slice_or_string", context
97
107
  }
98
108
 
99
109
  func UnwrapBookshopPartial(context interface{}) (string, interface{}) {
@@ -102,6 +112,11 @@ func UnwrapBookshopPartial(context interface{}) (string, interface{}) {
102
112
  key := fmt.Sprintf("shared/hugo/%s.hugo.html", partial)
103
113
  return key, partialData[1]
104
114
  }
115
+ return "err_slice_zero_not_string", context
116
+ }
117
+ if partialName, ok := context.(string); ok {
118
+ key := fmt.Sprintf("shared/hugo/%s.hugo.html", partialName)
119
+ return key, nil
105
120
  }
106
- return "err_no_bookshop_name", context
121
+ return "err_not_slice_or_string", context
107
122
  }
@@ -0,0 +1,39 @@
1
+ // Copyright 2019 The Hugo Authors. All rights reserved.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+
14
+ package transform
15
+
16
+ import (
17
+ "encoding/json"
18
+
19
+ "github.com/pkg/errors"
20
+ )
21
+
22
+ // Unmarshal unmarshals the data given, which can be either a string, json.RawMessage
23
+ // or a Resource. Supported formats are JSON, TOML, YAML, and CSV.
24
+ // You can optionally provide an options map as the first argument.
25
+ func (ns *Namespace) BookshopUnmarshal(args ...interface{}) (interface{}, error) {
26
+ if len(args) != 1 {
27
+ return nil, errors.New("Bookshop unmarshal takes 1 argument")
28
+ }
29
+
30
+ var data interface{}
31
+
32
+ if r, ok := args[0].(string); ok {
33
+ if err := json.Unmarshal([]byte(r), &data); err != nil {
34
+ return nil, err
35
+ }
36
+ return data, nil
37
+ }
38
+ return nil, errors.New("Needed a string")
39
+ }
@@ -99,6 +99,13 @@ func init() {
99
99
 
100
100
  // Bookshop: unmarshal has been removed (for now) due to tendrils
101
101
 
102
+ ns.AddMethodMapping(ctx.BookshopUnmarshal,
103
+ []string{"bookshopunmarshal"},
104
+ [][2]string{
105
+ {`{{ "{ hello = \"Hello World\" }" | transform.BookshopUnmarshal }}`, "map[hello:Hello World]"},
106
+ },
107
+ )
108
+
102
109
  return ns
103
110
  }
104
111
 
@@ -99,6 +99,9 @@ func (ns *Namespace) Markdownify(s interface{}) (template.HTML, error) {
99
99
  // This includes using a different (smaller) markdown implementation.
100
100
  output := markdown.ToHTML([]byte(ss), nil, nil)
101
101
 
102
+ // Strip if this is a short inline type of text.
103
+ output = helpers.TrimShortHTML(output)
104
+
102
105
  return helpers.BytesToHTML(output), nil
103
106
  }
104
107
 
package/lib/builder.js CHANGED
@@ -6,11 +6,11 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
6
 
7
7
  export const extensions = [".hugo.html"];
8
8
 
9
- export const esbuildConfigFn = (esbuildConfig) => {
9
+ export const esbuildConfigFn = (esbuildConfig, options) => {
10
10
  esbuildConfig.loader = {
11
11
  ...esbuildConfig.loader,
12
12
  ".hugo.html": "text",
13
- ".wasm": "file"
13
+ ".wasm": options?.hosted ? "file" : "binary"
14
14
  };
15
15
 
16
16
  const wasm_exec_banner = fs.readFileSync(path.join(__dirname, "../hugo-renderer/wasm_exec.js"));
package/lib/engine.js CHANGED
@@ -31,10 +31,15 @@ export class Engine {
31
31
  }
32
32
 
33
33
  async initializeHugo() {
34
- if (window.CloudCannon?.isMocked) {
35
- await this.initializeLocalHugo();
34
+ // When this script is run locally, the hugo wasm is loaded as binary rather than output as a file.
35
+ if (hugoWasm?.constructor === Uint8Array) {
36
+ await this.initializeInlineHugo();
36
37
  } else {
37
- await this.initializeRemoteHugo();
38
+ if (window.CloudCannon?.isMocked) {
39
+ await this.initializeLocalHugo();
40
+ } else {
41
+ await this.initializeRemoteHugo();
42
+ }
38
43
  }
39
44
 
40
45
  // TODO: Tidy
@@ -75,6 +80,13 @@ export class Engine {
75
80
  go.run(result.instance);
76
81
  }
77
82
 
83
+ async initializeInlineHugo() {
84
+ const go = new Go();
85
+ const buffer = hugoWasm.buffer;
86
+ const result = await WebAssembly.instantiate(buffer, go.importObject);
87
+ go.run(result.instance);
88
+ }
89
+
78
90
  getShared(name) {
79
91
  const key = `shared/hugo/${name}.hugo.html`
80
92
  return this.files?.[key];
@@ -150,13 +162,22 @@ export class Engine {
150
162
  return `index (${obj}) ${index}`;
151
163
  })
152
164
 
153
- const eval_str = `{{ jsonify (${str}) }}`;
165
+ const assignments = Object.entries(props_obj).filter(([key]) => key.startsWith('$')).map(([key, value]) => {
166
+ if (Array.isArray(value)) {
167
+ return `{{ ${key} := index ( \`{"a": ${JSON.stringify(value)}}\` | transform.BookshopUnmarshal ) "a" }}`
168
+ } else if (typeof value === 'object') {
169
+ return `{{ ${key} := \`${JSON.stringify(value)}\` | transform.BookshopUnmarshal }}`
170
+ } else {
171
+ return `{{ ${key} := ${JSON.stringify(value)} }}`
172
+ }
173
+ }).join('');
174
+ const eval_str = `${assignments}{{ jsonify (${str}) }}`;
154
175
  const output = window.renderHugo(eval_str, JSON.stringify(props_obj));
155
176
 
156
177
  try {
157
178
  return JSON.parse(output);
158
179
  } catch (e) {
159
- console.error(`Error evaluating \`${str}\` in the Hugo engine`, output);
180
+ console.warn(`Error evaluating \`${str}\` in the Hugo engine`, output);
160
181
  return null;
161
182
  }
162
183
  }
@@ -1,7 +1,7 @@
1
1
  const TOKENS = {
2
2
  DELIM: /"|'|`/,
3
3
  ESCAPE: /\\/,
4
- SPACE: /\s/,
4
+ SPACE: /\s|\r|\n/,
5
5
  INSCOPE: /\(/,
6
6
  OUTSCOPE: /\)/,
7
7
  SCOPE: /\./,
@@ -88,4 +88,23 @@ test(`Converts a dot index into a JS object property`, async t => {
88
88
  ident = `(index . 1234)`;
89
89
  output = (new IdentifierParser(ident)).build();
90
90
  t.deepEqual(output, `1234`);
91
+ });
92
+
93
+ test(`Multiline dicts and slices`, async t => {
94
+ const ident = `(dict
95
+ "contents"
96
+ (slice 1 "2"
97
+ (dict
98
+ "a" .Params.b)
99
+ ) "len" (
100
+ len
101
+ .some_array
102
+ ))`;
103
+ const output = (new IdentifierParser(ident)).build();
104
+ t.deepEqual(output, {
105
+ "contents": ["1", `"2"`, {
106
+ "a": "Params.b"
107
+ }],
108
+ "len": `(\n len\n .some_array\n)`
109
+ });
91
110
  });
@@ -1,17 +1,26 @@
1
1
  import { Tokenizer } from 'liquidjs';
2
2
 
3
+ // Homebrewed pretty-regex writer
3
4
  const tokens = {
4
- END: /{{\s*end\s*}}/,
5
- BEGIN: /{{\s*(if)/,
6
- BEGIN_SCOPED: /{{\s*(range|with|define|block|template)/,
7
- LOOP: /{{\s*range\s+(.+?)\s*}}/,
8
- INDEX_LOOP: /{{\s*range\s+(\$.+), \$.+ := (.+?)\s*}}/,
9
- ASSIGN: /{{\s*(\$\S+)\s+:=\s+(.+?)\s*}}/,
10
- WITH: /{{\s*with\s+(.+?)\s*}}/,
11
- BOOKSHOP: /{{\s*partial\s+"bookshop"\s+\(\s*slice\s+"(.+?)" (.+?)\s*\)\s*}}/,
12
- BOOKSHOP_SCOPED: /{{\s*partial\s+"bookshop"\s+\(?\s*\.\s*\)?\s*}}/,
5
+ END: `{{ end }}`,
6
+ BEGIN: `{{ (if)`,
7
+ BEGIN_SCOPED: `{{ (range|with|define|block|template)`,
8
+ LOOP: `{{ range () }}`,
9
+ INDEX_LOOP: `{{ range (\\$.+), \\$.+ := () }}`,
10
+ ASSIGN: `{{ (\\$\\S+) := () }}`,
11
+ REASSIGN: `{{ (\\$\\S+) = () }}`,
12
+ WITH: `{{ with () }}`,
13
+ BOOKSHOP: `{{ partial "bookshop" \\( slice "()" () \\) }}`,
14
+ BOOKSHOP_SCOPED: `{{ partial "bookshop" \\(? \\. \\)? }}`,
13
15
  }
14
16
 
17
+ const TOKENS = {};
18
+ Object.entries(tokens).forEach(([name, r]) => {
19
+ TOKENS[name] = new RegExp(r
20
+ .replace(/\(\)/g, '([\\S\\s]+?)') // Empty capturing group defaults to lazy multiline capture
21
+ .replace(/ /g, '[\\n\\r\\s]+') // Two spaces actually means one or more blanks
22
+ .replace(/ /g, '[\\n\\r\\s]*')); // One space means zero or more blanks
23
+ });
15
24
  /**
16
25
  * Parse a go text/template using the liquidjs parser
17
26
  * that we already have in the bundle.
@@ -25,44 +34,53 @@ const rewriteTag = function (token, src, endTags, liveMarkup) {
25
34
 
26
35
  // Skip non-value tags
27
36
  if (token.kind !== 8) return outputToken;
28
- if (tokens.END.test(raw)) {
37
+ if (TOKENS.END.test(raw)) {
29
38
  endTags.push(outputToken);
30
39
  return outputToken;
31
40
  }
32
41
 
33
- if (tokens.BEGIN.test(raw)) {
42
+ if (TOKENS.BEGIN.test(raw)) {
34
43
  endTags.pop();
35
44
  }
36
45
 
37
- if (tokens.BEGIN_SCOPED.test(raw)) {
46
+ if (TOKENS.BEGIN_SCOPED.test(raw)) {
38
47
  outputToken.text = `${outputToken.text}{{ \`<!--bookshop-live stack-->\` | safeHTML }}`;
39
48
 
40
49
  let matchingEnd = endTags.pop();
41
50
  matchingEnd.text = `{{ \`<!--bookshop-live unstack-->\` | safeHTML }}${matchingEnd.text}`;
42
51
  }
43
52
 
44
- if (liveMarkup && tokens.INDEX_LOOP.test(raw)) {
45
- let [, index_variable, iterator] = raw.match(tokens.INDEX_LOOP);
53
+ if (liveMarkup && TOKENS.INDEX_LOOP.test(raw)) {
54
+ let [, index_variable, iterator] = raw.match(TOKENS.INDEX_LOOP);
55
+ const r = required_wrapper_hugo_func(iterator);
46
56
  outputToken.text = [`${outputToken.text}`,
47
- `{{ (printf \`<!--bookshop-live context(.: (index (${iterator}) %d))-->\` ${index_variable}) | safeHTML }}`
57
+ `{{${r[0]} (printf \`<!--bookshop-live context(.: (index (${tidy(iterator)}) %v))-->\` (jsonify ${index_variable}))${r[1]} | safeHTML }}`
48
58
  ].join('')
49
- } else if (liveMarkup && tokens.LOOP.test(raw)) {
50
- let [, iterator] = raw.match(tokens.LOOP);
59
+ } else if (liveMarkup && TOKENS.LOOP.test(raw)) {
60
+ let [, iterator] = raw.match(TOKENS.LOOP);
61
+ const r = required_wrapper_hugo_func(iterator);
51
62
  outputToken.text = [`{{ $bookshop__live__iterator := 0 }}`,
52
63
  `${outputToken.text}`,
53
- `{{ (printf \`<!--bookshop-live context(.: (index (${iterator}) %d))-->\` $bookshop__live__iterator) | safeHTML }}`,
64
+ `{{${r[0]} (printf \`<!--bookshop-live context(.: (index (${tidy(iterator)}) %d))-->\` $bookshop__live__iterator)${r[1]} | safeHTML }}`,
54
65
  `{{ $bookshop__live__iterator = (add $bookshop__live__iterator 1) }}`
55
66
  ].join('')
56
- } else if (liveMarkup && tokens.ASSIGN.test(raw)) {
57
- let [, identifier, value] = raw.match(tokens.ASSIGN);
58
- outputToken.text = `${outputToken.text}{{ \`<!--bookshop-live context(${identifier}: (${value}))-->\` | safeHTML }}`
59
- } else if (liveMarkup && tokens.WITH.test(raw)) {
60
- let [, value] = raw.match(tokens.WITH);
61
- outputToken.text = `${outputToken.text}{{ \`<!--bookshop-live context(.: (${value}))-->\` | safeHTML }}`
62
- } else if (liveMarkup && tokens.BOOKSHOP.test(raw)) {
63
- let [, name, params] = raw.match(tokens.BOOKSHOP);
64
- outputToken.text = `{{ \`<!--bookshop-live name(${name}) params(.: (${params}))-->\` | safeHTML }}${outputToken.text}{{ \`<!--bookshop-live end-->\` | safeHTML }}`
65
- } else if (liveMarkup && tokens.BOOKSHOP_SCOPED.test(raw)) {
67
+ } else if (liveMarkup && TOKENS.ASSIGN.test(raw)) {
68
+ let [, identifier, value] = raw.match(TOKENS.ASSIGN);
69
+ const r = required_wrapper_hugo_func(value);
70
+ outputToken.text = `${outputToken.text}{{${r[0]} \`<!--bookshop-live context(${identifier}: (${tidy(value)}))-->\`${r[1]} | safeHTML }}`
71
+ } else if (liveMarkup && TOKENS.REASSIGN.test(raw)) {
72
+ let [, identifier, value] = raw.match(TOKENS.REASSIGN);
73
+ const r = required_wrapper_hugo_func(value);
74
+ outputToken.text = `${outputToken.text}{{${r[0]} \`<!--bookshop-live reassign(${identifier}: (${tidy(value)}))-->\`${r[1]} | safeHTML }}`
75
+ } else if (liveMarkup && TOKENS.WITH.test(raw)) {
76
+ let [, value] = raw.match(TOKENS.WITH);
77
+ const r = required_wrapper_hugo_func(value);
78
+ outputToken.text = `${outputToken.text}{{${r[0]} \`<!--bookshop-live context(.: (${tidy(value)}))-->\`${r[1]} | safeHTML }}`
79
+ } else if (liveMarkup && TOKENS.BOOKSHOP.test(raw)) {
80
+ let [, name, params] = raw.match(TOKENS.BOOKSHOP);
81
+ const r = required_wrapper_hugo_func(params);
82
+ outputToken.text = `{{${r[0]} \`<!--bookshop-live name(${name}) params(.: (${tidy(params)}))-->\`${r[1]} | safeHTML }}${outputToken.text}{{ \`<!--bookshop-live end-->\` | safeHTML }}`
83
+ } else if (liveMarkup && TOKENS.BOOKSHOP_SCOPED.test(raw)) {
66
84
  outputToken.text = [`{{ if reflect.IsSlice . }}{{ (printf \`<!--bookshop-live name(%s) params(.: .)-->\` (index . 0)) | safeHTML }}`,
67
85
  `{{- else if reflect.IsMap . -}}{{ (printf \`<!--bookshop-live name(%s) params(.: .)-->\` ._bookshop_name) | safeHTML }}{{ end }}`,
68
86
  `${outputToken.text}`,
@@ -73,6 +91,12 @@ const rewriteTag = function (token, src, endTags, liveMarkup) {
73
91
  return outputToken;
74
92
  }
75
93
 
94
+ // limit comments to one line & escape backticks to something we undo later
95
+ const tidy = val => val.replace(/[\r\n]/g, ' ').replace(/`/g, 'BKSH_BACKTICK');
96
+
97
+ // The replace function we need to add to undo the backtick tidy above
98
+ const required_wrapper_hugo_func = val => /`/.test(val) ? [` replace`, ` "BKSH_BACKTICK" "\`"`] : [``, ``];
99
+
76
100
  export default function (text, opts) {
77
101
  opts = {
78
102
  liveMarkup: true,
@@ -36,6 +36,10 @@ test("add live markup to assigns", t => {
36
36
  input = `{{ $a := .b | chomp }}`;
37
37
  expected = `{{ $a := .b | chomp }}{{ \`<!--bookshop-live context($a: (.b | chomp))-->\` | safeHTML }}`;
38
38
  t.is(translateTextTemplate(input, {}), expected);
39
+
40
+ input = `{{ $a = .b }}`;
41
+ expected = `{{ $a = .b }}{{ \`<!--bookshop-live reassign($a: (.b))-->\` | safeHTML }}`;
42
+ t.is(translateTextTemplate(input, {}), expected);
39
43
  });
40
44
 
41
45
  test("add live markup to withs", t => {
@@ -68,7 +72,7 @@ test("add live markup to loops with iterators", t => {
68
72
  const input = `{{range $loop_index, $element := .columns}}<p>{{$element}}</p>{{ end }}`;
69
73
  const expected = [`{{range $loop_index, $element := .columns}}`,
70
74
  `{{ \`<!--bookshop-live stack-->\` | safeHTML }}`,
71
- `{{ (printf \`<!--bookshop-live context(.: (index (.columns) %d))-->\` $loop_index) | safeHTML }}`,
75
+ `{{ (printf \`<!--bookshop-live context(.: (index (.columns) %v))-->\` (jsonify $loop_index)) | safeHTML }}`,
72
76
  `<p>{{$element}}</p>`,
73
77
  `{{ \`<!--bookshop-live unstack-->\` | safeHTML }}`,
74
78
  `{{ end }}`
@@ -76,6 +80,12 @@ test("add live markup to loops with iterators", t => {
76
80
  t.is(translateTextTemplate(input, {}), expected);
77
81
  });
78
82
 
83
+ test("escape backticks in values", t => {
84
+ let input = `{{ $a := "hi\`:)" }}`;
85
+ let expected = `{{ $a := "hi\`:)" }}{{ replace \`<!--bookshop-live context($a: ("hiBKSH_BACKTICK:)"))-->\` "BKSH_BACKTICK" "\`" | safeHTML }}`;
86
+ t.is(translateTextTemplate(input, {}), expected);
87
+ });
88
+
79
89
  test("add live markup to complex end structures", t => {
80
90
  const input = `
81
91
  {{ range .items }}
@@ -108,6 +118,52 @@ test("add live markup to complex end structures", t => {
108
118
  {{ \`<!--bookshop-live unstack-->\` | safeHTML }}{{end}}
109
119
  {{ end }}
110
120
 
121
+ {{ \`<!--bookshop-live unstack-->\` | safeHTML }}{{ end }}`;
122
+ t.is(translateTextTemplate(input, {}), expected);
123
+ });
124
+
125
+ test("add live markup to complex components", t => {
126
+ const input = `
127
+ {{ $level := default 2 .level }}
128
+ {{ $level = string
129
+ $level }}
130
+ {{ $level_classes := dict
131
+ "1" "border-b py-8 text-4xl"
132
+ "2" "border-b py-6 text-3xl"
133
+ "3" "border-b py-4 text-2xl font-bold"
134
+ "4" "border-b text-xl font-bold"
135
+ }}
136
+ {{ $level_class := "lg" }}
137
+ {{ with index $level_classes $level }}
138
+ {{ $level_class = . }}
139
+ {{ end }}
140
+ {{ $open := printf \`<h%s class="%s">\` $level $level_class }}
141
+ {{ $close := printf \`</h%s>\` $level }}
142
+ {{ with .copy }}
143
+ {{ safeHTML $open }}
144
+ {{ markdownify . }} | bookshop
145
+ {{ safeHTML $close }}
146
+ {{ end }}`;
147
+ const expected = `
148
+ {{ $level := default 2 .level }}{{ \`<!--bookshop-live context($level: (default 2 .level))-->\` | safeHTML }}
149
+ {{ $level = string
150
+ $level }}{{ \`<!--bookshop-live reassign($level: (string $level))-->\` | safeHTML }}
151
+ {{ $level_classes := dict
152
+ "1" "border-b py-8 text-4xl"
153
+ "2" "border-b py-6 text-3xl"
154
+ "3" "border-b py-4 text-2xl font-bold"
155
+ "4" "border-b text-xl font-bold"
156
+ }}{{ \`<!--bookshop-live context($level_classes: (dict "1" "border-b py-8 text-4xl" "2" "border-b py-6 text-3xl" "3" "border-b py-4 text-2xl font-bold" "4" "border-b text-xl font-bold"))-->\` | safeHTML }}
157
+ {{ $level_class := "lg" }}{{ \`<!--bookshop-live context($level_class: ("lg"))-->\` | safeHTML }}
158
+ {{ with index $level_classes $level }}{{ \`<!--bookshop-live stack-->\` | safeHTML }}{{ \`<!--bookshop-live context(.: (index $level_classes $level))-->\` | safeHTML }}
159
+ {{ $level_class = . }}{{ \`<!--bookshop-live reassign($level_class: (.))-->\` | safeHTML }}
160
+ {{ \`<!--bookshop-live unstack-->\` | safeHTML }}{{ end }}
161
+ {{ $open := printf \`<h%s class="%s">\` $level $level_class }}{{ replace \`<!--bookshop-live context($open: (printf BKSH_BACKTICK<h%s class="%s">BKSH_BACKTICK $level $level_class))-->\` "BKSH_BACKTICK" "\`" | safeHTML }}
162
+ {{ $close := printf \`</h%s>\` $level }}{{ replace \`<!--bookshop-live context($close: (printf BKSH_BACKTICK</h%s>BKSH_BACKTICK $level))-->\` "BKSH_BACKTICK" "\`" | safeHTML }}
163
+ {{ with .copy }}{{ \`<!--bookshop-live stack-->\` | safeHTML }}{{ \`<!--bookshop-live context(.: (.copy))-->\` | safeHTML }}
164
+ {{ safeHTML $open }}
165
+ {{ markdownify . }} | bookshop
166
+ {{ safeHTML $close }}
111
167
  {{ \`<!--bookshop-live unstack-->\` | safeHTML }}{{ end }}`;
112
168
  t.is(translateTextTemplate(input, {}), expected);
113
169
  });
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": "2.4.2",
4
+ "version": "2.6.1",
5
5
  "description": "Bookshop frontend Hugo renderer",
6
6
  "type": "module",
7
7
  "sideEffects": false,
@@ -29,7 +29,7 @@
29
29
  "nyc": "^15.1.0"
30
30
  },
31
31
  "dependencies": {
32
- "@bookshop/helpers": "2.4.2",
32
+ "@bookshop/helpers": "2.6.1",
33
33
  "esbuild": "^0.13.10",
34
34
  "liquidjs": "9.28.0"
35
35
  },
@@ -1 +0,0 @@
1
- {"parent":null,"pid":94690,"argv":["/Users/liambigelow/.nvm/versions/node/v14.16.0/bin/node","/Users/liambigelow/Projects/CC/bookshop/javascript-modules/node_modules/ava/cli.js","-v"],"execArgv":[],"cwd":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine","time":1643783907226,"ppid":94689,"coverageFilename":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine/.nyc_output/05c20625-d974-4cff-8627-ffd39eb87964.json","externalId":"","uuid":"05c20625-d974-4cff-8627-ffd39eb87964","files":[]}