From b799b12f4a693dfeae8a5a362f131081a727bb8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Mon, 15 Apr 2019 09:38:14 +0200 Subject: [PATCH] hugolib: Fix panic for unused taxonomy content files In Hugo 0.55 we connected the taxonomy nodes with their owning Page. This failed if you had, say, a content file for a author that did not author anything in the site: ``` content/authors/silent-persin/_index.md ``` Fixes #5847 --- hugolib/hugo_sites.go | 16 +++++++++++++++- hugolib/page.go | 6 ++++-- hugolib/site.go | 15 +++++++-------- hugolib/taxonomy.go | 27 ++++++++++++++++++++++----- hugolib/taxonomy_test.go | 5 ++++- 5 files changed, 52 insertions(+), 17 deletions(-) diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go index 8c80e189c..6f95dbb12 100644 --- a/hugolib/hugo_sites.go +++ b/hugolib/hugo_sites.go @@ -14,7 +14,9 @@ package hugolib import ( + "fmt" "io" + "path" "path/filepath" "sort" "strings" @@ -650,7 +652,19 @@ func (h *HugoSites) createMissingPages() error { // Make them navigable from WeightedPage etc. for _, p := range taxonomyPages { - p.getTaxonomyNodeInfo().TransferValues(p) + ni := p.getTaxonomyNodeInfo() + if ni == nil { + // This can be nil for taxonomies, e.g. an author, + // with a content file, but no actual usage. + // Create one. + sections := p.SectionsEntries() + if len(sections) < 2 { + // Invalid state + panic(fmt.Sprintf("invalid taxonomy state for %q with sections %v", p.pathOrTitle(), sections)) + } + ni = p.s.taxonomyNodes.GetOrAdd(sections[0], path.Join(sections[1:]...)) + } + ni.TransferValues(p) } for _, p := range taxonomyTermsPages { p.getTaxonomyNodeInfo().TransferValues(p) diff --git a/hugolib/page.go b/hugolib/page.go index 2ed828df6..ec4b23d90 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -746,8 +746,10 @@ func (p *pageState) getTaxonomyNodeInfo() *taxonomyNodeInfo { info := p.s.taxonomyNodes.Get(p.SectionsEntries()...) if info == nil { - // This should never happpen - panic(fmt.Sprintf("invalid taxonomy state for %q with sections %v", p.pathOrTitle(), p.SectionsEntries())) + // There can be unused content pages for taxonomies (e.g. author that + // has not written anything, yet), and these will not have a taxonomy + // node created in the assemble taxonomies step. + return nil } return info diff --git a/hugolib/site.go b/hugolib/site.go index 2653479ea..47ca77016 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -94,7 +94,7 @@ type Site struct { Taxonomies TaxonomyList - taxonomyNodes taxonomyNodeInfos + taxonomyNodes *taxonomyNodeInfos Sections Taxonomy Info SiteInfo @@ -1566,24 +1566,23 @@ func (s *Site) assembleTaxonomies() error { s.Taxonomies[plural] = make(Taxonomy) } - s.taxonomyNodes = make(taxonomyNodeInfos) + s.taxonomyNodes = &taxonomyNodeInfos{ + m: make(map[string]*taxonomyNodeInfo), + getKey: s.getTaxonomyKey, + } s.Log.INFO.Printf("found taxonomies: %#v\n", taxonomies) for singular, plural := range taxonomies { - parent := s.taxonomyNodes.GetOrCreate(plural, "", "") + parent := s.taxonomyNodes.GetOrCreate(plural, "") parent.singular = singular addTaxonomy := func(plural, term string, weight int, p page.Page) { key := s.getTaxonomyKey(term) - n := s.taxonomyNodes.GetOrCreate(plural, key, term) + n := s.taxonomyNodes.GetOrCreate(plural, term) n.parent = parent - // There may be different spellings before normalization, so the - // last one will win, e.g. "hugo" vs "Hugo". - n.term = term - w := page.NewWeightedPage(weight, p, n.owner) s.Taxonomies[plural].add(key, w) diff --git a/hugolib/taxonomy.go b/hugolib/taxonomy.go index b91318ff1..e6c80161a 100644 --- a/hugolib/taxonomy.go +++ b/hugolib/taxonomy.go @@ -193,16 +193,33 @@ func (t *taxonomyNodeInfo) TransferValues(p *pageState) { // Maps either plural or plural/term to a taxonomy node. // TODO(bep) consolidate somehow with s.Taxonomies -type taxonomyNodeInfos map[string]*taxonomyNodeInfo +type taxonomyNodeInfos struct { + m map[string]*taxonomyNodeInfo + getKey func(string) string +} +// map[string]*taxonomyNodeInfo func (t taxonomyNodeInfos) key(parts ...string) string { return path.Join(parts...) } -func (t taxonomyNodeInfos) GetOrCreate(plural, termKey, term string) *taxonomyNodeInfo { +// GetOrAdd will get or create and add a new taxonomy node to the parent identified with plural. +// It will panic if the parent does not exist. +func (t taxonomyNodeInfos) GetOrAdd(plural, term string) *taxonomyNodeInfo { + parent := t.GetOrCreate(plural, "") + if parent == nil { + panic(fmt.Sprintf("no parent found with plural %q", plural)) + } + child := t.GetOrCreate(plural, term) + child.parent = parent + return child +} + +func (t taxonomyNodeInfos) GetOrCreate(plural, term string) *taxonomyNodeInfo { + termKey := t.getKey(term) key := t.key(plural, termKey) - n, found := t[key] + n, found := t.m[key] if found { return n } @@ -214,7 +231,7 @@ func (t taxonomyNodeInfos) GetOrCreate(plural, termKey, term string) *taxonomyNo owner: &page.PageWrapper{}, // Page will be assigned later. } - t[key] = n + t.m[key] = n return n } @@ -222,7 +239,7 @@ func (t taxonomyNodeInfos) GetOrCreate(plural, termKey, term string) *taxonomyNo func (t taxonomyNodeInfos) Get(sections ...string) *taxonomyNodeInfo { key := t.key(sections...) - n, found := t[key] + n, found := t.m[key] if found { return n } diff --git a/hugolib/taxonomy_test.go b/hugolib/taxonomy_test.go index 2501ed2e4..f4902ae8d 100644 --- a/hugolib/taxonomy_test.go +++ b/hugolib/taxonomy_test.go @@ -117,6 +117,9 @@ permalinkeds: writeNewContentFile(t, fs.Source, "Category Terms", "2017-01-01", "content/categories/_index.md", 10) writeNewContentFile(t, fs.Source, "Tag1 List", "2017-01-01", "content/tags/Tag1/_index.md", 10) + // https://github.com/gohugoio/hugo/issues/5847 + writeNewContentFile(t, fs.Source, "Unused Tag List", "2018-01-01", "content/tags/not-used/_index.md", 10) + err := h.Build(BuildCfg{}) require.NoError(t, err) @@ -159,7 +162,7 @@ permalinkeds: // Make sure that each page.KindTaxonomyTerm page has an appropriate number // of page.KindTaxonomy pages in its Pages slice. taxonomyTermPageCounts := map[string]int{ - "tags": 2, + "tags": 3, "categories": 2, "others": 2, "empties": 0,