@bookshop/hugo-engine 2.4.0-hugo.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/50eb2ebd-c797-4d3f-8df7-a707c1b211eb.json +1 -0
- package/.nyc_output/86f85916-4f58-43da-8c32-c1ec29ca783e.json +1 -0
- package/.nyc_output/a27680c8-8858-4ede-921d-6e6617f42a0b.json +1 -0
- package/.nyc_output/b5f86652-cbe8-44f9-a36c-d5721dfe5bbe.json +1 -0
- package/.nyc_output/ecca87c1-5774-4962-864a-2e4537aa9915.json +1 -0
- package/.nyc_output/processinfo/50eb2ebd-c797-4d3f-8df7-a707c1b211eb.json +1 -0
- package/.nyc_output/processinfo/86f85916-4f58-43da-8c32-c1ec29ca783e.json +1 -0
- package/.nyc_output/processinfo/a27680c8-8858-4ede-921d-6e6617f42a0b.json +1 -0
- package/.nyc_output/processinfo/b5f86652-cbe8-44f9-a36c-d5721dfe5bbe.json +1 -0
- package/.nyc_output/processinfo/ecca87c1-5774-4962-864a-2e4537aa9915.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/build.js +1 -0
- package/hugo-renderer/build.sh +16 -0
- package/hugo-renderer/deps/bookshop_modified_deps.go +6 -0
- package/hugo-renderer/go.mod +55 -0
- package/hugo-renderer/go.sum +886 -0
- package/hugo-renderer/helpers/bookshop_modified_content.go +64 -0
- package/hugo-renderer/helpers/bookshop_modified_general.go +33 -0
- package/hugo-renderer/hugo_renderer.wasm +0 -0
- package/hugo-renderer/main.go +28 -0
- package/hugo-renderer/tpl/bookshop_engine/bookshop_func_importer.go +34 -0
- package/hugo-renderer/tpl/bookshop_engine/bookshop_render.go +64 -0
- package/hugo-renderer/tpl/bookshop_library/bookshop_components.go +107 -0
- package/hugo-renderer/tpl/cast/cast.go +63 -0
- package/hugo-renderer/tpl/cast/init.go +57 -0
- package/hugo-renderer/tpl/collections/append.go +37 -0
- package/hugo-renderer/tpl/collections/apply.go +131 -0
- package/hugo-renderer/tpl/collections/collections.go +767 -0
- package/hugo-renderer/tpl/collections/complement.go +55 -0
- package/hugo-renderer/tpl/collections/index.go +133 -0
- package/hugo-renderer/tpl/collections/init.go +214 -0
- package/hugo-renderer/tpl/collections/merge.go +126 -0
- package/hugo-renderer/tpl/collections/reflect_helpers.go +216 -0
- package/hugo-renderer/tpl/collections/sort.go +183 -0
- package/hugo-renderer/tpl/collections/symdiff.go +68 -0
- package/hugo-renderer/tpl/collections/where.go +517 -0
- package/hugo-renderer/tpl/compare/compare.go +324 -0
- package/hugo-renderer/tpl/compare/init.go +85 -0
- package/hugo-renderer/tpl/crypto/crypto.go +109 -0
- package/hugo-renderer/tpl/crypto/init.go +65 -0
- package/hugo-renderer/tpl/debug/debug.go +40 -0
- package/hugo-renderer/tpl/debug/init.go +45 -0
- package/hugo-renderer/tpl/encoding/encoding.go +90 -0
- package/hugo-renderer/tpl/encoding/init.go +59 -0
- package/hugo-renderer/tpl/fmt/fmt.go +64 -0
- package/hugo-renderer/tpl/fmt/init.go +77 -0
- package/hugo-renderer/tpl/inflect/inflect.go +79 -0
- package/hugo-renderer/tpl/inflect/init.go +60 -0
- package/hugo-renderer/tpl/internal/bookshop_modified_templatefuncsRegistry.go +88 -0
- package/hugo-renderer/tpl/math/init.go +134 -0
- package/hugo-renderer/tpl/math/math.go +164 -0
- package/hugo-renderer/tpl/math/round.go +61 -0
- package/hugo-renderer/tpl/partials/init.go +55 -0
- package/hugo-renderer/tpl/partials/partials.go +86 -0
- package/hugo-renderer/tpl/path/init.go +60 -0
- package/hugo-renderer/tpl/path/path.go +159 -0
- package/hugo-renderer/tpl/reflect/init.go +51 -0
- package/hugo-renderer/tpl/reflect/reflect.go +36 -0
- package/hugo-renderer/tpl/safe/init.go +80 -0
- package/hugo-renderer/tpl/safe/safe.go +72 -0
- package/hugo-renderer/tpl/strings/init.go +229 -0
- package/hugo-renderer/tpl/strings/regexp.go +125 -0
- package/hugo-renderer/tpl/strings/strings.go +500 -0
- package/hugo-renderer/tpl/strings/truncate.go +157 -0
- package/hugo-renderer/wasm_exec.js +636 -0
- package/lib/builder.js +21 -0
- package/lib/engine.js +134 -0
- package/lib/engine.test.js +5 -0
- package/lib/hugoIdentifierParser.js +324 -0
- package/lib/hugoIdentifierParser.test.js +72 -0
- package/lib/translateTextTemplate.js +58 -0
- package/lib/translateTextTemplate.test.js +39 -0
- package/main.js +1 -0
- package/main.test.js +5 -0
- package/package.json +32 -0
|
@@ -0,0 +1,64 @@
|
|
|
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 helpers implements general utility functions that work with
|
|
15
|
+
// and on content. The helper functions defined here lay down the
|
|
16
|
+
// foundation of how Hugo works with files and filepaths, and perform
|
|
17
|
+
// string operations on content.
|
|
18
|
+
package helpers
|
|
19
|
+
|
|
20
|
+
import (
|
|
21
|
+
"strings"
|
|
22
|
+
"unicode"
|
|
23
|
+
|
|
24
|
+
bp "github.com/gohugoio/hugo/bufferpool"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
var stripHTMLReplacer = strings.NewReplacer("\n", " ", "</p>", "\n", "<br>", "\n", "<br />", "\n")
|
|
28
|
+
|
|
29
|
+
// StripHTML accepts a string, strips out all HTML tags and returns it.
|
|
30
|
+
func StripHTML(s string) string {
|
|
31
|
+
// Shortcut strings with no tags in them
|
|
32
|
+
if !strings.ContainsAny(s, "<>") {
|
|
33
|
+
return s
|
|
34
|
+
}
|
|
35
|
+
s = stripHTMLReplacer.Replace(s)
|
|
36
|
+
|
|
37
|
+
// Walk through the string removing all tags
|
|
38
|
+
b := bp.GetBuffer()
|
|
39
|
+
defer bp.PutBuffer(b)
|
|
40
|
+
var inTag, isSpace, wasSpace bool
|
|
41
|
+
for _, r := range s {
|
|
42
|
+
if !inTag {
|
|
43
|
+
isSpace = false
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
switch {
|
|
47
|
+
case r == '<':
|
|
48
|
+
inTag = true
|
|
49
|
+
case r == '>':
|
|
50
|
+
inTag = false
|
|
51
|
+
case unicode.IsSpace(r):
|
|
52
|
+
isSpace = true
|
|
53
|
+
fallthrough
|
|
54
|
+
default:
|
|
55
|
+
if !inTag && (!isSpace || (isSpace && !wasSpace)) {
|
|
56
|
+
b.WriteRune(r)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
wasSpace = isSpace
|
|
61
|
+
|
|
62
|
+
}
|
|
63
|
+
return b.String()
|
|
64
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
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 helpers
|
|
15
|
+
|
|
16
|
+
import (
|
|
17
|
+
"unicode"
|
|
18
|
+
"unicode/utf8"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
// FirstUpper returns a string with the first character as upper case.
|
|
22
|
+
func FirstUpper(s string) string {
|
|
23
|
+
if s == "" {
|
|
24
|
+
return ""
|
|
25
|
+
}
|
|
26
|
+
r, n := utf8.DecodeRuneInString(s)
|
|
27
|
+
return string(unicode.ToUpper(r)) + s[n:]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// IsWhitespace determines if the given rune is whitespace.
|
|
31
|
+
func IsWhitespace(r rune) bool {
|
|
32
|
+
return r == ' ' || r == '\t' || r == '\n' || r == '\r'
|
|
33
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"syscall/js"
|
|
5
|
+
|
|
6
|
+
engine "hugo-renderer/tpl/bookshop_engine"
|
|
7
|
+
library "hugo-renderer/tpl/bookshop_library"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
func main() {
|
|
11
|
+
c := make(chan struct{}, 0)
|
|
12
|
+
js.Global().Set("renderHugo", js.FuncOf(renderHugo))
|
|
13
|
+
js.Global().Set("loadHugoBookshopPartials", js.FuncOf(loadHugoBookshopPartials))
|
|
14
|
+
<-c
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
func renderHugo(this js.Value, args []js.Value) interface{} {
|
|
18
|
+
templateString := args[0].String()
|
|
19
|
+
propsString := args[1].String()
|
|
20
|
+
|
|
21
|
+
return engine.RenderHugo(templateString, propsString)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
func loadHugoBookshopPartials(this js.Value, args []js.Value) interface{} {
|
|
25
|
+
bookshopPartials := args[0].String()
|
|
26
|
+
|
|
27
|
+
return library.LoadHugoBookshopPartials(bookshopPartials)
|
|
28
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
package engine
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
_ "hugo-renderer/tpl/cast"
|
|
5
|
+
_ "hugo-renderer/tpl/collections" // ———————————— 🥴 TODO: `apply` NYI, relies on tpl.TemplateHandler & co. Reimplement based on bookshop_render -> createFuncMap
|
|
6
|
+
_ "hugo-renderer/tpl/compare"
|
|
7
|
+
_ "hugo-renderer/tpl/crypto"
|
|
8
|
+
|
|
9
|
+
// _ "hugo-renderer/tpl/data" // ———————————————— ⏭️ NTBI: Networking / FS / Resources
|
|
10
|
+
_ "hugo-renderer/tpl/debug"
|
|
11
|
+
_ "hugo-renderer/tpl/encoding"
|
|
12
|
+
_ "hugo-renderer/tpl/fmt"
|
|
13
|
+
|
|
14
|
+
// _ "hugo-renderer/tpl/hugo" // ———————————————— ❗ TODO: Should this be stubbed out ??
|
|
15
|
+
// _ "hugo-renderer/tpl/images" // —————————————— ❗ TODO: Should this be mocked ??
|
|
16
|
+
_ "hugo-renderer/tpl/inflect"
|
|
17
|
+
// _ "hugo-renderer/tpl/js" // —————————————————— ⏭️ NTBI: Shouldn't be done inside a component
|
|
18
|
+
// _ "hugo-renderer/tpl/lang" // ———————————————— ❗ TODO: Not sure how this would interact in the Bookshop environment
|
|
19
|
+
_ "hugo-renderer/tpl/math"
|
|
20
|
+
// _ "hugo-renderer/tpl/openapi/openapi3" // ———— ❗ TODO: Maybe a rabbit hole
|
|
21
|
+
// _ "hugo-renderer/tpl/os" // —————————————————— ⏭️ NTBI: We don't have an OS
|
|
22
|
+
_ "hugo-renderer/tpl/partials" // ——————————————— ❗ TODO: Return partials / use bookshop module partials
|
|
23
|
+
_ "hugo-renderer/tpl/path"
|
|
24
|
+
_ "hugo-renderer/tpl/reflect"
|
|
25
|
+
|
|
26
|
+
// _ "hugo-renderer/tpl/resources" // ——————————— ⏭️ NTBI: Bookshop components won't support resources
|
|
27
|
+
_ "hugo-renderer/tpl/safe" // ——————————————————— 🥴 TODO: `SanitizeURL` NYI — helpers require is too large
|
|
28
|
+
// _ "hugo-renderer/tpl/site" // ———————————————— ❗ TODO: Should this be stubbed out ??
|
|
29
|
+
_ "hugo-renderer/tpl/strings" // ———————————————— 🥴 TODO: Only the "Go" title formatting is supported
|
|
30
|
+
// _ "hugo-renderer/tpl/templates" // ——————————— ❗ TODO: Maybe a rabbit hole
|
|
31
|
+
// _ "hugo-renderer/tpl/time" // ———————————————— ❗ TODO: This relies on lang stuff that is thus far excluded
|
|
32
|
+
// _ "hugo-renderer/tpl/transform" // ——————————— ❗ TODO: Definitely a rabbit hole
|
|
33
|
+
// _ "hugo-renderer/tpl/urls" // ———————————————— ❗ TODO: Relies on pathspec that relies on FS impls
|
|
34
|
+
)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
package engine
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"fmt"
|
|
7
|
+
"html/template"
|
|
8
|
+
|
|
9
|
+
"hugo-renderer/deps"
|
|
10
|
+
"hugo-renderer/tpl/internal"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
// TODO(bookshop): DRY
|
|
14
|
+
func createFuncMap(d *deps.Deps) map[string]interface{} {
|
|
15
|
+
funcMap := template.FuncMap{}
|
|
16
|
+
|
|
17
|
+
// Merge the namespace funcs
|
|
18
|
+
for _, nsf := range internal.TemplateFuncsNamespaceRegistry {
|
|
19
|
+
ns := nsf(d)
|
|
20
|
+
if _, exists := funcMap[ns.Name]; exists {
|
|
21
|
+
panic(ns.Name + " is a duplicate template func")
|
|
22
|
+
}
|
|
23
|
+
funcMap[ns.Name] = ns.Context
|
|
24
|
+
for _, mm := range ns.MethodMappings {
|
|
25
|
+
for _, alias := range mm.Aliases {
|
|
26
|
+
if _, exists := funcMap[alias]; exists {
|
|
27
|
+
panic(alias + " is a duplicate template func")
|
|
28
|
+
}
|
|
29
|
+
funcMap[alias] = mm.Method
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return funcMap
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// RenderHugo takes a component from the browser,
|
|
38
|
+
// as well as a JSON blob of its data,
|
|
39
|
+
// and renders it.
|
|
40
|
+
func RenderHugo(templateString string, props string) interface{} {
|
|
41
|
+
var parsedProps map[string]interface{}
|
|
42
|
+
err := json.Unmarshal([]byte(props), &parsedProps)
|
|
43
|
+
if err != nil {
|
|
44
|
+
buf := bytes.NewBufferString(fmt.Sprintf("bad json unmarshal: %s", err.Error()))
|
|
45
|
+
return buf.String()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
d := deps.Deps{}
|
|
49
|
+
funcMap := createFuncMap(&d)
|
|
50
|
+
|
|
51
|
+
templ, err := template.New("").Funcs(funcMap).Parse(templateString)
|
|
52
|
+
if err != nil {
|
|
53
|
+
buf := bytes.NewBufferString(fmt.Sprintf("bad template parse: %s", err.Error()))
|
|
54
|
+
return buf.String()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
buf := bytes.NewBufferString("")
|
|
58
|
+
err = templ.Execute(buf, parsedProps)
|
|
59
|
+
if err != nil {
|
|
60
|
+
buf := bytes.NewBufferString(fmt.Sprintf("bad template render: %s", err.Error()))
|
|
61
|
+
return buf.String()
|
|
62
|
+
}
|
|
63
|
+
return buf.String()
|
|
64
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
package bookshop_library
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"fmt"
|
|
7
|
+
"html/template"
|
|
8
|
+
|
|
9
|
+
"hugo-renderer/deps"
|
|
10
|
+
"hugo-renderer/tpl/internal"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
// TODO(bookshop): DRY
|
|
14
|
+
func createFuncMap(d *deps.Deps) map[string]interface{} {
|
|
15
|
+
funcMap := template.FuncMap{}
|
|
16
|
+
|
|
17
|
+
// Merge the namespace funcs
|
|
18
|
+
for _, nsf := range internal.TemplateFuncsNamespaceRegistry {
|
|
19
|
+
ns := nsf(d)
|
|
20
|
+
if _, exists := funcMap[ns.Name]; exists {
|
|
21
|
+
panic(ns.Name + " is a duplicate template func")
|
|
22
|
+
}
|
|
23
|
+
funcMap[ns.Name] = ns.Context
|
|
24
|
+
for _, mm := range ns.MethodMappings {
|
|
25
|
+
for _, alias := range mm.Aliases {
|
|
26
|
+
if _, exists := funcMap[alias]; exists {
|
|
27
|
+
panic(alias + " is a duplicate template func")
|
|
28
|
+
}
|
|
29
|
+
funcMap[alias] = mm.Method
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return funcMap
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type bookshopPartial struct {
|
|
38
|
+
Contents string `json:"contents"`
|
|
39
|
+
ParsedContents *template.Template
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
var componentLibrary map[string]bookshopPartial
|
|
43
|
+
|
|
44
|
+
// LoadHugoBookshopPartials takes all files that the
|
|
45
|
+
// @bookshop/hugo-engine was given and stashes them away
|
|
46
|
+
// on this side of the wasm boundary, for the partials
|
|
47
|
+
// function to use.
|
|
48
|
+
func LoadHugoBookshopPartials(partials string) interface{} {
|
|
49
|
+
err := json.Unmarshal([]byte(partials), &componentLibrary)
|
|
50
|
+
if err != nil {
|
|
51
|
+
buf := bytes.NewBufferString(fmt.Sprintf("bad json unmarshal: %s", err.Error()))
|
|
52
|
+
return buf.String()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
buf := bytes.NewBufferString(fmt.Sprintf("loaded %d components", len(componentLibrary)))
|
|
56
|
+
return buf.String()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// RetrieveBookshopPartial looks for a Bookshop file
|
|
60
|
+
// and returns its template in a standard lib go html/template.
|
|
61
|
+
// TODO(bookshop): This should probably one day play
|
|
62
|
+
// nicer with the Hugo templating packages.
|
|
63
|
+
func RetrieveBookshopPartial(bookshop_key string) (*template.Template, bool) {
|
|
64
|
+
if component, ok := componentLibrary[bookshop_key]; ok {
|
|
65
|
+
if component.ParsedContents == nil {
|
|
66
|
+
d := deps.Deps{}
|
|
67
|
+
funcMap := createFuncMap(&d)
|
|
68
|
+
templ, err := template.New("").Funcs(funcMap).Parse(component.Contents)
|
|
69
|
+
if err != nil {
|
|
70
|
+
return nil, false
|
|
71
|
+
}
|
|
72
|
+
component.ParsedContents = templ
|
|
73
|
+
}
|
|
74
|
+
return component.ParsedContents, true
|
|
75
|
+
}
|
|
76
|
+
return nil, false
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// UnwrapBookshopPartial takes the dict or slice that the {{ partial "bookshop" ... }}
|
|
80
|
+
// was given, and pulls out the component / partial name that should be rendered.
|
|
81
|
+
// TODO(bookshop): Ideally our templating support gets better,
|
|
82
|
+
// and we can call the production bookshop module partials.
|
|
83
|
+
func UnwrapBookshopComponent(context interface{}) (string, interface{}) {
|
|
84
|
+
if componentData, ok := context.(map[string]interface{}); ok {
|
|
85
|
+
if component, ok := componentData["_bookshop_name"]; ok {
|
|
86
|
+
key := fmt.Sprintf("components/%s/%s.hugo.html", component.(string), component.(string))
|
|
87
|
+
return key, context
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if componentData, ok := context.([]interface{}); ok {
|
|
91
|
+
if component, ok := componentData[0].(string); ok {
|
|
92
|
+
key := fmt.Sprintf("components/%s/%s.hugo.html", component, component)
|
|
93
|
+
return key, componentData[1]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return "err_no_bookshop_name", context
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
func UnwrapBookshopPartial(context interface{}) (string, interface{}) {
|
|
100
|
+
if partialData, ok := context.([]interface{}); ok {
|
|
101
|
+
if partial, ok := partialData[0].(string); ok {
|
|
102
|
+
key := fmt.Sprintf("shared/hugo/%s.hugo.html", partial)
|
|
103
|
+
return key, partialData[1]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return "err_no_bookshop_name", context
|
|
107
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
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 cast provides template functions for data type conversions.
|
|
15
|
+
package cast
|
|
16
|
+
|
|
17
|
+
import (
|
|
18
|
+
"html/template"
|
|
19
|
+
|
|
20
|
+
_cast "github.com/spf13/cast"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
// New returns a new instance of the cast-namespaced template functions.
|
|
24
|
+
func New() *Namespace {
|
|
25
|
+
return &Namespace{}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Namespace provides template functions for the "cast" namespace.
|
|
29
|
+
type Namespace struct {
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ToInt converts the given value to an int.
|
|
33
|
+
func (ns *Namespace) ToInt(v interface{}) (int, error) {
|
|
34
|
+
v = convertTemplateToString(v)
|
|
35
|
+
return _cast.ToIntE(v)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ToString converts the given value to a string.
|
|
39
|
+
func (ns *Namespace) ToString(v interface{}) (string, error) {
|
|
40
|
+
return _cast.ToStringE(v)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ToFloat converts the given value to a float.
|
|
44
|
+
func (ns *Namespace) ToFloat(v interface{}) (float64, error) {
|
|
45
|
+
v = convertTemplateToString(v)
|
|
46
|
+
return _cast.ToFloat64E(v)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
func convertTemplateToString(v interface{}) interface{} {
|
|
50
|
+
switch vv := v.(type) {
|
|
51
|
+
case template.HTML:
|
|
52
|
+
v = string(vv)
|
|
53
|
+
case template.CSS:
|
|
54
|
+
v = string(vv)
|
|
55
|
+
case template.HTMLAttr:
|
|
56
|
+
v = string(vv)
|
|
57
|
+
case template.JS:
|
|
58
|
+
v = string(vv)
|
|
59
|
+
case template.JSStr:
|
|
60
|
+
v = string(vv)
|
|
61
|
+
}
|
|
62
|
+
return v
|
|
63
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
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 cast
|
|
15
|
+
|
|
16
|
+
import (
|
|
17
|
+
"hugo-renderer/deps"
|
|
18
|
+
"hugo-renderer/tpl/internal"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
const name = "cast"
|
|
22
|
+
|
|
23
|
+
func init() {
|
|
24
|
+
f := func(d *deps.Deps) *internal.TemplateFuncsNamespace {
|
|
25
|
+
ctx := New()
|
|
26
|
+
|
|
27
|
+
ns := &internal.TemplateFuncsNamespace{
|
|
28
|
+
Name: name,
|
|
29
|
+
Context: func(args ...interface{}) (interface{}, error) { return ctx, nil },
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
ns.AddMethodMapping(ctx.ToInt,
|
|
33
|
+
[]string{"int"},
|
|
34
|
+
[][2]string{
|
|
35
|
+
{`{{ "1234" | int | printf "%T" }}`, `int`},
|
|
36
|
+
},
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
ns.AddMethodMapping(ctx.ToString,
|
|
40
|
+
[]string{"string"},
|
|
41
|
+
[][2]string{
|
|
42
|
+
{`{{ 1234 | string | printf "%T" }}`, `string`},
|
|
43
|
+
},
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
ns.AddMethodMapping(ctx.ToFloat,
|
|
47
|
+
[]string{"float"},
|
|
48
|
+
[][2]string{
|
|
49
|
+
{`{{ "1234" | float | printf "%T" }}`, `float64`},
|
|
50
|
+
},
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return ns
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
internal.AddTemplateFuncsNamespace(f)
|
|
57
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Copyright 2018 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 collections
|
|
15
|
+
|
|
16
|
+
import (
|
|
17
|
+
"errors"
|
|
18
|
+
|
|
19
|
+
"github.com/gohugoio/hugo/common/collections"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
// Append appends the arguments up to the last one to the slice in the last argument.
|
|
23
|
+
// This construct allows template constructs like this:
|
|
24
|
+
// {{ $pages = $pages | append $p2 $p1 }}
|
|
25
|
+
// Note that with 2 arguments where both are slices of the same type,
|
|
26
|
+
// the first slice will be appended to the second:
|
|
27
|
+
// {{ $pages = $pages | append .Site.RegularPages }}
|
|
28
|
+
func (ns *Namespace) Append(args ...interface{}) (interface{}, error) {
|
|
29
|
+
if len(args) < 2 {
|
|
30
|
+
return nil, errors.New("need at least 2 arguments to append")
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
to := args[len(args)-1]
|
|
34
|
+
from := args[:len(args)-1]
|
|
35
|
+
|
|
36
|
+
return collections.Append(to, from...)
|
|
37
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
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 collections
|
|
15
|
+
|
|
16
|
+
import (
|
|
17
|
+
"errors"
|
|
18
|
+
"fmt"
|
|
19
|
+
"reflect"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
// Apply takes a map, array, or slice and returns a new slice with the function fname applied over it.
|
|
23
|
+
func (ns *Namespace) Apply(seq interface{}, fname string, args ...interface{}) (interface{}, error) {
|
|
24
|
+
if seq == nil {
|
|
25
|
+
return make([]interface{}, 0), nil
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if fname == "apply" {
|
|
29
|
+
return nil, errors.New("can't apply myself (no turtles allowed)")
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
seqv := reflect.ValueOf(seq)
|
|
33
|
+
seqv, isNil := indirect(seqv)
|
|
34
|
+
if isNil {
|
|
35
|
+
return nil, errors.New("can't iterate over a nil value")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
fnv, found := ns.lookupFunc(fname)
|
|
39
|
+
if !found {
|
|
40
|
+
return nil, errors.New("can't find function " + fname)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// fnv := reflect.ValueOf(fn)
|
|
44
|
+
|
|
45
|
+
switch seqv.Kind() {
|
|
46
|
+
case reflect.Array, reflect.Slice:
|
|
47
|
+
r := make([]interface{}, seqv.Len())
|
|
48
|
+
for i := 0; i < seqv.Len(); i++ {
|
|
49
|
+
vv := seqv.Index(i)
|
|
50
|
+
|
|
51
|
+
vvv, err := applyFnToThis(fnv, vv, args...)
|
|
52
|
+
if err != nil {
|
|
53
|
+
return nil, err
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
r[i] = vvv.Interface()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return r, nil
|
|
60
|
+
default:
|
|
61
|
+
return nil, fmt.Errorf("can't apply over %v", seq)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
func applyFnToThis(fn, this reflect.Value, args ...interface{}) (reflect.Value, error) {
|
|
66
|
+
n := make([]reflect.Value, len(args))
|
|
67
|
+
for i, arg := range args {
|
|
68
|
+
if arg == "." {
|
|
69
|
+
n[i] = this
|
|
70
|
+
} else {
|
|
71
|
+
n[i] = reflect.ValueOf(arg)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
num := fn.Type().NumIn()
|
|
76
|
+
|
|
77
|
+
if fn.Type().IsVariadic() {
|
|
78
|
+
num--
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// TODO(bep) see #1098 - also see template_tests.go
|
|
82
|
+
/*if len(args) < num {
|
|
83
|
+
return reflect.ValueOf(nil), errors.New("Too few arguments")
|
|
84
|
+
} else if len(args) > num {
|
|
85
|
+
return reflect.ValueOf(nil), errors.New("Too many arguments")
|
|
86
|
+
}*/
|
|
87
|
+
|
|
88
|
+
for i := 0; i < num; i++ {
|
|
89
|
+
// AssignableTo reports whether xt is assignable to type targ.
|
|
90
|
+
if xt, targ := n[i].Type(), fn.Type().In(i); !xt.AssignableTo(targ) {
|
|
91
|
+
return reflect.ValueOf(nil), errors.New("called apply using " + xt.String() + " as type " + targ.String())
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
res := fn.Call(n)
|
|
96
|
+
|
|
97
|
+
if len(res) == 1 || res[1].IsNil() {
|
|
98
|
+
return res[0], nil
|
|
99
|
+
}
|
|
100
|
+
return reflect.ValueOf(nil), res[1].Interface().(error)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
func (ns *Namespace) lookupFunc(fname string) (reflect.Value, bool) {
|
|
104
|
+
// TODO(bookshop): NYI
|
|
105
|
+
return reflect.Value{}, false
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// indirect is borrowed from the Go stdlib: 'text/template/exec.go'
|
|
109
|
+
func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
|
|
110
|
+
for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
|
|
111
|
+
if v.IsNil() {
|
|
112
|
+
return v, true
|
|
113
|
+
}
|
|
114
|
+
if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
|
|
115
|
+
break
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return v, false
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
func indirectInterface(v reflect.Value) (rv reflect.Value, isNil bool) {
|
|
122
|
+
for ; v.Kind() == reflect.Interface; v = v.Elem() {
|
|
123
|
+
if v.IsNil() {
|
|
124
|
+
return v, true
|
|
125
|
+
}
|
|
126
|
+
if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
|
|
127
|
+
break
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return v, false
|
|
131
|
+
}
|