Skip to content

Commit

Permalink
Implement cascade support in front matter
Browse files Browse the repository at this point in the history
  • Loading branch information
bep committed Aug 1, 2019
1 parent 2d1d336 commit f34aab5
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 31 deletions.
103 changes: 103 additions & 0 deletions hugolib/cascade_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2019 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package hugolib

import (
"bytes"
"fmt"
"testing"

"github.com/gohugoio/hugo/parser"
"github.com/gohugoio/hugo/parser/metadecoders"
)

func TestCascade(t *testing.T) {

weight := 0

p := func(m map[string]interface{}) string {
var yamlStr string

if len(m) > 0 {
var b bytes.Buffer

parser.InterfaceToConfig(m, metadecoders.YAML, &b)
yamlStr = b.String()
}

weight += 10

metaStr := "---\n" + yamlStr + fmt.Sprintf("\nweight: %d\n---", weight)

return metaStr

}

b := newTestSitesBuilder(t).WithConfigFile("toml", `
baseURL = "https://example.org"
disableKinds = ["taxonomy", "taxonomyTerm"]
`)
b.WithContent(
"_index.md", p(map[string]interface{}{
"title": "Home",
"cascade": map[string]interface{}{
"title": "Cascade Home",
"icon": "home.png",
},
}),
"p1.md", p(map[string]interface{}{
"title": "p1",
}),
"p2.md", p(map[string]interface{}{}),
"sect1/_index.md", p(map[string]interface{}{
"title": "Sect1",
"cascade": map[string]interface{}{
"title": "Cascade Sect1",
"icon": "sect1.png",
},
}),
"sect1/s1_2/_index.md", p(map[string]interface{}{
"title": "Sect1_2",
}),
"sect1/s1_2/p1.md", p(map[string]interface{}{
"title": "Sect1_2_p1",
}),
"sect2/_index.md", p(map[string]interface{}{
"title": "Sect2",
}),
"sect2/p1.md", p(map[string]interface{}{
"title": "Sect2_p1",
}),
"sect2/p2.md", p(map[string]interface{}{}),
)

b.WithTemplates("index.html", `
{{ range .Site.Pages }}
{{- .Weight }}|{{ .Kind }}|{{ .Path }}|{{ .Title }}|{{ .Params.icon }}|
{{ end }}
`)

b.Build(BuildCfg{})

b.AssertFileContent("public/index.html", `10|home|_index.md|Home|home.png|
20|page|p1.md|p1|home.png|
30|page|p2.md|Cascade Home|home.png|
40|section|sect1/_index.md|Sect1|sect1.png|
50|section|sect1/s1_2/_index.md|Sect1_2|sect1.png|
60|page|sect1/s1_2/p1.md|Sect1_2_p1|sect1.png|
70|section|sect2/_index.md|Sect2|home.png|
80|page|sect2/p1.md|Sect2_p1|home.png|
90|page|sect2/p2.md|Cascade Home|home.png|`)
}
143 changes: 132 additions & 11 deletions hugolib/hugo_sites.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"strings"
"sync"

"github.com/gohugoio/hugo/parser/pageparser"

radix "github.com/hashicorp/go-immutable-radix"

"github.com/gohugoio/hugo/output"
Expand Down Expand Up @@ -623,10 +625,105 @@ func (h *HugoSites) renderCrossSitesArtifacts() error {
s.siteCfg.sitemap.Filename, h.toSiteInfos(), smLayouts...)
}

// createMissingPages creates home page, taxonomies etc. that isnt't created as an
// effect of having a content file.
func (h *HugoSites) createMissingPages() error {
// assignMetaData parses any front matter stored away and merges it with any
// cascading data available.
// TODO(bep) cascade workers
func (h *HugoSites) assignMetaData() error {
ma := &metaDataAssigner{}
for _, s := range h.Sites {
if err := ma.handleSection(nil, s.home); err != nil {
return err
}
}

return nil
}

const cascadeKey = "cascade"

type metaDataAssigner struct {
}

func (m *metaDataAssigner) parseMeta(it pageparser.Item) (map[string]interface{}, error) {
f := metadecoders.FormatFromFrontMatterType(it.Type)
meta, err := metadecoders.Default.UnmarshalToMap(it.Val, f)
if err != nil {
if fe, ok := err.(herrors.FileError); ok {
return nil, herrors.ToFileErrorWithOffset(fe, 32) // TODO(bep) cascade iter.LineNumber()-1)
} else {
return nil, err
}
}

return meta, nil
}

func (m *metaDataAssigner) parseAndAssignMeta(isSection bool, parentCascade map[string]interface{}, p *pageState) (map[string]interface{}, error) {
var meta map[string]interface{}
newCascade := parentCascade

if p.source.frontMatter.Val != nil {
var err error
meta, err = m.parseMeta(p.source.frontMatter)
if err != nil {
return nil, err
}

if isSection {
if c, found := meta[cascadeKey]; found {
// Use this section's cascade for all children.
newCascade = c.(map[string]interface{})
delete(meta, cascadeKey)
}
}
} else {
meta = make(map[string]interface{})
}

if newCascade != nil {
for k, v := range newCascade {
// TODO(bep) cascade case
if k == cascadeKey {
continue
}
if _, found := meta[k]; !found {
meta[k] = v
}
}
}

if err := p.m.setMetadata(p, meta); err != nil {
return nil, err
}

return newCascade, nil
}

func (m *metaDataAssigner) handleSection(parentCascade map[string]interface{}, section *pageState) error {
var err error

if parentCascade, err = m.parseAndAssignMeta(true, parentCascade, section); err != nil {
return err
}

for _, p := range section.Pages() {
if parentCascade, err = m.parseAndAssignMeta(false, parentCascade, p.(*pageState)); err != nil {
return err
}
}

for _, p := range section.Sections() {
if err := m.handleSection(parentCascade, p.(*pageState)); err != nil {
return err
}
}

return nil

}

// Will create sections (including home) with not content page.
func (h *HugoSites) createMissingSectionPages() error {
for _, s := range h.Sites {
if s.isEnabled(page.KindHome) {
// home pages
Expand All @@ -648,6 +745,15 @@ func (h *HugoSites) createMissingPages() error {
// Will create content-less root sections.
newSections := s.assembleSections()
s.workAllPages = append(s.workAllPages, newSections...)
}

return nil
}

// Will create taxonomy term/list pages with not content page.
func (h *HugoSites) createMissingTaxonomyPages() error {

for _, s := range h.Sites {

taxonomyTermEnabled := s.isEnabled(page.KindTaxonomyTerm)
taxonomyEnabled := s.isEnabled(page.KindTaxonomy)
Expand Down Expand Up @@ -741,21 +847,36 @@ func (h *HugoSites) removePageByFilename(filename string) {
}
}

func (h *HugoSites) removeNoBuildPages() error {
for _, s := range h.Sites {
s.headlessPages = s.removeNoBuildPages(s.headlessPages)
s.workAllPages = s.removeNoBuildPages(s.workAllPages)
}

return nil
}

func (s *Site) removeNoBuildPages(pages pageStatePages) pageStatePages {
tmp := pages[:0]
for _, p := range pages {
if s.shouldBuild(p) {
tmp = append(tmp, p)
}
}
return tmp
}

func (h *HugoSites) createPageCollections() error {
for _, s := range h.Sites {
for _, p := range s.rawAllPages {
if !s.isEnabled(p.Kind()) {
continue
}

shouldBuild := s.shouldBuild(p)
s.buildStats.update(p)
if shouldBuild {
if p.m.headless {
s.headlessPages = append(s.headlessPages, p)
} else {
s.workAllPages = append(s.workAllPages, p)
}
if p.m.headless {
s.headlessPages = append(s.headlessPages, p)
} else {
s.workAllPages = append(s.workAllPages, p)
}
}
}
Expand Down
21 changes: 19 additions & 2 deletions hugolib/hugo_sites_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,16 +239,33 @@ func (h *HugoSites) assemble(config *BuildCfg) error {
return err
}

// Create pages for the section pages etc. without content file.
if err := h.createMissingSectionPages(); err != nil {
return err
}

if config.whatChanged.source {
// TODO(bep) cascade config.whatChanged.source
// TODO(bep) cascade taxo meta?
// TODO(bep) cascade headless
if err := h.assignMetaData(); err != nil {
return err
}

for _, s := range h.Sites {
if err := s.assembleTaxonomies(); err != nil {
return err
}
}
}

// Create pagexs for the section pages etc. without content file.
if err := h.createMissingPages(); err != nil {
// Remove drafts, future posts etc.
if err := h.removeNoBuildPages(); err != nil {
return nil
}

// Create pages for the taxonomies without a content file.
if err := h.createMissingTaxonomyPages(); err != nil {
return err
}

Expand Down
3 changes: 2 additions & 1 deletion hugolib/hugo_smoke_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func TestSmoke(t *testing.T) {
baseURL = "https://example.com"
title = "Simple Site"
rssLimit = 3
paginate = 4
defaultContentLanguage = "en"
enableRobotsTXT = true
Expand Down Expand Up @@ -193,7 +194,7 @@ Some **Markdown** in JSON shortcode.
b.AssertFileContent("public/index.html",
"home|In English",
"Site params: Rules",
"Pages: Pages(18)|Data Pages: Pages(18)",
"Pages: Pages(5)|Data Pages: Pages(5)",
"Paginator: 1",
"First Site: In English",
"RelPermalink: /",
Expand Down
33 changes: 19 additions & 14 deletions hugolib/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import (
"github.com/gohugoio/hugo/helpers"

"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/parser/metadecoders"

"github.com/gohugoio/hugo/parser/pageparser"
"github.com/pkg/errors"
Expand Down Expand Up @@ -131,8 +130,7 @@ func (p *pageState) Pages() page.Pages {

switch p.Kind() {
case page.KindPage:
case page.KindHome:
pages = p.s.RegularPages()
case page.KindHome, page.KindSection:
case page.KindTaxonomy:
termInfo := p.getTaxonomyNodeInfo()
taxonomy := p.s.Taxonomies[termInfo.plural].Get(termInfo.termKey)
Expand Down Expand Up @@ -529,19 +527,26 @@ Loop:
p.renderable = false
rn.AddBytes(it)
case it.IsFrontMatter():
f := metadecoders.FormatFromFrontMatterType(it.Type)
m, err := metadecoders.Default.UnmarshalToMap(it.Val, f)
if err != nil {
if fe, ok := err.(herrors.FileError); ok {
return herrors.ToFileErrorWithOffset(fe, iter.LineNumber()-1)
} else {
// TODO(bep) cascade
// To support cascading front matter, we need to wait until
// we have created the section tree until we have the full set
// of metadata, so store the raw front matter away for later.
p.source.frontMatter = it

/*
f := metadecoders.FormatFromFrontMatterType(it.Type)
m, err := metadecoders.Default.UnmarshalToMap(it.Val, f)
if err != nil {
if fe, ok := err.(herrors.FileError); ok {
return herrors.ToFileErrorWithOffset(fe, iter.LineNumber()-1)
} else {
return err
}
}
if err := meta.setMetadata(p, m); err != nil {
return err
}
}

if err := meta.setMetadata(p, m); err != nil {
return err
}
*/

next := iter.Peek()
if !next.IsDone() {
Expand Down
Loading

0 comments on commit f34aab5

Please sign in to comment.