@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.
Files changed (75) hide show
  1. package/.nyc_output/50eb2ebd-c797-4d3f-8df7-a707c1b211eb.json +1 -0
  2. package/.nyc_output/86f85916-4f58-43da-8c32-c1ec29ca783e.json +1 -0
  3. package/.nyc_output/a27680c8-8858-4ede-921d-6e6617f42a0b.json +1 -0
  4. package/.nyc_output/b5f86652-cbe8-44f9-a36c-d5721dfe5bbe.json +1 -0
  5. package/.nyc_output/ecca87c1-5774-4962-864a-2e4537aa9915.json +1 -0
  6. package/.nyc_output/processinfo/50eb2ebd-c797-4d3f-8df7-a707c1b211eb.json +1 -0
  7. package/.nyc_output/processinfo/86f85916-4f58-43da-8c32-c1ec29ca783e.json +1 -0
  8. package/.nyc_output/processinfo/a27680c8-8858-4ede-921d-6e6617f42a0b.json +1 -0
  9. package/.nyc_output/processinfo/b5f86652-cbe8-44f9-a36c-d5721dfe5bbe.json +1 -0
  10. package/.nyc_output/processinfo/ecca87c1-5774-4962-864a-2e4537aa9915.json +1 -0
  11. package/.nyc_output/processinfo/index.json +1 -0
  12. package/build.js +1 -0
  13. package/hugo-renderer/build.sh +16 -0
  14. package/hugo-renderer/deps/bookshop_modified_deps.go +6 -0
  15. package/hugo-renderer/go.mod +55 -0
  16. package/hugo-renderer/go.sum +886 -0
  17. package/hugo-renderer/helpers/bookshop_modified_content.go +64 -0
  18. package/hugo-renderer/helpers/bookshop_modified_general.go +33 -0
  19. package/hugo-renderer/hugo_renderer.wasm +0 -0
  20. package/hugo-renderer/main.go +28 -0
  21. package/hugo-renderer/tpl/bookshop_engine/bookshop_func_importer.go +34 -0
  22. package/hugo-renderer/tpl/bookshop_engine/bookshop_render.go +64 -0
  23. package/hugo-renderer/tpl/bookshop_library/bookshop_components.go +107 -0
  24. package/hugo-renderer/tpl/cast/cast.go +63 -0
  25. package/hugo-renderer/tpl/cast/init.go +57 -0
  26. package/hugo-renderer/tpl/collections/append.go +37 -0
  27. package/hugo-renderer/tpl/collections/apply.go +131 -0
  28. package/hugo-renderer/tpl/collections/collections.go +767 -0
  29. package/hugo-renderer/tpl/collections/complement.go +55 -0
  30. package/hugo-renderer/tpl/collections/index.go +133 -0
  31. package/hugo-renderer/tpl/collections/init.go +214 -0
  32. package/hugo-renderer/tpl/collections/merge.go +126 -0
  33. package/hugo-renderer/tpl/collections/reflect_helpers.go +216 -0
  34. package/hugo-renderer/tpl/collections/sort.go +183 -0
  35. package/hugo-renderer/tpl/collections/symdiff.go +68 -0
  36. package/hugo-renderer/tpl/collections/where.go +517 -0
  37. package/hugo-renderer/tpl/compare/compare.go +324 -0
  38. package/hugo-renderer/tpl/compare/init.go +85 -0
  39. package/hugo-renderer/tpl/crypto/crypto.go +109 -0
  40. package/hugo-renderer/tpl/crypto/init.go +65 -0
  41. package/hugo-renderer/tpl/debug/debug.go +40 -0
  42. package/hugo-renderer/tpl/debug/init.go +45 -0
  43. package/hugo-renderer/tpl/encoding/encoding.go +90 -0
  44. package/hugo-renderer/tpl/encoding/init.go +59 -0
  45. package/hugo-renderer/tpl/fmt/fmt.go +64 -0
  46. package/hugo-renderer/tpl/fmt/init.go +77 -0
  47. package/hugo-renderer/tpl/inflect/inflect.go +79 -0
  48. package/hugo-renderer/tpl/inflect/init.go +60 -0
  49. package/hugo-renderer/tpl/internal/bookshop_modified_templatefuncsRegistry.go +88 -0
  50. package/hugo-renderer/tpl/math/init.go +134 -0
  51. package/hugo-renderer/tpl/math/math.go +164 -0
  52. package/hugo-renderer/tpl/math/round.go +61 -0
  53. package/hugo-renderer/tpl/partials/init.go +55 -0
  54. package/hugo-renderer/tpl/partials/partials.go +86 -0
  55. package/hugo-renderer/tpl/path/init.go +60 -0
  56. package/hugo-renderer/tpl/path/path.go +159 -0
  57. package/hugo-renderer/tpl/reflect/init.go +51 -0
  58. package/hugo-renderer/tpl/reflect/reflect.go +36 -0
  59. package/hugo-renderer/tpl/safe/init.go +80 -0
  60. package/hugo-renderer/tpl/safe/safe.go +72 -0
  61. package/hugo-renderer/tpl/strings/init.go +229 -0
  62. package/hugo-renderer/tpl/strings/regexp.go +125 -0
  63. package/hugo-renderer/tpl/strings/strings.go +500 -0
  64. package/hugo-renderer/tpl/strings/truncate.go +157 -0
  65. package/hugo-renderer/wasm_exec.js +636 -0
  66. package/lib/builder.js +21 -0
  67. package/lib/engine.js +134 -0
  68. package/lib/engine.test.js +5 -0
  69. package/lib/hugoIdentifierParser.js +324 -0
  70. package/lib/hugoIdentifierParser.test.js +72 -0
  71. package/lib/translateTextTemplate.js +58 -0
  72. package/lib/translateTextTemplate.test.js +39 -0
  73. package/main.js +1 -0
  74. package/main.test.js +5 -0
  75. 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
+ }