URI: 
       Simplify .Site.GetPage etc. - hugo - [fork] hugo port for 9front
  HTML git clone git@git.drkhsh.at/hugo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
   DIR commit 3eb313fef495a39731dafa6bddbf77760090230d
   DIR parent b93417aa1d3d38a9e56bad25937e0e638a113faf
  HTML Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
       Date:   Tue, 17 Jul 2018 11:18:29 +0200
       
       Simplify .Site.GetPage etc.
       
       This commit is a follow up to a recent overhaul of the GetPage/ref/relref implemenation.
       
       The most important change in this commit is the update to `.Site.GetPage`:
       
       * To reduce the amount of breakage in the wild to its minimum, I have reworked .Site.GetPage with some rules:
       
       * We cannot support more than 2 arguments, i.e. .Site.GetPage "page" "posts" "mypage.md" will now throw an error. I think this is the most uncommon syntax and should be OK. It is an easy fix to change the above to .Site.GetPage "/posts/mypage.md" or similar.
       * .Site.GetPage "home", .Site.GetPage "home" "" and .Site.GetPage "home" "/" will give you the home page. This means that if you have page in root with the name home.md you need to do .Site.GetPage "/home.md" or similar
       
       This commit also fixes some multilingual issues, most notable it is now possible to do cross-language ref/relref lookups by prepending the language code to the path, e.g. `/jp/posts/mypage.md`.
       
       This commit also reverts the site building tests related to this to "Hugo 0.44 state", to get better control of the changes made.
       
       Closes #4147
       Closes #4727
       Closes #4728
       Closes #4728
       Closes #4726
       Closes #4652
       
       Diffstat:
         M cache/partitioned_lazy_cache.go     |      41 ++++++++++++++++++++++---------
         M hugolib/disableKinds_test.go        |      12 ++++++------
         M hugolib/hugo_sites_build_test.go    |      30 +++++++++++++++---------------
         M hugolib/hugo_sites_multihost_test.… |      10 +++++-----
         M hugolib/language_content_dir_test.… |      25 ++++++++++++++++++++++++-
         M hugolib/page.go                     |      26 +++++++++++++-------------
         M hugolib/page_bundler_test.go        |      50 +++++++++++++-------------------
         M hugolib/page_collections.go         |     171 ++++++++++++++++---------------
         M hugolib/page_collections_test.go    |      15 +++++++++------
         M hugolib/pages_language_merge_test.… |       4 ++--
         M hugolib/shortcode_test.go           |       2 +-
         M hugolib/site.go                     |      47 +++++++++++++++++++++++--------
         M hugolib/site_output_test.go         |       4 ++--
         M hugolib/site_sections_test.go       |      31 ++++++++++++++-----------------
         M hugolib/site_test.go                |      19 ++++++-------------
         M hugolib/site_url_test.go            |       6 +++---
         M hugolib/taxonomy_test.go            |      12 ++++++------
       
       17 files changed, 282 insertions(+), 223 deletions(-)
       ---
   DIR diff --git a/cache/partitioned_lazy_cache.go b/cache/partitioned_lazy_cache.go
       @@ -24,34 +24,52 @@ type Partition struct {
                Load func() (map[string]interface{}, error)
        }
        
       -type lazyPartition struct {
       +// Lazy represents a lazily loaded cache.
       +type Lazy struct {
                initSync sync.Once
       +        initErr  error
                cache    map[string]interface{}
                load     func() (map[string]interface{}, error)
        }
        
       -func (l *lazyPartition) init() error {
       -        var err error
       +// NewLazy creates a lazy cache with the given load func.
       +func NewLazy(load func() (map[string]interface{}, error)) *Lazy {
       +        return &Lazy{load: load}
       +}
       +
       +func (l *Lazy) init() error {
                l.initSync.Do(func() {
       -                var c map[string]interface{}
       -                c, err = l.load()
       +                c, err := l.load()
                        l.cache = c
       +                l.initErr = err
       +
                })
        
       -        return err
       +        return l.initErr
       +}
       +
       +// Get initializes the cache if not already initialized, then looks up the
       +// given key.
       +func (l *Lazy) Get(key string) (interface{}, bool, error) {
       +        l.init()
       +        if l.initErr != nil {
       +                return nil, false, l.initErr
       +        }
       +        v, found := l.cache[key]
       +        return v, found, nil
        }
        
        // PartitionedLazyCache is a lazily loaded cache paritioned by a supplied string key.
        type PartitionedLazyCache struct {
       -        partitions map[string]*lazyPartition
       +        partitions map[string]*Lazy
        }
        
        // NewPartitionedLazyCache creates a new NewPartitionedLazyCache with the supplied
        // partitions.
        func NewPartitionedLazyCache(partitions ...Partition) *PartitionedLazyCache {
       -        lazyPartitions := make(map[string]*lazyPartition, len(partitions))
       +        lazyPartitions := make(map[string]*Lazy, len(partitions))
                for _, partition := range partitions {
       -                lazyPartitions[partition.Key] = &lazyPartition{load: partition.Load}
       +                lazyPartitions[partition.Key] = NewLazy(partition.Load)
                }
                cache := &PartitionedLazyCache{partitions: lazyPartitions}
        
       @@ -67,11 +85,12 @@ func (c *PartitionedLazyCache) Get(partition, key string) (interface{}, error) {
                        return nil, nil
                }
        
       -        if err := p.init(); err != nil {
       +        v, found, err := p.Get(key)
       +        if err != nil {
                        return nil, err
                }
        
       -        if v, found := p.cache[key]; found {
       +        if found {
                        return v, nil
                }
        
   DIR diff --git a/hugolib/disableKinds_test.go b/hugolib/disableKinds_test.go
       @@ -130,7 +130,7 @@ func assertDisabledKinds(th testHelper, s *Site, disabled ...string) {
                        }, disabled, KindPage, "public/sect/p1/index.html", "Single|P1")
                assertDisabledKind(th,
                        func(isDisabled bool) bool {
       -                        p, _ := s.getPage(nil, "/")
       +                        p := s.getPage(KindHome)
                                if isDisabled {
                                        return p == nil
                                }
       @@ -138,7 +138,7 @@ func assertDisabledKinds(th testHelper, s *Site, disabled ...string) {
                        }, disabled, KindHome, "public/index.html", "Home")
                assertDisabledKind(th,
                        func(isDisabled bool) bool {
       -                        p, _ := s.getPage(nil, "sect")
       +                        p := s.getPage(KindSection, "sect")
                                if isDisabled {
                                        return p == nil
                                }
       @@ -146,7 +146,7 @@ func assertDisabledKinds(th testHelper, s *Site, disabled ...string) {
                        }, disabled, KindSection, "public/sect/index.html", "Sects")
                assertDisabledKind(th,
                        func(isDisabled bool) bool {
       -                        p, _ := s.getPage(nil, "tags/tag1")
       +                        p := s.getPage(KindTaxonomy, "tags", "tag1")
        
                                if isDisabled {
                                        return p == nil
       @@ -156,7 +156,7 @@ func assertDisabledKinds(th testHelper, s *Site, disabled ...string) {
                        }, disabled, KindTaxonomy, "public/tags/tag1/index.html", "Tag1")
                assertDisabledKind(th,
                        func(isDisabled bool) bool {
       -                        p, _ := s.getPage(nil, "tags")
       +                        p := s.getPage(KindTaxonomyTerm, "tags")
                                if isDisabled {
                                        return p == nil
                                }
       @@ -165,7 +165,7 @@ func assertDisabledKinds(th testHelper, s *Site, disabled ...string) {
                        }, disabled, KindTaxonomyTerm, "public/tags/index.html", "Tags")
                assertDisabledKind(th,
                        func(isDisabled bool) bool {
       -                        p, _ := s.getPage(nil, "categories")
       +                        p := s.getPage(KindTaxonomyTerm, "categories")
        
                                if isDisabled {
                                        return p == nil
       @@ -175,7 +175,7 @@ func assertDisabledKinds(th testHelper, s *Site, disabled ...string) {
                        }, disabled, KindTaxonomyTerm, "public/categories/index.html", "Category Terms")
                assertDisabledKind(th,
                        func(isDisabled bool) bool {
       -                        p, _ := s.getPage(nil, "categories/hugo")
       +                        p := s.getPage(KindTaxonomy, "categories", "hugo")
                                if isDisabled {
                                        return p == nil
                                }
   DIR diff --git a/hugolib/hugo_sites_build_test.go b/hugolib/hugo_sites_build_test.go
       @@ -186,12 +186,12 @@ p1 = "p1en"
                assert.Len(sites, 2)
        
                nnSite := sites[0]
       -        nnHome, _ := nnSite.getPage(nil, "/")
       +        nnHome := nnSite.getPage(KindHome)
                assert.Len(nnHome.AllTranslations(), 2)
                assert.Len(nnHome.Translations(), 1)
                assert.True(nnHome.IsTranslated())
        
       -        enHome, _ := sites[1].getPage(nil, "/")
       +        enHome := sites[1].getPage(KindHome)
        
                p1, err := enHome.Param("p1")
                assert.NoError(err)
       @@ -242,7 +242,7 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
                require.Nil(t, gp2)
        
                enSite := sites[0]
       -        enSiteHome, _ := enSite.getPage(nil, "/")
       +        enSiteHome := enSite.getPage(KindHome)
                require.True(t, enSiteHome.IsTranslated())
        
                require.Equal(t, "en", enSite.Language.Lang)
       @@ -310,10 +310,10 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
                // isn't ideal in a multilingual setup. You want a way to get the current language version if available.
                // Now you can do lookups with translation base name to get that behaviour.
                // Let us test all the regular page variants:
       -        getPageDoc1En, _ := enSite.getPage(nil, filepath.ToSlash(doc1en.Path()))
       -        getPageDoc1EnBase, _ := enSite.getPage(nil, "sect/doc1")
       -        getPageDoc1Fr, _ := frSite.getPage(nil, filepath.ToSlash(doc1fr.Path()))
       -        getPageDoc1FrBase, _ := frSite.getPage(nil, "sect/doc1")
       +        getPageDoc1En := enSite.getPage(KindPage, filepath.ToSlash(doc1en.Path()))
       +        getPageDoc1EnBase := enSite.getPage(KindPage, "sect/doc1")
       +        getPageDoc1Fr := frSite.getPage(KindPage, filepath.ToSlash(doc1fr.Path()))
       +        getPageDoc1FrBase := frSite.getPage(KindPage, "sect/doc1")
                require.Equal(t, doc1en, getPageDoc1En)
                require.Equal(t, doc1fr, getPageDoc1Fr)
                require.Equal(t, doc1en, getPageDoc1EnBase)
       @@ -331,7 +331,7 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
                b.AssertFileContent("public/en/sect/doc1-slug/index.html", "Single", "Shortcode: Hello", "LingoDefault")
        
                // Check node translations
       -        homeEn, _ := enSite.getPage(nil, "/")
       +        homeEn := enSite.getPage(KindHome)
                require.NotNil(t, homeEn)
                require.Len(t, homeEn.Translations(), 3)
                require.Equal(t, "fr", homeEn.Translations()[0].Lang())
       @@ -341,7 +341,7 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
                require.Equal(t, "På bokmål", homeEn.Translations()[2].title, configSuffix)
                require.Equal(t, "Bokmål", homeEn.Translations()[2].Language().LanguageName, configSuffix)
        
       -        sectFr, _ := frSite.getPage(nil, "sect")
       +        sectFr := frSite.getPage(KindSection, "sect")
                require.NotNil(t, sectFr)
        
                require.Equal(t, "fr", sectFr.Lang())
       @@ -351,12 +351,12 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
        
                nnSite := sites[2]
                require.Equal(t, "nn", nnSite.Language.Lang)
       -        taxNn, _ := nnSite.getPage(nil, "lag")
       +        taxNn := nnSite.getPage(KindTaxonomyTerm, "lag")
                require.NotNil(t, taxNn)
                require.Len(t, taxNn.Translations(), 1)
                require.Equal(t, "nb", taxNn.Translations()[0].Lang())
        
       -        taxTermNn, _ := nnSite.getPage(nil, "lag/sogndal")
       +        taxTermNn := nnSite.getPage(KindTaxonomy, "lag", "sogndal")
                require.NotNil(t, taxTermNn)
                require.Len(t, taxTermNn.Translations(), 1)
                require.Equal(t, "nb", taxTermNn.Translations()[0].Lang())
       @@ -411,7 +411,7 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
                }
        
                // Check bundles
       -        bundleFr, _ := frSite.getPage(nil, "bundles/b1/index.md")
       +        bundleFr := frSite.getPage(KindPage, "bundles/b1/index.md")
                require.NotNil(t, bundleFr)
                require.Equal(t, "/blog/fr/bundles/b1/", bundleFr.RelPermalink())
                require.Equal(t, 1, len(bundleFr.Resources))
       @@ -420,7 +420,7 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
                require.Equal(t, "/blog/fr/bundles/b1/logo.png", logoFr.RelPermalink())
                b.AssertFileContent("public/fr/bundles/b1/logo.png", "PNG Data")
        
       -        bundleEn, _ := enSite.getPage(nil, "bundles/b1/index.en.md")
       +        bundleEn := enSite.getPage(KindPage, "bundles/b1/index.en.md")
                require.NotNil(t, bundleEn)
                require.Equal(t, "/blog/en/bundles/b1/", bundleEn.RelPermalink())
                require.Equal(t, 1, len(bundleEn.Resources))
       @@ -582,7 +582,7 @@ func TestMultiSitesRebuild(t *testing.T) {
                                        docFr := readDestination(t, fs, "public/fr/sect/doc1/index.html")
                                        require.True(t, strings.Contains(docFr, "Salut"), "No Salut")
        
       -                                homeEn, _ := enSite.getPage(nil, "/")
       +                                homeEn := enSite.getPage(KindHome)
                                        require.NotNil(t, homeEn)
                                        assert.Len(homeEn.Translations(), 3)
                                        require.Equal(t, "fr", homeEn.Translations()[0].Lang())
       @@ -678,7 +678,7 @@ title = "Svenska"
                require.True(t, svSite.Language.Lang == "sv", svSite.Language.Lang)
                require.True(t, frSite.Language.Lang == "fr", frSite.Language.Lang)
        
       -        homeEn, _ := enSite.getPage(nil, "/")
       +        homeEn := enSite.getPage(KindHome)
                require.NotNil(t, homeEn)
                require.Len(t, homeEn.Translations(), 4)
                require.Equal(t, "sv", homeEn.Translations()[0].Lang())
   DIR diff --git a/hugolib/hugo_sites_multihost_test.go b/hugolib/hugo_sites_multihost_test.go
       @@ -55,7 +55,7 @@ languageName = "Nynorsk"
        
                s1 := b.H.Sites[0]
        
       -        s1h, _ := s1.getPage(nil, "/")
       +        s1h := s1.getPage(KindHome)
                assert.True(s1h.IsTranslated())
                assert.Len(s1h.Translations(), 2)
                assert.Equal("https://example.com/docs/", s1h.Permalink())
       @@ -66,7 +66,7 @@ languageName = "Nynorsk"
                // For multihost, we never want any content in the root.
                //
                // check url in front matter:
       -        pageWithURLInFrontMatter, _ := s1.getPage(nil, "sect/doc3.en.md")
       +        pageWithURLInFrontMatter := s1.getPage(KindPage, "sect/doc3.en.md")
                assert.NotNil(pageWithURLInFrontMatter)
                assert.Equal("/superbob", pageWithURLInFrontMatter.URL())
                assert.Equal("/docs/superbob/", pageWithURLInFrontMatter.RelPermalink())
       @@ -78,7 +78,7 @@ languageName = "Nynorsk"
        
                s2 := b.H.Sites[1]
        
       -        s2h, _ := s2.getPage(nil, "/")
       +        s2h := s2.getPage(KindHome)
                assert.Equal("https://example.fr/", s2h.Permalink())
        
                b.AssertFileContent("public/fr/index.html", "French Home Page")
       @@ -92,7 +92,7 @@ languageName = "Nynorsk"
        
                // Check bundles
        
       -        bundleEn, _ := s1.getPage(nil, "bundles/b1/index.en.md")
       +        bundleEn := s1.getPage(KindPage, "bundles/b1/index.en.md")
                require.NotNil(t, bundleEn)
                require.Equal(t, "/docs/bundles/b1/", bundleEn.RelPermalink())
                require.Equal(t, 1, len(bundleEn.Resources))
       @@ -101,7 +101,7 @@ languageName = "Nynorsk"
                require.Equal(t, "/docs/bundles/b1/logo.png", logoEn.RelPermalink())
                b.AssertFileContent("public/en/bundles/b1/logo.png", "PNG Data")
        
       -        bundleFr, _ := s2.getPage(nil, "bundles/b1/index.md")
       +        bundleFr := s2.getPage(KindPage, "bundles/b1/index.md")
                require.NotNil(t, bundleFr)
                require.Equal(t, "/bundles/b1/", bundleFr.RelPermalink())
                require.Equal(t, 1, len(bundleFr.Resources))
   DIR diff --git a/hugolib/language_content_dir_test.go b/hugolib/language_content_dir_test.go
       @@ -207,6 +207,29 @@ Content.
        
                assert.Equal(10, len(svSite.RegularPages))
        
       +        svP2, err := svSite.getPageNew(nil, "/sect/page2.md")
       +        assert.NoError(err)
       +        nnP2, err := nnSite.getPageNew(nil, "/sect/page2.md")
       +        assert.NoError(err)
       +        nnP2_2, err := svSite.getPageNew(nil, "/nn/sect/page2.md")
       +        assert.NoError(err)
       +        enP2_2, err := nnSite.getPageNew(nil, "/en/sect/page2.md")
       +        assert.NoError(err)
       +        svP2_2, err := enSite.getPageNew(nil, "/sv/sect/page2.md")
       +        assert.NoError(err)
       +
       +        enP2, err := enSite.getPageNew(nil, "/sect/page2.md")
       +        assert.NoError(err)
       +        assert.NotNil(enP2)
       +        assert.NotNil(svP2)
       +        assert.NotNil(nnP2)
       +        assert.Equal("sv", svP2.Lang())
       +        assert.Equal("nn", nnP2.Lang())
       +        assert.Equal("en", enP2.Lang())
       +        assert.Equal(nnP2, nnP2_2)
       +        assert.Equal(enP2, enP2_2)
       +        assert.Equal(svP2, svP2_2)
       +
                for i, p := range enSite.RegularPages {
                        j := i + 1
                        msg := fmt.Sprintf("Test %d", j)
       @@ -244,7 +267,7 @@ Content.
                b.AssertFileContent("/my/project/public/sv/sect/mybundle/logo.png", "PNG Data")
                b.AssertFileContent("/my/project/public/nn/sect/mybundle/logo.png", "PNG Data")
        
       -        nnSect, _ := nnSite.getPage(nil, "sect")
       +        nnSect := nnSite.getPage(KindSection, "sect")
                assert.NotNil(nnSect)
                assert.Equal(12, len(nnSect.Pages))
                nnHome, _ := nnSite.Info.Home()
   DIR diff --git a/hugolib/page.go b/hugolib/page.go
       @@ -1875,24 +1875,24 @@ func (p *Page) FullFilePath() string {
        }
        
        // Returns the canonical, absolute fully-qualifed logical reference used by
       -// methods such as GetPage and ref/relref shortcodes to unambiguously refer to
       -// this page. As an absolute path, it is prefixed with a "/".
       +// methods such as GetPage and ref/relref shortcodes to refer to
       +// this page. It is prefixed with a "/".
        //
       -// For pages that have a backing file in the content directory, it is returns
       -// the path to this file as an absolute path rooted in the content dir. For
       -// pages or nodes that do not, it returns the virtual path, consistent with
       -// where you would add a backing content file.
       -//
       -// The "/" prefix and support for pages without backing files should be the
       -// only difference with FullFilePath()
       +// For pages that have a source file, it is returns the path to this file as an
       +// absolute path rooted in this site's content dir.
       +// For pages that do not (sections witout content page etc.), it returns the
       +// virtual path, consistent with where you would add a source file.
        func (p *Page) absoluteSourceRef() string {
                sourcePath := p.Source.Path()
                if sourcePath != "" {
                        return "/" + filepath.ToSlash(sourcePath)
       -        } else if len(p.sections) > 0 {
       +        }
       +
       +        if len(p.sections) > 0 {
                        // no backing file, return the virtual source path
                        return "/" + path.Join(p.sections...)
                }
       +
                return ""
        }
        
       @@ -2035,7 +2035,7 @@ func (p *Page) Hugo() *HugoInfo {
        // This will return nil when no page could be found, and will return
        // an error if the ref is ambiguous.
        func (p *Page) GetPage(ref string) (*Page, error) {
       -        return p.s.getPage(p, ref)
       +        return p.s.getPageNew(p, ref)
        }
        
        func (p *Page) Ref(refs ...string) (string, error) {
       @@ -2059,8 +2059,8 @@ func (p *Page) RelRef(refs ...string) (string, error) {
        }
        
        func (p *Page) String() string {
       -        if p.absoluteSourceRef() != "" {
       -                return fmt.Sprintf("Page(%s)", p.absoluteSourceRef())
       +        if sourceRef := p.absoluteSourceRef(); sourceRef != "" {
       +                return fmt.Sprintf("Page(%s)", sourceRef)
                }
                return fmt.Sprintf("Page(%q)", p.title)
        }
   DIR diff --git a/hugolib/page_bundler_test.go b/hugolib/page_bundler_test.go
       @@ -48,7 +48,6 @@ func TestPageBundlerSiteRegular(t *testing.T) {
                for _, ugly := range []bool{false, true} {
                        t.Run(fmt.Sprintf("ugly=%t", ugly),
                                func(t *testing.T) {
       -                                var samePage *Page
        
                                        assert := require.New(t)
                                        fs, cfg := newTestBundleSources(t)
       @@ -84,14 +83,12 @@ func TestPageBundlerSiteRegular(t *testing.T) {
        
                                        assert.Len(s.RegularPages, 8)
        
       -                                singlePage, _ := s.getPage(nil, "a/1.md")
       +                                singlePage := s.getPage(KindPage, "a/1.md")
                                        assert.Equal("", singlePage.BundleType())
        
                                        assert.NotNil(singlePage)
       -                                samePage, _ = s.getPage(nil, "a/1")
       -                                assert.Equal(singlePage, samePage)
       -                                samePage, _ = s.getPage(nil, "1")
       -                                assert.Equal(singlePage, samePage)
       +                                assert.Equal(singlePage, s.getPage("page", "a/1"))
       +                                assert.Equal(singlePage, s.getPage("page", "1"))
        
                                        assert.Contains(singlePage.content(), "TheContent")
        
       @@ -109,18 +106,18 @@ func TestPageBundlerSiteRegular(t *testing.T) {
                                        // This should be just copied to destination.
                                        th.assertFileContent(filepath.FromSlash("/work/public/assets/pic1.png"), "content")
        
       -                                leafBundle1, _ := s.getPage(nil, "b/my-bundle/index.md")
       +                                leafBundle1 := s.getPage(KindPage, "b/my-bundle/index.md")
                                        assert.NotNil(leafBundle1)
                                        assert.Equal("leaf", leafBundle1.BundleType())
                                        assert.Equal("b", leafBundle1.Section())
       -                                sectionB, _ := s.getPage(nil, "/b")
       +                                sectionB := s.getPage(KindSection, "b")
                                        assert.NotNil(sectionB)
                                        home, _ := s.Info.Home()
                                        assert.Equal("branch", home.BundleType())
        
                                        // This is a root bundle and should live in the "home section"
                                        // See https://github.com/gohugoio/hugo/issues/4332
       -                                rootBundle, _ := s.getPage(nil, "root")
       +                                rootBundle := s.getPage(KindPage, "root")
                                        assert.NotNil(rootBundle)
                                        assert.True(rootBundle.Parent().IsHome())
                                        if ugly {
       @@ -129,9 +126,9 @@ func TestPageBundlerSiteRegular(t *testing.T) {
                                                assert.Equal("/root/", rootBundle.RelPermalink())
                                        }
        
       -                                leafBundle2, _ := s.getPage(nil, "a/b/index.md")
       +                                leafBundle2 := s.getPage(KindPage, "a/b/index.md")
                                        assert.NotNil(leafBundle2)
       -                                unicodeBundle, _ := s.getPage(nil, "c/bundle/index.md")
       +                                unicodeBundle := s.getPage(KindPage, "c/bundle/index.md")
                                        assert.NotNil(unicodeBundle)
        
                                        pageResources := leafBundle1.Resources.ByType(pageResourceType)
       @@ -214,7 +211,6 @@ func TestPageBundlerSiteMultilingual(t *testing.T) {
                for _, ugly := range []bool{false, true} {
                        t.Run(fmt.Sprintf("ugly=%t", ugly),
                                func(t *testing.T) {
       -                                var samePage *Page
        
                                        assert := require.New(t)
                                        fs, cfg := newTestBundleSourcesMultilingual(t)
       @@ -234,7 +230,7 @@ func TestPageBundlerSiteMultilingual(t *testing.T) {
                                        assert.Equal(16, len(s.Pages))
                                        assert.Equal(31, len(s.AllPages))
        
       -                                bundleWithSubPath, _ := s.getPage(nil, "lb/index")
       +                                bundleWithSubPath := s.getPage(KindPage, "lb/index")
                                        assert.NotNil(bundleWithSubPath)
        
                                        // See https://github.com/gohugoio/hugo/issues/4312
       @@ -248,28 +244,22 @@ func TestPageBundlerSiteMultilingual(t *testing.T) {
                                        // and probably also just b (aka "my-bundle")
                                        // These may also be translated, so we also need to test that.
                                        //  "bf", "my-bf-bundle", "index.md + nn
       -                                bfBundle, _ := s.getPage(nil, "bf/my-bf-bundle/index")
       +                                bfBundle := s.getPage(KindPage, "bf/my-bf-bundle/index")
                                        assert.NotNil(bfBundle)
                                        assert.Equal("en", bfBundle.Lang())
       -                                samePage, _ = s.getPage(nil, "bf/my-bf-bundle/index.md")
       -                                assert.Equal(bfBundle, samePage)
       -                                samePage, _ = s.getPage(nil, "bf/my-bf-bundle")
       -                                assert.Equal(bfBundle, samePage)
       -                                samePage, _ = s.getPage(nil, "my-bf-bundle")
       -                                assert.Equal(bfBundle, samePage)
       +                                assert.Equal(bfBundle, s.getPage(KindPage, "bf/my-bf-bundle/index.md"))
       +                                assert.Equal(bfBundle, s.getPage(KindPage, "bf/my-bf-bundle"))
       +                                assert.Equal(bfBundle, s.getPage(KindPage, "my-bf-bundle"))
        
                                        nnSite := sites.Sites[1]
                                        assert.Equal(7, len(nnSite.RegularPages))
        
       -                                bfBundleNN, _ := nnSite.getPage(nil, "bf/my-bf-bundle/index")
       +                                bfBundleNN := nnSite.getPage(KindPage, "bf/my-bf-bundle/index")
                                        assert.NotNil(bfBundleNN)
                                        assert.Equal("nn", bfBundleNN.Lang())
       -                                samePage, _ = nnSite.getPage(nil, "bf/my-bf-bundle/index.nn.md")
       -                                assert.Equal(bfBundleNN, samePage)
       -                                samePage, _ = nnSite.getPage(nil, "bf/my-bf-bundle")
       -                                assert.Equal(bfBundleNN, samePage)
       -                                samePage, _ = nnSite.getPage(nil, "my-bf-bundle")
       -                                assert.Equal(bfBundleNN, samePage)
       +                                assert.Equal(bfBundleNN, nnSite.getPage(KindPage, "bf/my-bf-bundle/index.nn.md"))
       +                                assert.Equal(bfBundleNN, nnSite.getPage(KindPage, "bf/my-bf-bundle"))
       +                                assert.Equal(bfBundleNN, nnSite.getPage(KindPage, "my-bf-bundle"))
        
                                        // See https://github.com/gohugoio/hugo/issues/4295
                                        // Every resource should have its Name prefixed with its base folder.
       @@ -344,7 +334,7 @@ func TestPageBundlerSiteWitSymbolicLinksInContent(t *testing.T) {
                th := testHelper{s.Cfg, s.Fs, t}
        
                assert.Equal(7, len(s.RegularPages))
       -        a1Bundle, _ := s.getPage(nil, "symbolic2/a1/index.md")
       +        a1Bundle := s.getPage(KindPage, "symbolic2/a1/index.md")
                assert.NotNil(a1Bundle)
                assert.Equal(2, len(a1Bundle.Resources))
                assert.Equal(1, len(a1Bundle.Resources.ByType(pageResourceType)))
       @@ -404,10 +394,10 @@ HEADLESS {{< myShort >}}
                assert.Equal(1, len(s.RegularPages))
                assert.Equal(1, len(s.headlessPages))
        
       -        regular, _ := s.getPage(nil, "a/index")
       +        regular := s.getPage(KindPage, "a/index")
                assert.Equal("/a/s1/", regular.RelPermalink())
        
       -        headless, _ := s.getPage(nil, "b/index")
       +        headless := s.getPage(KindPage, "b/index")
                assert.NotNil(headless)
                assert.True(headless.headless)
                assert.Equal("Headless Bundle in Topless Bar", headless.Title())
   DIR diff --git a/hugolib/page_collections.go b/hugolib/page_collections.go
       @@ -16,11 +16,9 @@ package hugolib
        import (
                "fmt"
                "path"
       -        "path/filepath"
                "strings"
       -        "sync"
        
       -        "github.com/gohugoio/hugo/helpers"
       +        "github.com/gohugoio/hugo/cache"
        )
        
        // PageCollections contains the page collections for a site.
       @@ -49,29 +47,29 @@ type PageCollections struct {
                // Includes headless bundles, i.e. bundles that produce no output for its content page.
                headlessPages Pages
        
       -        pageIndex
       -}
       -
       -type pageIndex struct {
       -        initSync sync.Once
       -        index    map[string]*Page
       -        load     func() map[string]*Page
       -}
       -
       -func (pi *pageIndex) init() {
       -        pi.initSync.Do(func() {
       -                pi.index = pi.load()
       -        })
       +        pageIndex *cache.Lazy
        }
        
        // Get initializes the index if not already done so, then
        // looks up the given page ref, returns nil if no value found.
       -func (pi *pageIndex) Get(ref string) *Page {
       -        pi.init()
       -        return pi.index[ref]
       +func (c *PageCollections) getFromCache(ref string) (*Page, error) {
       +        v, found, err := c.pageIndex.Get(ref)
       +        if err != nil {
       +                return nil, err
       +        }
       +        if !found {
       +                return nil, nil
       +        }
       +
       +        p := v.(*Page)
       +
       +        if p != ambiguityFlag {
       +                return p, nil
       +        }
       +        return nil, fmt.Errorf("page reference %q is ambiguous", ref)
        }
        
       -var ambiguityFlag = &Page{Kind: "dummy", title: "ambiguity flag"}
       +var ambiguityFlag = &Page{Kind: kindUnknown, title: "ambiguity flag"}
        
        func (c *PageCollections) refreshPageCaches() {
                c.indexPages = c.findPagesByKindNotIn(KindPage, c.Pages)
       @@ -84,46 +82,55 @@ func (c *PageCollections) refreshPageCaches() {
                        s = c.Pages[0].s
                }
        
       -        indexLoader := func() map[string]*Page {
       -                index := make(map[string]*Page)
       +        indexLoader := func() (map[string]interface{}, error) {
       +                index := make(map[string]interface{})
       +
       +                add := func(ref string, p *Page) {
       +                        existing := index[ref]
       +                        if existing == nil {
       +                                index[ref] = p
       +                        } else if existing != ambiguityFlag && existing != p {
       +                                index[ref] = ambiguityFlag
       +                        }
       +                }
        
                        // Note that we deliberately use the pages from all sites
                        // in this index, as we intend to use this in the ref and relref
       -                // shortcodes. If the user says "sect/doc1.en.md", he/she knows
       -                // what he/she is looking for.
       +                // shortcodes.
                        for _, pageCollection := range []Pages{c.AllRegularPages, c.headlessPages} {
                                for _, p := range pageCollection {
       -
                                        sourceRef := p.absoluteSourceRef()
       -                                if sourceRef != "" {
       -                                        // index the canonical, unambiguous ref
       -                                        // e.g. /section/article.md
       -                                        indexPage(index, sourceRef, p)
       -
       -                                        // also index the legacy canonical lookup (not guaranteed to be unambiguous)
       -                                        // e.g. section/article.md
       -                                        indexPage(index, sourceRef[1:], p)
       -                                }
        
       +                                // Allow cross language references by
       +                                // adding the language code as prefix.
       +                                add(path.Join("/"+p.Lang(), sourceRef), p)
       +
       +                                // For pages in the current language.
                                        if s != nil && p.s == s {
       +                                        if sourceRef != "" {
       +                                                // index the canonical ref
       +                                                // e.g. /section/article.md
       +                                                add(sourceRef, p)
       +                                        }
       +
                                                // Ref/Relref supports this potentially ambiguous lookup.
       -                                        indexPage(index, p.Source.LogicalName(), p)
       +                                        add(p.Source.LogicalName(), p)
        
                                                translationBaseName := p.Source.TranslationBaseName()
       -                                        dir := filepath.ToSlash(strings.TrimSuffix(p.Dir(), helpers.FilePathSeparator))
       +
       +                                        dir, _ := path.Split(sourceRef)
       +                                        dir = strings.TrimSuffix(dir, "/")
        
                                                if translationBaseName == "index" {
       -                                                _, name := path.Split(dir)
       -                                                indexPage(index, name, p)
       -                                                indexPage(index, dir, p)
       +                                                add(dir, p)
       +                                                add(path.Base(dir), p)
                                                } else {
       -                                                // Again, ambiguous
       -                                                indexPage(index, translationBaseName, p)
       +                                                add(translationBaseName, p)
                                                }
        
                                                // We need a way to get to the current language version.
                                                pathWithNoExtensions := path.Join(dir, translationBaseName)
       -                                        indexPage(index, pathWithNoExtensions, p)
       +                                        add(pathWithNoExtensions, p)
                                        }
                                }
                        }
       @@ -133,7 +140,7 @@ func (c *PageCollections) refreshPageCaches() {
                                // e.g. /section/_index.md
                                sourceRef := p.absoluteSourceRef()
                                if sourceRef != "" {
       -                                indexPage(index, sourceRef, p)
       +                                add(sourceRef, p)
                                }
        
                                ref := path.Join(p.sections...)
       @@ -141,25 +148,13 @@ func (c *PageCollections) refreshPageCaches() {
                                // index the canonical, unambiguous virtual ref
                                // e.g. /section
                                // (this may already have been indexed above)
       -                        indexPage(index, "/"+ref, p)
       -
       -                        // index the legacy canonical ref (not guaranteed to be unambiguous)
       -                        // e.g. section
       -                        indexPage(index, ref, p)
       +                        add("/"+ref, p)
                        }
       -                return index
       -        }
       -
       -        c.pageIndex = pageIndex{load: indexLoader}
       -}
        
       -func indexPage(index map[string]*Page, ref string, p *Page) {
       -        existing := index[ref]
       -        if existing == nil {
       -                index[ref] = p
       -        } else if existing != ambiguityFlag && existing != p {
       -                index[ref] = ambiguityFlag
       +                return index, nil
                }
       +
       +        c.pageIndex = cache.NewLazy(indexLoader)
        }
        
        func newPageCollections() *PageCollections {
       @@ -170,39 +165,53 @@ func newPageCollectionsFromPages(pages Pages) *PageCollections {
                return &PageCollections{rawAllPages: pages}
        }
        
       -// context: page used to resolve relative paths
       -// ref: either unix-style paths (i.e. callers responsible for
       -// calling filepath.ToSlash as necessary) or shorthand refs.
       -func (c *PageCollections) getPage(context *Page, ref string) (*Page, error) {
       +// getPage is the "old style" get page. Deprecated in Hugo 0.45 in favour of
       +// the "path only" syntax.
       +// TODO(bep) remove this an rename below once this is all working.
       +func (c *PageCollections) getPage(typ string, sections ...string) *Page {
       +        p, _ := c.getPageNew(nil, "/"+path.Join(sections...))
       +        return p
        
       -        var result *Page
       +}
        
       -        if len(ref) > 0 && ref[0:1] == "/" {
       +// Ref is either unix-style paths (i.e. callers responsible for
       +// calling filepath.ToSlash as necessary) or shorthand refs.
       +func (c *PageCollections) getPageNew(context *Page, ref string) (*Page, error) {
        
       -                // it's an absolute path
       -                result = c.pageIndex.Get(ref)
       +        // Absolute (content root relative) reference.
       +        if strings.HasPrefix(ref, "/") {
       +                if p, err := c.getFromCache(ref); err == nil && p != nil {
       +                        return p, nil
       +                }
       +        }
        
       -        } else { // either relative path or other supported ref
       +        // If there's a page context, try the page relative path.
       +        if context != nil {
       +                ppath := path.Join("/", strings.Join(context.sections, "/"), ref)
       +                if p, err := c.getFromCache(ppath); err == nil && p != nil {
       +                        return p, nil
       +                }
       +        }
        
       -                // If there's a page context. relative ref interpretation takes precedence.
       -                if context != nil {
       -                        // For relative refs `filepath.Join` will properly resolve ".." (parent dir)
       -                        // and other elements in the path
       -                        apath := path.Join("/", strings.Join(context.sections, "/"), ref)
       -                        result = c.pageIndex.Get(apath)
       +        if !strings.HasPrefix(ref, "/") {
       +                // Many people will have "post/foo.md" in their content files.
       +                if p, err := c.getFromCache("/" + ref); err == nil && p != nil {
       +                        return p, nil
                        }
       +        }
        
       -                // finally, let's try it as-is for a match against all the alternate refs indexed for each page
       -                if result == nil {
       -                        result = c.pageIndex.Get(ref)
       +        // Last try.
       +        ref = strings.TrimPrefix(ref, "/")
       +        context, err := c.getFromCache(ref)
        
       -                        if result == ambiguityFlag {
       -                                return nil, fmt.Errorf("The reference \"%s\" in %q resolves to more than one page. Use either an absolute path (begins with \"/\") or relative path to the content directory target.", ref, context.absoluteSourceRef())
       -                        }
       +        if err != nil {
       +                if context != nil {
       +                        return nil, fmt.Errorf("failed to resolve page relative to page %q: %s", context.absoluteSourceRef(), err)
                        }
       +                return nil, fmt.Errorf("failed to resolve page: %s", err)
                }
        
       -        return result, nil
       +        return context, nil
        }
        
        func (*PageCollections) findPagesByKindIn(kind string, inPages Pages) Pages {
   DIR diff --git a/hugolib/page_collections_test.go b/hugolib/page_collections_test.go
       @@ -55,12 +55,12 @@ func BenchmarkGetPage(b *testing.B) {
        
                b.ResetTimer()
                for i := 0; i < b.N; i++ {
       -                home, _ := s.getPage(nil, "/")
       +                home, _ := s.getPageNew(nil, "/")
                        if home == nil {
                                b.Fatal("Home is nil")
                        }
        
       -                p, _ := s.getPage(nil, pagePaths[i])
       +                p, _ := s.getPageNew(nil, pagePaths[i])
                        if p == nil {
                                b.Fatal("Section is nil")
                        }
       @@ -91,7 +91,7 @@ func BenchmarkGetPageRegular(b *testing.B) {
        
                b.ResetTimer()
                for i := 0; i < b.N; i++ {
       -                page, _ := s.getPage(nil, pagePaths[i])
       +                page, _ := s.getPageNew(nil, pagePaths[i])
                        require.NotNil(b, page)
                }
        }
       @@ -122,7 +122,7 @@ func TestGetPage(t *testing.T) {
                }{
                        {KindHome, []string{}, ""},
                        {KindSection, []string{"sect3"}, "Sect3s"},
       -                {KindPage, []string{"sect3", "page1.md"}, "Title3_1"},
       +                {KindPage, []string{"sect3/page1.md"}, "Title3_1"},
                        {KindPage, []string{"sect4/page2.md"}, "Title4_2"},
                        {KindPage, []string{filepath.FromSlash("sect5/page3.md")}, "Title5_3"},
                        // Ref/Relref supports this potentially ambiguous lookup.
       @@ -133,7 +133,9 @@ func TestGetPage(t *testing.T) {
                        errorMsg := fmt.Sprintf("Test %d", i)
        
                        // test legacy public Site.GetPage
       -                page, _ := s.Info.GetPage(test.kind, test.path...)
       +                args := append([]string{test.kind}, test.path...)
       +                page, err := s.Info.GetPage(args...)
       +                assert.NoError(err)
                        assert.NotNil(page, errorMsg)
                        assert.Equal(test.kind, page.Kind, errorMsg)
                        assert.Equal(test.expectedTitle, page.title)
       @@ -145,7 +147,8 @@ func TestGetPage(t *testing.T) {
                        } else {
                                ref = path.Join(test.path...)
                        }
       -                page2, _ := s.getPage(nil, ref)
       +                page2, err := s.getPageNew(nil, ref)
       +                assert.NoError(err)
                        assert.NotNil(page2, errorMsg)
                        assert.Equal(test.kind, page2.Kind, errorMsg)
                        assert.Equal(test.expectedTitle, page2.title)
   DIR diff --git a/hugolib/pages_language_merge_test.go b/hugolib/pages_language_merge_test.go
       @@ -68,8 +68,8 @@ func TestMergeLanguages(t *testing.T) {
                assert.Equal(4, len(firstNN.Sites()))
                assert.Equal("en", firstNN.Sites().First().Language.Lang)
        
       -        nnBundle, _ := nnSite.getPage(nil, "bundle")
       -        enBundle, _ := enSite.getPage(nil, "bundle")
       +        nnBundle := nnSite.getPage("page", "bundle")
       +        enBundle := enSite.getPage("page", "bundle")
        
                assert.Equal(6, len(enBundle.Resources))
                assert.Equal(2, len(nnBundle.Resources))
   DIR diff --git a/hugolib/shortcode_test.go b/hugolib/shortcode_test.go
       @@ -687,7 +687,7 @@ NotFound: {{< thisDoesNotExist >}}
                require.Len(t, h.Sites, 1)
        
                s := h.Sites[0]
       -        home, _ := s.getPage(nil, "/")
       +        home := s.getPage(KindHome)
                require.NotNil(t, home)
                require.Len(t, home.outputFormats, 3)
        
   DIR diff --git a/hugolib/site.go b/hugolib/site.go
       @@ -21,7 +21,6 @@ import (
                "mime"
                "net/url"
                "os"
       -        "path"
                "path/filepath"
                "sort"
                "strconv"
       @@ -504,7 +503,7 @@ func (s *SiteInfo) refLink(ref string, page *Page, relative bool, outputFormat s
                var link string
        
                if refURL.Path != "" {
       -                target, err := s.getPage(page, refURL.Path)
       +                target, err := s.getPageNew(page, refURL.Path)
        
                        if err != nil {
                                return "", err
       @@ -1603,19 +1602,45 @@ func (s *Site) appendThemeTemplates(in []string) []string {
        }
        
        // GetPage looks up a page of a given type for the given ref.
       -//    {{ with .Site.GetPage "section" "blog" }}{{ .Title }}{{ end }}
       -//
       -// This will return nil when no page could be found, and will return an
       -// error if the key is ambiguous.
       -func (s *SiteInfo) GetPage(typ string, ref ...string) (*Page, error) {
       +// In Hugo <= 0.44 you had to add Page Kind (section, home) etc. as the first
       +// argument and then either a unix styled path (with or without a leading slash))
       +// or path elements separated.
       +// When we now remove the Kind from this API, we need to make the transition as painless
       +// as possible for existing sites. Most sites will use {{ .Site.GetPage "section" "my/section" }},
       +// i.e. 2 arguments, so we test for that.
       +func (s *SiteInfo) GetPage(ref ...string) (*Page, error) {
       +        var refs []string
       +        for _, r := range ref {
       +                // A common construct in the wild is
       +                // .Site.GetPage "home" "" or
       +                // .Site.GetPage "home" "/"
       +                if r != "" && r != "/" {
       +                        refs = append(refs, r)
       +                }
       +        }
       +
                var key string
       -        if len(ref) == 1 {
       -                key = filepath.ToSlash(ref[0])
       +
       +        if len(refs) > 2 {
       +                // This was allowed in Hugo <= 0.44, but we cannot support this with the
       +                // new API. This should be the most unusual case.
       +                return nil, fmt.Errorf(`too many arguments to .Site.GetPage: %v. Use lookups on the form {{ .Site.GetPage "/posts/mypage-md" }}`, ref)
       +        }
       +
       +        if len(refs) == 0 || refs[0] == KindHome {
       +                key = "/"
       +        } else if len(refs) == 1 {
       +                key = refs[0]
                } else {
       -                key = path.Join(ref...)
       +                key = refs[1]
       +        }
       +
       +        key = filepath.ToSlash(key)
       +        if !strings.HasPrefix(key, "/") {
       +                key = "/" + key
                }
        
       -        return s.getPage(nil, key)
       +        return s.getPageNew(nil, key)
        }
        
        func (s *Site) permalinkForOutputFormat(link string, f output.Format) (string, error) {
   DIR diff --git a/hugolib/site_output_test.go b/hugolib/site_output_test.go
       @@ -150,7 +150,7 @@ Len Pages: {{ .Kind }} {{ len .Site.RegularPages }} Page Number: {{ .Paginator.P
                s := h.Sites[0]
                require.Equal(t, "en", s.Language.Lang)
        
       -        home, _ := s.getPage(nil, "/")
       +        home := s.getPage(KindHome)
        
                require.NotNil(t, home)
        
       @@ -325,7 +325,7 @@ baseName = "customdelimbase"
                th.assertFileContent("public/customdelimbase_del", "custom delim")
        
                s := h.Sites[0]
       -        home, _ := s.getPage(nil, "/")
       +        home := s.getPage(KindHome)
                require.NotNil(t, home)
        
                outputs := home.OutputFormats()
   DIR diff --git a/hugolib/site_sections_test.go b/hugolib/site_sections_test.go
       @@ -104,7 +104,7 @@ Content
                writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), "<html>Single|{{ .Title }}</html>")
                writeSource(t, fs, filepath.Join("layouts", "_default", "list.html"),
                        `
       -{{ $sect := (.Site.GetPage "section" "l1" "l2") }}
       +{{ $sect := (.Site.GetPage "l1/l2") }}
        <html>List|{{ .Title }}|L1/l2-IsActive: {{ .InSection $sect }}
        {{ range .Paginator.Pages }}
        PAG|{{ .Title }}|{{ $sect.InSection . }}
       @@ -135,19 +135,19 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
                        }},
                        {"empty1", func(p *Page) {
                                // > b,c
       -                        assert.NotNil(p.s.getPage(nil, "empty1/b"))
       -                        assert.NotNil(p.s.getPage(nil, "empty1/b/c"))
       +                        assert.NotNil(p.s.getPage(KindSection, "empty1", "b"))
       +                        assert.NotNil(p.s.getPage(KindSection, "empty1", "b", "c"))
        
                        }},
                        {"empty2", func(p *Page) {
                                // > b,c,d where b and d have content files.
       -                        b, _ := p.s.getPage(nil, "empty2/b")
       +                        b := p.s.getPage(KindSection, "empty2", "b")
                                assert.NotNil(b)
                                assert.Equal("T40_-1", b.title)
       -                        c, _ := p.s.getPage(nil, "empty2/b/c")
       +                        c := p.s.getPage(KindSection, "empty2", "b", "c")
                                assert.NotNil(c)
                                assert.Equal("Cs", c.title)
       -                        d, _ := p.s.getPage(nil, "empty2/b/c/d")
       +                        d := p.s.getPage(KindSection, "empty2", "b", "c", "d")
                                assert.NotNil(d)
                                assert.Equal("T41_-1", d.title)
        
       @@ -156,9 +156,9 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
                                assert.False(c.Eq("asdf"))
        
                        }},
       -                {"/empty3", func(p *Page) {
       +                {"empty3", func(p *Page) {
                                // b,c,d with regular page in b
       -                        b, _ := p.s.getPage(nil, "/empty3/b")
       +                        b := p.s.getPage(KindSection, "empty3", "b")
                                assert.NotNil(b)
                                assert.Len(b.Pages, 1)
                                assert.Equal("empty3.md", b.Pages[0].File.LogicalName())
       @@ -200,8 +200,7 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
                                        active, err = p.InSection(child)
                                        assert.NoError(err)
                                        assert.True(active)
       -                                homePage, _ := p.s.getPage(nil, "/")
       -                                active, err = p.InSection(homePage)
       +                                active, err = p.InSection(p.s.getPage(KindHome))
                                        assert.NoError(err)
                                        assert.False(active)
        
       @@ -236,7 +235,7 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
                                assert.Equal("T2_-1", p.Parent().title)
                                assert.Len(p.Sections(), 0)
        
       -                        l1, _ := p.s.getPage(nil, "l1")
       +                        l1 := p.s.getPage(KindSection, "l1")
                                isDescendant, err := l1.IsDescendant(p)
                                assert.NoError(err)
                                assert.False(isDescendant)
       @@ -266,18 +265,16 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
                        }},
                }
        
       -        home, _ := s.getPage(nil, "/")
       +        home := s.getPage(KindHome)
        
                for _, test := range tests {
                        sections := strings.Split(test.sections, ",")
       -                p, _ := s.Info.GetPage(KindSection, sections...)
       +                p := s.getPage(KindSection, sections...)
                        assert.NotNil(p, fmt.Sprint(sections))
        
                        if p.Pages != nil {
                                assert.Equal(p.Pages, p.data["Pages"])
                        }
       -
       -                fmt.Println(p, test.sections)
                        assert.NotNil(p.Parent(), fmt.Sprintf("Parent nil: %q", test.sections))
                        test.verify(p)
                }
       @@ -287,7 +284,7 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
                assert.Len(home.Sections(), 9)
                assert.Equal(home.Sections(), s.Info.Sections())
        
       -        rootPage, _ := s.getPage(nil, "mypage.md")
       +        rootPage := s.getPage(KindPage, "mypage.md")
                assert.NotNil(rootPage)
                assert.True(rootPage.Parent().IsHome())
        
       @@ -297,7 +294,7 @@ PAG|{{ .Title }}|{{ $sect.InSection . }}
                // If we later decide to do something about this, we will have to do some normalization in
                // getPage.
                // TODO(bep)
       -        sectionWithSpace, _ := s.getPage(nil, "Spaces in Section")
       +        sectionWithSpace := s.getPage(KindSection, "Spaces in Section")
                require.NotNil(t, sectionWithSpace)
                require.Equal(t, "/spaces-in-section/", sectionWithSpace.RelPermalink())
        
   DIR diff --git a/hugolib/site_test.go b/hugolib/site_test.go
       @@ -628,8 +628,7 @@ func TestOrderedPages(t *testing.T) {
        
                s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
        
       -        sect, _ := s.getPage(nil, "sect")
       -        if sect.Pages[1].title != "Three" || sect.Pages[2].title != "Four" {
       +        if s.getPage(KindSection, "sect").Pages[1].title != "Three" || s.getPage(KindSection, "sect").Pages[2].title != "Four" {
                        t.Error("Pages in unexpected order.")
                }
        
       @@ -875,10 +874,8 @@ func TestWeightedTaxonomies(t *testing.T) {
        func setupLinkingMockSite(t *testing.T) *Site {
                sources := [][2]string{
                        {filepath.FromSlash("level2/unique.md"), ""},
       -                {filepath.FromSlash("_index.md"), ""},
                        {filepath.FromSlash("rootfile.md"), ""},
                        {filepath.FromSlash("root-image.png"), ""},
       -                {filepath.FromSlash("common.md"), ""},
        
                        {filepath.FromSlash("level2/2-root.md"), ""},
                        {filepath.FromSlash("level2/common.md"), ""},
       @@ -886,7 +883,7 @@ func setupLinkingMockSite(t *testing.T) *Site {
                        {filepath.FromSlash("level2/2-image.png"), ""},
                        {filepath.FromSlash("level2/common.png"), ""},
        
       -                {filepath.FromSlash("level2/level3/current.md"), ""},
       +                {filepath.FromSlash("level2/level3/start.md"), ""},
                        {filepath.FromSlash("level2/level3/3-root.md"), ""},
                        {filepath.FromSlash("level2/level3/common.md"), ""},
                        {filepath.FromSlash("level2/level3/3-image.png"), ""},
       @@ -913,7 +910,7 @@ func TestRefLinking(t *testing.T) {
                t.Parallel()
                site := setupLinkingMockSite(t)
        
       -        currentPage, _ := site.getPage(nil, "level2/level3/current.md")
       +        currentPage := site.getPage(KindPage, "level2/level3/start.md")
                if currentPage == nil {
                        t.Fatalf("failed to find current page in site")
                }
       @@ -924,16 +921,12 @@ func TestRefLinking(t *testing.T) {
                        relative     bool
                        expected     string
                }{
       -                {"/level2/unique.md", "", true, "/level2/unique/"},
       -                {"../unique.md", "", true, "/level2/unique/"},
       -                {"/level2/common.md", "", true, "/level2/common/"},
       -                {"../common.md", "", true, "/level2/common/"},
       -                {"common.md", "", true, "/level2/level3/common/"},
       -                {"/common.md", "", true, "/common/"},
       +                {"unique.md", "", true, "/level2/unique/"},
       +                {"level2/common.md", "", true, "/level2/common/"},
                        {"3-root.md", "", true, "/level2/level3/3-root/"},
                } {
                        if out, err := site.Info.refLink(test.link, currentPage, test.relative, test.outputFormat); err != nil || out != test.expected {
       -                        t.Errorf("[%d] Expected %q from %q to resolve to %q, got %q - error: %s", i, test.link, currentPage.absoluteSourceRef(), test.expected, out, err)
       +                        t.Errorf("[%d] Expected %s to resolve to (%s), got (%s) - error: %s", i, test.link, test.expected, out, err)
                        }
                }
        
   DIR diff --git a/hugolib/site_url_test.go b/hugolib/site_url_test.go
       @@ -117,12 +117,12 @@ Do not go gentle into that good night.
        
                assert.Len(s.RegularPages, 2)
        
       -        notUgly, _ := s.getPage(nil, "sect1/p1.md")
       +        notUgly := s.getPage(KindPage, "sect1/p1.md")
                assert.NotNil(notUgly)
                assert.Equal("sect1", notUgly.Section())
                assert.Equal("/sect1/p1/", notUgly.RelPermalink())
        
       -        ugly, _ := s.getPage(nil, "sect2/p2.md")
       +        ugly := s.getPage(KindPage, "sect2/p2.md")
                assert.NotNil(ugly)
                assert.Equal("sect2", ugly.Section())
                assert.Equal("/sect2/p2.html", ugly.RelPermalink())
       @@ -175,7 +175,7 @@ Do not go gentle into that good night.
        
                assert.Len(s.RegularPages, 10)
        
       -        sect1, _ := s.getPage(nil, "sect1")
       +        sect1 := s.getPage(KindSection, "sect1")
                assert.NotNil(sect1)
                assert.Equal("/ss1/", sect1.RelPermalink())
                th.assertFileContent(filepath.Join("public", "ss1", "index.html"), "P1|URL: /ss1/|Next: /ss1/page/2/")
   DIR diff --git a/hugolib/taxonomy_test.go b/hugolib/taxonomy_test.go
       @@ -167,7 +167,7 @@ permalinkeds:
                }
        
                for taxonomy, count := range taxonomyTermPageCounts {
       -                term, _ := s.getPage(nil, taxonomy)
       +                term := s.getPage(KindTaxonomyTerm, taxonomy)
                        require.NotNil(t, term)
                        require.Len(t, term.Pages, count)
        
       @@ -176,7 +176,7 @@ permalinkeds:
                        }
                }
        
       -        cat1, _ := s.getPage(nil, "categories/cat1")
       +        cat1 := s.getPage(KindTaxonomy, "categories", "cat1")
                require.NotNil(t, cat1)
                if uglyURLs {
                        require.Equal(t, "/blog/categories/cat1.html", cat1.RelPermalink())
       @@ -184,8 +184,8 @@ permalinkeds:
                        require.Equal(t, "/blog/categories/cat1/", cat1.RelPermalink())
                }
        
       -        pl1, _ := s.getPage(nil, "permalinkeds/pl1")
       -        permalinkeds, _ := s.getPage(nil, "permalinkeds")
       +        pl1 := s.getPage(KindTaxonomy, "permalinkeds", "pl1")
       +        permalinkeds := s.getPage(KindTaxonomyTerm, "permalinkeds")
                require.NotNil(t, pl1)
                require.NotNil(t, permalinkeds)
                if uglyURLs {
       @@ -198,11 +198,11 @@ permalinkeds:
        
                // Issue #3070 preserveTaxonomyNames
                if preserveTaxonomyNames {
       -                helloWorld, _ := s.getPage(nil, "others/Hello Hugo world")
       +                helloWorld := s.getPage(KindTaxonomy, "others", "Hello Hugo world")
                        require.NotNil(t, helloWorld)
                        require.Equal(t, "Hello Hugo world", helloWorld.title)
                } else {
       -                helloWorld, _ := s.getPage(nil, "others/hello-hugo-world")
       +                helloWorld := s.getPage(KindTaxonomy, "others", "hello-hugo-world")
                        require.NotNil(t, helloWorld)
                        require.Equal(t, "Hello Hugo World", helloWorld.title)
                }