diff --git a/hugolib/page__output.go b/hugolib/page__output.go index 377e16df522..7f1140e74d8 100644 --- a/hugolib/page__output.go +++ b/hugolib/page__output.go @@ -55,9 +55,21 @@ func newPageOutput( } po := &pageOutput{ - f: f, - pagePerOutputProviders: providers, - ContentProvider: page.NopPage, + f: f, + pagePerOutputProviders: providers, + // We attempt to assign pageContentOutputs while preparing each site + // for rendering and before rendering each site. This lets us share + // content between page outputs and conserve resources. However, this + // does not guarantee that all pageContentOutputs required by templates + // will be assigned. In the case of an unanticipated request, we lazily + // initialize a pageContentOutput. + ContentProvider: page.NewLazyContentProvider(func() (page.ContentProvider, error) { + cp, err := newPageContentOutput(ps, ps.pageOutput) + if err != nil { + return nil, err + } + return cp, nil + }), TableOfContentsProvider: page.NopPage, render: render, paginator: pag, diff --git a/hugolib/page_test.go b/hugolib/page_test.go index 7d55787c8e3..fc01bbf25eb 100644 --- a/hugolib/page_test.go +++ b/hugolib/page_test.go @@ -36,6 +36,7 @@ import ( "github.com/gohugoio/hugo/resources/page" "github.com/gohugoio/hugo/resources/resource" "github.com/spf13/afero" + "github.com/spf13/jwalterweatherman" qt "github.com/frankban/quicktest" "github.com/gohugoio/hugo/deps" @@ -767,6 +768,184 @@ Here is the last report for commits in the year 2016. It covers hrev50718-hrev50 `) } +// Issue 8919 +func TestContentProviderWithCustomOutputFormat(t *testing.T) { + b := newTestSitesBuilder(t) + b.WithLogger(loggers.NewBasicLoggerForWriter(jwalterweatherman.LevelDebug, os.Stderr)) + b.WithConfigFile("toml", `baseURL = 'http://example.org/' +title = 'My New Hugo Site' + +timeout = 600000 # ten minutes in case we want to pause and debug + +defaultContentLanguage = "en" + +[languages] + [languages.en] + title = "Repro" + languageName = "English" + contentDir = "content/en" + + [languages.zh_CN] + title = "Repro" + languageName = "简体中文" + contentDir = "content/zh_CN" + +[outputFormats] + [outputFormats.metadata] + baseName = "metadata" + mediaType = "text/html" + isPlainText = true + notAlternative = true + +[outputs] + home = ["HTML", "metadata"]`) + + b.WithTemplates("home.metadata.html", `

Translations metadata

+`) + + b.WithTemplates("_default/baseof.html", ` + + + {{ block "main" . }}{{ end }} + + +`) + + b.WithTemplates("_default/home.html", `{{ define "main" }} +

Translations

+ +{{ end }}`) + + b.WithContent("en/_index.md", `--- +title: Title (en) +summary: Summary (en) +--- + +Here is some content. +`) + + b.WithContent("zh_CN/_index.md", `--- +title: Title (zh) +summary: Summary (zh) +--- + +这是一些内容 +`) + + b.Build(BuildCfg{}) + + b.AssertFileContent("public/index.html", ` + + + +

Translations

+ + + + +`) + b.AssertFileContent("public/metadata.html", `

Translations metadata

+`) + b.AssertFileContent("public/zh_cn/index.html", ` + + + +

Translations

+ + + + +`) + b.AssertFileContent("public/zh_cn/metadata.html", `

Translations metadata

+`) +} + func TestPageWithDate(t *testing.T) { t.Parallel() cfg, fs := newTestCfg() diff --git a/resources/page/page_lazy_contentprovider.go b/resources/page/page_lazy_contentprovider.go new file mode 100644 index 00000000000..d8d92d7cdc4 --- /dev/null +++ b/resources/page/page_lazy_contentprovider.go @@ -0,0 +1,101 @@ +// 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 page + +import ( + "html/template" + + "github.com/gohugoio/hugo/lazy" +) + +// LazyContentProvider initializes itself when read. Each method of the +// ContentProvider interface initializes a content provider and shares it +// with other methods. +// +// Used in cases where we cannot guarantee whether the content provider +// will be needed. Must create via NewLazyContentProvider. +type LazyContentProvider struct { + init *lazy.Init + cp ContentProvider +} + +// NewLazyContentProvider returns a LazyContentProvider initialized with +// function f. The resulting LazyContentProvider calls f in order to +// retrieve a ContentProvider +func NewLazyContentProvider(f func() (ContentProvider, error)) *LazyContentProvider { + lcp := LazyContentProvider{ + init: lazy.New(), + cp: NopPage, + } + lcp.init.Add(func() (interface{}, error) { + cp, err := f() + if err != nil { + return nil, err + } + lcp.cp = cp + return nil, nil + }) + return &lcp +} + +func (lcp *LazyContentProvider) Content() (interface{}, error) { + lcp.init.Do() + return lcp.cp.Content() +} + +func (lcp *LazyContentProvider) Plain() string { + lcp.init.Do() + return lcp.cp.Plain() +} + +func (lcp *LazyContentProvider) PlainWords() []string { + lcp.init.Do() + return lcp.cp.PlainWords() +} + +func (lcp *LazyContentProvider) Summary() template.HTML { + lcp.init.Do() + return lcp.cp.Summary() + +} + +func (lcp *LazyContentProvider) Truncated() bool { + lcp.init.Do() + return lcp.cp.Truncated() + +} + +func (lcp *LazyContentProvider) FuzzyWordCount() int { + lcp.init.Do() + return lcp.cp.FuzzyWordCount() + +} + +func (lcp *LazyContentProvider) WordCount() int { + lcp.init.Do() + return lcp.cp.WordCount() + +} + +func (lcp *LazyContentProvider) ReadingTime() int { + lcp.init.Do() + return lcp.cp.ReadingTime() + +} + +func (lcp *LazyContentProvider) Len() int { + lcp.init.Do() + return lcp.cp.Len() + +}