diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2024-01-29 10:02:24 +0100 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2024-01-30 20:12:03 +0100 |
commit | f31a6db797c9251a362ef9f8ad4c03fb608b5ac0 (patch) | |
tree | a47dbb5764cfc96c49882e13a7da279dc43b1eb5 | |
parent | ec22bb31a89883db5ca95404cda4f74344fd3762 (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
-rw-r--r-- | go.mod | 2 | ||||
-rw-r--r-- | go.sum | 2 | ||||
-rw-r--r-- | hugolib/content_map.go | 6 | ||||
-rw-r--r-- | hugolib/content_map_page.go | 89 | ||||
-rw-r--r-- | hugolib/hugo_sites.go | 3 | ||||
-rw-r--r-- | hugolib/integrationtest_builder.go | 9 | ||||
-rw-r--r-- | hugolib/page.go | 33 | ||||
-rw-r--r-- | hugolib/page__common.go | 3 | ||||
-rw-r--r-- | hugolib/page__content.go | 180 | ||||
-rw-r--r-- | hugolib/page__meta.go | 352 | ||||
-rw-r--r-- | hugolib/page__new.go | 112 | ||||
-rw-r--r-- | hugolib/page__paths.go | 2 | ||||
-rw-r--r-- | hugolib/page__per_output.go | 27 | ||||
-rw-r--r-- | hugolib/params_test.go | 109 | ||||
-rw-r--r-- | hugolib/shortcode.go | 4 | ||||
-rw-r--r-- | hugolib/site_new.go | 2 | ||||
-rw-r--r-- | hugolib/site_render.go | 2 | ||||
-rw-r--r-- | resources/page/page_matcher.go | 13 | ||||
-rw-r--r-- | resources/page/pagemeta/page_frontmatter.go | 101 | ||||
-rw-r--r-- | resources/page/pagemeta/page_frontmatter_test.go | 37 | ||||
-rw-r--r-- | resources/page/pagemeta/pagemeta.go | 7 | ||||
-rw-r--r-- | resources/resource/dates.go | 41 |
22 files changed, 707 insertions, 429 deletions
@@ -48,7 +48,7 @@ require ( github.com/marekm4/color-extractor v1.2.1 github.com/mattn/go-isatty v0.0.20 github.com/mitchellh/hashstructure v1.1.0 - github.com/mitchellh/mapstructure v1.5.0 + github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c github.com/muesli/smartcrop v0.3.0 github.com/niklasfasching/go-org v1.7.0 github.com/olekukonko/tablewriter v0.0.5 @@ -359,6 +359,8 @@ github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9km github.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/6d8ulp4AwfLKrmA= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/montanaflynn/stats v0.6.3/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= 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() + } |