mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
tpl/urls: Add JoinPath template function
See https://pkg.go.dev/net/url#JoinPath Closes #9694
This commit is contained in:
parent
03cb38e6c6
commit
5b3e165bad
4 changed files with 102 additions and 0 deletions
24
docs/content/en/functions/urls.JoinPath.md
Normal file
24
docs/content/en/functions/urls.JoinPath.md
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
---
|
||||||
|
title: urls.JoinPath
|
||||||
|
description: Joins the provided elements into a URL string and cleans the result of any ./ or ../ elements.
|
||||||
|
categories: [functions]
|
||||||
|
menu:
|
||||||
|
docs:
|
||||||
|
parent: functions
|
||||||
|
keywords: [urls,path,join]
|
||||||
|
signature: ["urls.JoinPath ELEMENT..."]
|
||||||
|
---
|
||||||
|
|
||||||
|
```go-html-template
|
||||||
|
{{ urls.JoinPath "" }} → "/"
|
||||||
|
{{ urls.JoinPath "a" }} → "a"
|
||||||
|
{{ urls.JoinPath "a" "b" }} → "a/b"
|
||||||
|
{{ urls.JoinPath "/a" "b" }} → "/a/b"
|
||||||
|
{{ urls.JoinPath "https://example.org" "b" }} → "https://example.org/b"
|
||||||
|
|
||||||
|
{{ urls.JoinPath (slice "a" "b") }} → "a/b"
|
||||||
|
```
|
||||||
|
|
||||||
|
Unlike the [`path.Join`] function, `urls.JoinPath` retains consecutive leading slashes.
|
||||||
|
|
||||||
|
[`path.Join`]: /functions/path.join/
|
|
@ -68,6 +68,14 @@ func init() {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ns.AddMethodMapping(ctx.JoinPath,
|
||||||
|
nil,
|
||||||
|
[][2]string{
|
||||||
|
{`{{ urls.JoinPath "https://example.org" "foo" }}`, `https://example.org/foo`},
|
||||||
|
{`{{ urls.JoinPath (slice "a" "b") }}`, `a/b`},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return ns
|
return ns
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -185,3 +185,38 @@ func (ns *Namespace) AbsLangURL(s any) (template.HTML, error) {
|
||||||
|
|
||||||
return template.HTML(ns.deps.PathSpec.AbsURL(ss, !ns.multihost)), nil
|
return template.HTML(ns.deps.PathSpec.AbsURL(ss, !ns.multihost)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JoinPath joins the provided elements into a URL string and cleans the result
|
||||||
|
// of any ./ or ../ elements.
|
||||||
|
func (ns *Namespace) JoinPath(elements ...any) (string, error) {
|
||||||
|
|
||||||
|
var selements []string
|
||||||
|
for _, e := range elements {
|
||||||
|
switch v := e.(type) {
|
||||||
|
case []string:
|
||||||
|
for _, e := range v {
|
||||||
|
selements = append(selements, e)
|
||||||
|
}
|
||||||
|
case []any:
|
||||||
|
for _, e := range v {
|
||||||
|
se, err := cast.ToStringE(e)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
selements = append(selements, se)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
se, err := cast.ToStringE(e)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
selements = append(selements, se)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := url.JoinPath(selements[0], selements[1:]...)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
|
@ -69,3 +69,38 @@ func TestParse(t *testing.T) {
|
||||||
qt.CmpEquals(hqt.DeepAllowUnexported(&url.URL{}, url.Userinfo{})), test.expect)
|
qt.CmpEquals(hqt.DeepAllowUnexported(&url.URL{}, url.Userinfo{})), test.expect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestJoinPath(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
c := qt.New(t)
|
||||||
|
|
||||||
|
for _, test := range []struct {
|
||||||
|
elements any
|
||||||
|
expect any
|
||||||
|
}{
|
||||||
|
{"", `/`},
|
||||||
|
{"a", `a`},
|
||||||
|
{"/a/b", `/a/b`},
|
||||||
|
{"./../a/b", `a/b`},
|
||||||
|
{[]any{""}, `/`},
|
||||||
|
{[]any{"a"}, `a`},
|
||||||
|
{[]any{"/a", "b"}, `/a/b`},
|
||||||
|
{[]any{".", "..", "/a", "b"}, `a/b`},
|
||||||
|
{[]any{"https://example.org", "a"}, `https://example.org/a`},
|
||||||
|
{[]any{nil}, `/`},
|
||||||
|
// errors
|
||||||
|
{tstNoStringer{}, false},
|
||||||
|
{[]any{tstNoStringer{}}, false},
|
||||||
|
} {
|
||||||
|
|
||||||
|
result, err := ns.JoinPath(test.elements)
|
||||||
|
|
||||||
|
if b, ok := test.expect.(bool); ok && !b {
|
||||||
|
c.Assert(err, qt.Not(qt.IsNil))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Assert(err, qt.IsNil)
|
||||||
|
c.Assert(result, qt.Equals, test.expect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue