diff --git a/docs/content/en/functions/path.Base.md b/docs/content/en/functions/path.Base.md index c44518a72..a6bfc2bd5 100644 --- a/docs/content/en/functions/path.Base.md +++ b/docs/content/en/functions/path.Base.md @@ -12,7 +12,7 @@ keywords: [path, base] signature: ["path.Base PATH"] workson: [] hugoversion: "0.40" -relatedfuncs: [path.Dir, path.Ext, path.Split] +relatedfuncs: [path.BaseName, path.Clean, path.Dir, path.Ext, path.Join, path.Split] deprecated: false --- diff --git a/docs/content/en/functions/path.BaseName.md b/docs/content/en/functions/path.BaseName.md new file mode 100644 index 000000000..6a5b9f0a0 --- /dev/null +++ b/docs/content/en/functions/path.BaseName.md @@ -0,0 +1,24 @@ +--- +title: path.BaseName +description: BaseName returns the last element of a path, removing the extension if present. +date: 2022-06-04 +categories: [functions] +menu: + docs: + parent: "functions" +keywords: [path, base] +signature: ["path.BaseName PATH"] +relatedfuncs: [path.Base, path.Clean, path.Dir, path.Ext, path.Join, path.Split] +deprecated: false +--- + +If `PATH` is empty, `.` is returned. + +**Note:** On Windows, `PATH` is converted to slash (`/`) separators. + +```go-html-template +{{ path.BaseName "a/news.html" }} → "news" +{{ path.BaseName "news.html" }} → "news" +{{ path.BaseName "a/b/c" }} → "c" +{{ path.BaseName "/x/y/z/" }} → "z" +``` diff --git a/docs/content/en/functions/path.Clean.md b/docs/content/en/functions/path.Clean.md index 03ffbd836..852de65fd 100644 --- a/docs/content/en/functions/path.Clean.md +++ b/docs/content/en/functions/path.Clean.md @@ -8,8 +8,9 @@ categories: [functions] menu: docs: parent: "functions" -keywords: [path] +keywords: [path, clean] signature: ["path.Clean PATH"] +relatedfuncs: [path.Base, path.BaseName, path.Dir, path.Ext, path.Join, path.Split] --- `path.Clean` replaces path separators with slashes (`/`) and removes extraneous separators, including trailing separators. diff --git a/docs/content/en/functions/path.Dir.md b/docs/content/en/functions/path.Dir.md index 851a3dc83..161a0daa8 100644 --- a/docs/content/en/functions/path.Dir.md +++ b/docs/content/en/functions/path.Dir.md @@ -12,7 +12,7 @@ keywords: [path, dir] signature: ["path.Dir PATH"] workson: [] hugoversion: "0.40" -relatedfuncs: [path.Base, path.Ext, path.Split] +relatedfuncs: [path.Base, path.BaseName, path.Clean, path.Ext, path.Join, path.Split] deprecated: false --- diff --git a/docs/content/en/functions/path.Ext.md b/docs/content/en/functions/path.Ext.md index 73db7ab38..8c6fe907c 100644 --- a/docs/content/en/functions/path.Ext.md +++ b/docs/content/en/functions/path.Ext.md @@ -12,7 +12,7 @@ keywords: [path, ext, extension] signature: ["path.Ext PATH"] workson: [] hugoversion: "0.40" -relatedfuncs: [path.Base, path.Dir, path.Split] +relatedfuncs: [path.Base, path.BaseName, path.Clean, path.Dir, path.Join, path.Split] deprecated: false --- diff --git a/docs/content/en/functions/path.Join.md b/docs/content/en/functions/path.Join.md index 96ce86322..579e8667e 100644 --- a/docs/content/en/functions/path.Join.md +++ b/docs/content/en/functions/path.Join.md @@ -12,7 +12,7 @@ keywords: [path, join] signature: ["path.Join ELEMENT..."] workson: [] hugoversion: "0.39" -relatedfuncs: [path.Split] +relatedfuncs: [path.Base, path.BaseName, path.Clean, path.Dir, path.Ext, path.Split] deprecated: false --- diff --git a/docs/content/en/functions/path.Split.md b/docs/content/en/functions/path.Split.md index 9a09fbe4a..7737b77d3 100644 --- a/docs/content/en/functions/path.Split.md +++ b/docs/content/en/functions/path.Split.md @@ -12,7 +12,7 @@ keywords: [path, split] signature: ["path.Split PATH"] workson: [] hugoversion: "0.39" -relatedfuncs: [path.Split] +relatedfuncs: [path.Base, path.BaseName, path.Clean, path.Dir, path.Ext, path.Join] deprecated: false --- diff --git a/tpl/path/path.go b/tpl/path/path.go index d334dd906..378b97e03 100644 --- a/tpl/path/path.go +++ b/tpl/path/path.go @@ -18,6 +18,7 @@ import ( "fmt" _path "path" "path/filepath" + "strings" "github.com/gohugoio/hugo/deps" "github.com/spf13/cast" @@ -94,6 +95,21 @@ func (ns *Namespace) Base(path any) (string, error) { return _path.Base(spath), nil } +// BaseName returns the last element of path, removing the extension if present. +// Trailing slashes are removed before extracting the last element. +// If the path is empty, Base returns ".". +// If the path consists entirely of slashes, Base returns "/". +// The input path is passed into filepath.ToSlash converting any Windows slashes +// to forward slashes. +func (ns *Namespace) BaseName(path any) (string, error) { + spath, err := cast.ToStringE(path) + if err != nil { + return "", err + } + spath = filepath.ToSlash(spath) + return strings.TrimSuffix(_path.Base(spath), _path.Ext(spath)), nil +} + // Split splits path immediately following the final slash, // separating it into a directory and file name component. // If there is no slash in path, Split returns an empty dir and diff --git a/tpl/path/path_test.go b/tpl/path/path_test.go index c9f8469e7..599d8367a 100644 --- a/tpl/path/path_test.go +++ b/tpl/path/path_test.go @@ -56,6 +56,36 @@ func TestBase(t *testing.T) { } } +func TestBaseName(t *testing.T) { + t.Parallel() + c := qt.New(t) + + for _, test := range []struct { + path any + expect any + }{ + {filepath.FromSlash(`foo/bar.txt`), `bar`}, + {filepath.FromSlash(`foo/bar/txt `), `txt `}, + {filepath.FromSlash(`foo/bar.t`), `bar`}, + {`foo.bar.txt`, `foo.bar`}, + {`.x`, ``}, + {``, `.`}, + // errors + {tstNoStringer{}, false}, + } { + + result, err := ns.BaseName(test.path) + + 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) + } +} + func TestDir(t *testing.T) { t.Parallel() c := qt.New(t)