aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <[email protected]>2024-09-17 10:14:51 +0200
committerBjørn Erik Pedersen <[email protected]>2024-09-17 10:15:14 +0200
commite079145373ed71707b9ed6e462f3dfd91ca85687 (patch)
treedda0a5def30a1d158b3b5973771be4571f723cca
parent5b442b3ccef28a5fcf2e14289f5005c303880393 (diff)
downloadhugo-e079145373ed71707b9ed6e462f3dfd91ca85687.tar.gz
hugo-e079145373ed71707b9ed6e462f3dfd91ca85687.zip
hugolib: Move hugolib/site_new.go into hugolib/site.go
-rw-r--r--hugolib/site.go569
-rw-r--r--hugolib/site_new.go603
2 files changed, 569 insertions, 603 deletions
diff --git a/hugolib/site.go b/hugolib/site.go
index d0546e910..d0a3bd370 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -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
index 19a7e42d7..000000000
--- a/hugolib/site_new.go
+++ /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")
- }
-}