@bookshop/hugo-engine 2.4.1 → 2.6.0
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/{0285db3a-0bb1-48c5-a949-061f568eaab6.json → 898d2cdb-39d3-43f1-8e67-73ff2486a451.json} +0 -0
- package/.nyc_output/{222873c9-9975-439a-a423-1e8cdec80767.json → 98716901-9bd9-4e10-89c7-27bb55998257.json} +0 -0
- package/.nyc_output/{3abd89cc-3259-442a-a584-30ba1c47f41f.json → b9ec436e-de9f-4018-a0c6-8c889bc94c66.json} +0 -0
- package/.nyc_output/{72cd08d5-81da-4a01-af7e-b82f5c58bfe2.json → f1d5464a-8b36-494a-b4b0-fdc2b034270e.json} +0 -0
- package/.nyc_output/{85ee537c-1dd7-4a62-96e1-954d77437eb0.json → f23634b1-d1b0-46bf-9098-6c7a68cbe98a.json} +0 -0
- package/.nyc_output/processinfo/{0285db3a-0bb1-48c5-a949-061f568eaab6.json → 898d2cdb-39d3-43f1-8e67-73ff2486a451.json} +1 -1
- package/.nyc_output/processinfo/{3abd89cc-3259-442a-a584-30ba1c47f41f.json → 98716901-9bd9-4e10-89c7-27bb55998257.json} +1 -1
- package/.nyc_output/processinfo/{72cd08d5-81da-4a01-af7e-b82f5c58bfe2.json → b9ec436e-de9f-4018-a0c6-8c889bc94c66.json} +1 -1
- package/.nyc_output/processinfo/f1d5464a-8b36-494a-b4b0-fdc2b034270e.json +1 -0
- package/.nyc_output/processinfo/{85ee537c-1dd7-4a62-96e1-954d77437eb0.json → f23634b1-d1b0-46bf-9098-6c7a68cbe98a.json} +1 -1
- package/.nyc_output/processinfo/index.json +1 -1
- package/hugo-renderer/build.sh +7 -6
- package/hugo-renderer/go.mod +6 -42
- package/hugo-renderer/go.sum +2 -49
- package/hugo-renderer/helpers/bookshop_modified_content.go +34 -0
- package/hugo-renderer/helpers/emoji.go +96 -0
- package/hugo-renderer/hugo_renderer.wasm +0 -0
- package/hugo-renderer/tpl/bookshop_engine/bookshop_func_importer.go +1 -1
- package/hugo-renderer/tpl/bookshop_library/bookshop_components.go +17 -2
- package/hugo-renderer/tpl/collections/collections.go +1 -0
- package/hugo-renderer/tpl/fmt/fmt.go +2 -0
- package/hugo-renderer/tpl/partials/partials.go +2 -0
- package/hugo-renderer/tpl/transform/bookshop_unmarshal.go +39 -0
- package/hugo-renderer/tpl/transform/init.go +113 -0
- package/hugo-renderer/tpl/transform/transform.go +116 -0
- package/hugo-renderer/update-from-hugo.sh +19 -0
- package/lib/builder.js +2 -2
- package/lib/engine.js +26 -5
- package/lib/hugoIdentifierParser.js +1 -1
- package/lib/hugoIdentifierParser.test.js +19 -0
- package/lib/translateTextTemplate.js +52 -28
- package/lib/translateTextTemplate.test.js +57 -1
- package/package.json +2 -2
- package/.nyc_output/processinfo/222873c9-9975-439a-a423-1e8cdec80767.json +0 -1
|
@@ -380,6 +380,7 @@ func (ns *Namespace) IsSet(a interface{}, key interface{}) (bool, error) {
|
|
|
380
380
|
return av.MapIndex(kv).IsValid(), nil
|
|
381
381
|
}
|
|
382
382
|
default:
|
|
383
|
+
// Bookshop: Removed deep Hugo function.
|
|
383
384
|
// helpers.DistinctErrorLog.Printf("WARNING: calling IsSet with unsupported type %q (%T) will always return false.\n", av.Kind(), a)
|
|
384
385
|
}
|
|
385
386
|
|
|
@@ -20,6 +20,8 @@ import (
|
|
|
20
20
|
"hugo-renderer/deps"
|
|
21
21
|
)
|
|
22
22
|
|
|
23
|
+
// Bookshop: This file has been stripped due to irrelevancy with live editing.
|
|
24
|
+
|
|
23
25
|
// New returns a new instance of the fmt-namespaced template functions.
|
|
24
26
|
func New(d *deps.Deps) *Namespace {
|
|
25
27
|
return &Namespace{}
|
|
@@ -26,6 +26,8 @@ import (
|
|
|
26
26
|
"hugo-renderer/deps"
|
|
27
27
|
)
|
|
28
28
|
|
|
29
|
+
// Bookshop: This file has been stripped to only support Bookshop partials when live editing.
|
|
30
|
+
|
|
29
31
|
// New returns a new instance of the templates-namespaced template functions.
|
|
30
32
|
func New(deps *deps.Deps) *Namespace {
|
|
31
33
|
return &Namespace{}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Copyright 2017 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
|
+
"hugo-renderer/deps"
|
|
18
|
+
"hugo-renderer/tpl/internal"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
const name = "transform"
|
|
22
|
+
|
|
23
|
+
func init() {
|
|
24
|
+
f := func(d *deps.Deps) *internal.TemplateFuncsNamespace {
|
|
25
|
+
ctx := New(d)
|
|
26
|
+
|
|
27
|
+
ns := &internal.TemplateFuncsNamespace{
|
|
28
|
+
Name: name,
|
|
29
|
+
Context: func(args ...interface{}) (interface{}, error) { return ctx, nil },
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
ns.AddMethodMapping(ctx.Emojify,
|
|
33
|
+
[]string{"emojify"},
|
|
34
|
+
[][2]string{
|
|
35
|
+
{`{{ "I :heart: Hugo" | emojify }}`, `I ❤️ Hugo`},
|
|
36
|
+
},
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
ns.AddMethodMapping(ctx.Highlight,
|
|
40
|
+
[]string{"highlight"},
|
|
41
|
+
[][2]string{},
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
ns.AddMethodMapping(ctx.HTMLEscape,
|
|
45
|
+
[]string{"htmlEscape"},
|
|
46
|
+
[][2]string{
|
|
47
|
+
{
|
|
48
|
+
`{{ htmlEscape "Cathal Garvey & The Sunshine Band <cathal@foo.bar>" | safeHTML}}`,
|
|
49
|
+
`Cathal Garvey & The Sunshine Band <cathal@foo.bar>`,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
`{{ htmlEscape "Cathal Garvey & The Sunshine Band <cathal@foo.bar>"}}`,
|
|
53
|
+
`Cathal Garvey &amp; The Sunshine Band &lt;cathal@foo.bar&gt;`,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
`{{ htmlEscape "Cathal Garvey & The Sunshine Band <cathal@foo.bar>" | htmlUnescape | safeHTML }}`,
|
|
57
|
+
`Cathal Garvey & The Sunshine Band <cathal@foo.bar>`,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
ns.AddMethodMapping(ctx.HTMLUnescape,
|
|
63
|
+
[]string{"htmlUnescape"},
|
|
64
|
+
[][2]string{
|
|
65
|
+
{
|
|
66
|
+
`{{ htmlUnescape "Cathal Garvey & The Sunshine Band <cathal@foo.bar>" | safeHTML}}`,
|
|
67
|
+
`Cathal Garvey & The Sunshine Band <cathal@foo.bar>`,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
`{{"Cathal Garvey &amp; The Sunshine Band &lt;cathal@foo.bar&gt;" | htmlUnescape | htmlUnescape | safeHTML}}`,
|
|
71
|
+
`Cathal Garvey & The Sunshine Band <cathal@foo.bar>`,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
`{{"Cathal Garvey &amp; The Sunshine Band &lt;cathal@foo.bar&gt;" | htmlUnescape | htmlUnescape }}`,
|
|
75
|
+
`Cathal Garvey & The Sunshine Band <cathal@foo.bar>`,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
`{{ htmlUnescape "Cathal Garvey & The Sunshine Band <cathal@foo.bar>" | htmlEscape | safeHTML }}`,
|
|
79
|
+
`Cathal Garvey & The Sunshine Band <cathal@foo.bar>`,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
ns.AddMethodMapping(ctx.Markdownify,
|
|
85
|
+
[]string{"markdownify"},
|
|
86
|
+
[][2]string{
|
|
87
|
+
{`{{ .Title | markdownify}}`, `<strong>BatMan</strong>`},
|
|
88
|
+
},
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
ns.AddMethodMapping(ctx.Plainify,
|
|
92
|
+
[]string{"plainify"},
|
|
93
|
+
[][2]string{
|
|
94
|
+
{`{{ plainify "Hello <strong>world</strong>, gophers!" }}`, `Hello world, gophers!`},
|
|
95
|
+
},
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
// Bookshop: remarshal has been intentionally removed due to instability
|
|
99
|
+
|
|
100
|
+
// Bookshop: unmarshal has been removed (for now) due to tendrils
|
|
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
|
+
|
|
109
|
+
return ns
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
internal.AddTemplateFuncsNamespace(f)
|
|
113
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Copyright 2017 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 provides template functions for transforming content.
|
|
15
|
+
package transform
|
|
16
|
+
|
|
17
|
+
import (
|
|
18
|
+
"html"
|
|
19
|
+
"html/template"
|
|
20
|
+
|
|
21
|
+
"github.com/gomarkdown/markdown"
|
|
22
|
+
|
|
23
|
+
"hugo-renderer/deps"
|
|
24
|
+
|
|
25
|
+
"hugo-renderer/helpers"
|
|
26
|
+
|
|
27
|
+
"github.com/spf13/cast"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
// New returns a new instance of the transform-namespaced template functions.
|
|
31
|
+
func New(deps *deps.Deps) *Namespace {
|
|
32
|
+
// Bookshop: Build listeners & cache intentionally removed
|
|
33
|
+
|
|
34
|
+
return &Namespace{
|
|
35
|
+
deps: deps,
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Namespace provides template functions for the "transform" namespace.
|
|
40
|
+
type Namespace struct {
|
|
41
|
+
// Bookshop: Build listeners & cache intentionally removed
|
|
42
|
+
deps *deps.Deps
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Emojify returns a copy of s with all emoji codes replaced with actual emojis.
|
|
46
|
+
//
|
|
47
|
+
// See http://www.emoji-cheat-sheet.com/
|
|
48
|
+
func (ns *Namespace) Emojify(s interface{}) (template.HTML, error) {
|
|
49
|
+
ss, err := cast.ToStringE(s)
|
|
50
|
+
if err != nil {
|
|
51
|
+
return "", err
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return template.HTML(helpers.Emojify([]byte(ss))), nil
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Highlight returns a copy of s as an HTML string with syntax
|
|
58
|
+
// highlighting applied.
|
|
59
|
+
func (ns *Namespace) Highlight(s interface{}, lang string, opts ...interface{}) (template.HTML, error) {
|
|
60
|
+
ss, err := cast.ToStringE(s)
|
|
61
|
+
if err != nil {
|
|
62
|
+
return "", err
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Bookshop: Highlighting logic intentionally removed for bloat
|
|
66
|
+
|
|
67
|
+
return template.HTML(ss), nil
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// HTMLEscape returns a copy of s with reserved HTML characters escaped.
|
|
71
|
+
func (ns *Namespace) HTMLEscape(s interface{}) (string, error) {
|
|
72
|
+
ss, err := cast.ToStringE(s)
|
|
73
|
+
if err != nil {
|
|
74
|
+
return "", err
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return html.EscapeString(ss), nil
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// HTMLUnescape returns a copy of with HTML escape requences converted to plain
|
|
81
|
+
// text.
|
|
82
|
+
func (ns *Namespace) HTMLUnescape(s interface{}) (string, error) {
|
|
83
|
+
ss, err := cast.ToStringE(s)
|
|
84
|
+
if err != nil {
|
|
85
|
+
return "", err
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return html.UnescapeString(ss), nil
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Markdownify renders a given input from Markdown to HTML.
|
|
92
|
+
func (ns *Namespace) Markdownify(s interface{}) (template.HTML, error) {
|
|
93
|
+
ss, err := cast.ToStringE(s)
|
|
94
|
+
if err != nil {
|
|
95
|
+
return "", err
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Bookshop: markdownify implementation has been intentionally modified for bloat
|
|
99
|
+
// This includes using a different (smaller) markdown implementation.
|
|
100
|
+
output := markdown.ToHTML([]byte(ss), nil, nil)
|
|
101
|
+
|
|
102
|
+
// Strip if this is a short inline type of text.
|
|
103
|
+
output = helpers.TrimShortHTML(output)
|
|
104
|
+
|
|
105
|
+
return helpers.BytesToHTML(output), nil
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Plainify returns a copy of s with all HTML tags removed.
|
|
109
|
+
func (ns *Namespace) Plainify(s interface{}) (string, error) {
|
|
110
|
+
ss, err := cast.ToStringE(s)
|
|
111
|
+
if err != nil {
|
|
112
|
+
return "", err
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return helpers.StripHTML(ss), nil
|
|
116
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
cd tpl
|
|
2
|
+
HUGO_LOC="../../../../../../../Scratchpad/hugo/tpl"
|
|
3
|
+
|
|
4
|
+
# Check Hugo loc
|
|
5
|
+
if [ ! -d $HUGO_LOC ]; then
|
|
6
|
+
echo "ERROR: hugo repo does not exist (on Liam's machine)"
|
|
7
|
+
exit 1
|
|
8
|
+
fi
|
|
9
|
+
|
|
10
|
+
for LOCAL_FILE in **/*.go; do
|
|
11
|
+
if [[ ! $LOCAL_FILE == *bookshop* ]]; then
|
|
12
|
+
HUGO_FILE="$HUGO_LOC/$LOCAL_FILE"
|
|
13
|
+
if [ ! -f $HUGO_FILE ]; then
|
|
14
|
+
echo "WARN: File $LOCAL_FILE doesn't exist in Hugo as $HUGO_FILE"
|
|
15
|
+
else
|
|
16
|
+
cp $HUGO_FILE $LOCAL_FILE
|
|
17
|
+
fi
|
|
18
|
+
fi
|
|
19
|
+
done
|
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
|
-
|
|
35
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
180
|
+
console.warn(`Error evaluating \`${str}\` in the Hugo engine`, output);
|
|
160
181
|
return null;
|
|
161
182
|
}
|
|
162
183
|
}
|
|
@@ -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:
|
|
5
|
-
BEGIN:
|
|
6
|
-
BEGIN_SCOPED:
|
|
7
|
-
LOOP:
|
|
8
|
-
INDEX_LOOP:
|
|
9
|
-
ASSIGN:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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 (
|
|
37
|
+
if (TOKENS.END.test(raw)) {
|
|
29
38
|
endTags.push(outputToken);
|
|
30
39
|
return outputToken;
|
|
31
40
|
}
|
|
32
41
|
|
|
33
|
-
if (
|
|
42
|
+
if (TOKENS.BEGIN.test(raw)) {
|
|
34
43
|
endTags.pop();
|
|
35
44
|
}
|
|
36
45
|
|
|
37
|
-
if (
|
|
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 &&
|
|
45
|
-
let [, index_variable, iterator] = raw.match(
|
|
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}) %
|
|
57
|
+
`{{${r[0]} (printf \`<!--bookshop-live context(.: (index (${tidy(iterator)}) %v))-->\` (jsonify ${index_variable}))${r[1]} | safeHTML }}`
|
|
48
58
|
].join('')
|
|
49
|
-
} else if (liveMarkup &&
|
|
50
|
-
let [, iterator] = raw.match(
|
|
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 &&
|
|
57
|
-
let [, identifier, value] = raw.match(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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) %
|
|
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
|
+
"version": "2.6.0",
|
|
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.
|
|
32
|
+
"@bookshop/helpers": "2.6.0",
|
|
33
33
|
"esbuild": "^0.13.10",
|
|
34
34
|
"liquidjs": "9.28.0"
|
|
35
35
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"parent":null,"pid":37256,"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":1643741987817,"ppid":37255,"coverageFilename":"/Users/liambigelow/Projects/CC/bookshop/javascript-modules/engines/hugo-engine/.nyc_output/222873c9-9975-439a-a423-1e8cdec80767.json","externalId":"","uuid":"222873c9-9975-439a-a423-1e8cdec80767","files":[]}
|