Skip to content

Commit

Permalink
Create pages from _content.gotmpl
Browse files Browse the repository at this point in the history
  • Loading branch information
bep committed May 11, 2024
1 parent 70c13f4 commit 779e246
Show file tree
Hide file tree
Showing 56 changed files with 1,841 additions and 367 deletions.
7 changes: 4 additions & 3 deletions commands/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,13 @@ import (
"github.com/fsnotify/fsnotify"
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/media"

"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/common/urls"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/hugolib"
"github.com/gohugoio/hugo/hugolib/filesystems"
"github.com/gohugoio/hugo/livereload"
Expand Down Expand Up @@ -1193,11 +1194,11 @@ func pickOneWriteOrCreatePath(events []fsnotify.Event) string {

for _, ev := range events {
if ev.Op&fsnotify.Write == fsnotify.Write || ev.Op&fsnotify.Create == fsnotify.Create {
if files.IsIndexContentFile(ev.Name) {
if media.IsIndexContentFile(ev.Name) {
return ev.Name
}

if files.IsContentFile(ev.Name) {
if media.IsContentFile(ev.Name) {
name = ev.Name
}

Expand Down
14 changes: 14 additions & 0 deletions common/maps/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ func NewCache[K comparable, T any]() *Cache[K, T] {
}

// Delete deletes the given key from the cache.
// If c is nil, this method is a no-op.
func (c *Cache[K, T]) Get(key K) (T, bool) {
if c == nil {
var zero T
return zero, false
}
c.RLock()
v, found := c.m[key]
c.RUnlock()
Expand Down Expand Up @@ -60,6 +65,15 @@ func (c *Cache[K, T]) Set(key K, value T) {
c.Unlock()
}

// ForEeach calls the given function for each key/value pair in the cache.
func (c *Cache[K, T]) ForEeach(f func(K, T)) {
c.RLock()
defer c.RUnlock()
for k, v := range c.m {
f(k, v)
}
}

// SliceCache is a simple thread safe cache backed by a map.
type SliceCache[T any] struct {
m map[string][]T
Expand Down
53 changes: 33 additions & 20 deletions common/paths/pathparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ import (
"github.com/gohugoio/hugo/identity"
)

var defaultPathParser PathParser
var defaultPathParser PathParser = PathParser{
IsContentExt: func(ext string) bool {
return false
},
}

// PathParser parses a path into a Path.
type PathParser struct {
Expand All @@ -34,9 +38,13 @@ type PathParser struct {

// Reports whether the given language is disabled.
IsLangDisabled func(string) bool

// Reports whether the given ext is a content file.
IsContentExt func(string) bool
}

// Parse parses component c with path s into Path using the default path parser.
// Only used in tests. TODO1
func Parse(c, s string) *Path {
return defaultPathParser.Parse(c, s)
}
Expand Down Expand Up @@ -108,7 +116,6 @@ func (pp *PathParser) parse(component, s string) (*Path, error) {
var err error
// Preserve the original case for titles etc.
p.unnormalized, err = pp.doParse(component, s, pp.newPath(component))

if err != nil {
return nil, err
}
Expand Down Expand Up @@ -195,23 +202,26 @@ func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) {
}
}

isContentComponent := p.component == files.ComponentFolderContent || p.component == files.ComponentFolderArchetypes
isContent := isContentComponent && files.IsContentExt(p.Ext())

if isContent {
if len(p.identifiers) > 0 {
isContentComponent := p.component == files.ComponentFolderContent || p.component == files.ComponentFolderArchetypes
isContent := isContentComponent && pp.IsContentExt(p.Ext())
id := p.identifiers[len(p.identifiers)-1]
b := p.s[p.posContainerHigh : id.Low-1]
switch b {
case "index":
p.bundleType = PathTypeLeaf
case "_index":
p.bundleType = PathTypeBranch
default:
p.bundleType = PathTypeContentSingle
}
if isContent {
switch b {
case "index":
p.bundleType = PathTypeLeaf
case "_index":
p.bundleType = PathTypeBranch
default:
p.bundleType = PathTypeContentSingle
}

if slashCount == 2 && p.IsLeafBundle() {
p.posSectionHigh = 0
if slashCount == 2 && p.IsLeafBundle() {
p.posSectionHigh = 0
}
} else if b == files.NameContentData && files.IsContentDataExt(p.Ext()) {
p.bundleType = PathTypeContentData
}
}

Expand Down Expand Up @@ -246,6 +256,9 @@ const (

// Branch bundles, e.g. /blog/_index.md
PathTypeBranch

// Content data file, _content.gotmpl.
PathTypeContentData
)

type Path struct {
Expand Down Expand Up @@ -521,10 +534,6 @@ func (p *Path) Identifiers() []string {
return ids
}

func (p *Path) IsHTML() bool {
return files.IsHTML(p.Ext())
}

func (p *Path) BundleType() PathType {
return p.bundleType
}
Expand All @@ -541,6 +550,10 @@ func (p *Path) IsLeafBundle() bool {
return p.bundleType == PathTypeLeaf
}

func (p *Path) IsContentData() bool {
return p.bundleType == PathTypeContentData
}

func (p Path) ForBundleType(t PathType) *Path {
p.bundleType = t
return &p
Expand Down
19 changes: 19 additions & 0 deletions common/paths/pathparser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ var testParser = &PathParser{
"no": 0,
"en": 1,
},
IsContentExt: func(ext string) bool {
return ext == "md"
},
}

func TestParse(t *testing.T) {
Expand Down Expand Up @@ -333,6 +336,22 @@ func TestParse(t *testing.T) {
c.Assert(p.Path(), qt.Equals, "/a/b/c.txt")
},
},
{
"Content data file gotmpl",
"/a/b/_content.gotmpl",
func(c *qt.C, p *Path) {
c.Assert(p.Path(), qt.Equals, "/a/b/_content.gotmpl")
c.Assert(p.Ext(), qt.Equals, "gotmpl")
c.Assert(p.IsContentData(), qt.IsTrue)
},
},
{
"Content data file yaml",
"/a/b/_content.yaml",
func(c *qt.C, p *Path) {
c.Assert(p.IsContentData(), qt.IsFalse)
},
},
}
for _, test := range tests {
c.Run(test.name, func(c *qt.C) {
Expand Down
2 changes: 1 addition & 1 deletion config/allconfig/allconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,7 @@ func (c *Configs) Init() error {
c.Languages = languages
c.LanguagesDefaultFirst = languagesDefaultFirst

c.ContentPathParser = &paths.PathParser{LanguageIndex: languagesDefaultFirst.AsIndexSet(), IsLangDisabled: c.Base.IsLangDisabled}
c.ContentPathParser = &paths.PathParser{LanguageIndex: languagesDefaultFirst.AsIndexSet(), IsLangDisabled: c.Base.IsLangDisabled, IsContentExt: media.IsContentSuffix}

c.configLangs = make([]config.AllProvider, len(c.Languages))
for i, l := range c.LanguagesDefaultFirst {
Expand Down
5 changes: 2 additions & 3 deletions create/content.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,12 @@ import (
"strings"

"github.com/gohugoio/hugo/hugofs/glob"
"github.com/gohugoio/hugo/media"

"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/hstrings"
"github.com/gohugoio/hugo/common/paths"

"github.com/gohugoio/hugo/hugofs/files"

"github.com/gohugoio/hugo/hugofs"

"github.com/gohugoio/hugo/helpers"
Expand Down Expand Up @@ -98,7 +97,7 @@ func NewContent(h *hugolib.HugoSites, kind, targetPath string, force bool) error
return "", fmt.Errorf("failed to resolve %q to an archetype template", targetPath)
}

if !files.IsContentFile(b.targetPath) {
if !media.IsContentFile(b.targetPath) {
return "", fmt.Errorf("target path %q is not a known content format", b.targetPath)
}

Expand Down
56 changes: 7 additions & 49 deletions hugofs/files/classifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,57 +29,13 @@ const (
FilenameHugoStatsJSON = "hugo_stats.json"
)

var (
// This should be the only list of valid extensions for content files.
contentFileExtensions = []string{
"html", "htm",
"mdown", "markdown", "md",
"asciidoc", "adoc", "ad",
"rest", "rst",
"org",
"pandoc", "pdc",
}

contentFileExtensionsSet map[string]bool

htmlFileExtensions = []string{
"html", "htm",
}

htmlFileExtensionsSet map[string]bool
)

func init() {
contentFileExtensionsSet = make(map[string]bool)
for _, ext := range contentFileExtensions {
contentFileExtensionsSet[ext] = true
}
htmlFileExtensionsSet = make(map[string]bool)
for _, ext := range htmlFileExtensions {
htmlFileExtensionsSet[ext] = true
}
func IsGoTmplExt(ext string) bool {
return ext == "gotmpl"
}

func IsContentFile(filename string) bool {
return contentFileExtensionsSet[strings.TrimPrefix(filepath.Ext(filename), ".")]
}

func IsIndexContentFile(filename string) bool {
if !IsContentFile(filename) {
return false
}

base := filepath.Base(filename)

return strings.HasPrefix(base, "index.") || strings.HasPrefix(base, "_index.")
}

func IsHTML(ext string) bool {
return htmlFileExtensionsSet[ext]
}

func IsContentExt(ext string) bool {
return contentFileExtensionsSet[ext]
// Supported data file extensions for _content.* files.
func IsContentDataExt(ext string) bool {
return IsGoTmplExt(ext)
}

const (
Expand All @@ -93,6 +49,8 @@ const (

FolderResources = "resources"
FolderJSConfig = "_jsconfig" // Mounted below /assets with postcss.config.js etc.

NameContentData = "_content"
)

var (
Expand Down
8 changes: 5 additions & 3 deletions hugofs/walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ type WalkwayConfig struct {
Root string

// The logger to use.
Logger loggers.Logger
Logger loggers.Logger
PathParser *paths.PathParser

// One or both of these may be pre-set.
Info FileMetaInfo // The start info.
Expand Down Expand Up @@ -160,8 +161,9 @@ func (w *Walkway) walk(path string, info FileMetaInfo, dirEntries []FileMetaInfo

dirEntries = DirEntriesToFileMetaInfos(fis)
for _, fi := range dirEntries {
if fi.Meta().PathInfo == nil {
fi.Meta().PathInfo = paths.Parse("", filepath.Join(pathRel, fi.Name()))
if fi.Meta().PathInfo == nil && w.cfg.PathParser != nil {
// TODO1
fi.Meta().PathInfo = w.cfg.PathParser.Parse("", filepath.Join(pathRel, fi.Name()))
}
}

Expand Down
2 changes: 1 addition & 1 deletion hugolib/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1144,7 +1144,7 @@ Home.

enConfig := b.H.Sites[0].conf
m, _ := enConfig.MediaTypes.Config.GetByType("text/html")
b.Assert(m.Suffixes(), qt.DeepEquals, []string{"html"})
b.Assert(m.Suffixes(), qt.DeepEquals, []string{"html", "htm"})

svConfig := b.H.Sites[1].conf
f, _ := svConfig.OutputFormats.Config.GetByName("html")
Expand Down

0 comments on commit 779e246

Please sign in to comment.