summaryrefslogtreecommitdiffstats
path: root/hugolib/site.go
diff options
context:
space:
mode:
Diffstat (limited to 'hugolib/site.go')
-rw-r--r--hugolib/site.go1132
1 files changed, 591 insertions, 541 deletions
diff --git a/hugolib/site.go b/hugolib/site.go
index 43b398b70..be70db5ee 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The Hugo Authors. All rights reserved.
+// 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.
@@ -22,59 +22,54 @@ import (
"mime"
"net/url"
"os"
+ "path"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
+ "github.com/gohugoio/hugo/common/maps"
+
"github.com/pkg/errors"
"github.com/gohugoio/hugo/common/text"
- "github.com/gohugoio/hugo/hugofs"
-
- "github.com/gohugoio/hugo/common/herrors"
-
"github.com/gohugoio/hugo/common/hugo"
- "github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/publisher"
_errors "github.com/pkg/errors"
"github.com/gohugoio/hugo/langs"
- src "github.com/gohugoio/hugo/source"
-
- "golang.org/x/sync/errgroup"
+ "github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/hugo/config"
+ "github.com/gohugoio/hugo/lazy"
+ "golang.org/x/sync/errgroup"
"github.com/gohugoio/hugo/media"
- "github.com/gohugoio/hugo/parser/metadecoders"
-
- "github.com/markbates/inflect"
"github.com/fsnotify/fsnotify"
bp "github.com/gohugoio/hugo/bufferpool"
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/helpers"
- "github.com/gohugoio/hugo/hugolib/pagemeta"
+ "github.com/gohugoio/hugo/navigation"
"github.com/gohugoio/hugo/output"
"github.com/gohugoio/hugo/related"
"github.com/gohugoio/hugo/resources"
+ "github.com/gohugoio/hugo/resources/page/pagemeta"
+ "github.com/gohugoio/hugo/resources/resource"
"github.com/gohugoio/hugo/source"
"github.com/gohugoio/hugo/tpl"
+
"github.com/spf13/afero"
"github.com/spf13/cast"
- "github.com/spf13/nitro"
"github.com/spf13/viper"
)
// used to indicate if run as a test.
var testMode bool
-var defaultTimer *nitro.B
-
// Site contains all the information relevant for constructing a static
// site. The basic flow of information is as follows:
//
@@ -93,34 +88,27 @@ var defaultTimer *nitro.B
//
// 5. The entire collection of files is written to disk.
type Site struct {
- owner *HugoSites
+
+ // The owning container. When multiple languages, there will be multiple
+ // sites.
+ h *HugoSites
*PageCollections
Taxonomies TaxonomyList
- // Plural is what we get in the folder, so keep track of this mapping
- // to get the singular form from that value.
- taxonomiesPluralSingular map[string]string
-
- // This is temporary, see https://github.com/gohugoio/hugo/issues/2835
- // Maps "actors-gerard-depardieu" to "GĂ©rard Depardieu" when preserveTaxonomyNames
- // is set.
- taxonomiesOrigKey map[string]string
+ taxonomyNodes taxonomyNodeInfos
Sections Taxonomy
Info SiteInfo
- Menus Menus
- timer *nitro.B
layoutHandler *output.LayoutHandler
- draftCount int
- futureCount int
- expiredCount int
+ buildStats *buildStats
- Data map[string]interface{}
- Language *langs.Language
+ language *langs.Language
+
+ siteCfg siteConfigHolder
disabledKinds map[string]bool
@@ -137,7 +125,7 @@ type Site struct {
outputFormatsConfig output.Formats
mediaTypesConfig media.Types
- siteConfig SiteConfig
+ siteConfigConfig SiteConfig
// How to handle page front matter.
frontmatterHandler pagemeta.FrontMatterHandler
@@ -158,23 +146,162 @@ type Site struct {
// The func used to title case titles.
titleFunc func(s string) string
- relatedDocsHandler *relatedDocsHandler
+ relatedDocsHandler *page.RelatedDocsHandler
siteRefLinker
- // Set in some tests
- shortcodePlaceholderFunc func() string
publisher publisher.Publisher
+
+ menus navigation.Menus
+
+ // Shortcut to the home page. Note that this may be nil if
+ // home page, for some odd reason, is disabled.
+ home *pageState
+
+ // The last modification date of this site.
+ lastmod time.Time
+
+ // Lazily loaded site dependencies
+ init *siteInit
+}
+
+type siteConfigHolder struct {
+ sitemap config.Sitemap
+ taxonomiesConfig map[string]string
+ timeout time.Duration
+ hasCJKLanguage bool
+ enableEmoji bool
+}
+
+// Lazily loaded site dependencies.
+type siteInit struct {
+ prevNext *lazy.Init
+ prevNextInSection *lazy.Init
+ menus *lazy.Init
+}
+
+func (init *siteInit) Reset() {
+ init.prevNext.Reset()
+ init.prevNextInSection.Reset()
+ init.menus.Reset()
+}
+
+func (s *Site) initInit(init *lazy.Init, pctx pageContext) {
+ _, err := init.Do()
+ if err != nil {
+ s.h.FatalError(pctx.wrapError(err))
+ }
+}
+
+func (s *Site) prepareInits() {
+ s.init = &siteInit{}
+
+ var init lazy.Init
+
+ s.init.prevNext = init.Branch(func() (interface{}, error) {
+ regularPages := s.findWorkPagesByKind(page.KindPage)
+ for i, p := range regularPages {
+ if p.posNextPrev == nil {
+ continue
+ }
+ p.posNextPrev.nextPage = nil
+ p.posNextPrev.prevPage = nil
+
+ if i > 0 {
+ p.posNextPrev.nextPage = regularPages[i-1]
+ }
+
+ if i < len(regularPages)-1 {
+ p.posNextPrev.prevPage = regularPages[i+1]
+ }
+ }
+ return nil, nil
+ })
+
+ s.init.prevNextInSection = init.Branch(func() (interface{}, error) {
+ var rootSection []int
+ for i, p1 := range s.workAllPages {
+ if p1.IsPage() && p1.Section() == "" {
+ rootSection = append(rootSection, i)
+ }
+ if p1.IsSection() && len(p1.SectionsEntries()) <= 1 {
+ sectionPages := p1.Pages()
+ for i, p2 := range sectionPages {
+ p2s := p2.(*pageState)
+ if p2s.posNextPrevSection == nil {
+ continue
+ }
+
+ p2s.posNextPrevSection.nextPage = nil
+ p2s.posNextPrevSection.prevPage = nil
+
+ if i > 0 {
+ p2s.posNextPrevSection.nextPage = sectionPages[i-1]
+ }
+
+ if i < len(sectionPages)-1 {
+ p2s.posNextPrevSection.prevPage = sectionPages[i+1]
+ }
+ }
+ }
+ }
+
+ for i, j := range rootSection {
+ p := s.workAllPages[j]
+ if i > 0 {
+ p.posNextPrevSection.nextPage = s.workAllPages[rootSection[i-1]]
+ }
+
+ if i < len(rootSection)-1 {
+ p.posNextPrevSection.prevPage = s.workAllPages[rootSection[i+1]]
+ }
+ }
+
+ return nil, nil
+ })
+
+ s.init.menus = init.Branch(func() (interface{}, error) {
+ s.assembleMenus()
+ return nil, nil
+ })
+
+}
+
+// Build stats for a given site.
+type buildStats struct {
+ draftCount int
+ futureCount int
+ expiredCount int
+}
+
+// TODO(bep) consolidate all site stats into this
+func (b *buildStats) update(p page.Page) {
+ if p.Draft() {
+ b.draftCount++
+ }
+
+ if resource.IsFuture(p) {
+ b.futureCount++
+ }
+
+ if resource.IsExpired(p) {
+ b.expiredCount++
+ }
}
type siteRenderingContext struct {
output.Format
}
+func (s *Site) Menus() navigation.Menus {
+ s.init.menus.Do()
+ return s.menus
+}
+
func (s *Site) initRenderFormats() {
formatSet := make(map[string]bool)
formats := output.Formats{}
- for _, p := range s.Pages {
- for _, f := range p.outputFormats {
+ for _, p := range s.workAllPages {
+ for _, f := range p.m.configuredOutputFormats {
if !formatSet[f.Name] {
formats = append(formats, f)
formatSet[f.Name] = true
@@ -182,10 +309,30 @@ func (s *Site) initRenderFormats() {
}
}
+ // Add the per kind configured output formats
+ for _, kind := range allKindsInPages {
+ if siteFormats, found := s.outputFormats[kind]; found {
+ for _, f := range siteFormats {
+ if !formatSet[f.Name] {
+ formats = append(formats, f)
+ formatSet[f.Name] = true
+ }
+ }
+ }
+ }
+
sort.Sort(formats)
s.renderFormats = formats
}
+func (s *Site) GetRelatedDocsHandler() *page.RelatedDocsHandler {
+ return s.relatedDocsHandler
+}
+
+func (s *Site) Language() *langs.Language {
+ return s.language
+}
+
func (s *Site) isEnabled(kind string) bool {
if kind == kindUnknown {
panic("Unknown kind")
@@ -199,19 +346,23 @@ func (s *Site) reset() *Site {
layoutHandler: output.NewLayoutHandler(),
disabledKinds: s.disabledKinds,
titleFunc: s.titleFunc,
- relatedDocsHandler: newSearchIndexHandler(s.relatedDocsHandler.cfg),
+ relatedDocsHandler: s.relatedDocsHandler.Clone(),
siteRefLinker: s.siteRefLinker,
outputFormats: s.outputFormats,
rc: s.rc,
outputFormatsConfig: s.outputFormatsConfig,
frontmatterHandler: s.frontmatterHandler,
mediaTypesConfig: s.mediaTypesConfig,
- Language: s.Language,
- owner: s.owner,
+ language: s.language,
+ h: s.h,
publisher: s.publisher,
- siteConfig: s.siteConfig,
+ siteConfigConfig: s.siteConfigConfig,
enableInlineShortcodes: s.enableInlineShortcodes,
- PageCollections: newPageCollections()}
+ buildStats: &buildStats{},
+ init: s.init,
+ PageCollections: newPageCollections(),
+ siteCfg: s.siteCfg,
+ }
}
@@ -262,6 +413,8 @@ func newSite(cfg deps.DepsCfg) (*Site, error) {
return nil, err
}
+ taxonomies := cfg.Language.GetStringMapString("taxonomies")
+
var relatedContentConfig related.Config
if cfg.Language.IsSet("related") {
@@ -271,7 +424,6 @@ func newSite(cfg deps.DepsCfg) (*Site, error) {
}
} else {
relatedContentConfig = related.DefaultConfig
- taxonomies := cfg.Language.GetStringMapString("taxonomies")
if _, found := taxonomies["tag"]; found {
relatedContentConfig.Add(related.IndexConfig{Name: "tags", Weight: 80})
}
@@ -284,21 +436,33 @@ func newSite(cfg deps.DepsCfg) (*Site, error) {
return nil, err
}
+ siteConfig := siteConfigHolder{
+ sitemap: config.DecodeSitemap(config.Sitemap{Priority: -1, Filename: "sitemap.xml"}, cfg.Language.GetStringMap("sitemap")),
+ taxonomiesConfig: taxonomies,
+ timeout: time.Duration(cfg.Language.GetInt("timeout")) * time.Millisecond,
+ hasCJKLanguage: cfg.Language.GetBool("hasCJKLanguage"),
+ enableEmoji: cfg.Language.Cfg.GetBool("enableEmoji"),
+ }
+
s := &Site{
PageCollections: c,
layoutHandler: output.NewLayoutHandler(),
- Language: cfg.Language,
+ language: cfg.Language,
disabledKinds: disabledKinds,
titleFunc: titleFunc,
- relatedDocsHandler: newSearchIndexHandler(relatedContentConfig),
+ relatedDocsHandler: page.NewRelatedDocsHandler(relatedContentConfig),
outputFormats: outputFormats,
rc: &siteRenderingContext{output.HTMLFormat},
outputFormatsConfig: siteOutputFormatsConfig,
mediaTypesConfig: siteMediaTypesConfig,
frontmatterHandler: frontMatterHandler,
+ buildStats: &buildStats{},
enableInlineShortcodes: cfg.Language.GetBool("enableInlineShortcodes"),
+ siteCfg: siteConfig,
}
+ s.prepareInits()
+
return s, nil
}
@@ -372,52 +536,94 @@ func NewSiteForCfg(cfg deps.DepsCfg) (*Site, error) {
}
-type SiteInfos []*SiteInfo
+type SiteInfo struct {
+ Authors page.AuthorList
+ Social SiteSocial
-// First is a convenience method to get the first Site, i.e. the main language.
-func (s SiteInfos) First() *SiteInfo {
- if len(s) == 0 {
- return nil
- }
- return s[0]
-}
+ hugoInfo hugo.Info
+ title string
+ RSSLink string
+ Author map[string]interface{}
+ LanguageCode string
+ Copyright string
+
+ permalinks map[string]string
+
+ LanguagePrefix string
+ Languages langs.Languages
+
+ BuildDrafts bool
+
+ canonifyURLs bool
+ relativeURLs bool
+ uglyURLs func(p page.Page) bool
-type SiteInfo struct {
- Taxonomies TaxonomyList
- Authors AuthorList
- Social SiteSocial
- *PageCollections
- Menus *Menus
- hugoInfo hugo.Info
- Title string
- RSSLink string
- Author map[string]interface{}
- LanguageCode string
- Copyright string
- LastChange time.Time
- Permalinks PermalinkOverrides
- Params map[string]interface{}
- BuildDrafts bool
- canonifyURLs bool
- relativeURLs bool
- uglyURLs func(p *Page) bool
- preserveTaxonomyNames bool
- Data *map[string]interface{}
owner *HugoSites
s *Site
language *langs.Language
- LanguagePrefix string
- Languages langs.Languages
defaultContentLanguageInSubdir bool
sectionPagesMenu string
}
+func (s *SiteInfo) Pages() page.Pages {
+ return s.s.Pages()
+
+}
+
+func (s *SiteInfo) RegularPages() page.Pages {
+ return s.s.RegularPages()
+
+}
+
+func (s *SiteInfo) AllPages() page.Pages {
+ return s.s.AllPages()
+}
+
+func (s *SiteInfo) AllRegularPages() page.Pages {
+ return s.s.AllRegularPages()
+}
+
+func (s *SiteInfo) Permalinks() map[string]string {
+ // Remove in 0.57
+ helpers.Deprecated("Site", ".Permalinks", "", false)
+ return s.permalinks
+}
+
+func (s *SiteInfo) LastChange() time.Time {
+ return s.s.lastmod
+}
+
+func (s *SiteInfo) Title() string {
+ return s.title
+}
+
+func (s *SiteInfo) Site() page.Site {
+ return s
+}
+
+func (s *SiteInfo) Menus() navigation.Menus {
+ return s.s.Menus()
+}
+
+// TODO(bep) type
+func (s *SiteInfo) Taxonomies() interface{} {
+ return s.s.Taxonomies
+}
+
+func (s *SiteInfo) Params() map[string]interface{} {
+ return s.s.Language().Params()
+}
+
+func (s *SiteInfo) Data() map[string]interface{} {
+ return s.s.h.Data()
+}
+
func (s *SiteInfo) Language() *langs.Language {
return s.language
}
func (s *SiteInfo) Config() SiteConfig {
- return s.s.siteConfig
+ return s.s.siteConfigConfig
}
func (s *SiteInfo) Hugo() hugo.Info {
@@ -425,11 +631,12 @@ func (s *SiteInfo) Hugo() hugo.Info {
}
// Sites is a convenience method to get all the Hugo sites/languages configured.
-func (s *SiteInfo) Sites() SiteInfos {
- return s.s.owner.siteInfos()
+func (s *SiteInfo) Sites() page.Sites {
+ return s.s.h.siteInfos()
}
+
func (s *SiteInfo) String() string {
- return fmt.Sprintf("Site(%q)", s.Title)
+ return fmt.Sprintf("Site(%q)", s.title)
}
func (s *SiteInfo) BaseURL() template.URL {
@@ -484,7 +691,7 @@ func (s *SiteInfo) Param(key interface{}) (interface{}, error) {
return nil, err
}
keyStr = strings.ToLower(keyStr)
- return s.Params[keyStr], nil
+ return s.Params()[keyStr], nil
}
func (s *SiteInfo) IsMultiLingual() bool {
@@ -513,28 +720,24 @@ func newSiteRefLinker(cfg config.Provider, s *Site) (siteRefLinker, error) {
return siteRefLinker{s: s, errorLogger: logger, notFoundURL: notFoundURL}, nil
}
-func (s siteRefLinker) logNotFound(ref, what string, p *Page, position text.Position) {
+func (s siteRefLinker) logNotFound(ref, what string, p page.Page, position text.Position) {
if position.IsValid() {
s.errorLogger.Printf("[%s] REF_NOT_FOUND: Ref %q: %s: %s", s.s.Lang(), ref, position.String(), what)
} else if p == nil {
s.errorLogger.Printf("[%s] REF_NOT_FOUND: Ref %q: %s", s.s.Lang(), ref, what)
} else {
- s.errorLogger.Printf("[%s] REF_NOT_FOUND: Ref %q from page %q: %s", s.s.Lang(), ref, p.pathOrTitle(), what)
+ s.errorLogger.Printf("[%s] REF_NOT_FOUND: Ref %q from page %q: %s", s.s.Lang(), ref, p.Path(), what)
}
}
func (s *siteRefLinker) refLink(ref string, source interface{}, relative bool, outputFormat string) (string, error) {
- var page *Page
- switch v := source.(type) {
- case *Page:
- page = v
- case pageContainer:
- page = v.page()
+ p, err := unwrapPage(source)
+ if err != nil {
+ return "", err
}
var refURL *url.URL
- var err error
ref = filepath.ToSlash(ref)
@@ -544,11 +747,11 @@ func (s *siteRefLinker) refLink(ref string, source interface{}, relative bool, o
return s.notFoundURL, err
}
- var target *Page
+ var target page.Page
var link string
if refURL.Path != "" {
- target, err := s.s.getPageNew(page, refURL.Path)
+ target, err := s.s.getPageNew(p, refURL.Path)
var pos text.Position
if err != nil || target == nil {
if p, ok := source.(text.Positioner); ok {
@@ -558,12 +761,12 @@ func (s *siteRefLinker) refLink(ref string, source interface{}, relative bool, o
}
if err != nil {
- s.logNotFound(refURL.Path, err.Error(), page, pos)
+ s.logNotFound(refURL.Path, err.Error(), p, pos)
return s.notFoundURL, nil
}
if target == nil {
- s.logNotFound(refURL.Path, "page not found", page, pos)
+ s.logNotFound(refURL.Path, "page not found", p, pos)
return s.notFoundURL, nil
}
@@ -573,7 +776,7 @@ func (s *siteRefLinker) refLink(ref string, source interface{}, relative bool, o
o := target.OutputFormats().Get(outputFormat)
if o == nil {
- s.logNotFound(refURL.Path, fmt.Sprintf("output format %q", outputFormat), page, pos)
+ s.logNotFound(refURL.Path, fmt.Sprintf("output format %q", outputFormat), p, pos)
return s.notFoundURL, nil
}
permalinker = o
@@ -587,22 +790,24 @@ func (s *siteRefLinker) refLink(ref string, source interface{}, relative bool, o
}
if refURL.Fragment != "" {
+ _ = target
link = link + "#" + refURL.Fragment
-
- if refURL.Path != "" && target != nil && !target.getRenderingConfig().PlainIDAnchors {
- link = link + ":" + target.UniqueID()
- } else if page != nil && !page.getRenderingConfig().PlainIDAnchors {
- link = link + ":" + page.UniqueID()
+ if pctx, ok := target.(pageContext); ok && target.File() != nil && !pctx.getRenderingConfig().PlainIDAnchors {
+ if refURL.Path != "" {
+ link = link + ":" + target.File().UniqueID()
+ }
+ } else if pctx, ok := p.(pageContext); ok && p.File() != nil && !pctx.getRenderingConfig().PlainIDAnchors {
+ link = link + ":" + p.File().UniqueID()
}
- }
+ }
return link, nil
}
// Ref will give an absolute URL to ref in the given Page.
-func (s *SiteInfo) Ref(ref string, page *Page, options ...string) (string, error) {
- // Remove in Hugo 0.53
- helpers.Deprecated("Site", ".Ref", "Use .Site.GetPage", false)
+func (s *SiteInfo) Ref(ref string, page page.Page, options ...string) (string, error) {
+ // Remove in Hugo 0.54
+ helpers.Deprecated("Site", ".Ref", "Use .Site.GetPage", true)
outputFormat := ""
if len(options) > 0 {
outputFormat = options[0]
@@ -612,9 +817,9 @@ func (s *SiteInfo) Ref(ref string, page *Page, options ...string) (string, error
}
// RelRef will give an relative URL to ref in the given Page.
-func (s *SiteInfo) RelRef(ref string, page *Page, options ...string) (string, error) {
- // Remove in Hugo 0.53
- helpers.Deprecated("Site", ".RelRef", "Use .Site.GetPage", false)
+func (s *SiteInfo) RelRef(ref string, page page.Page, options ...string) (string, error) {
+ // Remove in Hugo 0.54
+ helpers.Deprecated("Site", ".RelRef", "Use .Site.GetPage", true)
outputFormat := ""
if len(options) > 0 {
outputFormat = options[0]
@@ -624,22 +829,11 @@ func (s *SiteInfo) RelRef(ref string, page *Page, options ...string) (string, er
}
func (s *Site) running() bool {
- return s.owner != nil && s.owner.running
+ return s.h != nil && s.h.running
}
func (s *Site) multilingual() *Multilingual {
- return s.owner.multilingual
-}
-
-func init() {
- defaultTimer = nitro.Initalize()
-}
-
-func (s *Site) timerStep(step string) {
- if s.timer == nil {
- s.timer = defaultTimer
- }
- s.timer.Step(step)
+ return s.h.multilingual
}
type whatChanged struct {
@@ -737,9 +931,7 @@ func (s *Site) processPartial(events []fsnotify.Event) (whatChanged, error) {
s.Log.DEBUG.Printf("Rebuild for events %q", events)
- h := s.owner
-
- s.timerStep("initialize rebuild")
+ h := s.h
// First we need to determine what changed
@@ -771,7 +963,6 @@ func (s *Site) processPartial(events []fsnotify.Event) (whatChanged, error) {
tmplChanged = append(tmplChanged, ev)
if strings.Contains(ev.Name, "shortcodes") {
- clearIsInnerShortcodeCache()
shortcode := filepath.Base(ev.Name)
shortcode = strings.TrimSuffix(shortcode, filepath.Ext(shortcode))
shortcodesChanged[shortcode] = true
@@ -788,14 +979,16 @@ func (s *Site) processPartial(events []fsnotify.Event) (whatChanged, error) {
}
// These in memory resource caches will be rebuilt on demand.
- for _, s := range s.owner.Sites {
+ for _, s := range s.h.Sites {
s.ResourceSpec.ResourceCache.DeletePartitions(cachePartitions...)
}
if len(tmplChanged) > 0 || len(i18nChanged) > 0 {
- sites := s.owner.Sites
+ sites := s.h.Sites
first := sites[0]
+ s.h.init.Reset()
+
// TOD(bep) globals clean
if err := first.Deps.LoadResources(); err != nil {
return whatChanged{}, err
@@ -805,7 +998,7 @@ func (s *Site) processPartial(events []fsnotify.Event) (whatChanged, error) {
site := sites[i]
var err error
depsCfg := deps.DepsCfg{
- Language: site.Language,
+ Language: site.language,
MediaTypes: site.mediaTypesConfig,
OutputFormats: site.outputFormatsConfig,
}
@@ -817,14 +1010,10 @@ func (s *Site) processPartial(events []fsnotify.Event) (whatChanged, error) {
return whatChanged{}, err
}
}
-
- s.timerStep("template prep")
}
if len(dataChanged) > 0 {
- if err := s.readDataFromSourceFS(); err != nil {
- return whatChanged{}, err
- }
+ s.h.init.data.Reset()
}
for _, ev := range sourceChanged {
@@ -860,7 +1049,7 @@ func (s *Site) processPartial(events []fsnotify.Event) (whatChanged, error) {
// pages that keeps a reference to the changed shortcode.
pagesWithShortcode := h.findPagesByShortcode(shortcode)
for _, p := range pagesWithShortcode {
- contentFilesChanged = append(contentFilesChanged, p.File.Filename())
+ contentFilesChanged = append(contentFilesChanged, p.File().Filename())
}
}
@@ -891,193 +1080,72 @@ func (s *Site) processPartial(events []fsnotify.Event) (whatChanged, error) {
}
-func (s *Site) loadData(fs afero.Fs) (err error) {
- spec := src.NewSourceSpec(s.PathSpec, fs)
- fileSystem := spec.NewFilesystem("")
- s.Data = make(map[string]interface{})
- for _, r := range fileSystem.Files() {
- if err := s.handleDataFile(r); err != nil {
- return err
- }
+func (s *Site) process(config BuildCfg) (err error) {
+ if err = s.initialize(); err != nil {
+ return
}
-
- return
-}
-
-func (s *Site) errWithFileContext(err error, f source.File) error {
- rfi, ok := f.FileInfo().(hugofs.RealFilenameInfo)
- if !ok {
+ if err := s.readAndProcessContent(); err != nil {
return err
}
-
- realFilename := rfi.RealFilename()
-
- err, _ = herrors.WithFileContextForFile(
- err,
- realFilename,
- realFilename,
- s.SourceSpec.Fs.Source,
- herrors.SimpleLineMatcher)
-
return err
-}
-func (s *Site) handleDataFile(r source.ReadableFile) error {
- var current map[string]interface{}
-
- f, err := r.Open()
- if err != nil {
- return _errors.Wrapf(err, "Failed to open data file %q:", r.LogicalName())
- }
- defer f.Close()
-
- // Crawl in data tree to insert data
- current = s.Data
- keyParts := strings.Split(r.Dir(), helpers.FilePathSeparator)
- // The first path element is the virtual folder (typically theme name), which is
- // not part of the key.
- if len(keyParts) > 1 {
- for _, key := range keyParts[1:] {
- if key != "" {
- if _, ok := current[key]; !ok {
- current[key] = make(map[string]interface{})
- }
- current = current[key].(map[string]interface{})
- }
- }
- }
-
- data, err := s.readData(r)
- if err != nil {
- return s.errWithFileContext(err, r)
- }
-
- if data == nil {
- return nil
- }
-
- // filepath.Walk walks the files in lexical order, '/' comes before '.'
- // this warning could happen if
- // 1. A theme uses the same key; the main data folder wins
- // 2. A sub folder uses the same key: the sub folder wins
- higherPrecedentData := current[r.BaseFileName()]
-
- switch data.(type) {
- case nil:
- // hear the crickets?
-
- case map[string]interface{}:
-
- switch higherPrecedentData.(type) {
- case nil:
- current[r.BaseFileName()] = data
- case map[string]interface{}:
- // merge maps: insert entries from data for keys that
- // don't already exist in higherPrecedentData
- higherPrecedentMap := higherPrecedentData.(map[string]interface{})
- for key, value := range data.(map[string]interface{}) {
- if _, exists := higherPrecedentMap[key]; exists {
- s.Log.WARN.Printf("Data for key '%s' in path '%s' is overridden by higher precedence data already in the data tree", key, r.Path())
- } else {
- higherPrecedentMap[key] = value
- }
- }
- default:
- // can't merge: higherPrecedentData is not a map
- s.Log.WARN.Printf("The %T data from '%s' overridden by "+
- "higher precedence %T data already in the data tree", data, r.Path(), higherPrecedentData)
- }
-
- case []interface{}:
- if higherPrecedentData == nil {
- current[r.BaseFileName()] = data
- } else {
- // we don't merge array data
- s.Log.WARN.Printf("The %T data from '%s' overridden by "+
- "higher precedence %T data already in the data tree", data, r.Path(), higherPrecedentData)
- }
-
- default:
- s.Log.ERROR.Printf("unexpected data type %T in file %s", data, r.LogicalName())
- }
-
- return nil
}
-func (s *Site) readData(f source.ReadableFile) (interface{}, error) {
- file, err := f.Open()
- if err != nil {
- return nil, _errors.Wrap(err, "readData: failed to open data file")
+func (s *Site) setupSitePages() {
+ var homeDates *resource.Dates
+ if s.home != nil {
+ // If the home page has no dates set, we fall back to the site dates.
+ homeDates = &s.home.m.Dates
}
- defer file.Close()
- content := helpers.ReaderToBytes(file)
-
- format := metadecoders.FormatFromString(f.Extension())
- return metadecoders.Default.Unmarshal(content, format)
-}
-func (s *Site) readDataFromSourceFS() error {
- err := s.loadData(s.PathSpec.BaseFs.Data.Fs)
- s.timerStep("load data")
- return err
-}
-
-func (s *Site) process(config BuildCfg) (err error) {
- if err = s.initialize(); err != nil {
+ if !s.lastmod.IsZero() && (homeDates == nil || !resource.IsZeroDates(homeDates)) {
return
}
- s.timerStep("initialize")
- if err = s.readDataFromSourceFS(); err != nil {
+ if homeDates != nil && !s.lastmod.IsZero() {
+ homeDates.FDate = s.lastmod
+ homeDates.FLastmod = s.lastmod
return
- }
-
- s.timerStep("load i18n")
- if err := s.readAndProcessContent(); err != nil {
- return err
}
- s.timerStep("read and convert pages from source")
- return err
+ var siteLastmod time.Time
+ var siteLastDate time.Time
-}
-
-func (s *Site) setupSitePages() {
- var siteLastChange time.Time
-
- for i, page := range s.RegularPages {
- if i > 0 {
- page.NextPage = s.RegularPages[i-1]
- }
-
- if i < len(s.RegularPages)-1 {
- page.PrevPage = s.RegularPages[i+1]
+ for _, page := range s.workAllPages {
+ if !page.IsPage() {
+ continue
}
-
// Determine Site.Info.LastChange
// Note that the logic to determine which date to use for Lastmod
// is already applied, so this is *the* date to use.
// We cannot just pick the last page in the default sort, because
// that may not be ordered by date.
- if page.Lastmod.After(siteLastChange) {
- siteLastChange = page.Lastmod
+ // TODO(bep) check if this can be done earlier
+ if page.Lastmod().After(siteLastmod) {
+ siteLastmod = page.Lastmod()
}
+ if page.Date().After(siteLastDate) {
+ siteLastDate = page.Date()
+ }
+ }
+
+ s.lastmod = siteLastmod
+
+ if homeDates != nil && resource.IsZeroDates(homeDates) {
+ homeDates.FDate = siteLastDate
+ homeDates.FLastmod = s.lastmod
}
- s.Info.LastChange = siteLastChange
}
-func (s *Site) render(config *BuildCfg, outFormatIdx int) (err error) {
- // Clear the global page cache.
- spc.clear()
+func (s *Site) render(ctx *siteRenderContext) (err error) {
- if outFormatIdx == 0 {
- if err = s.preparePages(); err != nil {
- return
- }
- s.timerStep("prepare pages")
+ if err := page.Clear(); err != nil {
+ return err
+ }
+ if ctx.outIdx == 0 {
// Note that even if disableAliases is set, the aliases themselves are
// preserved on page. The motivation with this is to be able to generate