summaryrefslogtreecommitdiffstats
path: root/hugolib
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2024-01-29 10:02:24 +0100
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2024-01-30 20:12:03 +0100
commitf31a6db797c9251a362ef9f8ad4c03fb608b5ac0 (patch)
treea47dbb5764cfc96c49882e13a7da279dc43b1eb5 /hugolib
parentec22bb31a89883db5ca95404cda4f74344fd3762 (diff)
Add path, kind and lang to content front matter
Note that none of these can be set via cascade (you will get an error) Fixes #11544
Diffstat (limited to 'hugolib')
-rw-r--r--hugolib/content_map.go6
-rw-r--r--hugolib/content_map_page.go89
-rw-r--r--hugolib/hugo_sites.go3
-rw-r--r--hugolib/integrationtest_builder.go9
-rw-r--r--hugolib/page.go33
-rw-r--r--hugolib/page__common.go3
-rw-r--r--hugolib/page__content.go180
-rw-r--r--hugolib/page__meta.go352
-rw-r--r--hugolib/page__new.go112
-rw-r--r--hugolib/page__paths.go2
-rw-r--r--hugolib/page__per_output.go27
-rw-r--r--hugolib/params_test.go109
-rw-r--r--hugolib/shortcode.go4
-rw-r--r--hugolib/site_new.go2
-rw-r--r--hugolib/site_render.go2
15 files changed, 595 insertions, 338 deletions
diff --git a/hugolib/content_map.go b/hugolib/content_map.go
index fefa90bf1..96013c4ed 100644
--- a/hugolib/content_map.go
+++ b/hugolib/content_map.go
@@ -187,7 +187,7 @@ func (m *pageMap) AddFi(fi hugofs.FileMetaInfo) error {
if pi.IsContent() {
// Create the page now as we need it at assemembly time.
// The other resources are created if needed.
- pageResource, err := m.s.h.newPage(
+ pageResource, pi, err := m.s.h.newPage(
&pageMeta{
f: source.NewFileInfo(fim),
pathInfo: pi,
@@ -197,6 +197,8 @@ func (m *pageMap) AddFi(fi hugofs.FileMetaInfo) error {
if err != nil {
return err
}
+ key = pi.Base()
+
rs = &resourceSource{r: pageResource}
} else {
rs = &resourceSource{path: pi, opener: r, fi: fim}
@@ -226,7 +228,7 @@ func (m *pageMap) AddFi(fi hugofs.FileMetaInfo) error {
},
))
// A content file.
- p, err := m.s.h.newPage(
+ p, pi, err := m.s.h.newPage(
&pageMeta{
f: source.NewFileInfo(fi),
pathInfo: pi,
diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go
index 536f23ccd..9fee74003 100644
--- a/hugolib/content_map_page.go
+++ b/hugolib/content_map_page.go
@@ -43,6 +43,7 @@ import (
"github.com/gohugoio/hugo/resources/kinds"
"github.com/gohugoio/hugo/resources/page"
+ "github.com/gohugoio/hugo/resources/page/pagemeta"
"github.com/gohugoio/hugo/resources/resource"
)
@@ -97,7 +98,6 @@ type pageMap struct {
cacheContentRendered *dynacache.Partition[string, *resources.StaleValue[contentSummary]]
cacheContentPlain *dynacache.Partition[string, *resources.StaleValue[contentPlainPlainWords]]
contentTableOfContents *dynacache.Partition[string, *resources.StaleValue[contentTableOfContents]]
- cacheContentSource *dynacache.Partition[string, *resources.StaleValue[[]byte]]
cfg contentMapConfig
}
@@ -147,7 +147,6 @@ func (t *pageTrees) collectIdentities(key string) []identity.Identity {
// collectIdentitiesSurrounding collects all identities surrounding the given key.
func (t *pageTrees) collectIdentitiesSurrounding(key string, maxSamplesPerTree int) []identity.Identity {
- // TODO1 test language coverage from this.
ids := t.collectIdentitiesSurroundingIn(key, maxSamplesPerTree, t.treePages)
ids = append(ids, t.collectIdentitiesSurroundingIn(key, maxSamplesPerTree, t.treeResources)...)
return ids
@@ -483,7 +482,7 @@ func (m *pageMap) getOrCreateResourcesForPage(ps *pageState) resource.Resources
return nil, err
}
- if translationKey := ps.m.translationKey; translationKey != "" {
+ if translationKey := ps.m.pageConfig.TranslationKey; translationKey != "" {
// This this should not be a very common case.
// Merge in resources from the other languages.
translatedPages, _ := m.s.h.translationKeyPages.Get(translationKey)
@@ -539,9 +538,9 @@ func (m *pageMap) getOrCreateResourcesForPage(ps *pageState) resource.Resources
sort.SliceStable(res, lessFunc)
- if len(ps.m.resourcesMetadata) > 0 {
+ if len(ps.m.pageConfig.Resources) > 0 {
for i, r := range res {
- res[i] = resources.CloneWithMetadataIfNeeded(ps.m.resourcesMetadata, r)
+ res[i] = resources.CloneWithMetadataIfNeeded(ps.m.pageConfig.Resources, r)
}
sort.SliceStable(res, lessFunc)
}
@@ -819,7 +818,6 @@ func newPageMap(i int, s *Site, mcache *dynacache.Cache, pageTrees *pageTrees) *
cacheContentRendered: dynacache.GetOrCreatePartition[string, *resources.StaleValue[contentSummary]](mcache, fmt.Sprintf("/cont/ren/%d", i), dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
cacheContentPlain: dynacache.GetOrCreatePartition[string, *resources.StaleValue[contentPlainPlainWords]](mcache, fmt.Sprintf("/cont/pla/%d", i), dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
contentTableOfContents: dynacache.GetOrCreatePartition[string, *resources.StaleValue[contentTableOfContents]](mcache, fmt.Sprintf("/cont/toc/%d", i), dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
- cacheContentSource: dynacache.GetOrCreatePartition[string, *resources.StaleValue[[]byte]](mcache, fmt.Sprintf("/cont/src/%d", i), dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
cfg: contentMapConfig{
lang: s.Lang(),
@@ -1215,7 +1213,7 @@ func (sa *sitePagesAssembler) applyAggregates() error {
// Home page gets it's cascade from the site config.
cascade = sa.conf.Cascade.Config
- if pageBundle.m.cascade == nil {
+ if pageBundle.m.pageConfig.Cascade == nil {
// Pass the site cascade downwards.
pw.WalkContext.Data().Insert(keyPage, cascade)
}
@@ -1227,12 +1225,12 @@ func (sa *sitePagesAssembler) applyAggregates() error {
}
if (pageBundle.IsHome() || pageBundle.IsSection()) && pageBundle.m.setMetaPostCount > 0 {
- oldDates := pageBundle.m.dates
+ oldDates := pageBundle.m.pageConfig.Dates
// We need to wait until after the walk to determine if any of the dates have changed.
pw.WalkContext.AddPostHook(
func() error {
- if oldDates != pageBundle.m.dates {
+ if oldDates != pageBundle.m.pageConfig.Dates {
sa.assembleChanges.Add(pageBundle)
}
return nil
@@ -1251,11 +1249,12 @@ func (sa *sitePagesAssembler) applyAggregates() error {
const eventName = "dates"
if n.isContentNodeBranch() {
- if pageBundle.m.cascade != nil {
+ if pageBundle.m.pageConfig.Cascade != nil {
// Pass it down.
- pw.WalkContext.Data().Insert(keyPage, pageBundle.m.cascade)
+ pw.WalkContext.Data().Insert(keyPage, pageBundle.m.pageConfig.Cascade)
}
- wasZeroDates := resource.IsZeroDates(pageBundle.m.dates)
+
+ wasZeroDates := pageBundle.m.pageConfig.Dates.IsAllDatesZero()
if wasZeroDates || pageBundle.IsHome() {
pw.WalkContext.AddEventListener(eventName, keyPage, func(e *doctree.Event[contentNodeI]) {
sp, ok := e.Source.(*pageState)
@@ -1264,15 +1263,15 @@ func (sa *sitePagesAssembler) applyAggregates() error {
}
if wasZeroDates {
- pageBundle.m.dates.UpdateDateAndLastmodIfAfter(sp.m.dates)
+ pageBundle.m.pageConfig.Dates.UpdateDateAndLastmodIfAfter(sp.m.pageConfig.Dates)
}
if pageBundle.IsHome() {
- if pageBundle.m.dates.Lastmod().After(pageBundle.s.lastmod) {
- pageBundle.s.lastmod = pageBundle.m.dates.Lastmod()
+ if pageBundle.m.pageConfig.Dates.Lastmod.After(pageBundle.s.lastmod) {
+ pageBundle.s.lastmod = pageBundle.m.pageConfig.Dates.Lastmod
}
- if sp.m.dates.Lastmod().After(pageBundle.s.lastmod) {
- pageBundle.s.lastmod = sp.m.dates.Lastmod()
+ if sp.m.pageConfig.Dates.Lastmod.After(pageBundle.s.lastmod) {
+ pageBundle.s.lastmod = sp.m.pageConfig.Dates.Lastmod
}
}
})
@@ -1351,9 +1350,9 @@ func (sa *sitePagesAssembler) applyAggregatesToTaxonomiesAndTerms() error {
p := n.(*pageState)
if p.Kind() != kinds.KindTerm {
// The other kinds were handled in applyAggregates.
- if p.m.cascade != nil {
+ if p.m.pageConfig.Cascade != nil {
// Pass it down.
- pw.WalkContext.Data().Insert(s, p.m.cascade)
+ pw.WalkContext.Data().Insert(s, p.m.pageConfig.Cascade)
}
}
@@ -1388,14 +1387,14 @@ func (sa *sitePagesAssembler) applyAggregatesToTaxonomiesAndTerms() error {
// Send the date info up the tree.
pw.WalkContext.SendEvent(&doctree.Event[contentNodeI]{Source: n, Path: s, Name: eventName})
- if resource.IsZeroDates(p.m.dates) {
+ if p.m.pageConfig.Dates.IsAllDatesZero() {
pw.WalkContext.AddEventListener(eventName, s, func(e *doctree.Event[contentNodeI]) {
sp, ok := e.Source.(*pageState)
if !ok {
return
}
- p.m.dates.UpdateDateAndLastmodIfAfter(sp.m.dates)
+ p.m.pageConfig.Dates.UpdateDateAndLastmodIfAfter(sp.m.pageConfig.Dates)
})
}
@@ -1443,8 +1442,8 @@ func (sa *sitePagesAssembler) assembleTermsAndTranslations() error {
// This is a little out of place, but is conveniently put here.
// Check if translationKey is set by user.
// This is to support the manual way of setting the translationKey in front matter.
- if ps.m.translationKey != "" {
- sa.s.h.translationKeyPages.Append(ps.m.translationKey, ps)
+ if ps.m.pageConfig.TranslationKey != "" {
+ sa.s.h.translationKeyPages.Append(ps.m.pageConfig.TranslationKey, ps)
}
if sa.pageMap.cfg.taxonomyTermDisabled {
@@ -1477,9 +1476,13 @@ func (sa *sitePagesAssembler) assembleTermsAndTranslations() error {
singular: viewName.singular,
s: sa.Site,
pathInfo: pi,
- kind: kinds.KindTerm,
+ pageMetaParams: pageMetaParams{
+ pageConfig: &pagemeta.PageConfig{
+ Kind: kinds.KindTerm,
+ },
+ },
}
- n, err := sa.h.newPage(m)
+ n, pi, err := sa.h.newPage(m)
if err != nil {
return false, err
}
@@ -1524,7 +1527,7 @@ func (sa *sitePagesAssembler) assembleResources() error {
targetPaths := ps.targetPaths()
baseTarget := targetPaths.SubResourceBaseTarget
duplicateResourceFiles := true
- if ps.s.ContentSpec.Converters.IsGoldmark(ps.m.markup) {
+ if ps.s.ContentSpec.Converters.IsGoldmark(ps.m.pageConfig.Markup) {
duplicateResourceFiles = ps.s.ContentSpec.Converters.GetMarkupConfig().Goldmark.DuplicateResourceFiles
}
@@ -1566,7 +1569,7 @@ func (sa *sitePagesAssembler) assembleResources() error {
BasePathTargetPath: baseTarget,
Name: relPath,
NameOriginal: relPathOriginal,
- LazyPublish: !ps.m.buildConfig.PublishResources,
+ LazyPublish: !ps.m.pageConfig.Build.PublishResources,
}
r, err := ps.m.s.ResourceSpec.NewResource(rd)
if err != nil {
@@ -1631,7 +1634,7 @@ func (sa *sitePagesAssembler) removeShouldNotBuild() error {
case kinds.KindHome, kinds.KindSection, kinds.KindTaxonomy:
// We need to keep these for the structure, but disable
// them so they don't get listed/rendered.
- (&p.m.buildConfig).Disable()
+ (&p.m.pageConfig.Build).Disable()
default:
keys = append(keys, key)
}
@@ -1673,13 +1676,17 @@ func (sa *sitePagesAssembler) addStandalonePages() error {
}
m := &pageMeta{
- s: s,
- pathInfo: s.Conf.PathParser().Parse(files.ComponentFolderContent, key+f.MediaType.FirstSuffix.FullSuffix),
- kind: kind,
+ s: s,
+ pathInfo: s.Conf.PathParser().Parse(files.ComponentFolderContent, key+f.MediaType.FirstSuffix.FullSuffix),
+ pageMetaParams: pageMetaParams{
+ pageConfig: &pagemeta.PageConfig{
+ Kind: kind,
+ },
+ },
standaloneOutputFormat: f,
}
- p, _ := s.h.newPage(m)
+ p, _, _ := s.h.newPage(m)
tree.InsertIntoValuesDimension(key, p)
}
@@ -1756,7 +1763,7 @@ func (sa *sitePagesAssembler) addMissingRootSections() error {
pathInfo: pth,
}
- ps, err := sa.h.newPage(m)
+ ps, pth, err := sa.h.newPage(m)
if err != nil {
return false, err
}
@@ -1781,9 +1788,13 @@ func (sa *sitePagesAssembler) addMissingRootSections() error {
m := &pageMeta{
s: sa.Site,
pathInfo: p,
- kind: kinds.KindHome,
+ pageMetaParams: pageMetaParams{
+ pageConfig: &pagemeta.PageConfig{
+ Kind: kinds.KindHome,
+ },
+ },
}
- n, err := sa.h.newPage(m)
+ n, p, err := sa.h.newPage(m)
if err != nil {
return err
}
@@ -1810,10 +1821,14 @@ func (sa *sitePagesAssembler) addMissingTaxonomies() error {
m := &pageMeta{
s: sa.Site,
pathInfo: sa.Conf.PathParser().Parse(files.ComponentFolderContent, key+"/_index.md"),
- kind: kinds.KindTaxonomy,
+ pageMetaParams: pageMetaParams{
+ pageConfig: &pagemeta.PageConfig{
+ Kind: kinds.KindTaxonomy,
+ },
+ },
singular: viewName.singular,
}
- p, _ := sa.h.newPage(m)
+ p, _, _ := sa.h.newPage(m)
tree.InsertIntoValuesDimension(key, p)
}
}
diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go
index 80e754453..1b2840617 100644
--- a/hugolib/hugo_sites.go
+++ b/hugolib/hugo_sites.go
@@ -26,6 +26,7 @@ import (
"github.com/gohugoio/hugo/config/allconfig"
"github.com/gohugoio/hugo/hugofs/glob"
"github.com/gohugoio/hugo/hugolib/doctree"
+ "github.com/gohugoio/hugo/resources"
"github.com/fsnotify/fsnotify"
@@ -72,6 +73,8 @@ type HugoSites struct {
// Cache for page listings.
cachePages *dynacache.Partition[string, page.Pages]
+ // Cache for content sources.
+ cacheContentSource *dynacache.Partition[string, *resources.StaleValue[[]byte]]
// Before Hugo 0.122.0 we managed all translations in a map using a translationKey
// that could be overridden in front matter.
diff --git a/hugolib/integrationtest_builder.go b/hugolib/integrationtest_builder.go
index 3fec04df0..a46ae7275 100644
--- a/hugolib/integrationtest_builder.go
+++ b/hugolib/integrationtest_builder.go
@@ -80,6 +80,15 @@ func Test(t testing.TB, files string, opts ...TestOpt) *IntegrationTestBuilder {
return NewIntegrationTestBuilder(cfg).Build()
}
+// TestE is the same as Test, but returns an error instead of failing the test.
+func TestE(t testing.TB, files string, opts ...TestOpt) (*IntegrationTestBuilder, error) {
+ cfg := IntegrationTestConfig{T: t, TxtarString: files}
+ for _, o := range opts {
+ o(&cfg)
+ }
+ return NewIntegrationTestBuilder(cfg).BuildE()
+}
+
// TestRunning is a convenience method to create a new IntegrationTestBuilder from some files with Running set to true and run a build.
// Deprecated: Use Test with TestOptRunning instead.
func TestRunning(t testing.TB, files string, opts ...TestOpt) *IntegrationTestBuilder {
diff --git a/hugolib/page.go b/hugolib/page.go
index f8ec5e225..822b7c021 100644
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -1,4 +1,4 @@
-// Copyright 2019 The Hugo Authors. All rights reserved.
+// Copyright 2024 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.
@@ -27,6 +27,7 @@ import (
"github.com/gohugoio/hugo/output"
"github.com/gohugoio/hugo/output/layouts"
"github.com/gohugoio/hugo/related"
+ "github.com/spf13/afero"
"github.com/gohugoio/hugo/markup/converter"
"github.com/gohugoio/hugo/markup/tableofcontents"
@@ -197,7 +198,7 @@ func (p *pageHeadingsFiltered) page() page.Page {
// For internal use by the related content feature.
func (p *pageState) ApplyFilterToHeadings(ctx context.Context, fn func(*tableofcontents.Heading) bool) related.Document {
- r, err := p.content.contentToC(ctx, p.pageOutput.pco)
+ r, err := p.m.content.contentToC(ctx, p.pageOutput.pco)
if err != nil {
panic(err)
}
@@ -313,14 +314,14 @@ func (p *pageState) Pages() page.Pages {
// RawContent returns the un-rendered source content without
// any leading front matter.
func (p *pageState) RawContent() string {
- if p.content.parseInfo.itemsStep2 == nil {
+ if p.m.content.pi.itemsStep2 == nil {
return ""
}
- start := p.content.parseInfo.posMainContent
+ start := p.m.content.pi.posMainContent
if start == -1 {
start = 0
}
- source, err := p.content.contentSource()
+ source, err := p.m.content.pi.contentSource(p.m.content)
if err != nil {
panic(err)
}
@@ -332,11 +333,11 @@ func (p *pageState) Resources() resource.Resources {
}
func (p *pageState) HasShortcode(name string) bool {
- if p.content.shortcodeState == nil {
+ if p.m.content.shortcodeState == nil {
return false
}
- return p.content.shortcodeState.hasName(name)
+ return p.m.content.shortcodeState.hasName(name)
}
func (p *pageState) Site() page.Site {
@@ -355,8 +356,8 @@ func (p *pageState) IsTranslated() bool {
// TranslationKey returns the key used to identify a translation of this content.
func (p *pageState) TranslationKey() string {
- if p.m.translationKey != "" {
- return p.m.translationKey
+ if p.m.pageConfig.TranslationKey != "" {
+ return p.m.pageConfig.TranslationKey
}
return p.Path()
}
@@ -365,9 +366,9 @@ func (p *pageState) TranslationKey() string {
func (p *pageState) AllTranslations() page.Pages {
key := p.Path() + "/" + "translations-all"
pages, err := p.s.pageMap.getOrCreatePagesFromCache(key, func(string) (page.Pages, error) {
- if p.m.translationKey != "" {
+ if p.m.pageConfig.TranslationKey != "" {
// translationKey set by user.
- pas, _ := p.s.h.translationKeyPages.Get(p.m.translationKey)
+ pas, _ := p.s.h.translationKeyPages.Get(p.m.pageConfig.TranslationKey)
pasc := make(page.Pages, len(pas))
copy(pasc, pas)
page.SortByLanguage(pasc)
@@ -534,7 +535,7 @@ var defaultRenderStringOpts = renderStringOpts{
Markup: "", // Will inherit the page's value when not set.
}
-func (p *pageMeta) wrapError(err error) error {
+func (p *pageMeta) wrapError(err error, sourceFs afero.Fs) error {
if err == nil {
panic("wrapError with nil")
}
@@ -544,18 +545,18 @@ func (p *pageMeta) wrapError(err error) error {
return fmt.Errorf("%q: %w", p.Path(), err)
}
- return hugofs.AddFileInfoToError(err, p.File().FileInfo(), p.s.SourceSpec.Fs.Source)
+ return hugofs.AddFileInfoToError(err, p.File().FileInfo(), sourceFs)
}
// wrapError adds some more context to the given error if possible/needed
func (p *pageState) wrapError(err error) error {
- return p.m.wrapError(err)
+ return p.m.wrapError(err, p.s.h.SourceFs)
}
func (p *pageState) getContentConverter() converter.Converter {
var err error
p.contentConverterInit.Do(func() {
- markup := p.m.markup
+ markup := p.m.pageConfig.Markup
if markup == "html" {
// Only used for shortcode inner content.
markup = "markdown"
@@ -612,7 +613,7 @@ func (p *pageState) posFromInput(input []byte, offset int) text.Position {
}
func (p *pageState) posOffset(offset int) text.Position {
- return p.posFromInput(p.content.mustSource(), offset)
+ return p.posFromInput(p.m.content.mustSource(), offset)
}
// shiftToOutputFormat is serialized. The output format idx refers to the
diff --git a/hugolib/page__common.go b/hugolib/page__common.go
index 0881affe7..164776842 100644
--- a/hugolib/page__common.go
+++ b/hugolib/page__common.go
@@ -91,9 +91,6 @@ type pageCommon struct {
layoutDescriptor layouts.LayoutDescriptor
layoutDescriptorInit sync.Once
- // The source and the parsed page content.
- content *cachedContent
-
// Set if feature enabled and this is in a Git repo.
gitInfo source.GitInfo
codeowners []string
diff --git a/hugolib/page__content.go b/hugolib/page__content.go
index 64ce83f0e..62e78c612 100644
--- a/hugolib/page__content.go
+++ b/hugolib/page__content.go
@@ -20,6 +20,7 @@ import (
"fmt"
"html/template"
"io"
+ "strconv"
"strings"
"unicode/utf8"
@@ -53,9 +54,8 @@ type pageContentReplacement struct {
source pageparser.Item
}
-func newCachedContent(m *pageMeta, pid uint64) (*cachedContent, error) {
+func (m *pageMeta) parseFrontMatter(h *HugoSites, pid uint64, sourceKey string) (*contentParseInfo, error) {
var openSource hugio.OpenReadSeekCloser
- var filename string
if m.f != nil {
meta := m.f.FileInfo().Meta()
openSource = func() (hugio.ReadSeekCloser, error) {
@@ -65,6 +65,44 @@ func newCachedContent(m *pageMeta, pid uint64) (*cachedContent, error) {
}
return r, nil
}
+ }
+
+ if sourceKey == "" {
+ sourceKey = strconv.Itoa(int(pid))
+ }
+
+ pi := &contentParseInfo{
+ h: h,
+ pid: pid,
+ sourceKey: sourceKey,
+ openSource: openSource,
+ }
+
+ source, err := pi.contentSource(m)
+ if err != nil {
+ return nil, err
+ }
+
+ items, err := pageparser.ParseBytes(
+ source,
+ pageparser.Config{},
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ pi.itemsStep1 = items
+
+ if err := pi.mapFrontMatter(source); err != nil {
+ return nil, err
+ }
+
+ return pi, nil
+}
+
+func (m *pageMeta) newCachedContent(h *HugoSites, pi *contentParseInfo) (*cachedContent, error) {
+ var filename string
+ if m.f != nil {
filename = m.f.Filename()
}
@@ -72,15 +110,11 @@ func newCachedContent(m *pageMeta, pid uint64) (*cachedContent, error) {
pm: m.s.pageMap,
StaleInfo: m,
shortcodeState: newShortcodeHandler(filename, m.s),
- parseInfo: &contentParseInfo{
- pid: pid,
- },
- cacheBaseKey: m.pathInfo.PathNoLang(),
- openSource: openSource,
- enableEmoji: m.s.conf.EnableEmoji,
+ pi: pi,
+ enableEmoji: m.s.conf.EnableEmoji,
}
- source, err := c.contentSource()
+ source, err := c.pi.contentSource(m)
if err != nil {
return nil, err
}
@@ -95,23 +129,25 @@ func newCachedContent(m *pageMeta, pid uint64) (*cachedContent, error) {
type cachedContent struct {
pm *pageMap
- cacheBaseKey string
-
- // The source bytes.
- openSource hugio.OpenReadSeekCloser
-
resource.StaleInfo
shortcodeState *shortcodeHandler
// Parsed content.
- parseInfo *contentParseInfo
+ pi *contentParseInfo
enableEmoji bool
}
type contentParseInfo struct {
- pid uint64
+ h *HugoSites
+
+ pid uint64
+ sourceKey string
+
+ // The source bytes.
+ openSource hugio.OpenReadSeekCloser
+
frontMatter map[string]any
// Whether the parsed content contains a summary separator.
@@ -190,25 +226,15 @@ func (pi *contentParseInfo) contentToRender(ctx context.Context, source []byte,
}
func (c *cachedContent) IsZero() bool {
- return len(c.parseInfo.itemsStep2) == 0
+ return len(c.pi.itemsStep2) == 0
}
func (c *cachedContent) parseContentFile(source []byte) error {
- if source == nil || c.openSource == nil {
+ if source == nil || c.pi.openSource == nil {
return nil
}
- items, err := pageparser.ParseBytes(
- source,
- pageparser.Config{},
- )
- if err != nil {
- return err
- }
-
- c.parseInfo.itemsStep1 = items
-
- return c.parseInfo.mapItems(source, c.shortcodeState)
+ return c.pi.mapItemsAfterFrontMatter(source, c.shortcodeState)
}
func (c *contentParseInfo) parseFrontMatter(it pageparser.Item, iter *pageparser.Iterator, source []byte) error {
@@ -242,7 +268,49 @@ func (c *contentParseInfo) parseFrontMatter(it pageparser.Item, iter *pageparser
return nil
}
-func (rn *contentParseInfo) mapItems(
+func (rn *contentParseInfo) failMap(source []byte, err error, i pageparser.Item) error {
+ if fe, ok := err.(herrors.FileError); ok {
+ return fe
+ }
+
+ pos := posFromInput("", source, i.Pos())
+
+ return herrors.NewFileErrorFromPos(err, pos)
+}
+
+func (rn *contentParseInfo) mapFrontMatter(source []byte) error {
+ if len(rn.itemsStep1) == 0 {
+ return nil
+ }
+ iter := pageparser.NewIterator(rn.itemsStep1)
+
+Loop:
+ for {
+ it := iter.Next()
+ switch {
+ case it.IsFrontMatter():
+ if err := rn.parseFrontMatter(it, iter, source); err != nil {
+ return err
+ }
+ next := iter.Peek()
+ if !next.IsDone() {
+ rn.posMainContent = next.Pos()
+ }
+ // Done.
+ break Loop
+ case it.IsEOF():
+ break Loop
+ case it.IsError():
+ return rn.failMap(source, it.Err, it)
+ default:
+
+ }
+ }
+
+ return nil
+}
+
+func (rn *contentParseInfo) mapItemsAfterFrontMatter(
source []byte,
s *shortcodeHandler,
) error {
@@ -273,13 +341,7 @@ Loop:
switch {
case it.Type == pageparser.TypeIgnore:
case it.IsFrontMatter():
- if err := rn.parseFrontMatter(it, iter, source); err != nil {
- return err
- }
- next := iter.Peek()
- if !next.IsDone() {
- rn.posMainContent = next.Pos()
- }
+ // Ignore.
case it.Type == pageparser.TypeLeadSummaryDivider:
posBody := -1
f := func(item pageparser.Item) bool {
@@ -347,16 +409,16 @@ Loop:
}
func (c *cachedContent) mustSource() []byte {
- source, err := c.contentSource()
+ source, err := c.pi.contentSource(c)
if err != nil {
panic(err)
}
return source
}
-func (c *cachedContent) contentSource() ([]byte, error) {
- key := c.cacheBaseKey
- v, err := c.pm.cacheContentSource.GetOrCreate(key, func(string) (*resources.StaleValue[[]byte], error) {
+func (c *contentParseInfo) contentSource(s resource.StaleInfo) ([]byte, error) {
+ key := c.sourceKey
+ v, err := c.h.cacheContentSource.GetOrCreate(key, func(string) (*resources.StaleValue[[]byte], error) {
b, err := c.readSourceAll()
if err != nil {
return nil, err
@@ -365,7 +427,7 @@ func (c *cachedContent) contentSource() ([]byte, error) {
return &resources.StaleValue[[]byte]{
Value: b,
IsStaleFunc: func() bool {
- return c.IsStale()
+ return s.IsStale()
},
}, nil
})
@@ -376,7 +438,7 @@ func (c *cachedContent) contentSource() ([]byte, error) {
return v.Value, nil
}
-func (c *cachedContent) readSourceAll() ([]byte, error) {
+func (c *contentParseInfo) readSourceAll() ([]byte, error) {
if c.openSource == nil {
return []byte{}, nil
}
@@ -424,7 +486,7 @@ type contentPlainPlainWords struct {
func (c *cachedContent) contentRendered(ctx context.Context, cp *pageContentOutput) (contentSummary, error) {
ctx = tpl.Context.DependencyScope.Set(ctx, pageDependencyScopeGlobal)
- key := c.cacheBaseKey + "/" + cp.po.f.Name
+ key := c.pi.sourceKey + "/" + cp.po.f.Name
versionv := cp.contentRenderedVersion
v, err := c.pm.cacheContentRendered.GetOrCreate(key, func(string) (*resources.StaleValue[contentSummary], error) {
@@ -447,7 +509,7 @@ func (c *cachedContent) contentRendered(ctx context.Context, cp *pageContentOutp
},
}
- if len(c.parseInfo.itemsStep2) == 0 {
+ if len(c.pi.itemsStep2) == 0 {
// Nothing to do.
return rs, nil
}
@@ -501,8 +563,8 @@ func (c *cachedContent) contentRendered(ctx context.Context, cp *pageContentOutp
var result contentSummary // hasVariants bool
- if c.parseInfo.hasSummaryDivider {
- isHTML := cp.po.p.m.markup == "html"
+ if c.pi.hasSummaryDivider {
+ isHTML :=