]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
hugolib: Move hugolib/site_new.go into hugolib/site.go
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Tue, 17 Sep 2024 08:14:51 +0000 (10:14 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Tue, 17 Sep 2024 08:15:14 +0000 (10:15 +0200)
hugolib/site.go
hugolib/site_new.go [deleted file]

index d0546e9107f247d101b4b2fd91b19236261d44c5..d0a3bd37091c4f6a41e0290ee50a89f5fda1a65b 100644 (file)
@@ -15,7 +15,9 @@ package hugolib
 
 import (
        "context"
+       "errors"
        "fmt"
+       "html/template"
        "io"
        "mime"
        "net/url"
@@ -28,10 +30,24 @@ import (
        "time"
 
        "github.com/bep/logg"
+       "github.com/gohugoio/hugo/cache/dynacache"
        "github.com/gohugoio/hugo/common/htime"
        "github.com/gohugoio/hugo/common/hugio"
+       "github.com/gohugoio/hugo/common/hugo"
+       "github.com/gohugoio/hugo/common/loggers"
+       "github.com/gohugoio/hugo/common/maps"
+       "github.com/gohugoio/hugo/common/para"
        "github.com/gohugoio/hugo/common/types"
+       "github.com/gohugoio/hugo/config"
+       "github.com/gohugoio/hugo/config/allconfig"
+       "github.com/gohugoio/hugo/deps"
        "github.com/gohugoio/hugo/hugolib/doctree"
+       "github.com/gohugoio/hugo/hugolib/pagesfromdata"
+       "github.com/gohugoio/hugo/internal/warpc"
+       "github.com/gohugoio/hugo/langs/i18n"
+       "github.com/gohugoio/hugo/modules"
+       "github.com/gohugoio/hugo/resources"
+       "github.com/gohugoio/hugo/tpl/tplimpl"
        "golang.org/x/text/unicode/norm"
 
        "github.com/gohugoio/hugo/common/paths"
@@ -50,6 +66,9 @@ 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/page/siteidentities"
+       "github.com/gohugoio/hugo/resources/resource"
 
        "github.com/gohugoio/hugo/lazy"
 
@@ -61,6 +80,556 @@ import (
        "github.com/gohugoio/hugo/tpl"
 )
 
+var _ page.Site = (*Site)(nil)
+
+type siteState int
+
+const (
+       siteStateInit siteState = iota
+       siteStateReady
+)
+
+type Site struct {
+       state     siteState
+       conf      *allconfig.Config
+       language  *langs.Language
+       languagei int
+       pageMap   *pageMap
+
+       // The owning container.
+       h *HugoSites
+
+       *deps.Deps
+
+       // Page navigation.
+       *pageFinder
+       taxonomies page.TaxonomyList
+       menus      navigation.Menus
+
+       // Shortcut to the home page. Note that this may be nil if
+       // home page, for some odd reason, is disabled.
+       home *pageState
+
+       // The last modification date of this site.
+       lastmod time.Time
+
+       relatedDocsHandler *page.RelatedDocsHandler
+       siteRefLinker
+       publisher          publisher.Publisher
+       frontmatterHandler pagemeta.FrontMatterHandler
+
+       // The output formats that we need to render this site in. This slice
+       // will be fixed once set.
+       // This will be the union of Site.Pages' outputFormats.
+       // This slice will be sorted.
+       renderFormats output.Formats
+
+       // Lazily loaded site dependencies
+       init *siteInit
+}
+
+func (s *Site) Debug() {
+       fmt.Println("Debugging site", s.Lang(), "=>")
+       // fmt.Println(s.pageMap.testDump())
+}
+
+// NewHugoSites creates HugoSites from the given config.
+func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
+       conf := cfg.Configs.GetFirstLanguageConfig()
+
+       var logger loggers.Logger
+       if cfg.TestLogger != nil {
+               logger = cfg.TestLogger
+       } else {
+               var logHookLast func(e *logg.Entry) error
+               if cfg.Configs.Base.PanicOnWarning {
+                       logHookLast = loggers.PanicOnWarningHook
+               }
+               if cfg.LogOut == nil {
+                       cfg.LogOut = os.Stdout
+               }
+               if cfg.LogLevel == 0 {
+                       cfg.LogLevel = logg.LevelWarn
+               }
+
+               logOpts := loggers.Options{
+                       Level:              cfg.LogLevel,
+                       DistinctLevel:      logg.LevelWarn, // This will drop duplicate log warning and errors.
+                       HandlerPost:        logHookLast,
+                       Stdout:             cfg.LogOut,
+                       Stderr:             cfg.LogOut,
+                       StoreErrors:        conf.Watching(),
+                       SuppressStatements: conf.IgnoredLogs(),
+               }
+               logger = loggers.New(logOpts)
+
+       }
+
+       memCache := dynacache.New(dynacache.Options{Watching: conf.Watching(), Log: logger})
+
+       var h *HugoSites
+       onSignalRebuild := func(ids ...identity.Identity) {
+               // This channel is buffered, but make sure we do this in a non-blocking way.
+               if cfg.ChangesFromBuild != nil {
+                       go func() {
+                               cfg.ChangesFromBuild <- ids
+                       }()
+               }
+       }
+
+       firstSiteDeps := &deps.Deps{
+               Fs:   cfg.Fs,
+               Log:  logger,
+               Conf: conf,
+               BuildState: &deps.BuildState{
+                       OnSignalRebuild: onSignalRebuild,
+               },
+               MemCache:            memCache,
+               TemplateProvider:    tplimpl.DefaultTemplateProvider,
+               TranslationProvider: i18n.NewTranslationProvider(),
+               WasmDispatchers: warpc.AllDispatchers(
+                       warpc.Options{
+                               CompilationCacheDir: filepath.Join(conf.Dirs().CacheDir, "_warpc"),
+
+                               // Katex is relatively slow.
+                               PoolSize: 8,
+                               Infof:    logger.InfoCommand("wasm").Logf,
+                       },
+               ),
+       }
+
+       if err := firstSiteDeps.Init(); err != nil {
+               return nil, err
+       }
+
+       confm := cfg.Configs
+       if err := confm.Validate(logger); err != nil {
+               return nil, err
+       }
+       var sites []*Site
+
+       ns := &contentNodeShifter{
+               numLanguages: len(confm.Languages),
+       }
+
+       treeConfig := doctree.Config[contentNodeI]{
+               Shifter: ns,
+       }
+
+       pageTrees := &pageTrees{
+               treePages: doctree.New(
+                       treeConfig,
+               ),
+               treeResources: doctree.New(
+                       treeConfig,
+               ),
+               treeTaxonomyEntries:           doctree.NewTreeShiftTree[*weightedContentNode](doctree.DimensionLanguage.Index(), len(confm.Languages)),
+               treePagesFromTemplateAdapters: doctree.NewTreeShiftTree[*pagesfromdata.PagesFromTemplate](doctree.DimensionLanguage.Index(), len(confm.Languages)),
+       }
+
+       pageTrees.createMutableTrees()
+
+       for i, confp := range confm.ConfigLangs() {
+               language := confp.Language()
+               if language.Disabled {
+                       continue
+               }
+               k := language.Lang
+               conf := confm.LanguageConfigMap[k]
+               frontmatterHandler, err := pagemeta.NewFrontmatterHandler(firstSiteDeps.Log, conf.Frontmatter)
+               if err != nil {
+                       return nil, err
+               }
+
+               langs.SetParams(language, conf.Params)
+
+               s := &Site{
+                       conf:               conf,
+                       language:           language,
+                       languagei:          i,
+                       frontmatterHandler: frontmatterHandler,
+               }
+
+               if i == 0 {
+                       firstSiteDeps.Site = s
+                       s.Deps = firstSiteDeps
+               } else {
+                       d, err := firstSiteDeps.Clone(s, confp)
+                       if err != nil {
+                               return nil, err
+                       }
+                       s.Deps = d
+               }
+
+               s.pageMap = newPageMap(i, s, memCache, pageTrees)
+
+               s.pageFinder = newPageFinder(s.pageMap)
+               s.siteRefLinker, err = newSiteRefLinker(s)
+               if err != nil {
+                       return nil, err
+               }
+               // Set up the main publishing chain.
+               pub, err := publisher.NewDestinationPublisher(
+                       firstSiteDeps.ResourceSpec,
+                       s.conf.OutputFormats.Config,
+                       s.conf.MediaTypes.Config,
+               )
+               if err != nil {
+                       return nil, err
+               }
+
+               s.publisher = pub
+               s.relatedDocsHandler = page.NewRelatedDocsHandler(s.conf.Related)
+               // Site deps end.
+
+               s.prepareInits()
+               sites = append(sites, s)
+       }
+
+       if len(sites) == 0 {
+               return nil, errors.New("no sites to build")
+       }
+
+       // Pull the default content language to the top, then sort the sites by language weight (if set) or lang.
+       defaultContentLanguage := confm.Base.DefaultContentLanguage
+       sort.Slice(sites, func(i, j int) bool {
+               li := sites[i].language
+               lj := sites[j].language
+               if li.Lang == defaultContentLanguage {
+                       return true
+               }
+
+               if lj.Lang == defaultContentLanguage {
+                       return false
+               }
+
+               if li.Weight != lj.Weight {
+                       return li.Weight < lj.Weight
+               }
+               return li.Lang < lj.Lang
+       })
+
+       var err error
+       h, err = newHugoSites(cfg, firstSiteDeps, pageTrees, sites)
+       if err == nil && h == nil {
+               panic("hugo: newHugoSitesNew returned nil error and nil HugoSites")
+       }
+
+       return h, err
+}
+
+func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []*Site) (*HugoSites, error) {
+       numWorkers := config.GetNumWorkerMultiplier()
+       numWorkersSite := numWorkers
+       if numWorkersSite > len(sites) {
+               numWorkersSite = len(sites)
+       }
+       workersSite := para.New(numWorkersSite)
+
+       h := &HugoSites{
+               Sites:           sites,
+               Deps:            sites[0].Deps,
+               Configs:         cfg.Configs,
+               workersSite:     workersSite,
+               numWorkersSites: numWorkers,
+               numWorkers:      numWorkers,
+               pageTrees:       pageTrees,
+               cachePages: dynacache.GetOrCreatePartition[string,
+                       page.Pages](d.MemCache, "/pags/all",
+                       dynacache.OptionsPartition{Weight: 10, ClearWhen: dynacache.ClearOnRebuild},
+               ),
+               cacheContentSource:      dynacache.GetOrCreatePartition[string, *resources.StaleValue[[]byte]](d.MemCache, "/cont/src", dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
+               translationKeyPages:     maps.NewSliceCache[page.Page](),
+               currentSite:             sites[0],
+               skipRebuildForFilenames: make(map[string]bool),
+               init: &hugoSitesInit{
+                       data:    lazy.New(),
+                       layouts: lazy.New(),
+                       gitInfo: lazy.New(),
+               },
+       }
+
+       // Assemble dependencies to be used in hugo.Deps.
+       var dependencies []*hugo.Dependency
+       var depFromMod func(m modules.Module) *hugo.Dependency
+       depFromMod = func(m modules.Module) *hugo.Dependency {
+               dep := &hugo.Dependency{
+                       Path:    m.Path(),
+                       Version: m.Version(),
+                       Time:    m.Time(),
+                       Vendor:  m.Vendor(),
+               }
+
+               // These are pointers, but this all came from JSON so there's no recursive navigation,
+               // so just create new values.
+               if m.Replace() != nil {
+                       dep.Replace = depFromMod(m.Replace())
+               }
+               if m.Owner() != nil {
+                       dep.Owner = depFromMod(m.Owner())
+               }
+               return dep
+       }
+       for _, m := range d.Paths.AllModules() {
+               dependencies = append(dependencies, depFromMod(m))
+       }
+
+       h.hugoInfo = hugo.NewInfo(h.Configs.GetFirstLanguageConfig(), dependencies)
+
+       var prototype *deps.Deps
+       for i, s := range sites {
+               s.h = h
+               if err := s.Deps.Compile(prototype); err != nil {
+                       return nil, err
+               }
+               if i == 0 {
+                       prototype = s.Deps
+               }
+       }
+
+       h.fatalErrorHandler = &fatalErrorHandler{
+               h:     h,
+               donec: make(chan bool),
+       }
+
+       h.init.data.Add(func(context.Context) (any, error) {
+               err := h.loadData()
+               if err != nil {
+                       return nil, fmt.Errorf("failed to load data: %w", err)
+               }
+               return nil, nil
+       })
+
+       h.init.layouts.Add(func(context.Context) (any, error) {
+               for _, s := range h.Sites {
+                       if err := s.Tmpl().(tpl.TemplateManager).MarkReady(); err != nil {
+                               return nil, err
+                       }
+               }
+               return nil, nil
+       })
+
+       h.init.gitInfo.Add(func(context.Context) (any, error) {
+               err := h.loadGitInfo()
+               if err != nil {
+                       return nil, fmt.Errorf("failed to load Git info: %w", err)
+               }
+               return nil, nil
+       })
+
+       return h, nil
+}
+
+// Deprecated: Use hugo.IsServer instead.
+func (s *Site) IsServer() bool {
+       hugo.Deprecate(".Site.IsServer", "Use hugo.IsServer instead.", "v0.120.0")
+       return s.conf.Internal.Running
+}
+
+// Returns the server port.
+func (s *Site) ServerPort() int {
+       return s.conf.C.BaseURL.Port()
+}
+
+// Returns the configured title for this Site.
+func (s *Site) Title() string {
+       return s.conf.Title
+}
+
+func (s *Site) Copyright() string {
+       return s.conf.Copyright
+}
+
+// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
+func (s *Site) RSSLink() template.URL {
+       hugo.Deprecate(".Site.RSSLink", "Use the Output Format's Permalink method instead, e.g. .OutputFormats.Get \"RSS\".Permalink", "v0.114.0")
+       rssOutputFormat := s.home.OutputFormats().Get("rss")
+       return template.URL(rssOutputFormat.Permalink())
+}
+
+func (s *Site) Config() page.SiteConfig {
+       return page.SiteConfig{
+               Privacy:  s.conf.Privacy,
+               Services: s.conf.Services,
+       }
+}
+
+func (s *Site) LanguageCode() string {
+       return s.Language().LanguageCode()
+}
+
+// Returns all Sites for all languages.
+func (s *Site) Sites() page.Sites {
+       sites := make(page.Sites, len(s.h.Sites))
+       for i, s := range s.h.Sites {
+               sites[i] = s.Site()
+       }
+       return sites
+}
+
+// Returns Site currently rendering.
+func (s *Site) Current() page.Site {
+       return s.h.currentSite
+}
+
+// MainSections returns the list of main sections.
+func (s *Site) MainSections() []string {
+       s.CheckReady()
+       return s.conf.C.MainSections
+}
+
+// Returns a struct with some information about the build.
+func (s *Site) Hugo() hugo.HugoInfo {
+       if s.h == nil || s.h.hugoInfo.Environment == "" {
+               panic("site: hugo: hugoInfo not initialized")
+       }
+       return s.h.hugoInfo
+}
+
+// Returns the BaseURL for this Site.
+func (s *Site) BaseURL() string {
+       return s.conf.C.BaseURL.WithPath
+}
+
+// Deprecated: Use .Site.Lastmod instead.
+func (s *Site) LastChange() time.Time {
+       s.CheckReady()
+       hugo.Deprecate(".Site.LastChange", "Use .Site.Lastmod instead.", "v0.123.0")
+       return s.lastmod
+}
+
+// Returns the last modification date of the content.
+func (s *Site) Lastmod() time.Time {
+       return s.lastmod
+}
+
+// Returns the Params configured for this site.
+func (s *Site) Params() maps.Params {
+       return s.conf.Params
+}
+
+// Deprecated: Use taxonomies instead.
+func (s *Site) Author() map[string]any {
+       if len(s.conf.Author) != 0 {
+               hugo.Deprecate(".Site.Author", "Use taxonomies instead.", "v0.124.0")
+       }
+       return s.conf.Author
+}
+
+// Deprecated: Use taxonomies instead.
+func (s *Site) Authors() page.AuthorList {
+       hugo.Deprecate(".Site.Authors", "Use taxonomies instead.", "v0.124.0")
+       return page.AuthorList{}
+}
+
+// Deprecated: Use .Site.Params instead.
+func (s *Site) Social() map[string]string {
+       hugo.Deprecate(".Site.Social", "Use .Site.Params instead.", "v0.124.0")
+       return s.conf.Social
+}
+
+// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead.
+func (s *Site) DisqusShortname() string {
+       hugo.Deprecate(".Site.DisqusShortname", "Use .Site.Config.Services.Disqus.Shortname instead.", "v0.120.0")
+       return s.Config().Services.Disqus.Shortname
+}
+
+// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead.
+func (s *Site) GoogleAnalytics() string {
+       hugo.Deprecate(".Site.GoogleAnalytics", "Use .Site.Config.Services.GoogleAnalytics.ID instead.", "v0.120.0")
+       return s.Config().Services.GoogleAnalytics.ID
+}
+
+func (s *Site) Param(key any) (any, error) {
+       return resource.Param(s, nil, key)
+}
+
+// Returns a map of all the data inside /data.
+func (s *Site) Data() map[string]any {
+       return s.s.h.Data()
+}
+
+func (s *Site) BuildDrafts() bool {
+       return s.conf.BuildDrafts
+}
+
+// Deprecated: Use hugo.IsMultilingual instead.
+func (s *Site) IsMultiLingual() bool {
+       hugo.Deprecate(".Site.IsMultiLingual", "Use hugo.IsMultilingual instead.", "v0.124.0")
+       return s.h.isMultilingual()
+}
+
+func (s *Site) LanguagePrefix() string {
+       prefix := s.GetLanguagePrefix()
+       if prefix == "" {
+               return ""
+       }
+       return "/" + prefix
+}
+
+func (s *Site) Site() page.Site {
+       return page.WrapSite(s)
+}
+
+func (s *Site) ForEeachIdentityByName(name string, f func(identity.Identity) bool) {
+       if id, found := siteidentities.FromString(name); found {
+               if f(id) {
+                       return
+               }
+       }
+}
+
+// Pages returns all pages.
+// This is for the current language only.
+func (s *Site) Pages() page.Pages {
+       s.CheckReady()
+       return s.pageMap.getPagesInSection(
+               pageMapQueryPagesInSection{
+                       pageMapQueryPagesBelowPath: pageMapQueryPagesBelowPath{
+                               Path:    "",
+                               KeyPart: "global",
+                               Include: pagePredicates.ShouldListGlobal,
+                       },
+                       Recursive:   true,
+                       IncludeSelf: true,
+               },
+       )
+}
+
+// RegularPages returns all the regular pages.
+// This is for the current language only.
+func (s *Site) RegularPages() page.Pages {
+       s.CheckReady()
+       return s.pageMap.getPagesInSection(
+               pageMapQueryPagesInSection{
+                       pageMapQueryPagesBelowPath: pageMapQueryPagesBelowPath{
+                               Path:    "",
+                               KeyPart: "global",
+                               Include: pagePredicates.ShouldListGlobal.And(pagePredicates.KindPage),
+                       },
+                       Recursive: true,
+               },
+       )
+}
+
+// AllPages returns all pages for all sites.
+func (s *Site) AllPages() page.Pages {
+       s.CheckReady()
+       return s.h.Pages()
+}
+
+// AllRegularPages returns all regular pages for all sites.
+func (s *Site) AllRegularPages() page.Pages {
+       s.CheckReady()
+       return s.h.RegularPages()
+}
+
+func (s *Site) CheckReady() {
+       if s.state != siteStateReady {
+               panic("this method cannot be called before the site is fully initialized")
+       }
+}
+
 func (s *Site) Taxonomies() page.TaxonomyList {
        s.CheckReady()
        s.init.taxonomies.Do(context.Background())
diff --git a/hugolib/site_new.go b/hugolib/site_new.go
deleted file mode 100644 (file)
index 19a7e42..0000000
+++ /dev/null
@@ -1,603 +0,0 @@
-// 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.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package hugolib
-
-import (
-       "context"
-       "errors"
-       "fmt"
-       "html/template"
-       "os"
-       "path/filepath"
-       "sort"
-       "time"
-
-       "github.com/bep/logg"
-       "github.com/gohugoio/hugo/cache/dynacache"
-       "github.com/gohugoio/hugo/common/hugo"
-       "github.com/gohugoio/hugo/common/loggers"
-       "github.com/gohugoio/hugo/common/maps"
-       "github.com/gohugoio/hugo/common/para"
-       "github.com/gohugoio/hugo/config"
-       "github.com/gohugoio/hugo/config/allconfig"
-       "github.com/gohugoio/hugo/deps"
-       "github.com/gohugoio/hugo/hugolib/doctree"
-       "github.com/gohugoio/hugo/hugolib/pagesfromdata"
-       "github.com/gohugoio/hugo/identity"
-       "github.com/gohugoio/hugo/internal/warpc"
-       "github.com/gohugoio/hugo/langs"
-       "github.com/gohugoio/hugo/langs/i18n"
-       "github.com/gohugoio/hugo/lazy"
-       "github.com/gohugoio/hugo/modules"
-       "github.com/gohugoio/hugo/navigation"
-       "github.com/gohugoio/hugo/output"
-       "github.com/gohugoio/hugo/publisher"
-       "github.com/gohugoio/hugo/resources"
-       "github.com/gohugoio/hugo/resources/page"
-       "github.com/gohugoio/hugo/resources/page/pagemeta"
-       "github.com/gohugoio/hugo/resources/page/siteidentities"
-       "github.com/gohugoio/hugo/resources/resource"
-       "github.com/gohugoio/hugo/tpl"
-       "github.com/gohugoio/hugo/tpl/tplimpl"
-)
-
-var _ page.Site = (*Site)(nil)
-
-type siteState int
-
-const (
-       siteStateInit siteState = iota
-       siteStateReady
-)
-
-type Site struct {
-       state     siteState
-       conf      *allconfig.Config
-       language  *langs.Language
-       languagei int
-       pageMap   *pageMap
-
-       // The owning container.
-       h *HugoSites
-
-       *deps.Deps
-
-       // Page navigation.
-       *pageFinder
-       taxonomies page.TaxonomyList
-       menus      navigation.Menus
-
-       // Shortcut to the home page. Note that this may be nil if
-       // home page, for some odd reason, is disabled.
-       home *pageState
-
-       // The last modification date of this site.
-       lastmod time.Time
-
-       relatedDocsHandler *page.RelatedDocsHandler
-       siteRefLinker
-       publisher          publisher.Publisher
-       frontmatterHandler pagemeta.FrontMatterHandler
-
-       // The output formats that we need to render this site in. This slice
-       // will be fixed once set.
-       // This will be the union of Site.Pages' outputFormats.
-       // This slice will be sorted.
-       renderFormats output.Formats
-
-       // Lazily loaded site dependencies
-       init *siteInit
-}
-
-func (s *Site) Debug() {
-       fmt.Println("Debugging site", s.Lang(), "=>")
-       // fmt.Println(s.pageMap.testDump())
-}
-
-// NewHugoSites creates HugoSites from the given config.
-func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
-       conf := cfg.Configs.GetFirstLanguageConfig()
-
-       var logger loggers.Logger
-       if cfg.TestLogger != nil {
-               logger = cfg.TestLogger
-       } else {
-               var logHookLast func(e *logg.Entry) error
-               if cfg.Configs.Base.PanicOnWarning {
-                       logHookLast = loggers.PanicOnWarningHook
-               }
-               if cfg.LogOut == nil {
-                       cfg.LogOut = os.Stdout
-               }
-               if cfg.LogLevel == 0 {
-                       cfg.LogLevel = logg.LevelWarn
-               }
-
-               logOpts := loggers.Options{
-                       Level:              cfg.LogLevel,
-                       DistinctLevel:      logg.LevelWarn, // This will drop duplicate log warning and errors.
-                       HandlerPost:        logHookLast,
-                       Stdout:             cfg.LogOut,
-                       Stderr:             cfg.LogOut,
-                       StoreErrors:        conf.Watching(),
-                       SuppressStatements: conf.IgnoredLogs(),
-               }
-               logger = loggers.New(logOpts)
-
-       }
-
-       memCache := dynacache.New(dynacache.Options{Watching: conf.Watching(), Log: logger})
-
-       var h *HugoSites
-       onSignalRebuild := func(ids ...identity.Identity) {
-               // This channel is buffered, but make sure we do this in a non-blocking way.
-               if cfg.ChangesFromBuild != nil {
-                       go func() {
-                               cfg.ChangesFromBuild <- ids
-                       }()
-               }
-       }
-
-       firstSiteDeps := &deps.Deps{
-               Fs:   cfg.Fs,
-               Log:  logger,
-               Conf: conf,
-               BuildState: &deps.BuildState{
-                       OnSignalRebuild: onSignalRebuild,
-               },
-               MemCache:            memCache,
-               TemplateProvider:    tplimpl.DefaultTemplateProvider,
-               TranslationProvider: i18n.NewTranslationProvider(),
-               WasmDispatchers: warpc.AllDispatchers(
-                       warpc.Options{
-                               CompilationCacheDir: filepath.Join(conf.Dirs().CacheDir, "_warpc"),
-
-                               // Katex is relatively slow.
-                               PoolSize: 8,
-                               Infof:    logger.InfoCommand("wasm").Logf,
-                       },
-               ),
-       }
-
-       if err := firstSiteDeps.Init(); err != nil {
-               return nil, err
-       }
-
-       confm := cfg.Configs
-       if err := confm.Validate(logger); err != nil {
-               return nil, err
-       }
-       var sites []*Site
-
-       ns := &contentNodeShifter{
-               numLanguages: len(confm.Languages),
-       }
-
-       treeConfig := doctree.Config[contentNodeI]{
-               Shifter: ns,
-       }
-
-       pageTrees := &pageTrees{
-               treePages: doctree.New(
-                       treeConfig,
-               ),
-               treeResources: doctree.New(
-                       treeConfig,
-               ),
-               treeTaxonomyEntries:           doctree.NewTreeShiftTree[*weightedContentNode](doctree.DimensionLanguage.Index(), len(confm.Languages)),
-               treePagesFromTemplateAdapters: doctree.NewTreeShiftTree[*pagesfromdata.PagesFromTemplate](doctree.DimensionLanguage.Index(), len(confm.Languages)),
-       }
-
-       pageTrees.createMutableTrees()
-
-       for i, confp := range confm.ConfigLangs() {
-               language := confp.Language()
-               if language.Disabled {
-                       continue
-               }
-               k := language.Lang
-               conf := confm.LanguageConfigMap[k]
-               frontmatterHandler, err := pagemeta.NewFrontmatterHandler(firstSiteDeps.Log, conf.Frontmatter)
-               if err != nil {
-                       return nil, err
-               }
-
-               langs.SetParams(language, conf.Params)
-
-               s := &Site{
-                       conf:               conf,
-                       language:           language,
-                       languagei:          i,
-                       frontmatterHandler: frontmatterHandler,
-               }
-
-               if i == 0 {
-                       firstSiteDeps.Site = s
-                       s.Deps = firstSiteDeps
-               } else {
-                       d, err := firstSiteDeps.Clone(s, confp)
-                       if err != nil {
-                               return nil, err
-                       }
-                       s.Deps = d
-               }
-
-               s.pageMap = newPageMap(i, s, memCache, pageTrees)
-
-               s.pageFinder = newPageFinder(s.pageMap)
-               s.siteRefLinker, err = newSiteRefLinker(s)
-               if err != nil {
-                       return nil, err
-               }
-               // Set up the main publishing chain.
-               pub, err := publisher.NewDestinationPublisher(
-                       firstSiteDeps.ResourceSpec,
-                       s.conf.OutputFormats.Config,
-                       s.conf.MediaTypes.Config,
-               )
-               if err != nil {
-                       return nil, err
-               }
-
-               s.publisher = pub
-               s.relatedDocsHandler = page.NewRelatedDocsHandler(s.conf.Related)
-               // Site deps end.
-
-               s.prepareInits()
-               sites = append(sites, s)
-       }
-
-       if len(sites) == 0 {
-               return nil, errors.New("no sites to build")
-       }
-
-       // Pull the default content language to the top, then sort the sites by language weight (if set) or lang.
-       defaultContentLanguage := confm.Base.DefaultContentLanguage
-       sort.Slice(sites, func(i, j int) bool {
-               li := sites[i].language
-               lj := sites[j].language
-               if li.Lang == defaultContentLanguage {
-                       return true
-               }
-
-               if lj.Lang == defaultContentLanguage {
-                       return false
-               }
-
-               if li.Weight != lj.Weight {
-                       return li.Weight < lj.Weight
-               }
-               return li.Lang < lj.Lang
-       })
-
-       var err error
-       h, err = newHugoSites(cfg, firstSiteDeps, pageTrees, sites)
-       if err == nil && h == nil {
-               panic("hugo: newHugoSitesNew returned nil error and nil HugoSites")
-       }
-
-       return h, err
-}
-
-func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []*Site) (*HugoSites, error) {
-       numWorkers := config.GetNumWorkerMultiplier()
-       numWorkersSite := numWorkers
-       if numWorkersSite > len(sites) {
-               numWorkersSite = len(sites)
-       }
-       workersSite := para.New(numWorkersSite)
-
-       h := &HugoSites{
-               Sites:           sites,
-               Deps:            sites[0].Deps,
-               Configs:         cfg.Configs,
-               workersSite:     workersSite,
-               numWorkersSites: numWorkers,
-               numWorkers:      numWorkers,
-               pageTrees:       pageTrees,
-               cachePages: dynacache.GetOrCreatePartition[string,
-                       page.Pages](d.MemCache, "/pags/all",
-                       dynacache.OptionsPartition{Weight: 10, ClearWhen: dynacache.ClearOnRebuild},
-               ),
-               cacheContentSource:      dynacache.GetOrCreatePartition[string, *resources.StaleValue[[]byte]](d.MemCache, "/cont/src", dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
-               translationKeyPages:     maps.NewSliceCache[page.Page](),
-               currentSite:             sites[0],
-               skipRebuildForFilenames: make(map[string]bool),
-               init: &hugoSitesInit{
-                       data:    lazy.New(),
-                       layouts: lazy.New(),
-                       gitInfo: lazy.New(),
-               },
-       }
-
-       // Assemble dependencies to be used in hugo.Deps.
-       var dependencies []*hugo.Dependency
-       var depFromMod func(m modules.Module) *hugo.Dependency
-       depFromMod = func(m modules.Module) *hugo.Dependency {
-               dep := &hugo.Dependency{
-                       Path:    m.Path(),
-                       Version: m.Version(),
-                       Time:    m.Time(),
-                       Vendor:  m.Vendor(),
-               }
-
-               // These are pointers, but this all came from JSON so there's no recursive navigation,
-               // so just create new values.
-               if m.Replace() != nil {
-                       dep.Replace = depFromMod(m.Replace())
-               }
-               if m.Owner() != nil {
-                       dep.Owner = depFromMod(m.Owner())
-               }
-               return dep
-       }
-       for _, m := range d.Paths.AllModules() {
-               dependencies = append(dependencies, depFromMod(m))
-       }
-
-       h.hugoInfo = hugo.NewInfo(h.Configs.GetFirstLanguageConfig(), dependencies)
-
-       var prototype *deps.Deps
-       for i, s := range sites {
-               s.h = h
-               if err := s.Deps.Compile(prototype); err != nil {
-                       return nil, err
-               }
-               if i == 0 {
-                       prototype = s.Deps
-               }
-       }
-
-       h.fatalErrorHandler = &fatalErrorHandler{
-               h:     h,
-               donec: make(chan bool),
-       }
-
-       h.init.data.Add(func(context.Context) (any, error) {
-               err := h.loadData()
-               if err != nil {
-                       return nil, fmt.Errorf("failed to load data: %w", err)
-               }
-               return nil, nil
-       })
-
-       h.init.layouts.Add(func(context.Context) (any, error) {
-               for _, s := range h.Sites {
-                       if err := s.Tmpl().(tpl.TemplateManager).MarkReady(); err != nil {
-                               return nil, err
-                       }
-               }
-               return nil, nil
-       })
-
-       h.init.gitInfo.Add(func(context.Context) (any, error) {
-               err := h.loadGitInfo()
-               if err != nil {
-                       return nil, fmt.Errorf("failed to load Git info: %w", err)
-               }
-               return nil, nil
-       })
-
-       return h, nil
-}
-
-// Deprecated: Use hugo.IsServer instead.
-func (s *Site) IsServer() bool {
-       hugo.Deprecate(".Site.IsServer", "Use hugo.IsServer instead.", "v0.120.0")
-       return s.conf.Internal.Running
-}
-
-// Returns the server port.
-func (s *Site) ServerPort() int {
-       return s.conf.C.BaseURL.Port()
-}
-
-// Returns the configured title for this Site.
-func (s *Site) Title() string {
-       return s.conf.Title
-}
-
-func (s *Site) Copyright() string {
-       return s.conf.Copyright
-}
-
-// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
-func (s *Site) RSSLink() template.URL {
-       hugo.Deprecate(".Site.RSSLink", "Use the Output Format's Permalink method instead, e.g. .OutputFormats.Get \"RSS\".Permalink", "v0.114.0")
-       rssOutputFormat := s.home.OutputFormats().Get("rss")
-       return template.URL(rssOutputFormat.Permalink())
-}
-
-func (s *Site) Config() page.SiteConfig {
-       return page.SiteConfig{
-               Privacy:  s.conf.Privacy,
-               Services: s.conf.Services,
-       }
-}
-
-func (s *Site) LanguageCode() string {
-       return s.Language().LanguageCode()
-}
-
-// Returns all Sites for all languages.
-func (s *Site) Sites() page.Sites {
-       sites := make(page.Sites, len(s.h.Sites))
-       for i, s := range s.h.Sites {
-               sites[i] = s.Site()
-       }
-       return sites
-}
-
-// Returns Site currently rendering.
-func (s *Site) Current() page.Site {
-       return s.h.currentSite
-}
-
-// MainSections returns the list of main sections.
-func (s *Site) MainSections() []string {
-       s.CheckReady()
-       return s.conf.C.MainSections
-}
-
-// Returns a struct with some information about the build.
-func (s *Site) Hugo() hugo.HugoInfo {
-       if s.h == nil || s.h.hugoInfo.Environment == "" {
-               panic("site: hugo: hugoInfo not initialized")
-       }
-       return s.h.hugoInfo
-}
-
-// Returns the BaseURL for this Site.
-func (s *Site) BaseURL() string {
-       return s.conf.C.BaseURL.WithPath
-}
-
-// Deprecated: Use .Site.Lastmod instead.
-func (s *Site) LastChange() time.Time {
-       s.CheckReady()
-       hugo.Deprecate(".Site.LastChange", "Use .Site.Lastmod instead.", "v0.123.0")
-       return s.lastmod
-}
-
-// Returns the last modification date of the content.
-func (s *Site) Lastmod() time.Time {
-       return s.lastmod
-}
-
-// Returns the Params configured for this site.
-func (s *Site) Params() maps.Params {
-       return s.conf.Params
-}
-
-// Deprecated: Use taxonomies instead.
-func (s *Site) Author() map[string]any {
-       if len(s.conf.Author) != 0 {
-               hugo.Deprecate(".Site.Author", "Use taxonomies instead.", "v0.124.0")
-       }
-       return s.conf.Author
-}
-
-// Deprecated: Use taxonomies instead.
-func (s *Site) Authors() page.AuthorList {
-       hugo.Deprecate(".Site.Authors", "Use taxonomies instead.", "v0.124.0")
-       return page.AuthorList{}
-}
-
-// Deprecated: Use .Site.Params instead.
-func (s *Site) Social() map[string]string {
-       hugo.Deprecate(".Site.Social", "Use .Site.Params instead.", "v0.124.0")
-       return s.conf.Social
-}
-
-// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead.
-func (s *Site) DisqusShortname() string {
-       hugo.Deprecate(".Site.DisqusShortname", "Use .Site.Config.Services.Disqus.Shortname instead.", "v0.120.0")
-       return s.Config().Services.Disqus.Shortname
-}
-
-// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead.
-func (s *Site) GoogleAnalytics() string {
-       hugo.Deprecate(".Site.GoogleAnalytics", "Use .Site.Config.Services.GoogleAnalytics.ID instead.", "v0.120.0")
-       return s.Config().Services.GoogleAnalytics.ID
-}
-
-func (s *Site) Param(key any) (any, error) {
-       return resource.Param(s, nil, key)
-}
-
-// Returns a map of all the data inside /data.
-func (s *Site) Data() map[string]any {
-       return s.s.h.Data()
-}
-
-func (s *Site) BuildDrafts() bool {
-       return s.conf.BuildDrafts
-}
-
-// Deprecated: Use hugo.IsMultilingual instead.
-func (s *Site) IsMultiLingual() bool {
-       hugo.Deprecate(".Site.IsMultiLingual", "Use hugo.IsMultilingual instead.", "v0.124.0")
-       return s.h.isMultilingual()
-}
-
-func (s *Site) LanguagePrefix() string {
-       prefix := s.GetLanguagePrefix()
-       if prefix == "" {
-               return ""
-       }
-       return "/" + prefix
-}
-
-func (s *Site) Site() page.Site {
-       return page.WrapSite(s)
-}
-
-func (s *Site) ForEeachIdentityByName(name string, f func(identity.Identity) bool) {
-       if id, found := siteidentities.FromString(name); found {
-               if f(id) {
-                       return
-               }
-       }
-}
-
-// Pages returns all pages.
-// This is for the current language only.
-func (s *Site) Pages() page.Pages {
-       s.CheckReady()
-       return s.pageMap.getPagesInSection(
-               pageMapQueryPagesInSection{
-                       pageMapQueryPagesBelowPath: pageMapQueryPagesBelowPath{
-                               Path:    "",
-                               KeyPart: "global",
-                               Include: pagePredicates.ShouldListGlobal,
-                       },
-                       Recursive:   true,
-                       IncludeSelf: true,
-               },
-       )
-}
-
-// RegularPages returns all the regular pages.
-// This is for the current language only.
-func (s *Site) RegularPages() page.Pages {
-       s.CheckReady()
-       return s.pageMap.getPagesInSection(
-               pageMapQueryPagesInSection{
-                       pageMapQueryPagesBelowPath: pageMapQueryPagesBelowPath{
-                               Path:    "",
-                               KeyPart: "global",
-                               Include: pagePredicates.ShouldListGlobal.And(pagePredicates.KindPage),
-                       },
-                       Recursive: true,
-               },
-       )
-}
-
-// AllPages returns all pages for all sites.
-func (s *Site) AllPages() page.Pages {
-       s.CheckReady()
-       return s.h.Pages()
-}
-
-// AllRegularPages returns all regular pages for all sites.
-func (s *Site) AllRegularPages() page.Pages {
-       s.CheckReady()
-       return s.h.RegularPages()
-}
-
-func (s *Site) CheckReady() {
-       if s.state != siteStateReady {
-               panic("this method cannot be called before the site is fully initialized")
-       }
-}