mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-29 22:29:56 +02:00
Reimplement archetypes
The old implementation had some issues, mostly related to the context (e.g. name, file paths) passed to the template. This new implementation is using the exact same code path for evaluating the pages as in a regular build. This also makes it more robust and easier to reason about in a multilingual setup. Now, if you are explicit about the target path, Hugo will now always pick the correct mount and language: ```bash hugo new content/en/posts/my-first-post.md ``` Fixes #9032 Fixes #7589 Fixes #9043 Fixes #9046 Fixes #9047
This commit is contained in:
@@ -16,6 +16,7 @@ package glob
|
||||
import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -23,46 +24,100 @@ import (
|
||||
"github.com/gobwas/glob/syntax"
|
||||
)
|
||||
|
||||
var (
|
||||
isWindows = runtime.GOOS == "windows"
|
||||
defaultGlobCache = &globCache{
|
||||
isCaseSensitive: false,
|
||||
isWindows: isWindows,
|
||||
cache: make(map[string]globErr),
|
||||
}
|
||||
|
||||
filenamesGlobCache = &globCache{
|
||||
isCaseSensitive: true, // TODO(bep) bench
|
||||
isWindows: isWindows,
|
||||
cache: make(map[string]globErr),
|
||||
}
|
||||
)
|
||||
|
||||
type globErr struct {
|
||||
glob glob.Glob
|
||||
err error
|
||||
}
|
||||
|
||||
var (
|
||||
globCache = make(map[string]globErr)
|
||||
globMu sync.RWMutex
|
||||
)
|
||||
type globCache struct {
|
||||
// Config
|
||||
isCaseSensitive bool
|
||||
isWindows bool
|
||||
|
||||
type caseInsensitiveGlob struct {
|
||||
g glob.Glob
|
||||
// Cache
|
||||
sync.RWMutex
|
||||
cache map[string]globErr
|
||||
}
|
||||
|
||||
func (g caseInsensitiveGlob) Match(s string) bool {
|
||||
return g.g.Match(strings.ToLower(s))
|
||||
|
||||
}
|
||||
func GetGlob(pattern string) (glob.Glob, error) {
|
||||
func (gc *globCache) GetGlob(pattern string) (glob.Glob, error) {
|
||||
var eg globErr
|
||||
|
||||
globMu.RLock()
|
||||
gc.RLock()
|
||||
var found bool
|
||||
eg, found = globCache[pattern]
|
||||
globMu.RUnlock()
|
||||
eg, found = gc.cache[pattern]
|
||||
gc.RUnlock()
|
||||
if found {
|
||||
return eg.glob, eg.err
|
||||
}
|
||||
|
||||
var g glob.Glob
|
||||
var err error
|
||||
g, err := glob.Compile(strings.ToLower(pattern), '/')
|
||||
eg = globErr{caseInsensitiveGlob{g: g}, err}
|
||||
|
||||
globMu.Lock()
|
||||
globCache[pattern] = eg
|
||||
globMu.Unlock()
|
||||
pattern = filepath.ToSlash(pattern)
|
||||
|
||||
if gc.isCaseSensitive {
|
||||
g, err = glob.Compile(pattern, '/')
|
||||
} else {
|
||||
g, err = glob.Compile(strings.ToLower(pattern), '/')
|
||||
|
||||
}
|
||||
|
||||
eg = globErr{
|
||||
globDecorator{
|
||||
g: g,
|
||||
isCaseSensitive: gc.isCaseSensitive,
|
||||
isWindows: gc.isWindows},
|
||||
err,
|
||||
}
|
||||
|
||||
gc.Lock()
|
||||
gc.cache[pattern] = eg
|
||||
gc.Unlock()
|
||||
|
||||
return eg.glob, eg.err
|
||||
}
|
||||
|
||||
type globDecorator struct {
|
||||
// Whether both pattern and the strings to match will be matched
|
||||
// by their original case.
|
||||
isCaseSensitive bool
|
||||
|
||||
// On Windows we may get filenames with Windows slashes to match,
|
||||
// which wee need to normalize.
|
||||
isWindows bool
|
||||
|
||||
g glob.Glob
|
||||
}
|
||||
|
||||
func (g globDecorator) Match(s string) bool {
|
||||
if g.isWindows {
|
||||
s = filepath.ToSlash(s)
|
||||
}
|
||||
if !g.isCaseSensitive {
|
||||
s = strings.ToLower(s)
|
||||
}
|
||||
return g.g.Match(s)
|
||||
}
|
||||
|
||||
func GetGlob(pattern string) (glob.Glob, error) {
|
||||
return defaultGlobCache.GetGlob(pattern)
|
||||
}
|
||||
|
||||
func NormalizePath(p string) string {
|
||||
return strings.Trim(path.Clean(filepath.ToSlash(strings.ToLower(p))), "/.")
|
||||
}
|
||||
@@ -106,3 +161,78 @@ func HasGlobChar(s string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type FilenameFilter struct {
|
||||
shouldInclude func(filename string) bool
|
||||
inclusions []glob.Glob
|
||||
exclusions []glob.Glob
|
||||
isWindows bool
|
||||
}
|
||||
|
||||
// NewFilenameFilter creates a new Glob where the Match method will
|
||||
// return true if the file should be exluded.
|
||||
// Note that the inclusions will be checked first.
|
||||
func NewFilenameFilter(inclusions, exclusions []string) (*FilenameFilter, error) {
|
||||
filter := &FilenameFilter{isWindows: isWindows}
|
||||
|
||||
for _, include := range inclusions {
|
||||
g, err := filenamesGlobCache.GetGlob(filepath.FromSlash(include))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filter.inclusions = append(filter.inclusions, g)
|
||||
}
|
||||
for _, exclude := range exclusions {
|
||||
g, err := filenamesGlobCache.GetGlob(filepath.FromSlash(exclude))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filter.exclusions = append(filter.exclusions, g)
|
||||
}
|
||||
|
||||
return filter, nil
|
||||
}
|
||||
|
||||
// NewFilenameFilterForInclusionFunc create a new filter using the provided inclusion func.
|
||||
func NewFilenameFilterForInclusionFunc(shouldInclude func(filename string) bool) *FilenameFilter {
|
||||
return &FilenameFilter{shouldInclude: shouldInclude, isWindows: isWindows}
|
||||
}
|
||||
|
||||
// Match returns whether filename should be included.
|
||||
func (f *FilenameFilter) Match(filename string) bool {
|
||||
if f == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if f.shouldInclude != nil {
|
||||
if f.shouldInclude(filename) {
|
||||
return true
|
||||
}
|
||||
if f.isWindows {
|
||||
// The Glob matchers below handles this by themselves,
|
||||
// for the shouldInclude we need to take some extra steps
|
||||
// to make this robust.
|
||||
winFilename := filepath.FromSlash(filename)
|
||||
if filename != winFilename {
|
||||
if f.shouldInclude(winFilename) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for _, inclusion := range f.inclusions {
|
||||
if inclusion.Match(filename) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, exclusion := range f.exclusions {
|
||||
if exclusion.Match(filename) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return f.inclusions == nil && f.shouldInclude == nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user