mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
parent
e7d0bc8a74
commit
e371ac0b6f
8 changed files with 185 additions and 17 deletions
|
@ -31,6 +31,15 @@ import (
|
||||||
jww "github.com/spf13/jwalterweatherman"
|
jww "github.com/spf13/jwalterweatherman"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Temporary feature flag to ease the refactoring of node vs page, see
|
||||||
|
// https://github.com/spf13/hugo/issues/2297
|
||||||
|
// TODO(bep) eventually remove
|
||||||
|
var nodePageFeatureFlag bool
|
||||||
|
|
||||||
|
func toggleNodePageFeatureFlag() {
|
||||||
|
nodePageFeatureFlag = !nodePageFeatureFlag
|
||||||
|
}
|
||||||
|
|
||||||
// HugoSites represents the sites to build. Each site represents a language.
|
// HugoSites represents the sites to build. Each site represents a language.
|
||||||
type HugoSites struct {
|
type HugoSites struct {
|
||||||
Sites []*Site
|
Sites []*Site
|
||||||
|
@ -563,6 +572,16 @@ func (s *Site) updateBuildStats(page *Page) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *HugoSites) findPagesByNodeType(n NodeType) Pages {
|
||||||
|
var pages Pages
|
||||||
|
for _, p := range h.Sites[0].AllPages {
|
||||||
|
if p.NodeType == n {
|
||||||
|
pages = append(pages, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pages
|
||||||
|
}
|
||||||
|
|
||||||
// Convenience func used in tests to build a single site/language excluding render phase.
|
// Convenience func used in tests to build a single site/language excluding render phase.
|
||||||
func buildSiteSkipRender(s *Site, additionalTemplates ...string) error {
|
func buildSiteSkipRender(s *Site, additionalTemplates ...string) error {
|
||||||
return doBuildSite(s, false, additionalTemplates...)
|
return doBuildSite(s, false, additionalTemplates...)
|
||||||
|
|
|
@ -26,7 +26,23 @@ import (
|
||||||
"github.com/spf13/hugo/helpers"
|
"github.com/spf13/hugo/helpers"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO(bep) np add String()
|
||||||
|
type NodeType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
NodePage NodeType = iota
|
||||||
|
|
||||||
|
// The rest are node types; home page, sections etc.
|
||||||
|
NodeHome
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p NodeType) IsNode() bool {
|
||||||
|
return p >= NodeHome
|
||||||
|
}
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
|
NodeType NodeType
|
||||||
|
|
||||||
// a natural key that should be unique for this site
|
// a natural key that should be unique for this site
|
||||||
// for the home page this will typically be "home", but it can anything
|
// for the home page this will typically be "home", but it can anything
|
||||||
// as long as it is the same for repeated builds.
|
// as long as it is the same for repeated builds.
|
||||||
|
@ -44,7 +60,6 @@ type Node struct {
|
||||||
Lastmod time.Time
|
Lastmod time.Time
|
||||||
Sitemap Sitemap
|
Sitemap Sitemap
|
||||||
URLPath
|
URLPath
|
||||||
IsHome bool
|
|
||||||
paginator *Pager
|
paginator *Pager
|
||||||
paginatorInit sync.Once
|
paginatorInit sync.Once
|
||||||
scratch *Scratch
|
scratch *Scratch
|
||||||
|
@ -151,11 +166,15 @@ func (n *Node) RSSlink() template.HTML {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) IsNode() bool {
|
func (n *Node) IsNode() bool {
|
||||||
return true
|
return n.NodeType.IsNode()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Node) IsHome() bool {
|
||||||
|
return n.NodeType == NodeHome
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) IsPage() bool {
|
func (n *Node) IsPage() bool {
|
||||||
return !n.IsNode()
|
return n.NodeType == NodePage
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) Ref(ref string) (string, error) {
|
func (n *Node) Ref(ref string) (string, error) {
|
||||||
|
@ -316,3 +335,11 @@ func (n *Node) addLangFilepathPrefix(outfile string) string {
|
||||||
}
|
}
|
||||||
return helpers.FilePathSeparator + filepath.Join(n.Lang(), outfile)
|
return helpers.FilePathSeparator + filepath.Join(n.Lang(), outfile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nodeTypeFromFilename(filename string) NodeType {
|
||||||
|
// TODO(bep) np
|
||||||
|
if !strings.HasPrefix(filename, "_node") {
|
||||||
|
return NodePage
|
||||||
|
}
|
||||||
|
return NodeHome
|
||||||
|
}
|
||||||
|
|
103
hugolib/node_as_page_test.go
Normal file
103
hugolib/node_as_page_test.go
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
// Copyright 2016 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 (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
This file will test the "making everything a page" transition.
|
||||||
|
|
||||||
|
See https://github.com/spf13/hugo/issues/2297
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestHomeAsPage(t *testing.T) {
|
||||||
|
nodePageFeatureFlag = true
|
||||||
|
defer toggleNodePageFeatureFlag()
|
||||||
|
|
||||||
|
/* Will have to decide what to name the node content files, but:
|
||||||
|
|
||||||
|
Home page should have:
|
||||||
|
Content, shortcode support
|
||||||
|
Metadata (title, dates etc.)
|
||||||
|
Params
|
||||||
|
Taxonomies (categories, tags)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
testCommonResetState()
|
||||||
|
|
||||||
|
writeSource(t, filepath.Join("content", "_node.md"), `---
|
||||||
|
title: Home Sweet Home!
|
||||||
|
---
|
||||||
|
Home **Content!**
|
||||||
|
`)
|
||||||
|
|
||||||
|
writeSource(t, filepath.Join("layouts", "index.html"), `
|
||||||
|
Index Title: {{ .Title }}
|
||||||
|
Index Content: {{ .Content }}
|
||||||
|
# Pages: {{ len .Data.Pages }}
|
||||||
|
`)
|
||||||
|
|
||||||
|
writeSource(t, filepath.Join("layouts", "_default", "single.html"), `
|
||||||
|
Single Title: {{ .Title }}
|
||||||
|
Single Content: {{ .Content }}
|
||||||
|
`)
|
||||||
|
|
||||||
|
// Add some regular pages
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
writeSource(t, filepath.Join("content", fmt.Sprintf("regular%d.md", i)), fmt.Sprintf(`---
|
||||||
|
title: Page %d
|
||||||
|
---
|
||||||
|
Content Page %d
|
||||||
|
`, i, i))
|
||||||
|
}
|
||||||
|
|
||||||
|
s := newSiteDefaultLang()
|
||||||
|
|
||||||
|
if err := buildAndRenderSite(s); err != nil {
|
||||||
|
t.Fatalf("Failed to build site: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertFileContent(t, filepath.Join("public", "index.html"), false,
|
||||||
|
"Index Title: Home Sweet Home!",
|
||||||
|
"Home <strong>Content!</strong>",
|
||||||
|
"# Pages: 10")
|
||||||
|
assertFileContent(t, filepath.Join("public", "regular1", "index.html"), false, "Single Title: Page 1", "Content Page 1")
|
||||||
|
|
||||||
|
h := s.owner
|
||||||
|
nodes := h.findPagesByNodeType(NodeHome)
|
||||||
|
require.Len(t, nodes, 1)
|
||||||
|
|
||||||
|
home := nodes[0]
|
||||||
|
|
||||||
|
require.True(t, home.IsHome())
|
||||||
|
require.True(t, home.IsNode())
|
||||||
|
require.False(t, home.IsPage())
|
||||||
|
|
||||||
|
pages := h.findPagesByNodeType(NodePage)
|
||||||
|
require.Len(t, pages, 10)
|
||||||
|
|
||||||
|
first := pages[0]
|
||||||
|
require.False(t, first.IsHome())
|
||||||
|
require.False(t, first.IsNode())
|
||||||
|
require.True(t, first.IsPage())
|
||||||
|
|
||||||
|
}
|
|
@ -30,7 +30,7 @@ func TestNodeSimpleMethods(t *testing.T) {
|
||||||
{func(n *Node) bool { return n.Now().Unix() == time.Now().Unix() }},
|
{func(n *Node) bool { return n.Now().Unix() == time.Now().Unix() }},
|
||||||
} {
|
} {
|
||||||
|
|
||||||
n := &Node{}
|
n := &Node{NodeType: NodeHome}
|
||||||
n.RSSLink = "rssLink"
|
n.RSSLink = "rssLink"
|
||||||
|
|
||||||
if !this.assertFunc(n) {
|
if !this.assertFunc(n) {
|
||||||
|
|
|
@ -182,14 +182,6 @@ func (p *Page) initPlainWords() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Page) IsNode() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Page) IsPage() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is a convenience method to do lookups in Page's and Site's Params map,
|
// Param is a convenience method to do lookups in Page's and Site's Params map,
|
||||||
// in that order.
|
// in that order.
|
||||||
//
|
//
|
||||||
|
@ -423,7 +415,7 @@ func (p *Page) getRenderingConfig() *helpers.Blackfriday {
|
||||||
func newPage(filename string) *Page {
|
func newPage(filename string) *Page {
|
||||||
page := Page{contentType: "",
|
page := Page{contentType: "",
|
||||||
Source: Source{File: *source.NewFile(filename)},
|
Source: Source{File: *source.NewFile(filename)},
|
||||||
Node: Node{Keywords: []string{}, Sitemap: Sitemap{Priority: -1}},
|
Node: Node{NodeType: nodeTypeFromFilename(filename), Keywords: []string{}, Sitemap: Sitemap{Priority: -1}},
|
||||||
Params: make(map[string]interface{}),
|
Params: make(map[string]interface{}),
|
||||||
translations: make(Pages, 0),
|
translations: make(Pages, 0),
|
||||||
}
|
}
|
||||||
|
@ -457,6 +449,11 @@ func (p *Page) layouts(l ...string) []string {
|
||||||
return p.layoutsCalculated
|
return p.layoutsCalculated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(bep) np
|
||||||
|
if p.NodeType == NodeHome {
|
||||||
|
return []string{"index.html", "_default/list.html"}
|
||||||
|
}
|
||||||
|
|
||||||
if p.Layout != "" {
|
if p.Layout != "" {
|
||||||
return layouts(p.Type(), p.Layout)
|
return layouts(p.Type(), p.Layout)
|
||||||
}
|
}
|
||||||
|
@ -1136,6 +1133,10 @@ func (p *Page) FullFilePath() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Page) TargetPath() (outfile string) {
|
func (p *Page) TargetPath() (outfile string) {
|
||||||
|
// TODO(bep) ml
|
||||||
|
if p.NodeType == NodeHome {
|
||||||
|
return "index.html"
|
||||||
|
}
|
||||||
// Always use URL if it's specified
|
// Always use URL if it's specified
|
||||||
if len(strings.TrimSpace(p.URLPath.URL)) > 2 {
|
if len(strings.TrimSpace(p.URLPath.URL)) > 2 {
|
||||||
outfile = strings.TrimSpace(p.URLPath.URL)
|
outfile = strings.TrimSpace(p.URLPath.URL)
|
||||||
|
|
|
@ -645,7 +645,7 @@ func TestCreateNewPage(t *testing.T) {
|
||||||
|
|
||||||
// issue #2290: Path is relative to the content dir and will continue to be so.
|
// issue #2290: Path is relative to the content dir and will continue to be so.
|
||||||
require.Equal(t, filepath.FromSlash(fmt.Sprintf("p0.%s", ext)), p.Path())
|
require.Equal(t, filepath.FromSlash(fmt.Sprintf("p0.%s", ext)), p.Path())
|
||||||
assert.False(t, p.IsHome)
|
assert.False(t, p.IsHome())
|
||||||
checkPageTitle(t, p, "Simple")
|
checkPageTitle(t, p, "Simple")
|
||||||
checkPageContent(t, p, normalizeExpected(ext, "<p>Simple Page</p>\n"))
|
checkPageContent(t, p, normalizeExpected(ext, "<p>Simple Page</p>\n"))
|
||||||
checkPageSummary(t, p, "Simple Page")
|
checkPageSummary(t, p, "Simple Page")
|
||||||
|
|
|
@ -1678,6 +1678,8 @@ func (s *Site) renderPages() error {
|
||||||
func pageRenderer(s *Site, pages <-chan *Page, results chan<- error, wg *sync.WaitGroup) {
|
func pageRenderer(s *Site, pages <-chan *Page, results chan<- error, wg *sync.WaitGroup) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
for p := range pages {
|
for p := range pages {
|
||||||
|
// TODO(bep) np paginator
|
||||||
|
s.preparePage(p)
|
||||||
err := s.renderAndWritePage("page "+p.FullFilePath(), p.TargetPath(), p, s.appendThemeTemplates(p.layouts())...)
|
err := s.renderAndWritePage("page "+p.FullFilePath(), p.TargetPath(), p, s.appendThemeTemplates(p.layouts())...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
results <- err
|
results <- err
|
||||||
|
@ -1685,6 +1687,17 @@ func pageRenderer(s *Site, pages <-chan *Page, results chan<- error, wg *sync.Wa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Site) preparePage(p *Page) {
|
||||||
|
// TODO(bep) np the order of it all
|
||||||
|
switch p.NodeType {
|
||||||
|
case NodePage:
|
||||||
|
case NodeHome:
|
||||||
|
p.Data = make(map[string]interface{})
|
||||||
|
// TODO(bep) np cache the below
|
||||||
|
p.Data["Pages"] = s.owner.findPagesByNodeType(NodePage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func errorCollator(results <-chan error, errs chan<- error) {
|
func errorCollator(results <-chan error, errs chan<- error) {
|
||||||
errMsgs := []string{}
|
errMsgs := []string{}
|
||||||
for err := range results {
|
for err := range results {
|
||||||
|
@ -2028,6 +2041,11 @@ func (s *Site) renderSectionLists(prepare bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Site) renderHomePage(prepare bool) error {
|
func (s *Site) renderHomePage(prepare bool) error {
|
||||||
|
// TODO(bep) np remove this and related
|
||||||
|
if nodePageFeatureFlag {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
n := s.newHomeNode(prepare, 0)
|
n := s.newHomeNode(prepare, 0)
|
||||||
if prepare {
|
if prepare {
|
||||||
return nil
|
return nil
|
||||||
|
@ -2118,7 +2136,7 @@ func (s *Site) renderHomePage(prepare bool) error {
|
||||||
func (s *Site) newHomeNode(prepare bool, counter int) *Node {
|
func (s *Site) newHomeNode(prepare bool, counter int) *Node {
|
||||||
n := s.nodeLookup("home", counter, prepare)
|
n := s.nodeLookup("home", counter, prepare)
|
||||||
n.Title = n.Site.Title
|
n.Title = n.Site.Title
|
||||||
n.IsHome = true
|
n.NodeType = NodeHome
|
||||||
s.setURLs(n, "/")
|
s.setURLs(n, "/")
|
||||||
n.Data["Pages"] = s.Pages
|
n.Data["Pages"] = s.Pages
|
||||||
if len(s.Pages) != 0 {
|
if len(s.Pages) != 0 {
|
||||||
|
@ -2373,7 +2391,7 @@ func (s *Site) renderAndWritePage(name string, dest string, d interface{}, layou
|
||||||
}
|
}
|
||||||
|
|
||||||
// For performance reasons we only inject the Hugo generator tag on the home page.
|
// For performance reasons we only inject the Hugo generator tag on the home page.
|
||||||
if n, ok := d.(*Node); ok && n.IsHome {
|
if n, ok := d.(*Node); ok && n.IsHome() {
|
||||||
if !viper.GetBool("disableHugoGeneratorInject") {
|
if !viper.GetBool("disableHugoGeneratorInject") {
|
||||||
transformLinks = append(transformLinks, transform.HugoGeneratorInject)
|
transformLinks = append(transformLinks, transform.HugoGeneratorInject)
|
||||||
}
|
}
|
||||||
|
|
|
@ -378,7 +378,7 @@ func doTestShouldAlwaysHaveUglyURLs(t *testing.T, uglyURLs bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range s.Pages {
|
for _, p := range s.Pages {
|
||||||
assert.False(t, p.IsHome)
|
assert.False(t, p.IsHome())
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
|
Loading…
Reference in a new issue