mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-30 02:02:38 +00:00
Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b38beec431 | |||
| a65622a13e | |||
| 146aedd7aa | |||
| bd0200da6e | |||
| a80c3021ba | |||
| 9df7b295bc | |||
| c37bf19c89 | |||
| 9c6d377872 | |||
| 58d7f83390 | |||
| 609d798e34 | |||
| 53f204310e | |||
| 7f82461407 | |||
| b72f909725 | |||
| 3a665ddbf9 | |||
| 46575baa02 | |||
| 058f230a1b | |||
| a66480f70c | |||
| e33a632551 | |||
| 2873324898 | |||
| d0788b96ae | |||
| 034fbef50d | |||
| 54ad51e8a6 | |||
| bd1bcc0f91 | |||
| 8d42a7942a | |||
| 4174a7866b | |||
| 5dd06b4136 | |||
| f5ec75db36 | |||
| 6cb3bda3d1 | |||
| 963cecc12c | |||
| d8f0e30715 | |||
| b6def61727 | |||
| 1891d5e6b5 | |||
| 156f08de35 | |||
| b332f243fd | |||
| d8c2734178 | |||
| d8e1e82188 | |||
| a1c64989df | |||
| 6c3b6ba3e6 | |||
| 4d98b0ed6a | |||
| 15b9976b7a | |||
| 34d63c8d1c | |||
| 51615440bf | |||
| bd66d30295 | |||
| 7caa5b3e50 | |||
| 309d61b220 | |||
| 5b7cb258ec | |||
| 80595bbe3e | |||
| afee781f03 | |||
| 4e84f57efb | |||
| f31a6db797 | |||
| ec22bb31a8 | |||
| a795acbcd8 | |||
| 982d9513e7 | |||
| 6dedb4efc7 | |||
| 292626e679 | |||
| 60d954c785 | |||
| 63e0a92894 | |||
| ce7daa6156 | |||
| 2a0329423c | |||
| 50dc327d1a |
Vendored
+41
-9
@@ -25,6 +25,7 @@ import (
|
||||
|
||||
"github.com/bep/lazycache"
|
||||
"github.com/bep/logg"
|
||||
"github.com/gohugoio/hugo/common/collections"
|
||||
"github.com/gohugoio/hugo/common/herrors"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
@@ -63,11 +64,26 @@ func New(opts Options) *Cache {
|
||||
|
||||
infol := opts.Log.InfoCommand("dynacache")
|
||||
|
||||
evictedIdentities := collections.NewStack[identity.Identity]()
|
||||
|
||||
onEvict := func(k, v any) {
|
||||
if !opts.Running {
|
||||
return
|
||||
}
|
||||
identity.WalkIdentitiesShallow(v, func(level int, id identity.Identity) bool {
|
||||
evictedIdentities.Push(id)
|
||||
return false
|
||||
})
|
||||
resource.MarkStale(v)
|
||||
}
|
||||
|
||||
c := &Cache{
|
||||
partitions: make(map[string]PartitionManager),
|
||||
opts: opts,
|
||||
stats: stats,
|
||||
infol: infol,
|
||||
partitions: make(map[string]PartitionManager),
|
||||
onEvict: onEvict,
|
||||
evictedIdentities: evictedIdentities,
|
||||
opts: opts,
|
||||
stats: stats,
|
||||
infol: infol,
|
||||
}
|
||||
|
||||
c.stop = c.start()
|
||||
@@ -106,14 +122,23 @@ type Cache struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
partitions map[string]PartitionManager
|
||||
opts Options
|
||||
infol logg.LevelLogger
|
||||
|
||||
onEvict func(k, v any)
|
||||
evictedIdentities *collections.Stack[identity.Identity]
|
||||
|
||||
opts Options
|
||||
infol logg.LevelLogger
|
||||
|
||||
stats *stats
|
||||
stopOnce sync.Once
|
||||
stop func()
|
||||
}
|
||||
|
||||
// DrainEvictedIdentities drains the evicted identities from the cache.
|
||||
func (c *Cache) DrainEvictedIdentities() []identity.Identity {
|
||||
return c.evictedIdentities.Drain()
|
||||
}
|
||||
|
||||
// ClearMatching clears all partition for which the predicate returns true.
|
||||
func (c *Cache) ClearMatching(predicate func(k, v any) bool) {
|
||||
g := rungroup.Run[PartitionManager](context.Background(), rungroup.Config[PartitionManager]{
|
||||
@@ -318,9 +343,13 @@ func GetOrCreatePartition[K comparable, V any](c *Cache, name string, opts Optio
|
||||
const numberOfPartitionsEstimate = 10
|
||||
maxSize := opts.CalculateMaxSize(c.opts.MaxSize / numberOfPartitionsEstimate)
|
||||
|
||||
onEvict := func(k K, v V) {
|
||||
c.onEvict(k, v)
|
||||
}
|
||||
|
||||
// Create a new partition and cache it.
|
||||
partition := &Partition[K, V]{
|
||||
c: lazycache.New(lazycache.Options[K, V]{MaxEntries: maxSize}),
|
||||
c: lazycache.New(lazycache.Options[K, V]{MaxEntries: maxSize, OnEvict: onEvict}),
|
||||
maxSize: maxSize,
|
||||
trace: c.opts.Log.Logger().WithLevel(logg.LevelTrace).WithField("partition", name),
|
||||
opts: opts,
|
||||
@@ -445,7 +474,6 @@ func (p *Partition[K, V]) clearOnRebuild(changeset ...identity.Identity) {
|
||||
},
|
||||
),
|
||||
)
|
||||
resource.MarkStale(v)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -483,6 +511,10 @@ func (p *Partition[K, V]) adjustMaxSize(newMaxSize int) int {
|
||||
if newMaxSize < minMaxSize {
|
||||
newMaxSize = minMaxSize
|
||||
}
|
||||
oldMaxSize := p.maxSize
|
||||
if newMaxSize == oldMaxSize {
|
||||
return 0
|
||||
}
|
||||
p.maxSize = newMaxSize
|
||||
// fmt.Println("Adjusting max size of partition from", oldMaxSize, "to", newMaxSize)
|
||||
return p.c.Resize(newMaxSize)
|
||||
@@ -535,7 +567,7 @@ type stats struct {
|
||||
func (s *stats) adjustCurrentMaxSize() bool {
|
||||
newCurrentMaxSize := int(math.Floor(float64(s.opts.MaxSize) * s.adjustmentFactor))
|
||||
|
||||
if newCurrentMaxSize < s.opts.MaxSize {
|
||||
if newCurrentMaxSize < s.opts.MinMaxSize {
|
||||
newCurrentMaxSize = int(s.opts.MinMaxSize)
|
||||
}
|
||||
changed := newCurrentMaxSize != s.currentMaxSize
|
||||
|
||||
Vendored
+1
-3
@@ -15,6 +15,7 @@
|
||||
package filecache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -24,8 +25,6 @@ import (
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
@@ -225,7 +224,6 @@ func DecodeConfig(fs afero.Fs, bcfg config.BaseConfig, m map[string]any) (Config
|
||||
|
||||
// Resolves :resourceDir => /myproject/resources etc., :cacheDir => ...
|
||||
func resolveDirPlaceholder(fs afero.Fs, bcfg config.BaseConfig, placeholder string) (cacheDir string, isResource bool, err error) {
|
||||
|
||||
switch strings.ToLower(placeholder) {
|
||||
case ":resourcedir":
|
||||
return "", true, nil
|
||||
|
||||
Vendored
-2
@@ -60,7 +60,6 @@ func (c *Cache) Prune(force bool) (int, error) {
|
||||
counter := 0
|
||||
|
||||
err := afero.Walk(c.Fs, "", func(name string, info os.FileInfo, err error) error {
|
||||
|
||||
if info == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -69,7 +68,6 @@ func (c *Cache) Prune(force bool) (int, error) {
|
||||
|
||||
if info.IsDir() {
|
||||
f, err := c.Fs.Open(name)
|
||||
|
||||
if err != nil {
|
||||
// This cache dir may not exist.
|
||||
return nil
|
||||
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env bash
|
||||
diff <(gofmt -d .) <(printf '')
|
||||
@@ -461,7 +461,6 @@ func collectMethodsRecursive(pkg string, f []*ast.Field) []string {
|
||||
pkg,
|
||||
tt.Methods.List)...)
|
||||
}
|
||||
|
||||
} else {
|
||||
// Embedded, but in a different file/package. Return the
|
||||
// package.Name and deal with that later.
|
||||
|
||||
+10
-8
@@ -22,6 +22,7 @@ import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -202,9 +203,6 @@ func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commo
|
||||
cfg = config.New()
|
||||
}
|
||||
|
||||
if !cfg.IsSet("renderToDisk") {
|
||||
cfg.Set("renderToDisk", true)
|
||||
}
|
||||
if !cfg.IsSet("workingDir") {
|
||||
cfg.Set("workingDir", dir)
|
||||
} else {
|
||||
@@ -238,9 +236,7 @@ func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commo
|
||||
|
||||
sourceFs := hugofs.Os
|
||||
var destinationFs afero.Fs
|
||||
if cfg.GetBool("renderToDisk") {
|
||||
destinationFs = hugofs.Os
|
||||
} else {
|
||||
if cfg.GetBool("renderToMemory") {
|
||||
destinationFs = afero.NewMemMapFs()
|
||||
if renderStaticToDisk {
|
||||
// Hybrid, render dynamic content to Root.
|
||||
@@ -250,6 +246,8 @@ func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commo
|
||||
cfg.Set("publishDirDynamic", "/")
|
||||
cfg.Set("publishDirStatic", "/")
|
||||
}
|
||||
} else {
|
||||
destinationFs = hugofs.Os
|
||||
}
|
||||
|
||||
fs := hugofs.NewFromSourceAndDestination(sourceFs, destinationFs, cfg)
|
||||
@@ -342,7 +340,10 @@ func (r *rootCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args
|
||||
defer r.timeTrack(time.Now(), "Built")
|
||||
}
|
||||
err := b.build()
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -411,6 +412,7 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
|
||||
MaxEntries: 1,
|
||||
OnEvict: func(key int32, value *hugolib.HugoSites) {
|
||||
value.Close()
|
||||
runtime.GC()
|
||||
},
|
||||
})
|
||||
|
||||
@@ -492,6 +494,7 @@ Complete documentation is available at https://gohugo.io/.`
|
||||
cmd.PersistentFlags().StringVar(&r.cfgFile, "config", "", "config file (default is hugo.yaml|json|toml)")
|
||||
cmd.PersistentFlags().StringVar(&r.cfgDir, "configDir", "config", "config dir")
|
||||
cmd.PersistentFlags().BoolVar(&r.quiet, "quiet", false, "build in quiet mode")
|
||||
cmd.PersistentFlags().BoolVar(&r.renderToMemory, "renderToMemory", false, "render to memory (mostly useful when running the server)")
|
||||
|
||||
// Set bash-completion
|
||||
_ = cmd.PersistentFlags().SetAnnotation("config", cobra.BashCompFilenameExt, config.ValidConfigFileExtensions)
|
||||
@@ -500,7 +503,6 @@ Complete documentation is available at https://gohugo.io/.`
|
||||
cmd.PersistentFlags().BoolVarP(&r.debug, "debug", "", false, "debug output")
|
||||
cmd.PersistentFlags().StringVar(&r.logLevel, "logLevel", "", "log level (debug|info|warn|error)")
|
||||
cmd.Flags().BoolVarP(&r.buildWatch, "watch", "w", false, "watch filesystem for changes and recreate as needed")
|
||||
cmd.Flags().BoolVar(&r.renderToMemory, "renderToMemory", false, "render to memory (only useful for benchmark testing)")
|
||||
|
||||
// Configure local flags
|
||||
applyLocalFlagsBuild(cmd, r)
|
||||
|
||||
+1
-1
@@ -195,7 +195,7 @@ url: %s
|
||||
configProvider := func() docshelper.DocProvider {
|
||||
conf := hugolib.DefaultConfig()
|
||||
conf.CacheDir = "" // The default value does not make sense in the docs.
|
||||
defaultConfig := parser.LowerCaseCamelJSONMarshaller{Value: conf}
|
||||
defaultConfig := parser.NullBoolJSONMarshaller{Wrapped: parser.LowerCaseCamelJSONMarshaller{Value: conf}}
|
||||
return docshelper.DocProvider{"config": defaultConfig}
|
||||
}
|
||||
|
||||
|
||||
@@ -930,7 +930,7 @@ func (c *hugoBuilder) hugoTry() *hugolib.HugoSites {
|
||||
|
||||
func (c *hugoBuilder) loadConfig(cd *simplecobra.Commandeer, running bool) error {
|
||||
cfg := config.New()
|
||||
cfg.Set("renderToDisk", (c.s == nil && !c.r.renderToMemory) || (c.s != nil && c.s.renderToDisk))
|
||||
cfg.Set("renderToMemory", c.r.renderToMemory)
|
||||
watch := c.r.buildWatch || (c.s != nil && c.s.serverWatch)
|
||||
if c.r.environment == "" {
|
||||
// We need to set the environment as early as possible because we need it to load the correct config.
|
||||
@@ -951,9 +951,10 @@ func (c *hugoBuilder) loadConfig(cd *simplecobra.Commandeer, running bool) error
|
||||
cfg.Set("environment", c.r.environment)
|
||||
|
||||
cfg.Set("internal", maps.Params{
|
||||
"running": running,
|
||||
"watch": watch,
|
||||
"verbose": c.r.isVerbose(),
|
||||
"running": running,
|
||||
"watch": watch,
|
||||
"verbose": c.r.isVerbose(),
|
||||
"fastRenderMode": c.fastRenderMode,
|
||||
})
|
||||
|
||||
conf, err := c.r.ConfigFromProvider(c.r.configVersionID.Load(), flagsToCfg(cd, cfg))
|
||||
|
||||
+22
-15
@@ -25,6 +25,7 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -238,12 +239,14 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
|
||||
r.Printf("Environment: %q\n", f.c.hugoTry().Deps.Site.Hugo().Environment)
|
||||
|
||||
if i == 0 {
|
||||
if f.c.renderToDisk {
|
||||
r.Println("Serving pages from disk")
|
||||
} else if f.c.renderStaticToDisk {
|
||||
r.Println("Serving pages from memory and static files from disk")
|
||||
mainTarget := "disk"
|
||||
if f.c.r.renderToMemory {
|
||||
mainTarget = "memory"
|
||||
}
|
||||
if f.c.renderStaticToDisk {
|
||||
r.Printf("Serving pages from %s and static files from disk\n", mainTarget)
|
||||
} else {
|
||||
r.Println("Serving pages from memory")
|
||||
r.Printf("Serving pages from %s\n", mainTarget)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,7 +446,6 @@ type serverCommand struct {
|
||||
doLiveReload bool
|
||||
|
||||
// Flags.
|
||||
renderToDisk bool
|
||||
renderStaticToDisk bool
|
||||
navigateToChanged bool
|
||||
serverAppend bool
|
||||
@@ -451,6 +453,7 @@ type serverCommand struct {
|
||||
tlsCertFile string
|
||||
tlsKeyFile string
|
||||
tlsAuto bool
|
||||
pprof bool
|
||||
serverPort int
|
||||
liveReloadPort int
|
||||
serverWatch bool
|
||||
@@ -465,6 +468,11 @@ func (c *serverCommand) Name() string {
|
||||
}
|
||||
|
||||
func (c *serverCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error {
|
||||
if c.pprof {
|
||||
go func() {
|
||||
http.ListenAndServe("localhost:8080", nil)
|
||||
}()
|
||||
}
|
||||
// Watch runs its own server as part of the routine
|
||||
if c.serverWatch {
|
||||
|
||||
@@ -489,8 +497,7 @@ func (c *serverCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg
|
||||
|
||||
err := func() error {
|
||||
defer c.r.timeTrack(time.Now(), "Built")
|
||||
err := c.build()
|
||||
return err
|
||||
return c.build()
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -505,8 +512,9 @@ func (c *serverCommand) Init(cd *simplecobra.Commandeer) error {
|
||||
cmd.Long = `Hugo provides its own webserver which builds and serves the site.
|
||||
While hugo server is high performance, it is a webserver with limited options.
|
||||
|
||||
'hugo server' will avoid writing the rendered and served content to disk,
|
||||
preferring to store it in memory.
|
||||
'hugo server' will by default write and server files from disk, but you can
|
||||
render to memory by using the '--renderToMemory' flag. This can be faster
|
||||
in some cases, but it will consume more memory.
|
||||
|
||||
By default hugo will also watch your files for any changes you make and
|
||||
automatically rebuild the site. It will then live reload any open browser pages
|
||||
@@ -520,19 +528,16 @@ of a second, you will be able to save and see your changes nearly instantly.`
|
||||
cmd.Flags().StringVarP(&c.tlsCertFile, "tlsCertFile", "", "", "path to TLS certificate file")
|
||||
cmd.Flags().StringVarP(&c.tlsKeyFile, "tlsKeyFile", "", "", "path to TLS key file")
|
||||
cmd.Flags().BoolVar(&c.tlsAuto, "tlsAuto", false, "generate and use locally-trusted certificates.")
|
||||
cmd.Flags().BoolVar(&c.pprof, "pprof", false, "enable the pprof server (port 8080)")
|
||||
cmd.Flags().BoolVarP(&c.serverWatch, "watch", "w", true, "watch filesystem for changes and recreate as needed")
|
||||
cmd.Flags().BoolVar(&c.noHTTPCache, "noHTTPCache", false, "prevent HTTP caching")
|
||||
cmd.Flags().BoolVarP(&c.serverAppend, "appendPort", "", true, "append port to baseURL")
|
||||
cmd.Flags().BoolVar(&c.disableLiveReload, "disableLiveReload", false, "watch without enabling live browser reload on rebuild")
|
||||
cmd.Flags().BoolVar(&c.navigateToChanged, "navigateToChanged", false, "navigate to changed content file on live browser reload")
|
||||
cmd.Flags().BoolVar(&c.renderToDisk, "renderToDisk", false, "serve all files from disk (default is from memory)")
|
||||
cmd.Flags().BoolVar(&c.renderStaticToDisk, "renderStaticToDisk", false, "serve static files from disk and dynamic files from memory")
|
||||
cmd.Flags().BoolVar(&c.disableFastRender, "disableFastRender", false, "enables full re-renders on changes")
|
||||
cmd.Flags().BoolVar(&c.disableBrowserError, "disableBrowserError", false, "do not show build errors in the browser")
|
||||
|
||||
cmd.Flags().String("memstats", "", "log memory usage to this file")
|
||||
cmd.Flags().String("meminterval", "100ms", "interval to poll memory usage (requires --memstats), valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\".")
|
||||
|
||||
cmd.Flags().SetAnnotation("tlsCertFile", cobra.BashCompSubdirsInDir, []string{})
|
||||
cmd.Flags().SetAnnotation("tlsKeyFile", cobra.BashCompSubdirsInDir, []string{})
|
||||
|
||||
@@ -577,7 +582,9 @@ func (c *serverCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
|
||||
)
|
||||
|
||||
destinationFlag := cd.CobraCommand.Flags().Lookup("destination")
|
||||
c.renderToDisk = c.renderToDisk || (destinationFlag != nil && destinationFlag.Changed)
|
||||
if c.r.renderToMemory && (destinationFlag != nil && destinationFlag.Changed) {
|
||||
return fmt.Errorf("cannot use --renderToMemory with --destination")
|
||||
}
|
||||
c.doLiveReload = !c.disableLiveReload
|
||||
c.fastRenderMode = !c.disableFastRender
|
||||
c.showErrorInBrowser = c.doLiveReload && !c.disableBrowserError
|
||||
|
||||
@@ -55,7 +55,7 @@ func TestAppend(t *testing.T) {
|
||||
[]any{&tstSlicerIn1{"c"}},
|
||||
testSlicerInterfaces{&tstSlicerIn1{"a"}, &tstSlicerIn1{"b"}, &tstSlicerIn1{"c"}},
|
||||
},
|
||||
//https://github.com/gohugoio/hugo/issues/5361
|
||||
// https://github.com/gohugoio/hugo/issues/5361
|
||||
{
|
||||
[]string{"a", "b"},
|
||||
[]any{tstSlicers{&tstSlicer{"a"}, &tstSlicer{"b"}}},
|
||||
@@ -102,14 +102,16 @@ func TestAppendToMultiDimensionalSlice(t *testing.T) {
|
||||
from []any
|
||||
expected any
|
||||
}{
|
||||
{[][]string{{"a", "b"}},
|
||||
{
|
||||
[][]string{{"a", "b"}},
|
||||
[]any{[]string{"c", "d"}},
|
||||
[][]string{
|
||||
{"a", "b"},
|
||||
{"c", "d"},
|
||||
},
|
||||
},
|
||||
{[][]string{{"a", "b"}},
|
||||
{
|
||||
[][]string{{"a", "b"}},
|
||||
[]any{[]string{"c", "d"}, []string{"e", "f"}},
|
||||
[][]string{
|
||||
{"a", "b"},
|
||||
@@ -117,7 +119,8 @@ func TestAppendToMultiDimensionalSlice(t *testing.T) {
|
||||
{"e", "f"},
|
||||
},
|
||||
},
|
||||
{[][]string{{"a", "b"}},
|
||||
{
|
||||
[][]string{{"a", "b"}},
|
||||
[]any{[]int{1, 2}},
|
||||
false,
|
||||
},
|
||||
@@ -130,7 +133,6 @@ func TestAppendToMultiDimensionalSlice(t *testing.T) {
|
||||
c.Assert(result, qt.DeepEquals, test.expected)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAppendShouldMakeACopyOfTheInputSlice(t *testing.T) {
|
||||
|
||||
@@ -73,7 +73,6 @@ func StringSliceToInterfaceSlice(ss []string) []any {
|
||||
result[i] = s
|
||||
}
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
type SortedStringSlice []string
|
||||
|
||||
@@ -135,5 +135,4 @@ func TestSortedStringSlice(t *testing.T) {
|
||||
c.Assert(s.Count("b"), qt.Equals, 3)
|
||||
c.Assert(s.Count("z"), qt.Equals, 0)
|
||||
c.Assert(s.Count("a"), qt.Equals, 1)
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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 collections
|
||||
|
||||
import "sync"
|
||||
|
||||
// Stack is a simple LIFO stack that is safe for concurrent use.
|
||||
type Stack[T any] struct {
|
||||
items []T
|
||||
zero T
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewStack[T any]() *Stack[T] {
|
||||
return &Stack[T]{}
|
||||
}
|
||||
|
||||
func (s *Stack[T]) Push(item T) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.items = append(s.items, item)
|
||||
}
|
||||
|
||||
func (s *Stack[T]) Pop() (T, bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if len(s.items) == 0 {
|
||||
return s.zero, false
|
||||
}
|
||||
item := s.items[len(s.items)-1]
|
||||
s.items = s.items[:len(s.items)-1]
|
||||
return item, true
|
||||
}
|
||||
|
||||
func (s *Stack[T]) Peek() (T, bool) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
if len(s.items) == 0 {
|
||||
return s.zero, false
|
||||
}
|
||||
return s.items[len(s.items)-1], true
|
||||
}
|
||||
|
||||
func (s *Stack[T]) Len() int {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return len(s.items)
|
||||
}
|
||||
|
||||
func (s *Stack[T]) Drain() []T {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
items := s.items
|
||||
s.items = nil
|
||||
return items
|
||||
}
|
||||
@@ -13,12 +13,14 @@
|
||||
|
||||
package constants
|
||||
|
||||
// Error IDs.
|
||||
// Error/Warning IDs.
|
||||
// Do not change these values.
|
||||
const (
|
||||
// IDs for remote errors in tpl/data.
|
||||
ErrRemoteGetJSON = "error-remote-getjson"
|
||||
ErrRemoteGetCSV = "error-remote-getcsv"
|
||||
|
||||
WarnFrontMatterParamsOverrides = "warning-frontmatter-params-overrides"
|
||||
)
|
||||
|
||||
// Field/method names with special meaning.
|
||||
|
||||
@@ -19,8 +19,10 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -116,3 +118,22 @@ func IsNotExist(err error) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
var nilPointerErrRe = regexp.MustCompile(`at <(.*)>: error calling (.*?): runtime error: invalid memory address or nil pointer dereference`)
|
||||
|
||||
func ImproveIfNilPointer(inErr error) (outErr error) {
|
||||
outErr = inErr
|
||||
|
||||
m := nilPointerErrRe.FindStringSubmatch(inErr.Error())
|
||||
if len(m) == 0 {
|
||||
return
|
||||
}
|
||||
call := m[1]
|
||||
field := m[2]
|
||||
parts := strings.Split(call, ".")
|
||||
receiverName := parts[len(parts)-2]
|
||||
receiver := strings.Join(parts[:len(parts)-1], ".")
|
||||
s := fmt.Sprintf("– %s is nil; wrap it in if or with: {{ with %s }}{{ .%s }}{{ end }}", receiverName, receiver, field)
|
||||
outErr = errors.New(nilPointerErrRe.ReplaceAllString(inErr.Error(), s))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -19,11 +19,10 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/cli/safeexec"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
@@ -142,7 +141,6 @@ func (e *Exec) New(name string, arg ...any) (Runner, error) {
|
||||
}
|
||||
|
||||
return cm.command(arg...)
|
||||
|
||||
}
|
||||
|
||||
// Npx is a convenience method to create a Runner running npx --no-install <name> <args.
|
||||
|
||||
@@ -47,12 +47,7 @@ defaultContentLanguage = 'it'
|
||||
{{ end }}
|
||||
`
|
||||
|
||||
b := hugolib.NewIntegrationTestBuilder(
|
||||
hugolib.IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", `
|
||||
month: _gennaio_ weekday: _lunedì_
|
||||
@@ -53,7 +53,6 @@ func TestTimeFormatter(t *testing.T) {
|
||||
c.Assert(f.Format(june06, ":time_long"), qt.Equals, "02:09:37 UTC")
|
||||
c.Assert(f.Format(june06, ":time_medium"), qt.Equals, "02:09:37")
|
||||
c.Assert(f.Format(june06, ":time_short"), qt.Equals, "02:09")
|
||||
|
||||
})
|
||||
|
||||
c.Run("Custom layouts English", func(c *qt.C) {
|
||||
@@ -68,7 +67,6 @@ func TestTimeFormatter(t *testing.T) {
|
||||
c.Assert(f.Format(june06, ":time_long"), qt.Equals, "2:09:37 am UTC")
|
||||
c.Assert(f.Format(june06, ":time_medium"), qt.Equals, "2:09:37 am")
|
||||
c.Assert(f.Format(june06, ":time_short"), qt.Equals, "2:09 am")
|
||||
|
||||
})
|
||||
|
||||
c.Run("English", func(c *qt.C) {
|
||||
@@ -107,9 +105,7 @@ func TestTimeFormatter(t *testing.T) {
|
||||
c.Assert(tr.MonthWide(date.Month()), qt.Equals, monthWideNorway)
|
||||
c.Assert(f.Format(date, "January"), qt.Equals, monthWideNorway)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func BenchmarkTimeFormatter(b *testing.B) {
|
||||
|
||||
+37
-10
@@ -37,32 +37,47 @@ type ReadSeekCloserProvider interface {
|
||||
ReadSeekCloser() (ReadSeekCloser, error)
|
||||
}
|
||||
|
||||
// ReadSeekerNoOpCloser implements ReadSeekCloser by doing nothing in Close.
|
||||
// TODO(bep) rename this and similar to ReadSeekerNopCloser, naming used in stdlib, which kind of makes sense.
|
||||
type ReadSeekerNoOpCloser struct {
|
||||
// readSeekerNopCloser implements ReadSeekCloser by doing nothing in Close.
|
||||
type readSeekerNopCloser struct {
|
||||
ReadSeeker
|
||||
}
|
||||
|
||||
// Close does nothing.
|
||||
func (r ReadSeekerNoOpCloser) Close() error {
|
||||
func (r readSeekerNopCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewReadSeekerNoOpCloser creates a new ReadSeekerNoOpCloser with the given ReadSeeker.
|
||||
func NewReadSeekerNoOpCloser(r ReadSeeker) ReadSeekerNoOpCloser {
|
||||
return ReadSeekerNoOpCloser{r}
|
||||
func NewReadSeekerNoOpCloser(r ReadSeeker) ReadSeekCloser {
|
||||
return readSeekerNopCloser{r}
|
||||
}
|
||||
|
||||
// NewReadSeekerNoOpCloserFromString uses strings.NewReader to create a new ReadSeekerNoOpCloser
|
||||
// from the given string.
|
||||
func NewReadSeekerNoOpCloserFromString(content string) ReadSeekerNoOpCloser {
|
||||
return ReadSeekerNoOpCloser{strings.NewReader(content)}
|
||||
func NewReadSeekerNoOpCloserFromString(content string) ReadSeekCloser {
|
||||
return stringReadSeeker{s: content, readSeekerNopCloser: readSeekerNopCloser{strings.NewReader(content)}}
|
||||
}
|
||||
|
||||
var _ StringReader = (*stringReadSeeker)(nil)
|
||||
|
||||
type stringReadSeeker struct {
|
||||
s string
|
||||
readSeekerNopCloser
|
||||
}
|
||||
|
||||
func (s *stringReadSeeker) ReadString() string {
|
||||
return s.s
|
||||
}
|
||||
|
||||
// StringReader provides a way to read a string.
|
||||
type StringReader interface {
|
||||
ReadString() string
|
||||
}
|
||||
|
||||
// NewReadSeekerNoOpCloserFromString uses strings.NewReader to create a new ReadSeekerNoOpCloser
|
||||
// from the given bytes slice.
|
||||
func NewReadSeekerNoOpCloserFromBytes(content []byte) ReadSeekerNoOpCloser {
|
||||
return ReadSeekerNoOpCloser{bytes.NewReader(content)}
|
||||
func NewReadSeekerNoOpCloserFromBytes(content []byte) readSeekerNopCloser {
|
||||
return readSeekerNopCloser{bytes.NewReader(content)}
|
||||
}
|
||||
|
||||
// NewReadSeekCloser creates a new ReadSeekCloser from the given ReadSeeker.
|
||||
@@ -77,3 +92,15 @@ func NewOpenReadSeekCloser(r ReadSeekCloser) OpenReadSeekCloser {
|
||||
// OpenReadSeekCloser allows setting some other way (than reading from a filesystem)
|
||||
// to open or create a ReadSeekCloser.
|
||||
type OpenReadSeekCloser func() (ReadSeekCloser, error)
|
||||
|
||||
// ReadString reads from the given reader and returns the content as a string.
|
||||
func ReadString(r io.Reader) (string, error) {
|
||||
if sr, ok := r.(StringReader); ok {
|
||||
return sr.ReadString(), nil
|
||||
}
|
||||
b, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
@@ -179,9 +179,9 @@ type Logger interface {
|
||||
Debugln(v ...any)
|
||||
Error() logg.LevelLogger
|
||||
Errorf(format string, v ...any)
|
||||
Erroridf(id, format string, v ...any)
|
||||
Errorln(v ...any)
|
||||
Errors() string
|
||||
Errorsf(id, format string, v ...any)
|
||||
Info() logg.LevelLogger
|
||||
InfoCommand(command string) logg.LevelLogger
|
||||
Infof(format string, v ...any)
|
||||
@@ -197,6 +197,7 @@ type Logger interface {
|
||||
Warn() logg.LevelLogger
|
||||
WarnCommand(command string) logg.LevelLogger
|
||||
Warnf(format string, v ...any)
|
||||
Warnidf(id, format string, v ...any)
|
||||
Warnln(v ...any)
|
||||
Deprecatef(fail bool, format string, v ...any)
|
||||
Trace(s logg.StringFunc)
|
||||
@@ -321,10 +322,20 @@ func (l *logAdapter) Errors() string {
|
||||
return l.errors.String()
|
||||
}
|
||||
|
||||
func (l *logAdapter) Errorsf(id, format string, v ...any) {
|
||||
func (l *logAdapter) Erroridf(id, format string, v ...any) {
|
||||
format += l.idfInfoStatement("error", id, format)
|
||||
l.errorl.WithField(FieldNameStatementID, id).Logf(format, v...)
|
||||
}
|
||||
|
||||
func (l *logAdapter) Warnidf(id, format string, v ...any) {
|
||||
format += l.idfInfoStatement("warning", id, format)
|
||||
l.warnl.WithField(FieldNameStatementID, id).Logf(format, v...)
|
||||
}
|
||||
|
||||
func (l *logAdapter) idfInfoStatement(what, id, format string) string {
|
||||
return fmt.Sprintf("\nYou can suppress this %s by adding the following to your site configuration:\nignoreLogs = ['%s']", what, id)
|
||||
}
|
||||
|
||||
func (l *logAdapter) Trace(s logg.StringFunc) {
|
||||
l.tracel.Log(s)
|
||||
}
|
||||
|
||||
@@ -192,5 +192,4 @@ func TestLookupEqualFold(t *testing.T) {
|
||||
v, found = LookupEqualFold(m2, "b")
|
||||
c.Assert(found, qt.IsTrue)
|
||||
c.Assert(v, qt.Equals, "bv")
|
||||
|
||||
}
|
||||
|
||||
@@ -154,7 +154,6 @@ func TestParamsSetAndMerge(t *testing.T) {
|
||||
"a": "av",
|
||||
"c": "cv",
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestParamsIsZero(t *testing.T) {
|
||||
|
||||
@@ -387,6 +387,11 @@ func ToSlashTrimLeading(s string) string {
|
||||
return strings.TrimPrefix(filepath.ToSlash(s), "/")
|
||||
}
|
||||
|
||||
// ToSlashTrimTrailing is just a filepath.ToSlash with an added / suffix trimmer.
|
||||
func ToSlashTrimTrailing(s string) string {
|
||||
return strings.TrimSuffix(filepath.ToSlash(s), "/")
|
||||
}
|
||||
|
||||
// ToSlashPreserveLeading converts the path given to a forward slash separated path
|
||||
// and preserves the leading slash if present trimming any trailing slash.
|
||||
func ToSlashPreserveLeading(s string) string {
|
||||
|
||||
@@ -29,6 +29,9 @@ var defaultPathParser PathParser
|
||||
type PathParser struct {
|
||||
// Maps the language code to its index in the languages/sites slice.
|
||||
LanguageIndex map[string]int
|
||||
|
||||
// Reports whether the given language is disabled.
|
||||
IsLangDisabled func(string) bool
|
||||
}
|
||||
|
||||
// Parse parses component c with path s into Path using the default path parser.
|
||||
@@ -134,7 +137,16 @@ func (pp *PathParser) doParse(component, s string) (*Path, error) {
|
||||
s := p.s[id.Low:id.High]
|
||||
|
||||
if hasLang {
|
||||
if _, found := pp.LanguageIndex[s]; found {
|
||||
var disabled bool
|
||||
_, langFound := pp.LanguageIndex[s]
|
||||
if !langFound {
|
||||
disabled = pp.IsLangDisabled != nil && pp.IsLangDisabled(s)
|
||||
if disabled {
|
||||
p.disabled = true
|
||||
langFound = true
|
||||
}
|
||||
}
|
||||
if langFound {
|
||||
p.posIdentifierLanguage = 1
|
||||
p.identifiers = append(p.identifiers, id)
|
||||
}
|
||||
@@ -220,6 +232,7 @@ type Path struct {
|
||||
identifiers []types.LowHigh
|
||||
|
||||
posIdentifierLanguage int
|
||||
disabled bool
|
||||
|
||||
trimLeadingSlash bool
|
||||
|
||||
@@ -346,8 +359,8 @@ func (p *Path) Path() (d string) {
|
||||
return p.norm(p.s)
|
||||
}
|
||||
|
||||
// Unmormalized returns the Path with the original case preserved.
|
||||
func (p *Path) Unmormalized() *Path {
|
||||
// Unnormalized returns the Path with the original case preserved.
|
||||
func (p *Path) Unnormalized() *Path {
|
||||
return p.unnormalized
|
||||
}
|
||||
|
||||
@@ -435,6 +448,10 @@ func (p *Path) Identifier(i int) string {
|
||||
return p.identifierAsString(i)
|
||||
}
|
||||
|
||||
func (p *Path) Disabled() bool {
|
||||
return p.disabled
|
||||
}
|
||||
|
||||
func (p *Path) Identifiers() []string {
|
||||
ids := make([]string, len(p.identifiers))
|
||||
for i, id := range p.identifiers {
|
||||
@@ -463,6 +480,11 @@ func (p *Path) IsLeafBundle() bool {
|
||||
return p.bundleType == PathTypeLeaf
|
||||
}
|
||||
|
||||
func (p Path) ForBundleType(t PathType) *Path {
|
||||
p.bundleType = t
|
||||
return &p
|
||||
}
|
||||
|
||||
func (p *Path) identifierAsString(i int) string {
|
||||
i = p.identifierIndex(i)
|
||||
if i == -1 {
|
||||
|
||||
@@ -93,7 +93,7 @@ func TestParse(t *testing.T) {
|
||||
"Basic text file, mixed case and spaces, unnormalized",
|
||||
"/a/Foo BAR.txt",
|
||||
func(c *qt.C, p *Path) {
|
||||
pp := p.Unmormalized()
|
||||
pp := p.Unnormalized()
|
||||
c.Assert(pp, qt.IsNotNil)
|
||||
c.Assert(pp.BaseNameNoIdentifier(), qt.Equals, "Foo BAR")
|
||||
},
|
||||
|
||||
@@ -163,7 +163,6 @@ func Uglify(in string) string {
|
||||
// If ParseRequestURI fails, the input is just converted to OS specific slashes and returned.
|
||||
func UrlToFilename(s string) (string, bool) {
|
||||
u, err := url.ParseRequestURI(s)
|
||||
|
||||
if err != nil {
|
||||
return filepath.FromSlash(s), false
|
||||
}
|
||||
|
||||
@@ -57,7 +57,6 @@ line 3`
|
||||
c := qt.New(t)
|
||||
|
||||
c.Assert(collected, qt.DeepEquals, []string{"line 1\n", "line 2\n", "\n", "line 3"})
|
||||
|
||||
}
|
||||
|
||||
func BenchmarkVisitLinesAfter(b *testing.B) {
|
||||
@@ -68,9 +67,6 @@ func BenchmarkVisitLinesAfter(b *testing.B) {
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
VisitLinesAfter(lines, func(s string) {
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -80,7 +80,6 @@ func ToStringSlicePreserveStringE(v any) ([]string, error) {
|
||||
default:
|
||||
return nil, fmt.Errorf("failed to convert %T to a string slice", v)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TypeToString converts v to a string if it's a valid string type.
|
||||
|
||||
@@ -45,5 +45,4 @@ func TestToDuration(t *testing.T) {
|
||||
c.Assert(ToDuration("200"), qt.Equals, 200*time.Millisecond)
|
||||
c.Assert(ToDuration("4m"), qt.Equals, 4*time.Minute)
|
||||
c.Assert(ToDuration("asdfadf"), qt.Equals, time.Duration(0))
|
||||
|
||||
}
|
||||
|
||||
@@ -107,3 +107,8 @@ type LowHigh struct {
|
||||
|
||||
// This is only used for debugging purposes.
|
||||
var InvocationCounter atomic.Int64
|
||||
|
||||
// NewTrue returns a pointer to b.
|
||||
func NewBool(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
||||
@@ -79,5 +79,4 @@ func BenchmarkStringSort(b *testing.B) {
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
"github.com/gohugoio/hugo/common/types"
|
||||
"github.com/gohugoio/hugo/common/urls"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/config/privacy"
|
||||
@@ -64,6 +65,7 @@ type InternalConfig struct {
|
||||
Verbose bool
|
||||
Clock string
|
||||
Watch bool
|
||||
FastRenderMode bool
|
||||
LiveReloadPort int
|
||||
}
|
||||
|
||||
@@ -206,7 +208,7 @@ func (c Config) cloneForLang() *Config {
|
||||
x.DisableKinds = copyStringSlice(x.DisableKinds)
|
||||
x.DisableLanguages = copyStringSlice(x.DisableLanguages)
|
||||
x.MainSections = copyStringSlice(x.MainSections)
|
||||
x.IgnoreErrors = copyStringSlice(x.IgnoreErrors)
|
||||
x.IgnoreLogs = copyStringSlice(x.IgnoreLogs)
|
||||
x.IgnoreFiles = copyStringSlice(x.IgnoreFiles)
|
||||
x.Theme = copyStringSlice(x.Theme)
|
||||
|
||||
@@ -299,9 +301,9 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
|
||||
}
|
||||
}
|
||||
|
||||
ignoredErrors := make(map[string]bool)
|
||||
for _, err := range c.IgnoreErrors {
|
||||
ignoredErrors[strings.ToLower(err)] = true
|
||||
ignoredLogIDs := make(map[string]bool)
|
||||
for _, err := range c.IgnoreLogs {
|
||||
ignoredLogIDs[strings.ToLower(err)] = true
|
||||
}
|
||||
|
||||
baseURL, err := urls.NewBaseURLFromString(c.BaseURL)
|
||||
@@ -357,7 +359,7 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
|
||||
BaseURLLiveReload: baseURL,
|
||||
DisabledKinds: disabledKinds,
|
||||
DisabledLanguages: disabledLangs,
|
||||
IgnoredErrors: ignoredErrors,
|
||||
IgnoredLogs: ignoredLogIDs,
|
||||
KindOutputFormats: kindOutputFormats,
|
||||
CreateTitle: helpers.GetTitleFunc(c.TitleCaseStyle),
|
||||
IsUglyURLSection: isUglyURL,
|
||||
@@ -394,7 +396,7 @@ type ConfigCompiled struct {
|
||||
KindOutputFormats map[string]output.Formats
|
||||
DisabledKinds map[string]bool
|
||||
DisabledLanguages map[string]bool
|
||||
IgnoredErrors map[string]bool
|
||||
IgnoredLogs map[string]bool
|
||||
CreateTitle func(s string) string
|
||||
IsUglyURLSection func(section string) bool
|
||||
IgnoreFile func(filename string) bool
|
||||
@@ -501,8 +503,8 @@ type RootConfig struct {
|
||||
// Enable to disable the build lock file.
|
||||
NoBuildLock bool
|
||||
|
||||
// A list of error IDs to ignore.
|
||||
IgnoreErrors []string
|
||||
// A list of log IDs to ignore.
|
||||
IgnoreLogs []string
|
||||
|
||||
// A list of regexps that match paths to ignore.
|
||||
// Deprecated: Use the settings on module imports.
|
||||
@@ -732,7 +734,8 @@ func (c *Configs) Init() error {
|
||||
|
||||
c.Languages = languages
|
||||
c.LanguagesDefaultFirst = languagesDefaultFirst
|
||||
c.ContentPathParser = paths.PathParser{LanguageIndex: languagesDefaultFirst.AsIndexSet()}
|
||||
|
||||
c.ContentPathParser = paths.PathParser{LanguageIndex: languagesDefaultFirst.AsIndexSet(), IsLangDisabled: c.Base.IsLangDisabled}
|
||||
|
||||
c.configLangs = make([]config.AllProvider, len(c.Languages))
|
||||
for i, l := range c.LanguagesDefaultFirst {
|
||||
@@ -899,6 +902,18 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Adjust Goldmark config defaults for multilingual, single-host sites.
|
||||
if len(languagesConfig) > 1 && !isMultiHost && !clone.Markup.Goldmark.DuplicateResourceFiles {
|
||||
if !clone.Markup.Goldmark.DuplicateResourceFiles {
|
||||
if clone.Markup.Goldmark.RenderHooks.Link.EnableDefault == nil {
|
||||
clone.Markup.Goldmark.RenderHooks.Link.EnableDefault = types.NewBool(true)
|
||||
}
|
||||
if clone.Markup.Goldmark.RenderHooks.Image.EnableDefault == nil {
|
||||
clone.Markup.Goldmark.RenderHooks.Image.EnableDefault = types.NewBool(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
langConfigMap[k] = clone
|
||||
case maps.ParamsMergeStrategy:
|
||||
default:
|
||||
@@ -951,7 +966,7 @@ func decodeConfigFromParams(fs afero.Fs, logger loggers.Logger, bcfg config.Base
|
||||
})
|
||||
|
||||
for _, v := range decoderSetups {
|
||||
p := decodeConfig{p: p, c: target, fs: fs, bcfg: bcfg}
|
||||
p := decodeConfig{p: p, c: target, fs: fs, logger: logger, bcfg: bcfg}
|
||||
if err := v.decode(v, p); err != nil {
|
||||
return fmt.Errorf("failed to decode %q: %w", v.key, err)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/cache/filecache"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/common/types"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
@@ -42,10 +43,11 @@ import (
|
||||
)
|
||||
|
||||
type decodeConfig struct {
|
||||
p config.Provider
|
||||
c *Config
|
||||
fs afero.Fs
|
||||
bcfg config.BaseConfig
|
||||
p config.Provider
|
||||
c *Config
|
||||
fs afero.Fs
|
||||
logger loggers.Logger
|
||||
bcfg config.BaseConfig
|
||||
}
|
||||
|
||||
type decodeWeight struct {
|
||||
@@ -291,7 +293,7 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
key: "cascade",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
var err error
|
||||
p.c.Cascade, err = page.DecodeCascadeConfig(p.p.Get(d.key))
|
||||
p.c.Cascade, err = page.DecodeCascadeConfig(p.logger, p.p.Get(d.key))
|
||||
return err
|
||||
},
|
||||
},
|
||||
|
||||
@@ -73,6 +73,10 @@ func (c ConfigLanguage) IsMultihost() bool {
|
||||
return c.m.IsMultihost
|
||||
}
|
||||
|
||||
func (c ConfigLanguage) FastRenderMode() bool {
|
||||
return c.config.Internal.FastRenderMode
|
||||
}
|
||||
|
||||
func (c ConfigLanguage) IsMultiLingual() bool {
|
||||
return len(c.m.Languages) > 1
|
||||
}
|
||||
@@ -89,8 +93,8 @@ func (c ConfigLanguage) IsLangDisabled(lang string) bool {
|
||||
return c.config.C.DisabledLanguages[lang]
|
||||
}
|
||||
|
||||
func (c ConfigLanguage) IgnoredErrors() map[string]bool {
|
||||
return c.config.C.IgnoredErrors
|
||||
func (c ConfigLanguage) IgnoredLogs() map[string]bool {
|
||||
return c.config.C.IgnoredLogs
|
||||
}
|
||||
|
||||
func (c ConfigLanguage) NoBuildLock() bool {
|
||||
|
||||
@@ -141,6 +141,7 @@ func (l configLoader) applyConfigAliases() error {
|
||||
{Key: "indexes", Value: "taxonomies"},
|
||||
{Key: "logI18nWarnings", Value: "printI18nWarnings"},
|
||||
{Key: "logPathWarnings", Value: "printPathWarnings"},
|
||||
{Key: "ignoreErrors", Value: "ignoreLogs"},
|
||||
}
|
||||
|
||||
for _, alias := range aliases {
|
||||
|
||||
@@ -50,7 +50,7 @@ weight = 3
|
||||
title = "Svenska"
|
||||
weight = 4
|
||||
`
|
||||
if err := os.WriteFile(configFilename, []byte(config), 0666); err != nil {
|
||||
if err := os.WriteFile(configFilename, []byte(config), 0o666); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
d := ConfigSourceDescriptor{
|
||||
|
||||
@@ -208,7 +208,6 @@ func LoadConfigFromDir(sourceFs afero.Fs, configDir, environment string) (Provid
|
||||
}
|
||||
|
||||
return cfg, dirnames, nil
|
||||
|
||||
}
|
||||
|
||||
var keyAliases maps.KeyRenamer
|
||||
|
||||
@@ -57,6 +57,7 @@ type AllProvider interface {
|
||||
BuildDrafts() bool
|
||||
Running() bool
|
||||
Watching() bool
|
||||
FastRenderMode() bool
|
||||
PrintUnusedTemplates() bool
|
||||
EnableMissingTranslationPlaceholders() bool
|
||||
TemplateMetrics() bool
|
||||
@@ -67,7 +68,7 @@ type AllProvider interface {
|
||||
NewContentEditor() string
|
||||
Timeout() time.Duration
|
||||
StaticDirs() []string
|
||||
IgnoredErrors() map[string]bool
|
||||
IgnoredLogs() map[string]bool
|
||||
WorkingDir() string
|
||||
EnableEmoji() bool
|
||||
}
|
||||
|
||||
@@ -370,7 +370,6 @@ func (c *defaultConfigProvider) SetDefaultMergeStrategy() {
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (c *defaultConfigProvider) getNestedKeyAndMap(key string, create bool) (string, maps.Params) {
|
||||
|
||||
@@ -49,7 +49,6 @@ func GetMemoryLimit() uint64 {
|
||||
if v := stringToGibabyte(mem); v > 0 {
|
||||
return v
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// There is a FreeMemory function, but as the kernel in most situations
|
||||
|
||||
@@ -42,7 +42,7 @@ var DefaultConfig = Config{
|
||||
),
|
||||
// These have been tested to work with Hugo's external programs
|
||||
// on Windows, Linux and MacOS.
|
||||
OsEnv: MustNewWhitelist(`(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG)$`),
|
||||
OsEnv: MustNewWhitelist(`(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|SYSTEMDRIVE)$`),
|
||||
},
|
||||
Funcs: Funcs{
|
||||
Getenv: MustNewWhitelist("^HUGO_", "^CI$"),
|
||||
|
||||
@@ -135,7 +135,7 @@ func TestToTOML(t *testing.T) {
|
||||
got := DefaultConfig.ToTOML()
|
||||
|
||||
c.Assert(got, qt.Equals,
|
||||
"[security]\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['^(dart-)?sass(-embedded)?$', '^go$', '^npx$', '^postcss$']\n osEnv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG)$']\n\n [security.funcs]\n getenv = ['^HUGO_', '^CI$']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['.*']",
|
||||
"[security]\n enableInlineShortcodes = false\n\n [security.exec]\n allow = ['^(dart-)?sass(-embedded)?$', '^go$', '^npx$', '^postcss$']\n osEnv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|SYSTEMDRIVE)$']\n\n [security.funcs]\n getenv = ['^HUGO_', '^CI$']\n\n [security.http]\n methods = ['(?i)GET|POST']\n urls = ['.*']",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -43,5 +43,4 @@ func TestWhitelist(t *testing.T) {
|
||||
c.Assert(w.Accept("bar"), qt.IsTrue)
|
||||
c.Assert(w.Accept("mbar"), qt.IsFalse)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
+28
-8
@@ -21,11 +21,21 @@ import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/cloudfront"
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/cloudfront"
|
||||
"github.com/aws/aws-sdk-go-v2/service/cloudfront/types"
|
||||
gcaws "gocloud.dev/aws"
|
||||
)
|
||||
|
||||
// V2ConfigFromURLParams will fail for any unknown params, so we need to remove them.
|
||||
// This is a mysterious API, but inspecting the code the known params are:
|
||||
var v2ConfigValidParams = map[string]bool{
|
||||
"endpoint": true,
|
||||
"region": true,
|
||||
"profile": true,
|
||||
"awssdk": true,
|
||||
}
|
||||
|
||||
// InvalidateCloudFront invalidates the CloudFront cache for distributionID.
|
||||
// Uses AWS credentials config from the bucket URL.
|
||||
func InvalidateCloudFront(ctx context.Context, target *Target) error {
|
||||
@@ -33,20 +43,30 @@ func InvalidateCloudFront(ctx context.Context, target *Target) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sess, _, err := gcaws.NewSessionFromURLParams(u.Query())
|
||||
vals := u.Query()
|
||||
|
||||
// Remove any unknown params.
|
||||
for k := range vals {
|
||||
if !v2ConfigValidParams[k] {
|
||||
vals.Del(k)
|
||||
}
|
||||
}
|
||||
|
||||
cfg, err := gcaws.V2ConfigFromURLParams(ctx, vals)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cf := cloudfront.NewFromConfig(cfg)
|
||||
req := &cloudfront.CreateInvalidationInput{
|
||||
DistributionId: aws.String(target.CloudFrontDistributionID),
|
||||
InvalidationBatch: &cloudfront.InvalidationBatch{
|
||||
InvalidationBatch: &types.InvalidationBatch{
|
||||
CallerReference: aws.String(time.Now().Format("20060102150405")),
|
||||
Paths: &cloudfront.Paths{
|
||||
Items: []*string{aws.String("/*")},
|
||||
Quantity: aws.Int64(1),
|
||||
Paths: &types.Paths{
|
||||
Items: []string{"/*"},
|
||||
Quantity: aws.Int32(1),
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err = cloudfront.New(sess).CreateInvalidationWithContext(ctx, req)
|
||||
_, err = cf.CreateInvalidation(ctx, req)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -14,11 +14,10 @@
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/gobwas/glob"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
hglob "github.com/gohugoio/hugo/hugofs/glob"
|
||||
@@ -132,7 +131,6 @@ var DefaultConfig = DeployConfig{
|
||||
|
||||
// DecodeConfig creates a config from a given Hugo configuration.
|
||||
func DecodeConfig(cfg config.Provider) (DeployConfig, error) {
|
||||
|
||||
dcfg := DefaultConfig
|
||||
|
||||
if !cfg.IsSet(deploymentConfigKey) {
|
||||
|
||||
+56
-33
@@ -917,14 +917,9 @@ config:
|
||||
disableTags: false
|
||||
enable: false
|
||||
cacheBusters:
|
||||
- source: assets/.*\.(js|ts|jsx|tsx)
|
||||
target: (js|scripts|javascript)
|
||||
- source: assets/.*\.(css|sass|scss)$
|
||||
target: (css|styles|scss|sass)
|
||||
- source: (postcss|tailwind)\.config\.js
|
||||
target: (css|styles|scss|sass)
|
||||
- source: assets/.*\.(.*)$
|
||||
target: $1
|
||||
duplicateResourceFiles: false
|
||||
noJSConfigInAssets: false
|
||||
useResourceCacheWhen: fallback
|
||||
buildDrafts: false
|
||||
@@ -1007,8 +1002,8 @@ config:
|
||||
hasCJKLanguage: false
|
||||
i18nDir: i18n
|
||||
ignoreCache: false
|
||||
ignoreErrors: null
|
||||
ignoreFiles: []
|
||||
ignoreLogs: null
|
||||
ignoreVendorPaths: ""
|
||||
imaging:
|
||||
bgColor: '#ffffff'
|
||||
@@ -1041,6 +1036,7 @@ config:
|
||||
workingFolderCurrent: false
|
||||
defaultMarkdownHandler: goldmark
|
||||
goldmark:
|
||||
duplicateResourceFiles: false
|
||||
extensions:
|
||||
cjk:
|
||||
eastAsianLineBreaks: false
|
||||
@@ -1078,6 +1074,11 @@ config:
|
||||
autoHeadingID: true
|
||||
autoHeadingIDType: github
|
||||
wrapStandAloneImageWithinParagraph: true
|
||||
renderHooks:
|
||||
image:
|
||||
enableDefault: false
|
||||
link:
|
||||
enableDefault: false
|
||||
renderer:
|
||||
hardWraps: false
|
||||
unsafe: false
|
||||
@@ -1352,143 +1353,154 @@ config:
|
||||
isHTML: true
|
||||
isPlainText: false
|
||||
mediaType: text/html
|
||||
name: amp
|
||||
noUgly: false
|
||||
notAlternative: false
|
||||
path: amp
|
||||
permalinkable: true
|
||||
protocol: ""
|
||||
rel: amphtml
|
||||
root: false
|
||||
ugly: false
|
||||
weight: 0
|
||||
calendar:
|
||||
baseName: index
|
||||
isHTML: false
|
||||
isPlainText: true
|
||||
mediaType: text/calendar
|
||||
name: calendar
|
||||
noUgly: false
|
||||
notAlternative: false
|
||||
path: ""
|
||||
permalinkable: false
|
||||
protocol: webcal://
|
||||
rel: alternate
|
||||
root: false
|
||||
ugly: false
|
||||
weight: 0
|
||||
css:
|
||||
baseName: styles
|
||||
isHTML: false
|
||||
isPlainText: true
|
||||
mediaType: text/css
|
||||
name: css
|
||||
noUgly: false
|
||||
notAlternative: true
|
||||
path: ""
|
||||
permalinkable: false
|
||||
protocol: ""
|
||||
rel: stylesheet
|
||||
root: false
|
||||
ugly: false
|
||||
weight: 0
|
||||
csv:
|
||||
baseName: index
|
||||
isHTML: false
|
||||
isPlainText: true
|
||||
mediaType: text/csv
|
||||
name: csv
|
||||
noUgly: false
|
||||
notAlternative: false
|
||||
path: ""
|
||||
permalinkable: false
|
||||
protocol: ""
|
||||
rel: alternate
|
||||
root: false
|
||||
ugly: false
|
||||
weight: 0
|
||||
html:
|
||||
baseName: index
|
||||
isHTML: true
|
||||
isPlainText: false
|
||||
mediaType: text/html
|
||||
name: html
|
||||
noUgly: false
|
||||
notAlternative: false
|
||||
path: ""
|
||||
permalinkable: true
|
||||
protocol: ""
|
||||
rel: canonical
|
||||
root: false
|
||||
ugly: false
|
||||
weight: 10
|
||||
json:
|
||||
baseName: index
|
||||
isHTML: false
|
||||
isPlainText: true
|
||||
mediaType: application/json
|
||||
name: json
|
||||
noUgly: false
|
||||
notAlternative: false
|
||||
path: ""
|
||||
permalinkable: false
|
||||
protocol: ""
|
||||
rel: alternate
|
||||
root: false
|
||||
ugly: false
|
||||
weight: 0
|
||||
markdown:
|
||||
baseName: index
|
||||
isHTML: false
|
||||
isPlainText: true
|
||||
mediaType: text/markdown
|
||||
name: markdown
|
||||
noUgly: false
|
||||
notAlternative: false
|
||||
path: ""
|
||||
permalinkable: false
|
||||
protocol: ""
|
||||
rel: alternate
|
||||
root: false
|
||||
ugly: false
|
||||
weight: 0
|
||||
robots:
|
||||
baseName: robots
|
||||
isHTML: false
|
||||
isPlainText: true
|
||||
mediaType: text/plain
|
||||
name: robots
|
||||
noUgly: false
|
||||
notAlternative: false
|
||||
path: ""
|
||||
permalinkable: false
|
||||
protocol: ""
|
||||
rel: alternate
|
||||
root: true
|
||||
ugly: false
|
||||
weight: 0
|
||||
rss:
|
||||
baseName: index
|
||||
isHTML: false
|
||||
isPlainText: false
|
||||
mediaType: application/rss+xml
|
||||
name: rss
|
||||
noUgly: true
|
||||
notAlternative: false
|
||||
path: ""
|
||||
permalinkable: false
|
||||
protocol: ""
|
||||
rel: alternate
|
||||
root: false
|
||||
ugly: false
|
||||
weight: 0
|
||||
sitemap:
|
||||
baseName: sitemap
|
||||
isHTML: false
|
||||
isPlainText: false
|
||||
mediaType: application/xml
|
||||
name: sitemap
|
||||
noUgly: true
|
||||
noUgly: false
|
||||
notAlternative: false
|
||||
path: ""
|
||||
permalinkable: false
|
||||
protocol: ""
|
||||
rel: sitemap
|
||||
root: false
|
||||
ugly: true
|
||||
weight: 0
|
||||
webappmanifest:
|
||||
baseName: manifest
|
||||
isHTML: false
|
||||
isPlainText: true
|
||||
mediaType: application/manifest+json
|
||||
name: webappmanifest
|
||||
noUgly: false
|
||||
notAlternative: true
|
||||
path: ""
|
||||
permalinkable: false
|
||||
protocol: ""
|
||||
rel: manifest
|
||||
root: false
|
||||
ugly: false
|
||||
weight: 0
|
||||
outputs:
|
||||
home:
|
||||
@@ -2913,6 +2925,20 @@ tpl:
|
||||
Examples:
|
||||
- - '{{ warnf "%s." "warning" }}'
|
||||
- ""
|
||||
Warnidf:
|
||||
Aliases:
|
||||
- warnidf
|
||||
Args:
|
||||
- id
|
||||
- format
|
||||
- args
|
||||
Description: |-
|
||||
Warnidf formats args according to a format specifier and logs an WARNING and
|
||||
an information text that the warning with the given id can be suppressed in config.
|
||||
It returns an empty string.
|
||||
Examples:
|
||||
- - '{{ warnidf "my-warn-id" "%s." "warning" }}'
|
||||
- ""
|
||||
Warnmf:
|
||||
Aliases: null
|
||||
Args: null
|
||||
@@ -3688,14 +3714,6 @@ tpl:
|
||||
- s
|
||||
Description: JSStr returns the given string as a html/template JSStr content.
|
||||
Examples: []
|
||||
SanitizeURL:
|
||||
Aliases:
|
||||
- sanitizeURL
|
||||
- sanitizeurl
|
||||
Args:
|
||||
- s
|
||||
Description: SanitizeURL returns the string s as html/template URL content.
|
||||
Examples: []
|
||||
URL:
|
||||
Aliases:
|
||||
- safeURL
|
||||
@@ -3756,7 +3774,7 @@ tpl:
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
GetIdentity:
|
||||
ForEeachIdentityByName:
|
||||
Aliases: null
|
||||
Args: null
|
||||
Description: ""
|
||||
@@ -3766,11 +3784,6 @@ tpl:
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
GetPageWithTemplateInfo:
|
||||
Aliases: null
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
GoogleAnalytics:
|
||||
Aliases: null
|
||||
Args: null
|
||||
@@ -3796,6 +3809,11 @@ tpl:
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
Key:
|
||||
Aliases: null
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
Language:
|
||||
Aliases: null
|
||||
Args: null
|
||||
@@ -3821,6 +3839,11 @@ tpl:
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
Lastmod:
|
||||
Aliases: null
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
MainSections:
|
||||
Aliases: null
|
||||
Args: null
|
||||
|
||||
@@ -4,7 +4,8 @@ require (
|
||||
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69
|
||||
github.com/alecthomas/chroma/v2 v2.12.0
|
||||
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c
|
||||
github.com/aws/aws-sdk-go v1.48.6
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.32.6
|
||||
github.com/bep/clocks v0.5.0
|
||||
github.com/bep/debounce v1.2.0
|
||||
github.com/bep/gitmap v1.1.2
|
||||
@@ -24,12 +25,12 @@ require (
|
||||
github.com/cli/safeexec v1.0.1
|
||||
github.com/disintegration/gift v1.2.1
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/evanw/esbuild v0.19.12
|
||||
github.com/evanw/esbuild v0.20.0
|
||||
github.com/fatih/color v1.16.0
|
||||
github.com/fortytw2/leaktest v1.3.0
|
||||
github.com/frankban/quicktest v1.14.6
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/getkin/kin-openapi v0.122.0
|
||||
github.com/getkin/kin-openapi v0.123.0
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/gobuffalo/flect v1.0.2
|
||||
github.com/gobwas/glob v0.2.3
|
||||
@@ -48,7 +49,7 @@ require (
|
||||
github.com/marekm4/color-extractor v1.2.1
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mitchellh/hashstructure v1.1.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c
|
||||
github.com/muesli/smartcrop v0.3.0
|
||||
github.com/niklasfasching/go-org v1.7.0
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
@@ -62,14 +63,14 @@ require (
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/fsync v0.10.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/tdewolff/minify/v2 v2.20.13
|
||||
github.com/tdewolff/parse/v2 v2.7.8
|
||||
github.com/yuin/goldmark v1.6.0
|
||||
github.com/tdewolff/minify/v2 v2.20.16
|
||||
github.com/tdewolff/parse/v2 v2.7.11
|
||||
github.com/yuin/goldmark v1.7.0
|
||||
github.com/yuin/goldmark-emoji v1.0.2
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
gocloud.dev v0.34.0
|
||||
gocloud.dev v0.36.0
|
||||
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326
|
||||
golang.org/x/image v0.14.0
|
||||
golang.org/x/image v0.15.0
|
||||
golang.org/x/mod v0.14.0
|
||||
golang.org/x/net v0.20.0
|
||||
golang.org/x/sync v0.6.0
|
||||
@@ -85,37 +86,37 @@ require (
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v1.1.5 // indirect
|
||||
cloud.google.com/go/storage v1.35.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.20.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.32 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.31 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.76 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.37 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.31 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.38 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.12 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.32 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.31 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.38.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.13.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.21.1 // indirect
|
||||
github.com/aws/smithy-go v1.14.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.50.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.12 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.47.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.5 // indirect
|
||||
github.com/aws/smithy-go v1.19.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||
github.com/dlclark/regexp2 v1.10.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/swag v0.22.4 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.8 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.1.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
@@ -144,10 +145,10 @@ require (
|
||||
golang.org/x/oauth2 v0.15.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
|
||||
google.golang.org/grpc v1.59.0 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
|
||||
@@ -46,21 +46,21 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
|
||||
cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w=
|
||||
cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0 h1:8q4SaHjFsClSvuVne0ID/5Ka8u3fcIHyqkLjcFpNRHQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 h1:fb8kj/Dh4CSwgsOzHeZY4Xh68cFVbzXx+ONXGMY//4w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0/go.mod h1:uReU2sSxZExRPBAg3qKzmAucSi51+SP1OhohieR821Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 h1:BMAjVKJM0U/CYF27gA0ZMmXGkOcvfFtD0oHVZ1TIPRI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 h1:d81/ng9rET2YqdVkVwkb6EXeRrLJIwyGnJcAlAWKwhs=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0 h1:Ma67P/GGprNwsslzEH6+Kb8nybI8jpDTm4Wmzu2ReK8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0 h1:nVocQV40OQne5613EeLayJiRAJuKlBGy+m22qWG+WRg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0/go.mod h1:7QJP7dr2wznCMeqIrhMgWGf7XpAQnVrJqDm9nvV3Cu4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 h1:gggzg0SUMs6SQbEw+3LoSsYf9YMjkupeAnHMX8O9mmY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0/go.mod h1:+6KLcKIVgxoBDMqMO/Nvy7bZ9a0nbU3I1DtFQK3YvB4=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 h1:hVeq+yCyUi+MsoO/CU95yqCIcdzra5ovzk8Q2BBpV2M=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69 h1:+tu3HOoMXB7RXEINRVIpxJCT+KdYiI7LAEAUrOw3dIU=
|
||||
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZdiDllfyYH5l5OkAaZtk7VkWe89bPJFmnDBNHxg=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
@@ -72,46 +72,48 @@ github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurG
|
||||
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
|
||||
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c h1:651/eoCRnQ7YtSjAnSzRucrJz+3iGEFt+ysraELS81M=
|
||||
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aws/aws-sdk-go v1.48.6 h1:hnL/TE3eRigirDLrdRE9AWE1ALZSVLAsC4wK8TGsMqk=
|
||||
github.com/aws/aws-sdk-go v1.48.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
||||
github.com/aws/aws-sdk-go-v2 v1.20.0 h1:INUDpYLt4oiPOJl0XwZDK2OVAVf0Rzo+MGVTv9f+gy8=
|
||||
github.com/aws/aws-sdk-go-v2 v1.20.0/go.mod h1:uWOr0m0jDsiWw8nnXiqZ+YG6LdvAlGYDLLf2NmHZoy4=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.11 h1:/MS8AzqYNAhhRNalOmxUvYs8VEbNGifTnzhPFdcRQkQ=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.11/go.mod h1:va22++AdXht4ccO3kH2SHkHHYvZ2G9Utz+CXKmm2CaU=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.32 h1:tqEOvkbTxwEV7hToRcJ1xZRjcATqwDVsWbAscgRKyNI=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.32/go.mod h1:U3ZF0fQRRA4gnbn9GGvOWLoT2EzzZfAWeKwnVrm1rDc=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.31 h1:vJyON3lG7R8VOErpJJBclBADiWTwzcwdkQpTKx8D2sk=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.31/go.mod h1:T4sESjBtY2lNxLgkIASmeP57b5j7hTQqCbqG0tWnxC4=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.7 h1:X3H6+SU21x+76LRglk21dFRgMTJMa5QcpW+SqUf5BBg=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.7/go.mod h1:3we0V09SwcJBzNlnyovrR2wWJhWmVdqAsmVs4uronv8=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.76 h1:DJ1kHj0GI9BbX+XhF0kHxlzOVjcncmDUXmCvXdbfdAE=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.76/go.mod h1:/AZCdswMSgwpB2yMSFfY5H4pVeBLnCuPehdmO/r3xSM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.37 h1:zr/gxAZkMcvP71ZhQOcvdm8ReLjFgIXnIn0fw5AM7mo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.37/go.mod h1:Pdn4j43v49Kk6+82spO3Tu5gSeQXRsxo56ePPQAvFiA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.31 h1:0HCMIkAkVY9KMgueD8tf4bRTUanzEYvhw7KkPXIMpO0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.31/go.mod h1:fTJDMe8LOFYtqiFFFeHA+SVMAwqLhoq0kcInYoLa9Js=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.38 h1:+i1DOFrW3YZ3apE45tCal9+aDKK6kNEbW6Ib7e1nFxE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.38/go.mod h1:1/jLp0OgOaWIetycOmycW+vYTYgTZFPttJQRgsI1PoU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.0 h1:U5yySdwt2HPo/pnQec04DImLzWORbeWML1fJiLkKruI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.0/go.mod h1:EhC/83j8/hL/UB1WmExo3gkElaja/KlmZM/gl1rTfjM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.12 h1:uAiiHnWihGP2rVp64fHwzLDrswGjEjsPszwRYMiYQPU=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.12/go.mod h1:fUTHpOXqRQpXvEpDPSa3zxCc2fnpW6YnBoba+eQr+Bg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.32 h1:kvN1jPHr9UffqqG3bSgZ8tx4+1zKVHz/Ktw/BwW6hX8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.32/go.mod h1:QmMEM7es84EUkbYWcpnkx8i5EW2uERPfrTFeOch128Y=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.31 h1:auGDJ0aLZahF5SPvkJ6WcUuX7iQ7kyl2MamV7Tm8QBk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.31/go.mod h1:3+lloe3sZuBQw1aBc5MyndvodzQlyqCZ7x1QPDHaWP4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.0 h1:Wgjft9X4W5pMeuqgPCHIQtbZ87wsgom7S5F8obreg+c=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.0/go.mod h1:FWNzS4+zcWAP05IF7TDYTY1ysZAzIvogxWaDT9p8fsA=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.38.1 h1:mTgFVlfQT8gikc5+/HwD8UL9jnUro5MGv8n/VEYF12I=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.38.1/go.mod h1:6SOWLiobcZZshbmECRTADIRYliPL0etqFSigauQEeT0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.13.1 h1:DSNpSbfEgFXRV+IfEcKE5kTbqxm+MeF5WgyeRlsLnHY=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.13.1/go.mod h1:TC9BubuFMVScIU+TLKamO6VZiYTkYoEHqlSQwAe2omw=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.1 h1:hd0SKLMdOL/Sl6Z0np1PX9LeH2gqNtBe0MhTedA8MGI=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.1/go.mod h1:XO/VcyoQ8nKyKfFW/3DMsRQXsfh/052tHTWmg3xBXRg=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.21.1 h1:pAOJj+80tC8sPVgSDHzMYD6KLWsaLQ1kZw31PTeORbs=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.21.1/go.mod h1:G8SbvL0rFk4WOJroU8tKBczhsbhj2p/YY7qeJezJ3CI=
|
||||
github.com/aws/smithy-go v1.14.0 h1:+X90sB94fizKjDmwb4vyl2cTTPXTE5E2G/1mjByb0io=
|
||||
github.com/aws/smithy-go v1.14.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/aws/aws-sdk-go v1.50.7 h1:odKb+uneeGgF2jgAerKjFzpljiyZxleV4SHB7oBK+YA=
|
||||
github.com/aws/aws-sdk-go v1.50.7/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4/go.mod h1:usURWEKSNNAcAZuzRn/9ZYPT8aZQkR7xcCtunK/LkJo=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.1 h1:z6DqMxclFGL3Zfo+4Q0rLnAZ6yVkzCRxhRMsiRQnD1o=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.1/go.mod h1:ZB+CuKHRbb5v5F0oJtGdhFTelmrxd4iWO1lf0rQwSAg=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.12 h1:v/WgB8NxprNvr5inKIiVVrXPuuTegM+K8nncFkr1usU=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.12/go.mod h1:X21k0FjEJe+/pauud82HYiQbEr9jRKY3kXEIQ4hXeTQ=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 h1:w98BT5w+ao1/r5sUuiH6JkVzjowOKeOJRHERyy1vh58=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10/go.mod h1:K2WGI7vUvkIv1HoNbfBA1bvIZ+9kL3YVmWxeKuLQsiw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7 h1:FnLf60PtjXp8ZOzQfhJVsqF0OtYKQZWQfqOLshh8YXg=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7/go.mod h1:tDVvl8hyU6E9B8TrnNrZQEVkQlB8hjJwcgpPhgtlnNg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 h1:ugD6qzjYtB7zM5PN/ZIeaAIyefPaD82G8+SJopgvUpw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9/go.mod h1:YD0aYBWCrPENpHolhKw2XDlTIWae2GKXT1T4o6N6hiM=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.32.6 h1:xKbFXea2CIF/Wskauz1TMr//wZ6FyzEafMdSBIQqn80=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.32.6/go.mod h1:iB6PQSb3ULRrrlEiuFfVE318JiBOdk4k46BbuzrrgXc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9 h1:/90OR2XbSYfXucBMJ4U14wrjlfleq/0SB6dZDPncgmo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9/go.mod h1:dN/Of9/fNZet7UrQQ6kTDo/VSwKPIq94vjlU16bRARc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9 h1:Nf2sHxjMJR8CSImIVCONRi4g0Su3J+TSTbS7G0pUeMU=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9/go.mod h1:idky4TER38YIjr2cADF1/ugFMKvZV7p//pVeV5LZbF0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9 h1:iEAeF6YC3l4FzlJPP9H3Ko1TXpdjdqWffxXjp8SY6uk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9/go.mod h1:kjsXoK23q9Z/tLBrckZLLyvjhZoS+AGrzqzUfEClvMM=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.47.5 h1:Keso8lIOS+IzI2MkPZyK6G0LYcK3My2LQ+T5bxghEAY=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.47.5/go.mod h1:vADO6Jn+Rq4nDtfwNjhgR84qkZwiC6FqCaXdw/kYwjA=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 h1:ldSFWz9tEHAwHNmjx2Cvy1MjP5/L9kNoR0skc6wyOOM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.5/go.mod h1:CaFfXLYL376jgbP7VKC96uFcU8Rlavak0UlAwk1Dlhc=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 h1:2k9KmFawS63euAkY4/ixVNsYYwrwnd5fIvgEKkfZFNM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5/go.mod h1:W+nd4wWDVkSUIox9bacmkBP5NMFQeTJ/xqNabpzSR38=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.5 h1:5UYvv8JUvllZsRnfrcMQ+hJ9jNICmcgKPAO1CER25Wg=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.5/go.mod h1:XX5gh4CB7wAs4KhcF46G6C8a2i7eupU19dcAAE+EydU=
|
||||
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
|
||||
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
|
||||
github.com/bep/clocks v0.5.0 h1:hhvKVGLPQWRVsBP/UB7ErrHYIO42gINVbvqxvYTPVps=
|
||||
github.com/bep/clocks v0.5.0/go.mod h1:SUq3q+OOq41y2lRQqH5fsOoxN8GbxSiT6jvoVVLCVhU=
|
||||
github.com/bep/debounce v1.2.0 h1:wXds8Kq8qRfwAOpAxHrJDbCXgC5aHSzgQb/0gKsHQqo=
|
||||
@@ -177,8 +179,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanw/esbuild v0.19.12 h1:p5WGo4o6TCN+kt+uZtYSGS3ZHPa+iIZ0SX+ys8UnP10=
|
||||
github.com/evanw/esbuild v0.19.12/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
|
||||
github.com/evanw/esbuild v0.20.0 h1:pcW+/LCNc99Pgfs0kUnvjRCba8Lr9tDMSVg89t1ZLW4=
|
||||
github.com/evanw/esbuild v0.20.0/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||
@@ -191,18 +193,17 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/getkin/kin-openapi v0.122.0 h1:WB9Jbl0Hp/T79/JF9xlSW5Kl9uYdk/AWD0yAd9HOM10=
|
||||
github.com/getkin/kin-openapi v0.122.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw=
|
||||
github.com/getkin/kin-openapi v0.123.0 h1:zIik0mRwFNLyvtXK274Q6ut+dPh6nlxBp0x7mNrPhs8=
|
||||
github.com/getkin/kin-openapi v0.123.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
||||
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
|
||||
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
|
||||
github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw=
|
||||
github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA=
|
||||
github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
|
||||
@@ -218,8 +219,8 @@ github.com/gohugoio/localescompressed v1.0.1 h1:KTYMi8fCWYLswFyJAeOtuk/EkXR/KPTH
|
||||
github.com/gohugoio/localescompressed v1.0.1/go.mod h1:jBF6q8D7a0vaEmcWPNcAjUZLJaIVNiwvM3WlmTvooB0=
|
||||
github.com/gohugoio/testmodBuilder/mods v0.0.0-20190520184928-c56af20f2e95 h1:sgew0XCnZwnzpWxTt3V8LLiCO7OQi3C6dycaE67wfkU=
|
||||
github.com/gohugoio/testmodBuilder/mods v0.0.0-20190520184928-c56af20f2e95/go.mod h1:bOlVlCa1/RajcHpXkrUXPSHB/Re1UnlXxD1Qp8SKOd8=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.1.0 h1:UGKbA/IPjtS6zLcdB7i5TyACMgSbOTiR8qzXgw8HWQU=
|
||||
github.com/golang-jwt/jwt/v5 v5.1.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -248,6 +249,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@@ -264,7 +266,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
@@ -357,8 +358,8 @@ github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/Qd
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0=
|
||||
github.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/6d8ulp4AwfLKrmA=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE=
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/montanaflynn/stats v0.6.3/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
@@ -424,10 +425,10 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tdewolff/minify/v2 v2.20.13 h1:TDWS1orkBJjq6Sz9NjEvHEeUnAvlfU7jgStGQBwBPGM=
|
||||
github.com/tdewolff/minify/v2 v2.20.13/go.mod h1:qnIJbnG2dSzk7LIa/UUwgN2OjS8ir6RRlqc0T/1q2xY=
|
||||
github.com/tdewolff/parse/v2 v2.7.8 h1:1cnVqa8L63xFkc2vfRsZTM6Qy35nJpTvQ2Uvdv3vbvs=
|
||||
github.com/tdewolff/parse/v2 v2.7.8/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
|
||||
github.com/tdewolff/minify/v2 v2.20.16 h1:/C8dtRkxLTIyUlKlBz46gDiktCrE8a6+c1gTrnPFz+U=
|
||||
github.com/tdewolff/minify/v2 v2.20.16/go.mod h1:/FvxV9KaTrFu35J9I2FhRvWSBxcHj8sDSdwBFh5voxM=
|
||||
github.com/tdewolff/parse/v2 v2.7.11 h1:v+W45LnzmjndVlfqPCT5gGjAAZKd1GJGOPJveTIkBY8=
|
||||
github.com/tdewolff/parse/v2 v2.7.11/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
|
||||
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
@@ -437,8 +438,9 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
|
||||
github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA=
|
||||
github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s=
|
||||
github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
@@ -451,14 +453,15 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
gocloud.dev v0.34.0 h1:LzlQY+4l2cMtuNfwT2ht4+fiXwWf/NmPTnXUlLmGif4=
|
||||
gocloud.dev v0.34.0/go.mod h1:psKOachbnvY3DAOPbsFVmLIErwsbWPUG2H5i65D38vE=
|
||||
gocloud.dev v0.36.0 h1:q5zoXux4xkOZP473e1EZbG8Gq9f0vlg1VNH5Du/ybus=
|
||||
gocloud.dev v0.36.0/go.mod h1:bLxah6JQVKBaIxzsr5BQLYB4IYdWHkMZdzCXlo6F0gg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
@@ -478,8 +481,8 @@ golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZ
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
|
||||
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
||||
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
|
||||
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -502,6 +505,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -538,6 +542,7 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -562,6 +567,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -602,12 +608,15 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -615,6 +624,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -671,14 +682,15 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
@@ -706,8 +718,9 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
@@ -744,10 +757,10 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
|
||||
google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f h1:Vn+VyHU5guc9KjB5KrjI2q0wCOWEOIh0OEsleqakHJg=
|
||||
google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f/go.mod h1:nWSwAFPb+qfNJXsoeO3Io7zf4tMSfN8EA8RlDA04GhY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f h1:2yNACc1O40tTnrsbk9Cv6oxiW8pxI/pXj0wRtdlYmgY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
@@ -785,7 +798,6 @@ google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/neurosnap/sentences.v1 v1.0.6/go.mod h1:YlK+SN+fLQZj+kY3r8DkGDhDr91+S3JmTb5LSxFRQo0=
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||
|
||||
+1
-1
@@ -328,7 +328,7 @@ func PrintFs(fs afero.Fs, path string, w io.Writer) {
|
||||
}
|
||||
|
||||
afero.Walk(fs, path, func(path string, info os.FileInfo, err error) error {
|
||||
fmt.Println(path)
|
||||
fmt.Fprintln(w, filepath.ToSlash(path))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -20,12 +20,10 @@ import (
|
||||
)
|
||||
|
||||
func TestExtractMinorVersionFromGoTag(t *testing.T) {
|
||||
|
||||
c := qt.New(t)
|
||||
|
||||
c.Assert(extractMinorVersionFromGoTag("go1.17"), qt.Equals, 17)
|
||||
c.Assert(extractMinorVersionFromGoTag("go1.16.7"), qt.Equals, 16)
|
||||
c.Assert(extractMinorVersionFromGoTag("go1.17beta1"), qt.Equals, 17)
|
||||
c.Assert(extractMinorVersionFromGoTag("asdfadf"), qt.Equals, -1)
|
||||
|
||||
}
|
||||
|
||||
+24
-9
@@ -94,11 +94,15 @@ func (f *componentFsDir) ReadDir(count int) ([]iofs.DirEntry, error) {
|
||||
|
||||
fis = fis[:n]
|
||||
|
||||
n = 0
|
||||
for _, fi := range fis {
|
||||
s := path.Join(f.name, fi.Name())
|
||||
_ = f.fs.applyMeta(fi, s)
|
||||
|
||||
if _, ok := f.fs.applyMeta(fi, s); ok {
|
||||
fis[n] = fi
|
||||
n++
|
||||
}
|
||||
}
|
||||
fis = fis[:n]
|
||||
|
||||
sort.Slice(fis, func(i, j int) bool {
|
||||
fimi, fimj := fis[i].(FileMetaInfo), fis[j].(FileMetaInfo)
|
||||
@@ -147,13 +151,13 @@ func (f *componentFsDir) ReadDir(count int) ([]iofs.DirEntry, error) {
|
||||
})
|
||||
|
||||
if f.fs.opts.Component == files.ComponentFolderContent {
|
||||
// Finally filter out any duplicate content files, e.g. page.md and page.html.
|
||||
// Finally filter out any duplicate content or resource files, e.g. page.md and page.html.
|
||||
n := 0
|
||||
seen := map[hstrings.Tuple]bool{}
|
||||
for _, fi := range fis {
|
||||
fim := fi.(FileMetaInfo)
|
||||
pi := fim.Meta().PathInfo
|
||||
keep := fim.IsDir() || !pi.IsContent()
|
||||
keep := fim.IsDir()
|
||||
|
||||
if !keep {
|
||||
baseLang := hstrings.Tuple{First: pi.Base(), Second: fim.Meta().Lang}
|
||||
@@ -180,7 +184,8 @@ func (f *componentFsDir) Stat() (iofs.FileInfo, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f.fs.applyMeta(fi, f.name), nil
|
||||
fim, _ := f.fs.applyMeta(fi, f.name)
|
||||
return fim, nil
|
||||
}
|
||||
|
||||
func (fs *componentFs) Stat(name string) (os.FileInfo, error) {
|
||||
@@ -188,16 +193,26 @@ func (fs *componentFs) Stat(name string) (os.FileInfo, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fs.applyMeta(fi, name), nil
|
||||
fim, _ := fs.applyMeta(fi, name)
|
||||
return fim, nil
|
||||
}
|
||||
|
||||
func (fs *componentFs) applyMeta(fi FileNameIsDir, name string) FileMetaInfo {
|
||||
func (fs *componentFs) applyMeta(fi FileNameIsDir, name string) (FileMetaInfo, bool) {
|
||||
if runtime.GOOS == "darwin" {
|
||||
name = norm.NFC.String(name)
|
||||
}
|
||||
fim := fi.(FileMetaInfo)
|
||||
meta := fim.Meta()
|
||||
meta.PathInfo = fs.opts.PathParser.Parse(fs.opts.Component, name)
|
||||
pi := fs.opts.PathParser.Parse(fs.opts.Component, name)
|
||||
if pi.Disabled() {
|
||||
return fim, false
|
||||
}
|
||||
if meta.Lang != "" {
|
||||
if isLangDisabled := fs.opts.PathParser.IsLangDisabled; isLangDisabled != nil && isLangDisabled(meta.Lang) {
|
||||
return fim, false
|
||||
}
|
||||
}
|
||||
meta.PathInfo = pi
|
||||
if !fim.IsDir() {
|
||||
if fileLang := meta.PathInfo.Lang(); fileLang != "" {
|
||||
// A valid lang set in filename.
|
||||
@@ -223,7 +238,7 @@ func (fs *componentFs) applyMeta(fi FileNameIsDir, name string) FileMetaInfo {
|
||||
}
|
||||
}
|
||||
|
||||
return fim
|
||||
return fim, true
|
||||
}
|
||||
|
||||
func (f *componentFsDir) Readdir(count int) ([]os.FileInfo, error) {
|
||||
|
||||
@@ -33,9 +33,7 @@ type DuplicatesReporter interface {
|
||||
ReportDuplicates() string
|
||||
}
|
||||
|
||||
var (
|
||||
_ FilesystemUnwrapper = (*createCountingFs)(nil)
|
||||
)
|
||||
var _ FilesystemUnwrapper = (*createCountingFs)(nil)
|
||||
|
||||
func NewCreateCountingFs(fs afero.Fs) afero.Fs {
|
||||
return &createCountingFs{Fs: fs, fileCount: make(map[string]int)}
|
||||
|
||||
@@ -75,7 +75,6 @@ func TestGetGlob(t *testing.T) {
|
||||
}
|
||||
|
||||
func BenchmarkGetGlob(b *testing.B) {
|
||||
|
||||
runBench := func(name string, cache *globCache, search string) {
|
||||
b.Run(name, func(b *testing.B) {
|
||||
g, err := GetGlob("**/foo")
|
||||
|
||||
+14
-2
@@ -53,8 +53,9 @@ type WalkwayConfig struct {
|
||||
Logger loggers.Logger
|
||||
|
||||
// One or both of these may be pre-set.
|
||||
Info FileMetaInfo // The start info.
|
||||
DirEntries []FileMetaInfo // The start info's dir entries.
|
||||
Info FileMetaInfo // The start info.
|
||||
DirEntries []FileMetaInfo // The start info's dir entries.
|
||||
IgnoreFile func(filename string) bool // Optional
|
||||
|
||||
// Will be called in order.
|
||||
HookPre WalkHook // Optional.
|
||||
@@ -172,6 +173,17 @@ func (w *Walkway) walk(path string, info FileMetaInfo, dirEntries []FileMetaInfo
|
||||
|
||||
}
|
||||
|
||||
if w.cfg.IgnoreFile != nil {
|
||||
n := 0
|
||||
for _, fi := range dirEntries {
|
||||
if !w.cfg.IgnoreFile(fi.Meta().Filename) {
|
||||
dirEntries[n] = fi
|
||||
n++
|
||||
}
|
||||
}
|
||||
dirEntries = dirEntries[:n]
|
||||
}
|
||||
|
||||
if w.cfg.HookPre != nil {
|
||||
var err error
|
||||
dirEntries, err = w.cfg.HookPre(info, path, dirEntries)
|
||||
|
||||
+2
-2
@@ -41,8 +41,8 @@ Data: {{ len .Data }}|
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
//LogLevel: logg.LevelTrace,
|
||||
//Verbose: true,
|
||||
// LogLevel: logg.LevelTrace,
|
||||
// Verbose: true,
|
||||
},
|
||||
).Build()
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 88 KiB |
@@ -671,3 +671,32 @@ S1|p1:|p2:p2|
|
||||
`)
|
||||
})
|
||||
}
|
||||
|
||||
// Issue 11977.
|
||||
func TestCascadeExtensionInPath(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.org"
|
||||
[languages]
|
||||
[languages.en]
|
||||
weight = 1
|
||||
[languages.de]
|
||||
-- content/_index.de.md --
|
||||
+++
|
||||
[[cascade]]
|
||||
[cascade.params]
|
||||
foo = 'bar'
|
||||
[cascade._target]
|
||||
path = '/posts/post-1.de.md'
|
||||
+++
|
||||
-- content/posts/post-1.de.md --
|
||||
---
|
||||
title: "Post 1"
|
||||
---
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Title }}|{{ .Params.foo }}$
|
||||
`
|
||||
b, err := TestE(t, files)
|
||||
b.Assert(err, qt.IsNotNil)
|
||||
b.AssertLogContains(`cascade target path "/posts/post-1.de.md" looks like a path with an extension; since Hugo v0.123.0 this will not match anything, see https://gohugo.io/methods/page/path/`)
|
||||
}
|
||||
|
||||
+3
-4
@@ -157,7 +157,7 @@ module github.com/bep/mymod
|
||||
|
||||
tempDir := os.TempDir()
|
||||
cacheDir := filepath.Join(tempDir, "hugocache")
|
||||
if err := os.MkdirAll(cacheDir, 0777); err != nil {
|
||||
if err := os.MkdirAll(cacheDir, 0o777); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.Set("cacheDir", cacheDir)
|
||||
@@ -168,11 +168,11 @@ module github.com/bep/mymod
|
||||
|
||||
fs := afero.NewOsFs()
|
||||
|
||||
if err := afero.WriteFile(fs, filepath.Join(tempDir, "hugo.toml"), []byte(configToml), 0644); err != nil {
|
||||
if err := afero.WriteFile(fs, filepath.Join(tempDir, "hugo.toml"), []byte(configToml), 0o644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := afero.WriteFile(fs, filepath.Join(tempDir, "go.mod"), []byte(goMod), 0644); err != nil {
|
||||
if err := afero.WriteFile(fs, filepath.Join(tempDir, "go.mod"), []byte(goMod), 0o644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -181,5 +181,4 @@ module github.com/bep/mymod
|
||||
return nil, err
|
||||
}
|
||||
return conf.Base, err
|
||||
|
||||
}
|
||||
|
||||
+16
-99
@@ -48,15 +48,8 @@ title = "English Title"
|
||||
[languages.en.params.comments]
|
||||
title = "English Comments Title"
|
||||
|
||||
|
||||
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
enSite := b.H.Sites[0]
|
||||
b.Assert(enSite.Title(), qt.Equals, "English Title")
|
||||
@@ -97,14 +90,8 @@ weight = 2
|
||||
[languages.sv.params]
|
||||
myparam = "svParamValue"
|
||||
|
||||
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
enSite := b.H.Sites[0]
|
||||
svSite := b.H.Sites[1]
|
||||
@@ -157,12 +144,7 @@ baseURL = "https://example.com"
|
||||
[internal]
|
||||
running = true
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.Assert(b.H.Conf.Running(), qt.Equals, false)
|
||||
})
|
||||
@@ -236,12 +218,7 @@ p1: {{ .Site.Params.p1 }}|
|
||||
p2: {{ .Site.Params.p2 }}|
|
||||
sub: {{ .Site.Params.sub }}|
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/en/index.html", `
|
||||
title: English Title|
|
||||
@@ -987,12 +964,7 @@ params:
|
||||
mainSections: {{ site.Params.mainSections }}
|
||||
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", `
|
||||
mainSections: []
|
||||
@@ -1062,12 +1034,7 @@ Ein "Zitat" auf Deutsch.
|
||||
|
||||
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "p1: p1base", "<p>A “quote” in English.</p>")
|
||||
b.AssertFileContent("public/de/index.html", "p1: p1de", "<p>Ein «Zitat» auf Deutsch.</p>")
|
||||
@@ -1129,12 +1096,7 @@ HTACCESS.
|
||||
|
||||
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/.htaccess", "HTACCESS")
|
||||
}
|
||||
@@ -1150,12 +1112,7 @@ LanguageCode: {{ .Site.LanguageCode }}|{{ site.Language.LanguageCode }}|
|
||||
|
||||
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "LanguageCode: en-US|en-US|")
|
||||
}
|
||||
@@ -1181,12 +1138,7 @@ Home.
|
||||
|
||||
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "Home.")
|
||||
|
||||
@@ -1214,12 +1166,7 @@ Foo: {{ site.Params.foo }}|
|
||||
|
||||
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "Foo: |")
|
||||
})
|
||||
@@ -1295,12 +1242,7 @@ Home.
|
||||
|
||||
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.Assert(b.H.Configs.Base.Module.Mounts, qt.HasLen, 7)
|
||||
b.Assert(b.H.Configs.LanguageConfigSlice[0].Module.Mounts, qt.HasLen, 7)
|
||||
@@ -1321,12 +1263,7 @@ Foo.
|
||||
-- layouts/index.html --
|
||||
Home.
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/en/index.html", "Home.")
|
||||
b.AssertFileContent("public/en/foo/bar.txt", "Foo.")
|
||||
@@ -1354,12 +1291,7 @@ Foo.
|
||||
-- layouts/index.html --
|
||||
Home.
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/en/index.html", "Home.")
|
||||
b.AssertFileContent("public/en/foo/bar.txt", "Foo.")
|
||||
@@ -1387,12 +1319,7 @@ Foo.
|
||||
-- layouts/index.html --
|
||||
Home.
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "Home.")
|
||||
b.AssertFileContent("public/foo/bar.txt", "Foo.")
|
||||
@@ -1417,12 +1344,7 @@ Home.
|
||||
|
||||
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.Assert(len(b.H.Sites), qt.Equals, 1)
|
||||
}
|
||||
@@ -1557,12 +1479,7 @@ List.
|
||||
|
||||
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileExists("public/index.html", true)
|
||||
b.AssertFileExists("public/categories/c1/index.html", true)
|
||||
|
||||
@@ -38,12 +38,7 @@ c = "c1"
|
||||
-- layouts/index.html --
|
||||
Params: {{ site.Params}}
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", `
|
||||
Params: map[a:acp1 b:bc1 c:c1 d:dcp1]
|
||||
|
||||
@@ -166,11 +166,6 @@ func (m *pageMap) AddFi(fi hugofs.FileMetaInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
meta := fi.Meta()
|
||||
if m.s.conf.IsLangDisabled(meta.Lang) {
|
||||
return nil
|
||||
}
|
||||
|
||||
insertResource := func(fim hugofs.FileMetaInfo) error {
|
||||
pi := fi.Meta().PathInfo
|
||||
key := pi.Base()
|
||||
@@ -187,7 +182,7 @@ func (m *pageMap) AddFi(fi hugofs.FileMetaInfo) error {
|
||||
if pi.IsContent() {
|
||||
// Create the page now as we need it at assemembly time.
|
||||
// The other resources are created if needed.
|
||||
pageResource, err := m.s.h.newPage(
|
||||
pageResource, pi, err := m.s.h.newPage(
|
||||
&pageMeta{
|
||||
f: source.NewFileInfo(fim),
|
||||
pathInfo: pi,
|
||||
@@ -197,6 +192,8 @@ func (m *pageMap) AddFi(fi hugofs.FileMetaInfo) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key = pi.Base()
|
||||
|
||||
rs = &resourceSource{r: pageResource}
|
||||
} else {
|
||||
rs = &resourceSource{path: pi, opener: r, fi: fim}
|
||||
@@ -207,6 +204,7 @@ func (m *pageMap) AddFi(fi hugofs.FileMetaInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
meta := fi.Meta()
|
||||
pi := meta.PathInfo
|
||||
|
||||
switch pi.BundleType() {
|
||||
@@ -226,7 +224,7 @@ func (m *pageMap) AddFi(fi hugofs.FileMetaInfo) error {
|
||||
},
|
||||
))
|
||||
// A content file.
|
||||
p, err := m.s.h.newPage(
|
||||
p, pi, err := m.s.h.newPage(
|
||||
&pageMeta{
|
||||
f: source.NewFileInfo(fi),
|
||||
pathInfo: pi,
|
||||
|
||||
+91
-54
@@ -43,6 +43,7 @@ import (
|
||||
|
||||
"github.com/gohugoio/hugo/resources/kinds"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/gohugoio/hugo/resources/page/pagemeta"
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
)
|
||||
|
||||
@@ -97,7 +98,6 @@ type pageMap struct {
|
||||
cacheContentRendered *dynacache.Partition[string, *resources.StaleValue[contentSummary]]
|
||||
cacheContentPlain *dynacache.Partition[string, *resources.StaleValue[contentPlainPlainWords]]
|
||||
contentTableOfContents *dynacache.Partition[string, *resources.StaleValue[contentTableOfContents]]
|
||||
cacheContentSource *dynacache.Partition[string, *resources.StaleValue[[]byte]]
|
||||
|
||||
cfg contentMapConfig
|
||||
}
|
||||
@@ -127,7 +127,22 @@ type pageTrees struct {
|
||||
|
||||
// collectIdentities collects all identities from in all trees matching the given key.
|
||||
// This will at most match in one tree, but may give identies from multiple dimensions (e.g. language).
|
||||
func (t *pageTrees) collectIdentities(key string) []identity.Identity {
|
||||
func (t *pageTrees) collectIdentities(p *paths.Path) []identity.Identity {
|
||||
ids := t.collectIdentitiesFor(p.Base())
|
||||
|
||||
if p.Component() == files.ComponentFolderContent {
|
||||
// It may also be a bundled content resource.
|
||||
if n := t.treeResources.Get(p.ForBundleType(paths.PathTypeContentResource).Base()); n != nil {
|
||||
n.ForEeachIdentity(func(id identity.Identity) bool {
|
||||
ids = append(ids, id)
|
||||
return false
|
||||
})
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func (t *pageTrees) collectIdentitiesFor(key string) []identity.Identity {
|
||||
var ids []identity.Identity
|
||||
if n := t.treePages.Get(key); n != nil {
|
||||
n.ForEeachIdentity(func(id identity.Identity) bool {
|
||||
@@ -135,6 +150,7 @@ func (t *pageTrees) collectIdentities(key string) []identity.Identity {
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
if n := t.treeResources.Get(key); n != nil {
|
||||
n.ForEeachIdentity(func(id identity.Identity) bool {
|
||||
ids = append(ids, id)
|
||||
@@ -147,7 +163,6 @@ func (t *pageTrees) collectIdentities(key string) []identity.Identity {
|
||||
|
||||
// collectIdentitiesSurrounding collects all identities surrounding the given key.
|
||||
func (t *pageTrees) collectIdentitiesSurrounding(key string, maxSamplesPerTree int) []identity.Identity {
|
||||
// TODO1 test language coverage from this.
|
||||
ids := t.collectIdentitiesSurroundingIn(key, maxSamplesPerTree, t.treePages)
|
||||
ids = append(ids, t.collectIdentitiesSurroundingIn(key, maxSamplesPerTree, t.treeResources)...)
|
||||
return ids
|
||||
@@ -483,7 +498,7 @@ func (m *pageMap) getOrCreateResourcesForPage(ps *pageState) resource.Resources
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if translationKey := ps.m.translationKey; translationKey != "" {
|
||||
if translationKey := ps.m.pageConfig.TranslationKey; translationKey != "" {
|
||||
// This this should not be a very common case.
|
||||
// Merge in resources from the other languages.
|
||||
translatedPages, _ := m.s.h.translationKeyPages.Get(translationKey)
|
||||
@@ -539,9 +554,9 @@ func (m *pageMap) getOrCreateResourcesForPage(ps *pageState) resource.Resources
|
||||
|
||||
sort.SliceStable(res, lessFunc)
|
||||
|
||||
if len(ps.m.resourcesMetadata) > 0 {
|
||||
if len(ps.m.pageConfig.Resources) > 0 {
|
||||
for i, r := range res {
|
||||
res[i] = resources.CloneWithMetadataIfNeeded(ps.m.resourcesMetadata, r)
|
||||
res[i] = resources.CloneWithMetadataIfNeeded(ps.m.pageConfig.Resources, r)
|
||||
}
|
||||
sort.SliceStable(res, lessFunc)
|
||||
}
|
||||
@@ -819,7 +834,6 @@ func newPageMap(i int, s *Site, mcache *dynacache.Cache, pageTrees *pageTrees) *
|
||||
cacheContentRendered: dynacache.GetOrCreatePartition[string, *resources.StaleValue[contentSummary]](mcache, fmt.Sprintf("/cont/ren/%d", i), dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
|
||||
cacheContentPlain: dynacache.GetOrCreatePartition[string, *resources.StaleValue[contentPlainPlainWords]](mcache, fmt.Sprintf("/cont/pla/%d", i), dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
|
||||
contentTableOfContents: dynacache.GetOrCreatePartition[string, *resources.StaleValue[contentTableOfContents]](mcache, fmt.Sprintf("/cont/toc/%d", i), dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
|
||||
cacheContentSource: dynacache.GetOrCreatePartition[string, *resources.StaleValue[[]byte]](mcache, fmt.Sprintf("/cont/src/%d", i), dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}),
|
||||
|
||||
cfg: contentMapConfig{
|
||||
lang: s.Lang(),
|
||||
@@ -1020,14 +1034,6 @@ func (h *HugoSites) resolveAndClearStateForIdentities(
|
||||
b = cachebuster(s)
|
||||
}
|
||||
|
||||
if b {
|
||||
identity.WalkIdentitiesShallow(v, func(level int, id identity.Identity) bool {
|
||||
// Add them to the change set so we can reset any page that depends on them.
|
||||
changes = append(changes, id)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -1039,6 +1045,15 @@ func (h *HugoSites) resolveAndClearStateForIdentities(
|
||||
}
|
||||
}
|
||||
|
||||
// Drain the the cache eviction stack.
|
||||
evicted := h.Deps.MemCache.DrainEvictedIdentities()
|
||||
if len(evicted) < 200 {
|
||||
changes = append(changes, evicted...)
|
||||
} else {
|
||||
// Mass eviction, we might as well invalidate everything.
|
||||
changes = []identity.Identity{identity.GenghisKhan}
|
||||
}
|
||||
|
||||
// Remove duplicates
|
||||
seen := make(map[identity.Identity]bool)
|
||||
var n int
|
||||
@@ -1215,7 +1230,7 @@ func (sa *sitePagesAssembler) applyAggregates() error {
|
||||
// Home page gets it's cascade from the site config.
|
||||
cascade = sa.conf.Cascade.Config
|
||||
|
||||
if pageBundle.m.cascade == nil {
|
||||
if pageBundle.m.pageConfig.Cascade == nil {
|
||||
// Pass the site cascade downwards.
|
||||
pw.WalkContext.Data().Insert(keyPage, cascade)
|
||||
}
|
||||
@@ -1227,12 +1242,12 @@ func (sa *sitePagesAssembler) applyAggregates() error {
|
||||
}
|
||||
|
||||
if (pageBundle.IsHome() || pageBundle.IsSection()) && pageBundle.m.setMetaPostCount > 0 {
|
||||
oldDates := pageBundle.m.dates
|
||||
oldDates := pageBundle.m.pageConfig.Dates
|
||||
|
||||
// We need to wait until after the walk to determine if any of the dates have changed.
|
||||
pw.WalkContext.AddPostHook(
|
||||
func() error {
|
||||
if oldDates != pageBundle.m.dates {
|
||||
if oldDates != pageBundle.m.pageConfig.Dates {
|
||||
sa.assembleChanges.Add(pageBundle)
|
||||
}
|
||||
return nil
|
||||
@@ -1241,7 +1256,9 @@ func (sa *sitePagesAssembler) applyAggregates() error {
|
||||
}
|
||||
|
||||
// Combine the cascade map with front matter.
|
||||
pageBundle.setMetaPost(cascade)
|
||||
if err := pageBundle.setMetaPost(cascade); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// We receive cascade values from above. If this leads to a change compared
|
||||
// to the previous value, we need to mark the page and its dependencies as changed.
|
||||
@@ -1251,11 +1268,12 @@ func (sa *sitePagesAssembler) applyAggregates() error {
|
||||
|
||||
const eventName = "dates"
|
||||
if n.isContentNodeBranch() {
|
||||
if pageBundle.m.cascade != nil {
|
||||
if pageBundle.m.pageConfig.Cascade != nil {
|
||||
// Pass it down.
|
||||
pw.WalkContext.Data().Insert(keyPage, pageBundle.m.cascade)
|
||||
pw.WalkContext.Data().Insert(keyPage, pageBundle.m.pageConfig.Cascade)
|
||||
}
|
||||
wasZeroDates := resource.IsZeroDates(pageBundle.m.dates)
|
||||
|
||||
wasZeroDates := pageBundle.m.pageConfig.Dates.IsAllDatesZero()
|
||||
if wasZeroDates || pageBundle.IsHome() {
|
||||
pw.WalkContext.AddEventListener(eventName, keyPage, func(e *doctree.Event[contentNodeI]) {
|
||||
sp, ok := e.Source.(*pageState)
|
||||
@@ -1264,15 +1282,15 @@ func (sa *sitePagesAssembler) applyAggregates() error {
|
||||
}
|
||||
|
||||
if wasZeroDates {
|
||||
pageBundle.m.dates.UpdateDateAndLastmodIfAfter(sp.m.dates)
|
||||
pageBundle.m.pageConfig.Dates.UpdateDateAndLastmodIfAfter(sp.m.pageConfig.Dates)
|
||||
}
|
||||
|
||||
if pageBundle.IsHome() {
|
||||
if pageBundle.m.dates.Lastmod().After(pageBundle.s.lastmod) {
|
||||
pageBundle.s.lastmod = pageBundle.m.dates.Lastmod()
|
||||
if pageBundle.m.pageConfig.Dates.Lastmod.After(pageBundle.s.lastmod) {
|
||||
pageBundle.s.lastmod = pageBundle.m.pageConfig.Dates.Lastmod
|
||||
}
|
||||
if sp.m.dates.Lastmod().After(pageBundle.s.lastmod) {
|
||||
pageBundle.s.lastmod = sp.m.dates.Lastmod()
|
||||
if sp.m.pageConfig.Dates.Lastmod.After(pageBundle.s.lastmod) {
|
||||
pageBundle.s.lastmod = sp.m.pageConfig.Dates.Lastmod
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1305,7 +1323,9 @@ func (sa *sitePagesAssembler) applyAggregates() error {
|
||||
if data != nil {
|
||||
cascade = data.(map[page.PageMatcher]maps.Params)
|
||||
}
|
||||
pageResource.setMetaPost(cascade)
|
||||
if err := pageResource.setMetaPost(cascade); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
@@ -1351,9 +1371,9 @@ func (sa *sitePagesAssembler) applyAggregatesToTaxonomiesAndTerms() error {
|
||||
p := n.(*pageState)
|
||||
if p.Kind() != kinds.KindTerm {
|
||||
// The other kinds were handled in applyAggregates.
|
||||
if p.m.cascade != nil {
|
||||
if p.m.pageConfig.Cascade != nil {
|
||||
// Pass it down.
|
||||
pw.WalkContext.Data().Insert(s, p.m.cascade)
|
||||
pw.WalkContext.Data().Insert(s, p.m.pageConfig.Cascade)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1370,7 +1390,9 @@ func (sa *sitePagesAssembler) applyAggregatesToTaxonomiesAndTerms() error {
|
||||
if data != nil {
|
||||
cascade = data.(map[page.PageMatcher]maps.Params)
|
||||
}
|
||||
p.setMetaPost(cascade)
|
||||
if err := p.setMetaPost(cascade); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err := sa.pageMap.treeTaxonomyEntries.WalkPrefix(
|
||||
doctree.LockTypeRead,
|
||||
@@ -1388,14 +1410,14 @@ func (sa *sitePagesAssembler) applyAggregatesToTaxonomiesAndTerms() error {
|
||||
// Send the date info up the tree.
|
||||
pw.WalkContext.SendEvent(&doctree.Event[contentNodeI]{Source: n, Path: s, Name: eventName})
|
||||
|
||||
if resource.IsZeroDates(p.m.dates) {
|
||||
if p.m.pageConfig.Dates.IsAllDatesZero() {
|
||||
pw.WalkContext.AddEventListener(eventName, s, func(e *doctree.Event[contentNodeI]) {
|
||||
sp, ok := e.Source.(*pageState)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
p.m.dates.UpdateDateAndLastmodIfAfter(sp.m.dates)
|
||||
p.m.pageConfig.Dates.UpdateDateAndLastmodIfAfter(sp.m.pageConfig.Dates)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1443,8 +1465,8 @@ func (sa *sitePagesAssembler) assembleTermsAndTranslations() error {
|
||||
// This is a little out of place, but is conveniently put here.
|
||||
// Check if translationKey is set by user.
|
||||
// This is to support the manual way of setting the translationKey in front matter.
|
||||
if ps.m.translationKey != "" {
|
||||
sa.s.h.translationKeyPages.Append(ps.m.translationKey, ps)
|
||||
if ps.m.pageConfig.TranslationKey != "" {
|
||||
sa.s.h.translationKeyPages.Append(ps.m.pageConfig.TranslationKey, ps)
|
||||
}
|
||||
|
||||
if sa.pageMap.cfg.taxonomyTermDisabled {
|
||||
@@ -1477,9 +1499,13 @@ func (sa *sitePagesAssembler) assembleTermsAndTranslations() error {
|
||||
singular: viewName.singular,
|
||||
s: sa.Site,
|
||||
pathInfo: pi,
|
||||
kind: kinds.KindTerm,
|
||||
pageMetaParams: pageMetaParams{
|
||||
pageConfig: &pagemeta.PageConfig{
|
||||
Kind: kinds.KindTerm,
|
||||
},
|
||||
},
|
||||
}
|
||||
n, err := sa.h.newPage(m)
|
||||
n, pi, err := sa.h.newPage(m)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -1524,7 +1550,7 @@ func (sa *sitePagesAssembler) assembleResources() error {
|
||||
targetPaths := ps.targetPaths()
|
||||
baseTarget := targetPaths.SubResourceBaseTarget
|
||||
duplicateResourceFiles := true
|
||||
if ps.s.ContentSpec.Converters.IsGoldmark(ps.m.markup) {
|
||||
if ps.s.ContentSpec.Converters.IsGoldmark(ps.m.pageConfig.Markup) {
|
||||
duplicateResourceFiles = ps.s.ContentSpec.Converters.GetMarkupConfig().Goldmark.DuplicateResourceFiles
|
||||
}
|
||||
|
||||
@@ -1545,7 +1571,7 @@ func (sa *sitePagesAssembler) assembleResources() error {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
relPathOriginal := rs.path.PathRel(ps.m.pathInfo)
|
||||
relPathOriginal := rs.path.Unnormalized().PathRel(ps.m.pathInfo.Unnormalized())
|
||||
relPath := rs.path.BaseRel(ps.m.pathInfo)
|
||||
|
||||
var targetBasePaths []string
|
||||
@@ -1566,7 +1592,7 @@ func (sa *sitePagesAssembler) assembleResources() error {
|
||||
BasePathTargetPath: baseTarget,
|
||||
Name: relPath,
|
||||
NameOriginal: relPathOriginal,
|
||||
LazyPublish: !ps.m.buildConfig.PublishResources,
|
||||
LazyPublish: !ps.m.pageConfig.Build.PublishResources,
|
||||
}
|
||||
r, err := ps.m.s.ResourceSpec.NewResource(rd)
|
||||
if err != nil {
|
||||
@@ -1631,7 +1657,7 @@ func (sa *sitePagesAssembler) removeShouldNotBuild() error {
|
||||
case kinds.KindHome, kinds.KindSection, kinds.KindTaxonomy:
|
||||
// We need to keep these for the structure, but disable
|
||||
// them so they don't get listed/rendered.
|
||||
(&p.m.buildConfig).Disable()
|
||||
(&p.m.pageConfig.Build).Disable()
|
||||
default:
|
||||
keys = append(keys, key)
|
||||
}
|
||||
@@ -1673,13 +1699,17 @@ func (sa *sitePagesAssembler) addStandalonePages() error {
|
||||
}
|
||||
|
||||
m := &pageMeta{
|
||||
s: s,
|
||||
pathInfo: s.Conf.PathParser().Parse(files.ComponentFolderContent, key+f.MediaType.FirstSuffix.FullSuffix),
|
||||
kind: kind,
|
||||
s: s,
|
||||
pathInfo: s.Conf.PathParser().Parse(files.ComponentFolderContent, key+f.MediaType.FirstSuffix.FullSuffix),
|
||||
pageMetaParams: pageMetaParams{
|
||||
pageConfig: &pagemeta.PageConfig{
|
||||
Kind: kind,
|
||||
},
|
||||
},
|
||||
standaloneOutputFormat: f,
|
||||
}
|
||||
|
||||
p, _ := s.h.newPage(m)
|
||||
p, _, _ := s.h.newPage(m)
|
||||
|
||||
tree.InsertIntoValuesDimension(key, p)
|
||||
}
|
||||
@@ -1746,7 +1776,7 @@ func (sa *sitePagesAssembler) addMissingRootSections() error {
|
||||
seen[section] = true
|
||||
|
||||
// Try to preserve the original casing if possible.
|
||||
sectionUnnormalized := p.Unmormalized().Section()
|
||||
sectionUnnormalized := p.Unnormalized().Section()
|
||||
pth := sa.s.Conf.PathParser().Parse(files.ComponentFolderContent, "/"+sectionUnnormalized+"/_index.md")
|
||||
nn := w.Tree.Get(pth.Base())
|
||||
|
||||
@@ -1756,7 +1786,7 @@ func (sa *sitePagesAssembler) addMissingRootSections() error {
|
||||
pathInfo: pth,
|
||||
}
|
||||
|
||||
ps, err := sa.h.newPage(m)
|
||||
ps, pth, err := sa.h.newPage(m)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -1781,9 +1811,13 @@ func (sa *sitePagesAssembler) addMissingRootSections() error {
|
||||
m := &pageMeta{
|
||||
s: sa.Site,
|
||||
pathInfo: p,
|
||||
kind: kinds.KindHome,
|
||||
pageMetaParams: pageMetaParams{
|
||||
pageConfig: &pagemeta.PageConfig{
|
||||
Kind: kinds.KindHome,
|
||||
},
|
||||
},
|
||||
}
|
||||
n, err := sa.h.newPage(m)
|
||||
n, p, err := sa.h.newPage(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1810,10 +1844,14 @@ func (sa *sitePagesAssembler) addMissingTaxonomies() error {
|
||||
m := &pageMeta{
|
||||
s: sa.Site,
|
||||
pathInfo: sa.Conf.PathParser().Parse(files.ComponentFolderContent, key+"/_index.md"),
|
||||
kind: kinds.KindTaxonomy,
|
||||
pageMetaParams: pageMetaParams{
|
||||
pageConfig: &pagemeta.PageConfig{
|
||||
Kind: kinds.KindTaxonomy,
|
||||
},
|
||||
},
|
||||
singular: viewName.singular,
|
||||
}
|
||||
p, _ := sa.h.newPage(m)
|
||||
p, _, _ := sa.h.newPage(m)
|
||||
tree.InsertIntoValuesDimension(key, p)
|
||||
}
|
||||
}
|
||||
@@ -1837,13 +1875,12 @@ func (m *pageMap) CreateSiteTaxonomies(ctx context.Context) error {
|
||||
LockType: doctree.LockTypeRead,
|
||||
Handle: func(s string, n contentNodeI, match doctree.DimensionFlag) (bool, error) {
|
||||
p := n.(*pageState)
|
||||
plural := p.Section()
|
||||
|
||||
switch p.Kind() {
|
||||
case kinds.KindTerm:
|
||||
taxonomy := m.s.taxonomies[plural]
|
||||
taxonomy := m.s.taxonomies[viewName.plural]
|
||||
if taxonomy == nil {
|
||||
return true, fmt.Errorf("missing taxonomy: %s", plural)
|
||||
return true, fmt.Errorf("missing taxonomy: %s", viewName.plural)
|
||||
}
|
||||
k := strings.ToLower(p.m.term)
|
||||
err := m.treeTaxonomyEntries.WalkPrefix(
|
||||
|
||||
@@ -280,3 +280,49 @@ P1: {{ $p1.Title }}|{{ $p1.Params.foo }}|{{ $p1.File.Filename }}|
|
||||
filepath.FromSlash("P1: P1 md|md|/content/p1.md|"),
|
||||
)
|
||||
}
|
||||
|
||||
// Issue #11944
|
||||
func TestBundleResourcesGetWithSpacesInFilename(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
disableKinds = ["taxonomy", "term"]
|
||||
-- content/bundle/index.md --
|
||||
-- content/bundle/data with Spaces.txt --
|
||||
Data.
|
||||
-- layouts/index.html --
|
||||
{{ $bundle := site.GetPage "bundle" }}
|
||||
{{ $r := $bundle.Resources.Get "data with Spaces.txt" }}
|
||||
R: {{ with $r }}{{ .Content }}{{ end }}|
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "R: Data.")
|
||||
}
|
||||
|
||||
// Issue #11946.
|
||||
func TestBundleResourcesGetDuplicateSortOrder(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
-- content/bundle/index.md --
|
||||
-- content/bundle/data-1.txt --
|
||||
data-1.txt
|
||||
-- content/bundle/data 1.txt --
|
||||
data 1.txt
|
||||
-- content/bundle/Data 1.txt --
|
||||
Data 1.txt
|
||||
-- content/bundle/Data-1.txt --
|
||||
Data-1.txt
|
||||
-- layouts/index.html --
|
||||
{{ $bundle := site.GetPage "bundle" }}
|
||||
{{ $r := $bundle.Resources.Get "data-1.txt" }}
|
||||
R: {{ with $r }}{{ .Content }}{{ end }}|Len: {{ len $bundle.Resources }}|$
|
||||
|
||||
`
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
b := Test(t, files)
|
||||
b.AssertFileContent("public/index.html", "R: Data 1.txt|", "Len: 1|")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
package hugolib
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -63,12 +64,7 @@ outputs: ["rss"]
|
||||
---
|
||||
P3. [I'm an inline-style link](https://www.example.org)
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", `
|
||||
P1: <p>P1. html-link: https://www.gohugo.io|</p>
|
||||
@@ -163,12 +159,7 @@ P1 Fragments: {{ .Fragments.Identifiers }}|
|
||||
{{ .Content}}
|
||||
`
|
||||
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", `
|
||||
Self Fragments: [b c z]
|
||||
@@ -179,3 +170,74 @@ Self Fragments: [d e f]
|
||||
P1 Fragments: [b c z]
|
||||
`)
|
||||
}
|
||||
|
||||
func TestDefaultRenderHooksMultilingual(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.org"
|
||||
disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT"]
|
||||
defaultContentLanguage = "nn"
|
||||
defaultContentLanguageInSubdir = true
|
||||
[markup]
|
||||
[markup.goldmark]
|
||||
duplicateResourceFiles = false
|
||||
[markup.goldmark.renderhooks]
|
||||
[markup.goldmark.renderhooks.link]
|
||||
#enableDefault = false
|
||||
[markup.goldmark.renderhooks.image]
|
||||
#enableDefault = false
|
||||
[languages]
|
||||
[languages.en]
|
||||
weight = 1
|
||||
[languages.nn]
|
||||
weight = 2
|
||||
-- content/p1/index.md --
|
||||
---
|
||||
title: "p1"
|
||||
---
|
||||
[P2](p2)
|
||||

|
||||
-- content/p2/index.md --
|
||||
---
|
||||
title: "p2"
|
||||
---
|
||||
[P1](p1)
|
||||

|
||||
-- content/p1/index.en.md --
|
||||
---
|
||||
title: "p1 en"
|
||||
---
|
||||
[P2](p2)
|
||||

|
||||
-- content/p2/index.en.md --
|
||||
---
|
||||
title: "p2 en"
|
||||
---
|
||||
[P1](p1)
|
||||

|
||||
|
||||
-- content/p1/pixel.nn.png --
|
||||
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
-- content/p2/pixel.png --
|
||||
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Title }}|{{ .Content }}|$
|
||||
|
||||
`
|
||||
|
||||
t.Run("Default multilingual", func(t *testing.T) {
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/nn/p1/index.html",
|
||||
"p1|<p><a href=\"/nn/p2/\">P2</a\n></p>", "<img alt=\"Pixel\" src=\"/nn/p1/pixel.nn.png\">")
|
||||
b.AssertFileContent("public/en/p1/index.html",
|
||||
"p1 en|<p><a href=\"/en/p2/\">P2</a\n></p>", "<img alt=\"Pixel\" src=\"/nn/p1/pixel.nn.png\">")
|
||||
})
|
||||
|
||||
t.Run("Disabled", func(t *testing.T) {
|
||||
b := Test(t, strings.ReplaceAll(files, "#enableDefault = false", "enableDefault = false"))
|
||||
|
||||
b.AssertFileContent("public/nn/p1/index.html",
|
||||
"p1|<p><a href=\"p2\">P2</a>", "<img src=\"pixel.png\" alt=\"Pixel\">")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
)
|
||||
|
||||
func TestData(t *testing.T) {
|
||||
|
||||
t.Run("with theme", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -43,14 +42,25 @@ b: {{ site.Data.b.v1 }}|
|
||||
cd: {{ site.Data.c.d.v1 }}|
|
||||
d: {{ site.Data.d.v1 }}|
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "a: a_v1|\nb: b_v1|\ncd: c_d_v1|\nd: d_v1_theme|")
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestDataMixedCaseFolders(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
-- data/MyFolder/MyData.toml --
|
||||
v1 = "my_v1"
|
||||
-- layouts/index.html --
|
||||
{{ site.Data }}
|
||||
v1: {{ site.Data.MyFolder.MyData.v1 }}|
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "v1: my_v1|")
|
||||
}
|
||||
|
||||
@@ -255,12 +255,7 @@ mydata.date: {{ site.Data.mydata.date }}
|
||||
Full time: {{ $p1Date | time.Format ":time_full" }}
|
||||
`
|
||||
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", `
|
||||
Future talks: 2
|
||||
|
||||
@@ -416,3 +416,50 @@ Section: MySection|RelPermalink: |Outputs: 0
|
||||
b.Assert(b.CheckExists("public/sect/no-render/index.html"), qt.Equals, false)
|
||||
b.Assert(b.CheckExists("public/sect-no-render/index.html"), qt.Equals, false)
|
||||
}
|
||||
|
||||
func TestDisableOneOfThreeLanguages(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
defaultContentLanguage = "en"
|
||||
defaultContentLanguageInSubdir = true
|
||||
[languages]
|
||||
[languages.en]
|
||||
weight = 1
|
||||
title = "English"
|
||||
[languages.nn]
|
||||
weight = 2
|
||||
title = "Nynorsk"
|
||||
disabled = true
|
||||
[languages.nb]
|
||||
weight = 3
|
||||
title = "Bokmål"
|
||||
-- content/p1.nn.md --
|
||||
---
|
||||
title: "Page 1 nn"
|
||||
---
|
||||
-- content/p1.nb.md --
|
||||
---
|
||||
title: "Page 1 nb"
|
||||
---
|
||||
-- content/p1.en.md --
|
||||
---
|
||||
title: "Page 1 en"
|
||||
---
|
||||
-- content/p2.nn.md --
|
||||
---
|
||||
title: "Page 2 nn"
|
||||
---
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Title }}
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
b.Assert(len(b.H.Sites), qt.Equals, 2)
|
||||
b.AssertFileContent("public/en/p1/index.html", "Page 1 en")
|
||||
b.AssertFileContent("public/nb/p1/index.html", "Page 1 nb")
|
||||
|
||||
b.AssertFileExists("public/en/p2/index.html", false)
|
||||
b.AssertFileExists("public/nn/p1/index.html", false)
|
||||
b.AssertFileExists("public/nn/p2/index.html", false)
|
||||
}
|
||||
|
||||
@@ -76,12 +76,7 @@ Foo: {{< param foo >}}
|
||||
-- layouts/index.html --
|
||||
Content: {{ .Content }}|
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", `
|
||||
<figure>
|
||||
@@ -94,6 +89,5 @@ Foo: bar
|
||||
|
||||
|
||||
`)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -657,30 +657,24 @@ min_version = 0.55.0
|
||||
func TestMountsProject(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
config := `
|
||||
|
||||
files := `
|
||||
-- config.toml --
|
||||
baseURL="https://example.org"
|
||||
|
||||
[module]
|
||||
[[module.mounts]]
|
||||
source="mycontent"
|
||||
target="content"
|
||||
|
||||
`
|
||||
b := newTestSitesBuilder(t).
|
||||
WithConfigFile("toml", config).
|
||||
WithSourceFile(filepath.Join("mycontent", "mypage.md"), `
|
||||
-- layouts/_default/single.html --
|
||||
Permalink: {{ .Permalink }}|
|
||||
-- mycontent/mypage.md --
|
||||
---
|
||||
title: "My Page"
|
||||
---
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
`)
|
||||
|
||||
b.Build(BuildCfg{})
|
||||
|
||||
// helpers.PrintFs(b.H.Fs.Source, "public", os.Stdout)
|
||||
|
||||
b.AssertFileContent("public/mypage/index.html", "Permalink: https://example.org/mypage/")
|
||||
b.AssertFileContent("public/mypage/index.html", "Permalink: https://example.org/mypage/|")
|
||||
}
|
||||
|
||||
// https://github.com/gohugoio/hugo/issues/6684
|
||||
@@ -706,25 +700,20 @@ Home: {{ .Title }}|{{ .Content }}|
|
||||
func TestSiteWithGoModButNoModules(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
c := qt.New(t)
|
||||
// We need to use the OS fs for this.
|
||||
workDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-no-mod")
|
||||
c.Assert(err, qt.IsNil)
|
||||
tempDir := t.TempDir()
|
||||
|
||||
cfg := config.New()
|
||||
cfg.Set("workingDir", workDir)
|
||||
cfg.Set("publishDir", "public")
|
||||
fs := hugofs.NewFromOld(hugofs.Os, cfg)
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.org"
|
||||
-- go.mod --
|
||||
|
||||
defer clean()
|
||||
`
|
||||
|
||||
b := newTestSitesBuilder(t)
|
||||
b.Fs = fs
|
||||
b := Test(t, files, TestOptWithConfig(func(cfg *IntegrationTestConfig) {
|
||||
cfg.WorkingDir = tempDir
|
||||
}))
|
||||
|
||||
b.WithWorkingDir(workDir).WithViper(cfg)
|
||||
|
||||
b.WithSourceFile("go.mod", "")
|
||||
b.Build(BuildCfg{})
|
||||
b.Build()
|
||||
}
|
||||
|
||||
// https://github.com/gohugoio/hugo/issues/6622
|
||||
@@ -783,7 +772,9 @@ P1: {{ $p1.Title }}|{{ $p1.RelPermalink }}|Filename: {{ $p1.File.Filename }}
|
||||
|
||||
// Issue 9426
|
||||
func TestMountSameSource(t *testing.T) {
|
||||
config := `baseURL = 'https://example.org/'
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = 'https://example.org/'
|
||||
languageCode = 'en-us'
|
||||
title = 'Hugo GitHub Issue #9426'
|
||||
|
||||
@@ -800,18 +791,15 @@ target = "content/resources-a"
|
||||
[[module.mounts]]
|
||||
source = "extra-content"
|
||||
target = "content/resources-b"
|
||||
-- layouts/_default/single.html --
|
||||
Single
|
||||
-- content/p1.md --
|
||||
-- extra-content/_index.md --
|
||||
-- extra-content/subdir/_index.md --
|
||||
-- extra-content/subdir/about.md --
|
||||
"
|
||||
`
|
||||
b := newTestSitesBuilder(t).WithConfigFile("toml", config)
|
||||
|
||||
b.WithContent("p1.md", "")
|
||||
|
||||
b.WithSourceFile(
|
||||
"extra-content/_index.md", "",
|
||||
"extra-content/subdir/_index.md", "",
|
||||
"extra-content/subdir/about.md", "",
|
||||
)
|
||||
|
||||
b.Build(BuildCfg{})
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/resources-a/subdir/about/index.html", "Single")
|
||||
b.AssertFileContent("public/resources-b/subdir/about/index.html", "Single")
|
||||
@@ -836,12 +824,7 @@ message: Hugo Rocks
|
||||
{{ site.Data.extra.test.message }}
|
||||
`
|
||||
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "Hugo Rocks")
|
||||
}
|
||||
|
||||
+10
-5
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/gohugoio/hugo/config/allconfig"
|
||||
"github.com/gohugoio/hugo/hugofs/glob"
|
||||
"github.com/gohugoio/hugo/hugolib/doctree"
|
||||
"github.com/gohugoio/hugo/resources"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
|
||||
@@ -72,6 +73,8 @@ type HugoSites struct {
|
||||
|
||||
// Cache for page listings.
|
||||
cachePages *dynacache.Partition[string, page.Pages]
|
||||
// Cache for content sources.
|
||||
cacheContentSource *dynacache.Partition[string, *resources.StaleValue[[]byte]]
|
||||
|
||||
// Before Hugo 0.122.0 we managed all translations in a map using a translationKey
|
||||
// that could be overridden in front matter.
|
||||
@@ -96,6 +99,8 @@ type HugoSites struct {
|
||||
|
||||
*fatalErrorHandler
|
||||
*buildCounters
|
||||
// Tracks invocations of the Build method.
|
||||
buildCounter atomic.Uint64
|
||||
}
|
||||
|
||||
// ShouldSkipFileChangeEvent allows skipping filesystem event early before
|
||||
@@ -417,10 +422,9 @@ func (cfg *BuildCfg) shouldRender(p *pageState) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
fastRenderMode := cfg.RecentlyVisited.Len() > 0
|
||||
fastRenderMode := p.s.Conf.FastRenderMode()
|
||||
|
||||
if !fastRenderMode {
|
||||
// Not in fast render mode or first time render.
|
||||
if !fastRenderMode || p.s.h.buildCounter.Load() == 0 {
|
||||
return shouldRender
|
||||
}
|
||||
|
||||
@@ -469,7 +473,8 @@ func (h *HugoSites) loadData() error {
|
||||
h.data = make(map[string]any)
|
||||
w := hugofs.NewWalkway(
|
||||
hugofs.WalkwayConfig{
|
||||
Fs: h.PathSpec.BaseFs.Data.Fs,
|
||||
Fs: h.PathSpec.BaseFs.Data.Fs,
|
||||
IgnoreFile: h.SourceSpec.IgnoreFile,
|
||||
WalkFn: func(path string, fi hugofs.FileMetaInfo) error {
|
||||
if fi.IsDir() {
|
||||
return nil
|
||||
@@ -499,7 +504,7 @@ func (h *HugoSites) handleDataFile(r *source.File) error {
|
||||
|
||||
// Crawl in data tree to insert data
|
||||
current = h.data
|
||||
dataPath := r.FileInfo().Meta().PathInfo.Dir()[1:]
|
||||
dataPath := r.FileInfo().Meta().PathInfo.Unnormalized().Dir()[1:]
|
||||
keyParts := strings.Split(dataPath, "/")
|
||||
|
||||
for _, key := range keyParts {
|
||||
|
||||
@@ -57,6 +57,9 @@ import (
|
||||
func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
|
||||
infol := h.Log.InfoCommand("build")
|
||||
defer loggers.TimeTrackf(infol, time.Now(), nil, "")
|
||||
defer func() {
|
||||
h.buildCounter.Add(1)
|
||||
}()
|
||||
|
||||
if h.Deps == nil {
|
||||
panic("must have deps")
|
||||
@@ -699,9 +702,7 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
|
||||
switch pathInfo.Component() {
|
||||
case files.ComponentFolderContent:
|
||||
logger.Println("Source changed", pathInfo.Path())
|
||||
base := pathInfo.Base()
|
||||
|
||||
if ids := h.pageTrees.collectIdentities(base); len(ids) > 0 {
|
||||
if ids := h.pageTrees.collectIdentities(pathInfo); len(ids) > 0 {
|
||||
changes = append(changes, ids...)
|
||||
}
|
||||
|
||||
@@ -723,6 +724,7 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
|
||||
_, ok := h.pageTrees.treePages.LongestPrefixAll(pathInfo.Base())
|
||||
if ok {
|
||||
h.pageTrees.treePages.DeleteAll(pathInfo.Base())
|
||||
h.pageTrees.resourceTrees.DeleteAll(pathInfo.Base())
|
||||
if pathInfo.IsBundle() {
|
||||
// Assume directory removed.
|
||||
h.pageTrees.treePages.DeletePrefixAll(pathInfo.Base() + "/")
|
||||
@@ -767,8 +769,9 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
|
||||
}
|
||||
case files.ComponentFolderAssets:
|
||||
logger.Println("Asset changed", pathInfo.Path())
|
||||
r, _ := h.ResourceSpec.ResourceCache.Get(context.Background(), dynacache.CleanKey(pathInfo.Base()))
|
||||
|
||||
var hasID bool
|
||||
r, _ := h.ResourceSpec.ResourceCache.Get(context.Background(), dynacache.CleanKey(pathInfo.Base()))
|
||||
identity.WalkIdentitiesShallow(r, func(level int, rid identity.Identity) bool {
|
||||
hasID = true
|
||||
changes = append(changes, rid)
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -57,6 +58,13 @@ func TestOptDebug() TestOpt {
|
||||
}
|
||||
}
|
||||
|
||||
// TestOptWarn will enable warn logging in integration tests.
|
||||
func TestOptWarn() TestOpt {
|
||||
return func(c *IntegrationTestConfig) {
|
||||
c.LogLevel = logg.LevelWarn
|
||||
}
|
||||
}
|
||||
|
||||
// TestOptWithNFDOnDarwin will normalize the Unicode filenames to NFD on Darwin.
|
||||
func TestOptWithNFDOnDarwin() TestOpt {
|
||||
return func(c *IntegrationTestConfig) {
|
||||
@@ -80,6 +88,15 @@ func Test(t testing.TB, files string, opts ...TestOpt) *IntegrationTestBuilder {
|
||||
return NewIntegrationTestBuilder(cfg).Build()
|
||||
}
|
||||
|
||||
// TestE is the same as Test, but returns an error instead of failing the test.
|
||||
func TestE(t testing.TB, files string, opts ...TestOpt) (*IntegrationTestBuilder, error) {
|
||||
cfg := IntegrationTestConfig{T: t, TxtarString: files}
|
||||
for _, o := range opts {
|
||||
o(&cfg)
|
||||
}
|
||||
return NewIntegrationTestBuilder(cfg).BuildE()
|
||||
}
|
||||
|
||||
// TestRunning is a convenience method to create a new IntegrationTestBuilder from some files with Running set to true and run a build.
|
||||
// Deprecated: Use Test with TestOptRunning instead.
|
||||
func TestRunning(t testing.TB, files string, opts ...TestOpt) *IntegrationTestBuilder {
|
||||
@@ -90,6 +107,7 @@ func TestRunning(t testing.TB, files string, opts ...TestOpt) *IntegrationTestBu
|
||||
return NewIntegrationTestBuilder(cfg).Build()
|
||||
}
|
||||
|
||||
// In most cases you should not use this function directly, but the Test or TestRunning function.
|
||||
func NewIntegrationTestBuilder(conf IntegrationTestConfig) *IntegrationTestBuilder {
|
||||
// Code fences.
|
||||
conf.TxtarString = strings.ReplaceAll(conf.TxtarString, "§§§", "```")
|
||||
@@ -171,9 +189,18 @@ func (b *lockingBuffer) Write(p []byte) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (s *IntegrationTestBuilder) AssertLogContains(text string) {
|
||||
func (s *IntegrationTestBuilder) AssertLogContains(els ...string) {
|
||||
s.Helper()
|
||||
s.Assert(s.logBuff.String(), qt.Contains, text)
|
||||
for _, el := range els {
|
||||
s.Assert(s.logBuff.String(), qt.Contains, el)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IntegrationTestBuilder) AssertLogNotContains(els ...string) {
|
||||
s.Helper()
|
||||
for _, el := range els {
|
||||
s.Assert(s.logBuff.String(), qt.Not(qt.Contains), el)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IntegrationTestBuilder) AssertLogMatches(expression string) {
|
||||
@@ -247,6 +274,32 @@ func (s *IntegrationTestBuilder) AssertFileContentExact(filename string, matches
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IntegrationTestBuilder) AssertPublishDir(matches ...string) {
|
||||
s.Helper()
|
||||
var buff bytes.Buffer
|
||||
helpers.PrintFs(s.H.Fs.PublishDir, "", &buff)
|
||||
printFsLines := strings.Split(buff.String(), "\n")
|
||||
sort.Strings(printFsLines)
|
||||
content := strings.TrimSpace((strings.Join(printFsLines, "\n")))
|
||||
for _, m := range matches {
|
||||
cm := qt.Commentf("Match: %q\nIn:\n%s", m, content)
|
||||
lines := strings.Split(m, "\n")
|
||||
for _, match := range lines {
|
||||
match = strings.TrimSpace(match)
|
||||
var negate bool
|
||||
if strings.HasPrefix(match, "! ") {
|
||||
negate = true
|
||||
match = strings.TrimPrefix(match, "! ")
|
||||
}
|
||||
if negate {
|
||||
s.Assert(content, qt.Not(qt.Contains), match, cm)
|
||||
continue
|
||||
}
|
||||
s.Assert(content, qt.Contains, match, cm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IntegrationTestBuilder) AssertFileExists(filename string, b bool) {
|
||||
checker := qt.IsNil
|
||||
if !b {
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
func TestI18n(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
//https://github.com/gohugoio/hugo/issues/7804
|
||||
// https://github.com/gohugoio/hugo/issues/7804
|
||||
c.Run("pt-br should be case insensitive", func(c *qt.C) {
|
||||
b := newTestSitesBuilder(c)
|
||||
langCode := func() string {
|
||||
@@ -76,12 +76,10 @@ name = "foo-a"
|
||||
|
||||
menus := b.H.Sites[0].Menus()
|
||||
c.Assert(menus, qt.HasLen, 1)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestLanguageNumberFormatting(t *testing.T) {
|
||||
|
||||
b := newTestSitesBuilder(t)
|
||||
b.WithConfigFile("toml", `
|
||||
baseURL = "https://example.org"
|
||||
@@ -137,3 +135,14 @@ FormatNumberCustom: 12,345.68
|
||||
NumFmt: -98,765.43
|
||||
`)
|
||||
}
|
||||
|
||||
// Issue 11993.
|
||||
func TestI18nDotFile(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --{}
|
||||
baseURL = "https://example.com"
|
||||
-- i18n/.keep --
|
||||
-- data/.keep --
|
||||
`
|
||||
Test(t, files)
|
||||
}
|
||||
|
||||
+3
-18
@@ -571,12 +571,7 @@ Page IsAncestor Self: {{ $page.IsAncestor $page }}
|
||||
Page IsDescendant Self: {{ $page.IsDescendant $page}}
|
||||
`
|
||||
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/tests/index.html", `
|
||||
Tests|/tests/|IsMenuCurrent = true|HasMenuCurrent = false
|
||||
@@ -609,12 +604,7 @@ Menu Item: {{ $i }}: {{ .Pre }}{{ .Name }}{{ .Post }}|{{ .URL }}|
|
||||
{{ end }}
|
||||
`
|
||||
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", `
|
||||
Menu Item: 0: <span>Home</span>|/|
|
||||
@@ -640,12 +630,7 @@ Menu Item: {{ $i }}|{{ .URL }}|
|
||||
{{ end }}
|
||||
`
|
||||
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", `
|
||||
Menu Item: 0|/foo/posts|
|
||||
|
||||
+25
-16
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019 The Hugo Authors. All rights reserved.
|
||||
// Copyright 2024 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/gohugoio/hugo/output"
|
||||
"github.com/gohugoio/hugo/output/layouts"
|
||||
"github.com/gohugoio/hugo/related"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/gohugoio/hugo/markup/converter"
|
||||
"github.com/gohugoio/hugo/markup/tableofcontents"
|
||||
@@ -197,7 +198,7 @@ func (p *pageHeadingsFiltered) page() page.Page {
|
||||
|
||||
// For internal use by the related content feature.
|
||||
func (p *pageState) ApplyFilterToHeadings(ctx context.Context, fn func(*tableofcontents.Heading) bool) related.Document {
|
||||
r, err := p.content.contentToC(ctx, p.pageOutput.pco)
|
||||
r, err := p.m.content.contentToC(ctx, p.pageOutput.pco)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -313,14 +314,14 @@ func (p *pageState) Pages() page.Pages {
|
||||
// RawContent returns the un-rendered source content without
|
||||
// any leading front matter.
|
||||
func (p *pageState) RawContent() string {
|
||||
if p.content.parseInfo.itemsStep2 == nil {
|
||||
if p.m.content.pi.itemsStep2 == nil {
|
||||
return ""
|
||||
}
|
||||
start := p.content.parseInfo.posMainContent
|
||||
start := p.m.content.pi.posMainContent
|
||||
if start == -1 {
|
||||
start = 0
|
||||
}
|
||||
source, err := p.content.contentSource()
|
||||
source, err := p.m.content.pi.contentSource(p.m.content)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -332,11 +333,11 @@ func (p *pageState) Resources() resource.Resources {
|
||||
}
|
||||
|
||||
func (p *pageState) HasShortcode(name string) bool {
|
||||
if p.content.shortcodeState == nil {
|
||||
if p.m.content.shortcodeState == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return p.content.shortcodeState.hasName(name)
|
||||
return p.m.content.shortcodeState.hasName(name)
|
||||
}
|
||||
|
||||
func (p *pageState) Site() page.Site {
|
||||
@@ -355,8 +356,8 @@ func (p *pageState) IsTranslated() bool {
|
||||
|
||||
// TranslationKey returns the key used to identify a translation of this content.
|
||||
func (p *pageState) TranslationKey() string {
|
||||
if p.m.translationKey != "" {
|
||||
return p.m.translationKey
|
||||
if p.m.pageConfig.TranslationKey != "" {
|
||||
return p.m.pageConfig.TranslationKey
|
||||
}
|
||||
return p.Path()
|
||||
}
|
||||
@@ -365,9 +366,9 @@ func (p *pageState) TranslationKey() string {
|
||||
func (p *pageState) AllTranslations() page.Pages {
|
||||
key := p.Path() + "/" + "translations-all"
|
||||
pages, err := p.s.pageMap.getOrCreatePagesFromCache(key, func(string) (page.Pages, error) {
|
||||
if p.m.translationKey != "" {
|
||||
if p.m.pageConfig.TranslationKey != "" {
|
||||
// translationKey set by user.
|
||||
pas, _ := p.s.h.translationKeyPages.Get(p.m.translationKey)
|
||||
pas, _ := p.s.h.translationKeyPages.Get(p.m.pageConfig.TranslationKey)
|
||||
pasc := make(page.Pages, len(pas))
|
||||
copy(pasc, pas)
|
||||
page.SortByLanguage(pasc)
|
||||
@@ -534,7 +535,7 @@ var defaultRenderStringOpts = renderStringOpts{
|
||||
Markup: "", // Will inherit the page's value when not set.
|
||||
}
|
||||
|
||||
func (p *pageMeta) wrapError(err error) error {
|
||||
func (p *pageMeta) wrapError(err error, sourceFs afero.Fs) error {
|
||||
if err == nil {
|
||||
panic("wrapError with nil")
|
||||
}
|
||||
@@ -544,18 +545,26 @@ func (p *pageMeta) wrapError(err error) error {
|
||||
return fmt.Errorf("%q: %w", p.Path(), err)
|
||||
}
|
||||
|
||||
return hugofs.AddFileInfoToError(err, p.File().FileInfo(), p.s.SourceSpec.Fs.Source)
|
||||
return hugofs.AddFileInfoToError(err, p.File().FileInfo(), sourceFs)
|
||||
}
|
||||
|
||||
// wrapError adds some more context to the given error if possible/needed
|
||||
func (p *pageState) wrapError(err error) error {
|
||||
return p.m.wrapError(err)
|
||||
return p.m.wrapError(err, p.s.h.SourceFs)
|
||||
}
|
||||
|
||||
func (p *pageState) getPageInfoForError() string {
|
||||
s := fmt.Sprintf("kind: %q, path: %q", p.Kind(), p.Path())
|
||||
if p.File() != nil {
|
||||
s += fmt.Sprintf(", file: %q", p.File().Filename())
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (p *pageState) getContentConverter() converter.Converter {
|
||||
var err error
|
||||
p.contentConverterInit.Do(func() {
|
||||
markup := p.m.markup
|
||||
markup := p.m.pageConfig.Markup
|
||||
if markup == "html" {
|
||||
// Only used for shortcode inner content.
|
||||
markup = "markdown"
|
||||
@@ -612,7 +621,7 @@ func (p *pageState) posFromInput(input []byte, offset int) text.Position {
|
||||
}
|
||||
|
||||
func (p *pageState) posOffset(offset int) text.Position {
|
||||
return p.posFromInput(p.content.mustSource(), offset)
|
||||
return p.posFromInput(p.m.content.mustSource(), offset)
|
||||
}
|
||||
|
||||
// shiftToOutputFormat is serialized. The output format idx refers to the
|
||||
|
||||
@@ -91,9 +91,6 @@ type pageCommon struct {
|
||||
layoutDescriptor layouts.LayoutDescriptor
|
||||
layoutDescriptorInit sync.Once
|
||||
|
||||
// The source and the parsed page content.
|
||||
content *cachedContent
|
||||
|
||||
// Set if feature enabled and this is in a Git repo.
|
||||
gitInfo source.GitInfo
|
||||
codeowners []string
|
||||
|
||||
+121
-59
@@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
@@ -53,9 +54,8 @@ type pageContentReplacement struct {
|
||||
source pageparser.Item
|
||||
}
|
||||
|
||||
func newCachedContent(m *pageMeta, pid uint64) (*cachedContent, error) {
|
||||
func (m *pageMeta) parseFrontMatter(h *HugoSites, pid uint64, sourceKey string) (*contentParseInfo, error) {
|
||||
var openSource hugio.OpenReadSeekCloser
|
||||
var filename string
|
||||
if m.f != nil {
|
||||
meta := m.f.FileInfo().Meta()
|
||||
openSource = func() (hugio.ReadSeekCloser, error) {
|
||||
@@ -65,6 +65,44 @@ func newCachedContent(m *pageMeta, pid uint64) (*cachedContent, error) {
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
|
||||
if sourceKey == "" {
|
||||
sourceKey = strconv.Itoa(int(pid))
|
||||
}
|
||||
|
||||
pi := &contentParseInfo{
|
||||
h: h,
|
||||
pid: pid,
|
||||
sourceKey: sourceKey,
|
||||
openSource: openSource,
|
||||
}
|
||||
|
||||
source, err := pi.contentSource(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items, err := pageparser.ParseBytes(
|
||||
source,
|
||||
pageparser.Config{},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pi.itemsStep1 = items
|
||||
|
||||
if err := pi.mapFrontMatter(source); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pi, nil
|
||||
}
|
||||
|
||||
func (m *pageMeta) newCachedContent(h *HugoSites, pi *contentParseInfo) (*cachedContent, error) {
|
||||
var filename string
|
||||
if m.f != nil {
|
||||
filename = m.f.Filename()
|
||||
}
|
||||
|
||||
@@ -72,15 +110,11 @@ func newCachedContent(m *pageMeta, pid uint64) (*cachedContent, error) {
|
||||
pm: m.s.pageMap,
|
||||
StaleInfo: m,
|
||||
shortcodeState: newShortcodeHandler(filename, m.s),
|
||||
parseInfo: &contentParseInfo{
|
||||
pid: pid,
|
||||
},
|
||||
cacheBaseKey: m.pathInfo.PathNoLang(),
|
||||
openSource: openSource,
|
||||
enableEmoji: m.s.conf.EnableEmoji,
|
||||
pi: pi,
|
||||
enableEmoji: m.s.conf.EnableEmoji,
|
||||
}
|
||||
|
||||
source, err := c.contentSource()
|
||||
source, err := c.pi.contentSource(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -95,23 +129,25 @@ func newCachedContent(m *pageMeta, pid uint64) (*cachedContent, error) {
|
||||
type cachedContent struct {
|
||||
pm *pageMap
|
||||
|
||||
cacheBaseKey string
|
||||
|
||||
// The source bytes.
|
||||
openSource hugio.OpenReadSeekCloser
|
||||
|
||||
resource.StaleInfo
|
||||
|
||||
shortcodeState *shortcodeHandler
|
||||
|
||||
// Parsed content.
|
||||
parseInfo *contentParseInfo
|
||||
pi *contentParseInfo
|
||||
|
||||
enableEmoji bool
|
||||
}
|
||||
|
||||
type contentParseInfo struct {
|
||||
pid uint64
|
||||
h *HugoSites
|
||||
|
||||
pid uint64
|
||||
sourceKey string
|
||||
|
||||
// The source bytes.
|
||||
openSource hugio.OpenReadSeekCloser
|
||||
|
||||
frontMatter map[string]any
|
||||
|
||||
// Whether the parsed content contains a summary separator.
|
||||
@@ -190,25 +226,15 @@ func (pi *contentParseInfo) contentToRender(ctx context.Context, source []byte,
|
||||
}
|
||||
|
||||
func (c *cachedContent) IsZero() bool {
|
||||
return len(c.parseInfo.itemsStep2) == 0
|
||||
return len(c.pi.itemsStep2) == 0
|
||||
}
|
||||
|
||||
func (c *cachedContent) parseContentFile(source []byte) error {
|
||||
if source == nil || c.openSource == nil {
|
||||
if source == nil || c.pi.openSource == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
items, err := pageparser.ParseBytes(
|
||||
source,
|
||||
pageparser.Config{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.parseInfo.itemsStep1 = items
|
||||
|
||||
return c.parseInfo.mapItems(source, c.shortcodeState)
|
||||
return c.pi.mapItemsAfterFrontMatter(source, c.shortcodeState)
|
||||
}
|
||||
|
||||
func (c *contentParseInfo) parseFrontMatter(it pageparser.Item, iter *pageparser.Iterator, source []byte) error {
|
||||
@@ -242,7 +268,49 @@ func (c *contentParseInfo) parseFrontMatter(it pageparser.Item, iter *pageparser
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rn *contentParseInfo) mapItems(
|
||||
func (rn *contentParseInfo) failMap(source []byte, err error, i pageparser.Item) error {
|
||||
if fe, ok := err.(herrors.FileError); ok {
|
||||
return fe
|
||||
}
|
||||
|
||||
pos := posFromInput("", source, i.Pos())
|
||||
|
||||
return herrors.NewFileErrorFromPos(err, pos)
|
||||
}
|
||||
|
||||
func (rn *contentParseInfo) mapFrontMatter(source []byte) error {
|
||||
if len(rn.itemsStep1) == 0 {
|
||||
return nil
|
||||
}
|
||||
iter := pageparser.NewIterator(rn.itemsStep1)
|
||||
|
||||
Loop:
|
||||
for {
|
||||
it := iter.Next()
|
||||
switch {
|
||||
case it.IsFrontMatter():
|
||||
if err := rn.parseFrontMatter(it, iter, source); err != nil {
|
||||
return err
|
||||
}
|
||||
next := iter.Peek()
|
||||
if !next.IsDone() {
|
||||
rn.posMainContent = next.Pos()
|
||||
}
|
||||
// Done.
|
||||
break Loop
|
||||
case it.IsEOF():
|
||||
break Loop
|
||||
case it.IsError():
|
||||
return rn.failMap(source, it.Err, it)
|
||||
default:
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rn *contentParseInfo) mapItemsAfterFrontMatter(
|
||||
source []byte,
|
||||
s *shortcodeHandler,
|
||||
) error {
|
||||
@@ -273,13 +341,7 @@ Loop:
|
||||
switch {
|
||||
case it.Type == pageparser.TypeIgnore:
|
||||
case it.IsFrontMatter():
|
||||
if err := rn.parseFrontMatter(it, iter, source); err != nil {
|
||||
return err
|
||||
}
|
||||
next := iter.Peek()
|
||||
if !next.IsDone() {
|
||||
rn.posMainContent = next.Pos()
|
||||
}
|
||||
// Ignore.
|
||||
case it.Type == pageparser.TypeLeadSummaryDivider:
|
||||
posBody := -1
|
||||
f := func(item pageparser.Item) bool {
|
||||
@@ -347,16 +409,16 @@ Loop:
|
||||
}
|
||||
|
||||
func (c *cachedContent) mustSource() []byte {
|
||||
source, err := c.contentSource()
|
||||
source, err := c.pi.contentSource(c)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return source
|
||||
}
|
||||
|
||||
func (c *cachedContent) contentSource() ([]byte, error) {
|
||||
key := c.cacheBaseKey
|
||||
v, err := c.pm.cacheContentSource.GetOrCreate(key, func(string) (*resources.StaleValue[[]byte], error) {
|
||||
func (c *contentParseInfo) contentSource(s resource.StaleInfo) ([]byte, error) {
|
||||
key := c.sourceKey
|
||||
v, err := c.h.cacheContentSource.GetOrCreate(key, func(string) (*resources.StaleValue[[]byte], error) {
|
||||
b, err := c.readSourceAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -365,7 +427,7 @@ func (c *cachedContent) contentSource() ([]byte, error) {
|
||||
return &resources.StaleValue[[]byte]{
|
||||
Value: b,
|
||||
IsStaleFunc: func() bool {
|
||||
return c.IsStale()
|
||||
return s.IsStale()
|
||||
},
|
||||
}, nil
|
||||
})
|
||||
@@ -376,7 +438,7 @@ func (c *cachedContent) contentSource() ([]byte, error) {
|
||||
return v.Value, nil
|
||||
}
|
||||
|
||||
func (c *cachedContent) readSourceAll() ([]byte, error) {
|
||||
func (c *contentParseInfo) readSourceAll() ([]byte, error) {
|
||||
if c.openSource == nil {
|
||||
return []byte{}, nil
|
||||
}
|
||||
@@ -424,7 +486,7 @@ type contentPlainPlainWords struct {
|
||||
|
||||
func (c *cachedContent) contentRendered(ctx context.Context, cp *pageContentOutput) (contentSummary, error) {
|
||||
ctx = tpl.Context.DependencyScope.Set(ctx, pageDependencyScopeGlobal)
|
||||
key := c.cacheBaseKey + "/" + cp.po.f.Name
|
||||
key := c.pi.sourceKey + "/" + cp.po.f.Name
|
||||
versionv := cp.contentRenderedVersion
|
||||
|
||||
v, err := c.pm.cacheContentRendered.GetOrCreate(key, func(string) (*resources.StaleValue[contentSummary], error) {
|
||||
@@ -447,7 +509,7 @@ func (c *cachedContent) contentRendered(ctx context.Context, cp *pageContentOutp
|
||||
},
|
||||
}
|
||||
|
||||
if len(c.parseInfo.itemsStep2) == 0 {
|
||||
if len(c.pi.itemsStep2) == 0 {
|
||||
// Nothing to do.
|
||||
return rs, nil
|
||||
}
|
||||
@@ -501,8 +563,8 @@ func (c *cachedContent) contentRendered(ctx context.Context, cp *pageContentOutp
|
||||
|
||||
var result contentSummary // hasVariants bool
|
||||
|
||||
if c.parseInfo.hasSummaryDivider {
|
||||
isHTML := cp.po.p.m.markup == "html"
|
||||
if c.pi.hasSummaryDivider {
|
||||
isHTML := cp.po.p.m.pageConfig.Markup == "html"
|
||||
if isHTML {
|
||||
// Use the summary sections as provided by the user.
|
||||
i := bytes.Index(b, internalSummaryDividerPre)
|
||||
@@ -510,7 +572,7 @@ func (c *cachedContent) contentRendered(ctx context.Context, cp *pageContentOutp
|
||||
b = b[i+len(internalSummaryDividerPre):]
|
||||
|
||||
} else {
|
||||
summary, content, err := splitUserDefinedSummaryAndContent(cp.po.p.m.markup, b)
|
||||
summary, content, err := splitUserDefinedSummaryAndContent(cp.po.p.m.pageConfig.Markup, b)
|
||||
if err != nil {
|
||||
cp.po.p.s.Log.Errorf("Failed to set user defined summary for page %q: %s", cp.po.p.pathOrTitle(), err)
|
||||
} else {
|
||||
@@ -518,7 +580,7 @@ func (c *cachedContent) contentRendered(ctx context.Context, cp *pageContentOutp
|
||||
result.summary = helpers.BytesToHTML(summary)
|
||||
}
|
||||
}
|
||||
result.summaryTruncated = c.parseInfo.summaryTruncated
|
||||
result.summaryTruncated = c.pi.summaryTruncated
|
||||
}
|
||||
result.content = helpers.BytesToHTML(b)
|
||||
rs.Value = result
|
||||
@@ -543,11 +605,11 @@ func (c *cachedContent) mustContentToC(ctx context.Context, cp *pageContentOutpu
|
||||
var setGetContentCallbackInContext = hcontext.NewContextDispatcher[func(*pageContentOutput, contentTableOfContents)]("contentCallback")
|
||||
|
||||
func (c *cachedContent) contentToC(ctx context.Context, cp *pageContentOutput) (contentTableOfContents, error) {
|
||||
key := c.cacheBaseKey + "/" + cp.po.f.Name
|
||||
key := c.pi.sourceKey + "/" + cp.po.f.Name
|
||||
versionv := cp.contentRenderedVersion
|
||||
|
||||
v, err := c.pm.contentTableOfContents.GetOrCreate(key, func(string) (*resources.StaleValue[contentTableOfContents], error) {
|
||||
source, err := c.contentSource()
|
||||
source, err := c.pi.contentSource(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -572,7 +634,7 @@ func (c *cachedContent) contentToC(ctx context.Context, cp *pageContentOutput) (
|
||||
}
|
||||
|
||||
if p.s.conf.Internal.Watch {
|
||||
for _, s := range cp2.po.p.content.shortcodeState.shortcodes {
|
||||
for _, s := range cp2.po.p.m.content.shortcodeState.shortcodes {
|
||||
for _, templ := range s.templs {
|
||||
cp.trackDependency(templ.(identity.IdentityProvider))
|
||||
}
|
||||
@@ -580,7 +642,7 @@ func (c *cachedContent) contentToC(ctx context.Context, cp *pageContentOutput) (
|
||||
}
|
||||
|
||||
// Transfer shortcode names so HasShortcode works for shortcodes from included pages.
|
||||
cp.po.p.content.shortcodeState.transferNames(cp2.po.p.content.shortcodeState)
|
||||
cp.po.p.m.content.shortcodeState.transferNames(cp2.po.p.m.content.shortcodeState)
|
||||
if cp2.po.p.pageOutputTemplateVariationsState.Load() > 0 {
|
||||
cp.po.p.pageOutputTemplateVariationsState.Add(1)
|
||||
}
|
||||
@@ -589,7 +651,7 @@ func (c *cachedContent) contentToC(ctx context.Context, cp *pageContentOutput) (
|
||||
ctx = setGetContentCallbackInContext.Set(ctx, ctxCallback)
|
||||
|
||||
var hasVariants bool
|
||||
ct.contentToRender, hasVariants, err = c.parseInfo.contentToRender(ctx, source, ct.contentPlaceholders)
|
||||
ct.contentToRender, hasVariants, err = c.pi.contentToRender(ctx, source, ct.contentPlaceholders)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -598,7 +660,7 @@ func (c *cachedContent) contentToC(ctx context.Context, cp *pageContentOutput) (
|
||||
p.pageOutputTemplateVariationsState.Add(1)
|
||||
}
|
||||
|
||||
isHTML := cp.po.p.m.markup == "html"
|
||||
isHTML := cp.po.p.m.pageConfig.Markup == "html"
|
||||
|
||||
if !isHTML {
|
||||
createAndSetToC := func(tocProvider converter.TableOfContentsProvider) {
|
||||
@@ -661,7 +723,7 @@ func (c *cachedContent) contentToC(ctx context.Context, cp *pageContentOutput) (
|
||||
}
|
||||
|
||||
func (c *cachedContent) contentPlain(ctx context.Context, cp *pageContentOutput) (contentPlainPlainWords, error) {
|
||||
key := c.cacheBaseKey + "/" + cp.po.f.Name
|
||||
key := c.pi.sourceKey + "/" + cp.po.f.Name
|
||||
|
||||
versionv := cp.contentRenderedVersion
|
||||
|
||||
@@ -681,7 +743,7 @@ func (c *cachedContent) contentPlain(ctx context.Context, cp *pageContentOutput)
|
||||
result.plain = tpl.StripHTML(string(rendered.content))
|
||||
result.plainWords = strings.Fields(result.plain)
|
||||
|
||||
isCJKLanguage := cp.po.p.m.isCJKLanguage
|
||||
isCJKLanguage := cp.po.p.m.pageConfig.IsCJKLanguage
|
||||
|
||||
if isCJKLanguage {
|
||||
result.wordCount = 0
|
||||
@@ -711,8 +773,8 @@ func (c *cachedContent) contentPlain(ctx context.Context, cp *pageContentOutput)
|
||||
if rendered.summary != "" {
|
||||
result.summary = rendered.summary
|
||||
result.summaryTruncated = rendered.summaryTruncated
|
||||
} else if cp.po.p.m.summary != "" {
|
||||
b, err := cp.po.contentRenderer.ParseAndRenderContent(ctx, []byte(cp.po.p.m.summary), false)
|
||||
} else if cp.po.p.m.pageConfig.Summary != "" {
|
||||
b, err := cp.po.contentRenderer.ParseAndRenderContent(ctx, []byte(cp.po.p.m.pageConfig.Summary), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+214
-179
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019 The Hugo Authors. All rights reserved.
|
||||
// Copyright 2024 The Hugo Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -31,7 +31,9 @@ import (
|
||||
|
||||
"github.com/gohugoio/hugo/source"
|
||||
|
||||
"github.com/gohugoio/hugo/common/constants"
|
||||
"github.com/gohugoio/hugo/common/hugo"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
@@ -48,13 +50,11 @@ import (
|
||||
var cjkRe = regexp.MustCompile(`\p{Han}|\p{Hangul}|\p{Hiragana}|\p{Katakana}`)
|
||||
|
||||
type pageMeta struct {
|
||||
kind string // Page kind.
|
||||
term string // Set for kind == KindTerm.
|
||||
singular string // Set for kind == KindTerm and kind == KindTaxonomy.
|
||||
|
||||
resource.Staler
|
||||
pageMetaParams
|
||||
|
||||
pageMetaFrontMatter
|
||||
|
||||
// Set for standalone pages, e.g. robotsTXT.
|
||||
@@ -66,13 +66,15 @@ type pageMeta struct {
|
||||
pathInfo *paths.Path // Always set. This the canonical path to the Page.
|
||||
f *source.File
|
||||
|
||||
content *cachedContent // The source and the parsed page content.
|
||||
|
||||
s *Site // The site this page belongs to.
|
||||
}
|
||||
|
||||
// Prepare for a rebuild of the data passed in from front matter.
|
||||
func (m *pageMeta) setMetaPostPrepareRebuild() {
|
||||
params := xmaps.Clone[map[string]any](m.paramsOriginal)
|
||||
m.pageMetaParams.params = params
|
||||
m.pageMetaParams.pageConfig.Params = params
|
||||
m.pageMetaFrontMatter = pageMetaFrontMatter{}
|
||||
}
|
||||
|
||||
@@ -80,50 +82,31 @@ type pageMetaParams struct {
|
||||
setMetaPostCount int
|
||||
setMetaPostCascadeChanged bool
|
||||
|
||||
params map[string]any // Params contains configuration defined in the params section of page frontmatter.
|
||||
cascade map[page.PageMatcher]maps.Params // cascade contains default configuration to be cascaded downwards.
|
||||
pageConfig *pagemeta.PageConfig
|
||||
|
||||
// These are only set in watch mode.
|
||||
datesOriginal pageMetaDates
|
||||
datesOriginal pagemeta.Dates
|
||||
paramsOriginal map[string]any // contains the original params as defined in the front matter.
|
||||
cascadeOriginal map[page.PageMatcher]maps.Params // contains the original cascade as defined in the front matter.
|
||||
}
|
||||
|
||||
// From page front matter.
|
||||
type pageMetaFrontMatter struct {
|
||||
draft bool // Only published when running with -D flag
|
||||
title string
|
||||
linkTitle string
|
||||
summary string
|
||||
weight int
|
||||
markup string
|
||||
contentType string // type in front matter.
|
||||
isCJKLanguage bool // whether the content is in a CJK language.
|
||||
layout string
|
||||
aliases []string
|
||||
description string
|
||||
keywords []string
|
||||
translationKey string // maps to translation(s) of this page.
|
||||
|
||||
buildConfig pagemeta.BuildConfig
|
||||
configuredOutputFormats output.Formats // outputs defiend in front matter.
|
||||
pageMetaDates // The 4 front matter dates that Hugo cares about.
|
||||
resourcesMetadata []map[string]any // Raw front matter metadata that is going to be assigned to the page resources.
|
||||
sitemap config.SitemapConfig // Sitemap overrides from front matter.
|
||||
urlPaths pagemeta.URLPath
|
||||
configuredOutputFormats output.Formats // outputs defiend in front matter.
|
||||
}
|
||||
|
||||
func (m *pageMetaParams) init(preserveOringal bool) {
|
||||
if preserveOringal {
|
||||
m.paramsOriginal = xmaps.Clone[maps.Params](m.params)
|
||||
m.cascadeOriginal = xmaps.Clone[map[page.PageMatcher]maps.Params](m.cascade)
|
||||
m.paramsOriginal = xmaps.Clone[maps.Params](m.pageConfig.Params)
|
||||
m.cascadeOriginal = xmaps.Clone[map[page.PageMatcher]maps.Params](m.pageConfig.Cascade)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pageMeta) Aliases() []string {
|
||||
return p.aliases
|
||||
return p.pageConfig.Aliases
|
||||
}
|
||||
|
||||
// Deprecated: use taxonomies.
|
||||
func (p *pageMeta) Author() page.Author {
|
||||
hugo.Deprecate(".Author", "Use taxonomies.", "v0.98.0")
|
||||
authors := p.Authors()
|
||||
@@ -134,6 +117,7 @@ func (p *pageMeta) Author() page.Author {
|
||||
return page.Author{}
|
||||
}
|
||||
|
||||
// Deprecated: use taxonomies.
|
||||
func (p *pageMeta) Authors() page.AuthorList {
|
||||
hugo.Deprecate(".Author", "Use taxonomies.", "v0.112.0")
|
||||
return nil
|
||||
@@ -150,8 +134,24 @@ func (p *pageMeta) BundleType() string {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pageMeta) Date() time.Time {
|
||||
return p.pageConfig.Date
|
||||
}
|
||||
|
||||
func (p *pageMeta) PublishDate() time.Time {
|
||||
return p.pageConfig.PublishDate
|
||||
}
|
||||
|
||||
func (p *pageMeta) Lastmod() time.Time {
|
||||
return p.pageConfig.Lastmod
|
||||
}
|
||||
|
||||
func (p *pageMeta) ExpiryDate() time.Time {
|
||||
return p.pageConfig.ExpiryDate
|
||||
}
|
||||
|
||||
func (p *pageMeta) Description() string {
|
||||
return p.description
|
||||
return p.pageConfig.Description
|
||||
}
|
||||
|
||||
func (p *pageMeta) Lang() string {
|
||||
@@ -159,7 +159,7 @@ func (p *pageMeta) Lang() string {
|
||||
}
|
||||
|
||||
func (p *pageMeta) Draft() bool {
|
||||
return p.draft
|
||||
return p.pageConfig.Draft
|
||||
}
|
||||
|
||||
func (p *pageMeta) File() *source.File {
|
||||
@@ -171,20 +171,20 @@ func (p *pageMeta) IsHome() bool {
|
||||
}
|
||||
|
||||
func (p *pageMeta) Keywords() []string {
|
||||
return p.keywords
|
||||
return p.pageConfig.Keywords
|
||||
}
|
||||
|
||||
func (p *pageMeta) Kind() string {
|
||||
return p.kind
|
||||
return p.pageConfig.Kind
|
||||
}
|
||||
|
||||
func (p *pageMeta) Layout() string {
|
||||
return p.layout
|
||||
return p.pageConfig.Layout
|
||||
}
|
||||
|
||||
func (p *pageMeta) LinkTitle() string {
|
||||
if p.linkTitle != "" {
|
||||
return p.linkTitle
|
||||
if p.pageConfig.LinkTitle != "" {
|
||||
return p.pageConfig.LinkTitle
|
||||
}
|
||||
|
||||
return p.Title()
|
||||
@@ -194,8 +194,8 @@ func (p *pageMeta) Name() string {
|
||||
if p.resourcePath != "" {
|
||||
return p.resourcePath
|
||||
}
|
||||
if p.kind == kinds.KindTerm {
|
||||
return p.pathInfo.Unmormalized().BaseNameNoIdentifier()
|
||||
if p.pageConfig.Kind == kinds.KindTerm {
|
||||
return p.pathInfo.Unnormalized().BaseNameNoIdentifier()
|
||||
}
|
||||
return p.Title()
|
||||
}
|
||||
@@ -218,7 +218,7 @@ func (p *pageMeta) Param(key any) (any, error) {
|
||||
}
|
||||
|
||||
func (p *pageMeta) Params() maps.Params {
|
||||
return p.params
|
||||
return p.pageConfig.Params
|
||||
}
|
||||
|
||||
func (p *pageMeta) Path() string {
|
||||
@@ -248,18 +248,18 @@ func (p *pageMeta) Section() string {
|
||||
}
|
||||
|
||||
func (p *pageMeta) Sitemap() config.SitemapConfig {
|
||||
return p.sitemap
|
||||
return p.pageConfig.Sitemap
|
||||
}
|
||||
|
||||
func (p *pageMeta) Title() string {
|
||||
return p.title
|
||||
return p.pageConfig.Title
|
||||
}
|
||||
|
||||
const defaultContentType = "page"
|
||||
|
||||
func (p *pageMeta) Type() string {
|
||||
if p.contentType != "" {
|
||||
return p.contentType
|
||||
if p.pageConfig.Type != "" {
|
||||
return p.pageConfig.Type
|
||||
}
|
||||
|
||||
if sect := p.Section(); sect != "" {
|
||||
@@ -270,36 +270,56 @@ func (p *pageMeta) Type() string {
|
||||
}
|
||||
|
||||
func (p *pageMeta) Weight() int {
|
||||
return p.weight
|
||||
return p.pageConfig.Weight
|
||||
}
|
||||
|
||||
func (ps *pageState) setMetaPre() error {
|
||||
pm := ps.m
|
||||
p := ps
|
||||
frontmatter := p.content.parseInfo.frontMatter
|
||||
watching := p.s.watching()
|
||||
|
||||
func (p *pageMeta) setMetaPre(pi *contentParseInfo, logger loggers.Logger, conf config.AllProvider) error {
|
||||
frontmatter := pi.frontMatter
|
||||
if frontmatter != nil {
|
||||
pcfg := p.pageConfig
|
||||
if pcfg == nil {
|
||||
panic("pageConfig not set")
|
||||
}
|
||||
// Needed for case insensitive fetching of params values
|
||||
maps.PrepareParams(frontmatter)
|
||||
pm.pageMetaParams.params = frontmatter
|
||||
if p.IsNode() {
|
||||
// Check for any cascade define on itself.
|
||||
if cv, found := frontmatter["cascade"]; found {
|
||||
var err error
|
||||
cascade, err := page.DecodeCascade(cv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pm.pageMetaParams.cascade = cascade
|
||||
pcfg.Params = frontmatter
|
||||
// Check for any cascade define on itself.
|
||||
if cv, found := frontmatter["cascade"]; found {
|
||||
var err error
|
||||
cascade, err := page.DecodeCascade(logger, cv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pcfg.Cascade = cascade
|
||||
}
|
||||
|
||||
// Look for path, lang and kind, all of which values we need early on.
|
||||
if v, found := frontmatter["path"]; found {
|
||||
pcfg.Path = paths.ToSlashPreserveLeading(cast.ToString(v))
|
||||
pcfg.Params["path"] = pcfg.Path
|
||||
}
|
||||
if v, found := frontmatter["lang"]; found {
|
||||
lang := strings.ToLower(cast.ToString(v))
|
||||
if _, ok := conf.PathParser().LanguageIndex[lang]; ok {
|
||||
pcfg.Lang = lang
|
||||
pcfg.Params["lang"] = pcfg.Lang
|
||||
}
|
||||
}
|
||||
} else if pm.pageMetaParams.params == nil {
|
||||
pm.pageMetaParams.params = make(maps.Params)
|
||||
if v, found := frontmatter["kind"]; found {
|
||||
s := cast.ToString(v)
|
||||
if s != "" {
|
||||
pcfg.Kind = kinds.GetKindMain(s)
|
||||
if pcfg.Kind == "" {
|
||||
return fmt.Errorf("unknown kind %q in front matter", s)
|
||||
}
|
||||
pcfg.Params["kind"] = pcfg.Kind
|
||||
}
|
||||
}
|
||||
} else if p.pageMetaParams.pageConfig.Params == nil {
|
||||
p.pageConfig.Params = make(maps.Params)
|
||||
}
|
||||
|
||||
pm.pageMetaParams.init(watching)
|
||||
p.pageMetaParams.init(conf.Watching())
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -308,18 +328,18 @@ func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error
|
||||
ps.m.setMetaPostCount++
|
||||
var cascadeHashPre uint64
|
||||
if ps.m.setMetaPostCount > 1 {
|
||||
cascadeHashPre = identity.HashUint64(ps.m.cascade)
|
||||
ps.m.cascade = xmaps.Clone[map[page.PageMatcher]maps.Params](ps.m.cascadeOriginal)
|
||||
cascadeHashPre = identity.HashUint64(ps.m.pageConfig.Cascade)
|
||||
ps.m.pageConfig.Cascade = xmaps.Clone[map[page.PageMatcher]maps.Params](ps.m.cascadeOriginal)
|
||||
|
||||
}
|
||||
|
||||
// Apply cascades first so they can be overriden later.
|
||||
if cascade != nil {
|
||||
if ps.m.cascade != nil {
|
||||
if ps.m.pageConfig.Cascade != nil {
|
||||
for k, v := range cascade {
|
||||
vv, found := ps.m.cascade[k]
|
||||
vv, found := ps.m.pageConfig.Cascade[k]
|
||||
if !found {
|
||||
ps.m.cascade[k] = v
|
||||
ps.m.pageConfig.Cascade[k] = v
|
||||
} else {
|
||||
// Merge
|
||||
for ck, cv := range v {
|
||||
@@ -329,21 +349,21 @@ func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error
|
||||
}
|
||||
}
|
||||
}
|
||||
cascade = ps.m.cascade
|
||||
cascade = ps.m.pageConfig.Cascade
|
||||
} else {
|
||||
ps.m.cascade = cascade
|
||||
ps.m.pageConfig.Cascade = cascade
|
||||
}
|
||||
}
|
||||
|
||||
if cascade == nil {
|
||||
cascade = ps.m.cascade
|
||||
cascade = ps.m.pageConfig.Cascade
|
||||
}
|
||||
|
||||
if ps.m.setMetaPostCount > 1 {
|
||||
ps.m.setMetaPostCascadeChanged = cascadeHashPre != identity.HashUint64(ps.m.cascade)
|
||||
ps.m.setMetaPostCascadeChanged = cascadeHashPre != identity.HashUint64(ps.m.pageConfig.Cascade)
|
||||
if !ps.m.setMetaPostCascadeChanged {
|
||||
// No changes, restore any value that may be changed by aggregation.
|
||||
ps.m.dates = ps.m.datesOriginal.dates
|
||||
ps.m.pageConfig.Dates = ps.m.datesOriginal
|
||||
return nil
|
||||
}
|
||||
ps.m.setMetaPostPrepareRebuild()
|
||||
@@ -356,8 +376,8 @@ func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error
|
||||
continue
|
||||
}
|
||||
for kk, vv := range v {
|
||||
if _, found := ps.m.params[kk]; !found {
|
||||
ps.m.params[kk] = vv
|
||||
if _, found := ps.m.pageConfig.Params[kk]; !found {
|
||||
ps.m.pageConfig.Params[kk] = vv
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -371,7 +391,7 @@ func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error
|
||||
}
|
||||
|
||||
// Store away any original values that may be changed from aggregation.
|
||||
ps.m.datesOriginal = ps.m.pageMetaDates
|
||||
ps.m.datesOriginal = ps.m.pageConfig.Dates
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -392,13 +412,8 @@ func (p *pageState) setMetaPostParams() error {
|
||||
gitAuthorDate = p.gitInfo.AuthorDate
|
||||
}
|
||||
|
||||
pm.pageMetaDates = pageMetaDates{}
|
||||
pm.urlPaths = pagemeta.URLPath{}
|
||||
|
||||
descriptor := &pagemeta.FrontMatterDescriptor{
|
||||
Params: pm.params,
|
||||
Dates: &pm.pageMetaDates.dates,
|
||||
PageURLs: &pm.urlPaths,
|
||||
PageConfig: pm.pageConfig,
|
||||
BaseFilename: contentBaseName,
|
||||
ModTime: mtime,
|
||||
GitAuthorDate: gitAuthorDate,
|
||||
@@ -413,17 +428,52 @@ func (p *pageState) setMetaPostParams() error {
|
||||
p.s.Log.Errorf("Failed to handle dates for page %q: %s", p.pathOrTitle(), err)
|
||||
}
|
||||
|
||||
pm.buildConfig, err = pagemeta.DecodeBuildConfig(pm.params["_build"])
|
||||
var buildConfig any
|
||||
var isNewBuildKeyword bool
|
||||
if v, ok := pm.pageConfig.Params["_build"]; ok {
|
||||
buildConfig = v
|
||||
} else {
|
||||
buildConfig = pm.pageConfig.Params["build"]
|
||||
isNewBuildKeyword = true
|
||||
}
|
||||
pm.pageConfig.Build, err = pagemeta.DecodeBuildConfig(buildConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
var msgDetail string
|
||||
if isNewBuildKeyword {
|
||||
msgDetail = `. We renamed the _build keyword to build in Hugo 0.123.0. We recommend putting user defined params in the params section, e.g.:
|
||||
---
|
||||
title: "My Title"
|
||||
params:
|
||||
build: "My Build"
|
||||
---
|
||||
´
|
||||
|
||||
`
|
||||
}
|
||||
return fmt.Errorf("failed to decode build config in front matter: %s%s", err, msgDetail)
|
||||
}
|
||||
|
||||
var sitemapSet bool
|
||||
|
||||
pcfg := pm.pageConfig
|
||||
|
||||
params := pcfg.Params
|
||||
|
||||
var draft, published, isCJKLanguage *bool
|
||||
for k, v := range pm.params {
|
||||
var userParams map[string]any
|
||||
for k, v := range pcfg.Params {
|
||||
loki := strings.ToLower(k)
|
||||
|
||||
if loki == "params" {
|
||||
vv, err := maps.ToStringMapE(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userParams = vv
|
||||
delete(pcfg.Params, k)
|
||||
continue
|
||||
}
|
||||
|
||||
if loki == "published" { // Intentionally undocumented
|
||||
vv, err := cast.ToBoolE(v)
|
||||
if err == nil {
|
||||
@@ -439,43 +489,43 @@ func (p *pageState) setMetaPostParams() error {
|
||||
|
||||
switch loki {
|
||||
case "title":
|
||||
pm.title = cast.ToString(v)
|
||||
pm.params[loki] = pm.title
|
||||
pcfg.Title = cast.ToString(v)
|
||||
params[loki] = pcfg.Title
|
||||
case "linktitle":
|
||||
pm.linkTitle = cast.ToString(v)
|
||||
pm.params[loki] = pm.linkTitle
|
||||
pcfg.LinkTitle = cast.ToString(v)
|
||||
params[loki] = pcfg.LinkTitle
|
||||
case "summary":
|
||||
pm.summary = cast.ToString(v)
|
||||
pm.params[loki] = pm.summary
|
||||
pcfg.Summary = cast.ToString(v)
|
||||
params[loki] = pcfg.Summary
|
||||
case "description":
|
||||
pm.description = cast.ToString(v)
|
||||
pm.params[loki] = pm.description
|
||||
pcfg.Description = cast.ToString(v)
|
||||
params[loki] = pcfg.Description
|
||||
case "slug":
|
||||
// Don't start or end with a -
|
||||
pm.urlPaths.Slug = strings.Trim(cast.ToString(v), "-")
|
||||
pm.params[loki] = pm.Slug()
|
||||
pcfg.Slug = strings.Trim(cast.ToString(v), "-")
|
||||
params[loki] = pm.Slug()
|
||||
case "url":
|
||||
url := cast.ToString(v)
|
||||
if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
|
||||
return fmt.Errorf("URLs with protocol (http*) not supported: %q. In page %q", url, p.pathOrTitle())
|
||||
}
|
||||
pm.urlPaths.URL = url
|
||||
pm.params[loki] = url
|
||||
pcfg.URL = url
|
||||
params[loki] = url
|
||||
case "type":
|
||||
pm.contentType = cast.ToString(v)
|
||||
pm.params[loki] = pm.contentType
|
||||
pcfg.Type = cast.ToString(v)
|
||||
params[loki] = pcfg.Type
|
||||
case "keywords":
|
||||
pm.keywords = cast.ToStringSlice(v)
|
||||
pm.params[loki] = pm.keywords
|
||||
pcfg.Keywords = cast.ToStringSlice(v)
|
||||
params[loki] = pcfg.Keywords
|
||||
case "headless":
|
||||
// Legacy setting for leaf bundles.
|
||||
// This is since Hugo 0.63 handled in a more general way for all
|
||||
// pages.
|
||||
isHeadless := cast.ToBool(v)
|
||||
pm.params[loki] = isHeadless
|
||||
params[loki] = isHeadless
|
||||
if p.File().TranslationBaseName() == "index" && isHeadless {
|
||||
pm.buildConfig.List = pagemeta.Never
|
||||
pm.buildConfig.Render = pagemeta.Never
|
||||
pm.pageConfig.Build.List = pagemeta.Never
|
||||
pm.pageConfig.Build.Render = pagemeta.Never
|
||||
}
|
||||
case "outputs":
|
||||
o := cast.ToStringSlice(v)
|
||||
@@ -490,43 +540,42 @@ func (p *pageState) setMetaPostParams() error {
|
||||
p.s.Log.Errorf("Failed to resolve output formats: %s", err)
|
||||
} else {
|
||||
pm.configuredOutputFormats = outFormats
|
||||
pm.params[loki] = outFormats
|
||||
params[loki] = outFormats
|
||||
}
|
||||
}
|
||||
case "draft":
|
||||
draft = new(bool)
|
||||
*draft = cast.ToBool(v)
|
||||
case "layout":
|
||||
pm.layout = cast.ToString(v)
|
||||
pm.params[loki] = pm.layout
|
||||
pcfg.Layout = cast.ToString(v)
|
||||
params[loki] = pcfg.Layout
|
||||
case "markup":
|
||||
pm.markup = cast.ToString(v)
|
||||
pm.params[loki] = pm.markup
|
||||
pcfg.Markup = cast.ToString(v)
|
||||
params[loki] = pcfg.Markup
|
||||
case "weight":
|
||||
pm.weight = cast.ToInt(v)
|
||||
pm.params[loki] = pm.weight
|
||||
pcfg.Weight = cast.ToInt(v)
|
||||
params[loki] = pcfg.Weight
|
||||
case "aliases":
|
||||
pm.aliases = cast.ToStringSlice(v)
|
||||
for i, alias := range pm.aliases {
|
||||
pcfg.Aliases = cast.ToStringSlice(v)
|
||||
for i, alias := range pcfg.Aliases {
|
||||
if strings.HasPrefix(alias, "http://") || strings.HasPrefix(alias, "https://") {
|
||||
return fmt.Errorf("http* aliases not supported: %q", alias)
|
||||
}
|
||||
pm.aliases[i] = filepath.ToSlash(alias)
|
||||
pcfg.Aliases[i] = filepath.ToSlash(alias)
|
||||
}
|
||||
pm.params[loki] = pm.aliases
|
||||
params[loki] = pcfg.Aliases
|
||||
case "sitemap":
|
||||
p.m.sitemap, err = config.DecodeSitemap(p.s.conf.Sitemap, maps.ToStringMap(v))
|
||||
pcfg.Sitemap, err = config.DecodeSitemap(p.s.conf.Sitemap, maps.ToStringMap(v))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode sitemap config in front matter: %s", err)
|
||||
}
|
||||
pm.params[loki] = p.m.sitemap
|
||||
sitemapSet = true
|
||||
case "iscjklanguage":
|
||||
isCJKLanguage = new(bool)
|
||||
*isCJKLanguage = cast.ToBool(v)
|
||||
case "translationkey":
|
||||
pm.translationKey = cast.ToString(v)
|
||||
pm.params[loki] = pm.translationKey
|
||||
pcfg.TranslationKey = cast.ToString(v)
|
||||
params[loki] = pcfg.TranslationKey
|
||||
case "resources":
|
||||
var resources []map[string]any
|
||||
handled := true
|
||||
@@ -552,8 +601,7 @@ func (p *pageState) setMetaPostParams() error {
|
||||
}
|
||||
|
||||
if handled {
|
||||
pm.params[loki] = resources
|
||||
pm.resourcesMetadata = resources
|
||||
pcfg.Resources = resources
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
@@ -575,47 +623,54 @@ func (p *pageState) setMetaPostParams() error {
|
||||
for i, u := range vv {
|
||||
a[i] = cast.ToString(u)
|
||||
}
|
||||
pm.params[loki] = a
|
||||
params[loki] = a
|
||||
} else {
|
||||
pm.params[loki] = vv
|
||||
params[loki] = vv
|
||||
}
|
||||
} else {
|
||||
pm.params[loki] = []string{}
|
||||
params[loki] = []string{}
|
||||
}
|
||||
|
||||
default:
|
||||
pm.params[loki] = vv
|
||||
params[loki] = vv
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !sitemapSet {
|
||||
pm.sitemap = p.s.conf.Sitemap
|
||||
for k, v := range userParams {
|
||||
if _, found := params[k]; found {
|
||||
p.s.Log.Warnidf(constants.WarnFrontMatterParamsOverrides, "Hugo front matter key %q is overridden in params section.", k)
|
||||
}
|
||||
params[strings.ToLower(k)] = v
|
||||
}
|
||||
|
||||
pm.markup = p.s.ContentSpec.ResolveMarkup(pm.markup)
|
||||
if !sitemapSet {
|
||||
pcfg.Sitemap = p.s.conf.Sitemap
|
||||
}
|
||||
|
||||
pcfg.Markup = p.s.ContentSpec.ResolveMarkup(pcfg.Markup)
|
||||
|
||||
if draft != nil && published != nil {
|
||||
pm.draft = *draft
|
||||
pcfg.Draft = *draft
|
||||
p.m.s.Log.Warnf("page %q has both draft and published settings in its frontmatter. Using draft.", p.File().Filename())
|
||||
} else if draft != nil {
|
||||
pm.draft = *draft
|
||||
pcfg.Draft = *draft
|
||||
} else if published != nil {
|
||||
pm.draft = !*published
|
||||
pcfg.Draft = !*published
|
||||
}
|
||||
pm.params["draft"] = pm.draft
|
||||
params["draft"] = pcfg.Draft
|
||||
|
||||
if isCJKLanguage != nil {
|
||||
pm.isCJKLanguage = *isCJKLanguage
|
||||
} else if p.s.conf.HasCJKLanguage && p.content.openSource != nil {
|
||||
if cjkRe.Match(p.content.mustSource()) {
|
||||
pm.isCJKLanguage = true
|
||||
pcfg.IsCJKLanguage = *isCJKLanguage
|
||||
} else if p.s.conf.HasCJKLanguage && p.m.content.pi.openSource != nil {
|
||||
if cjkRe.Match(p.m.content.mustSource()) {
|
||||
pcfg.IsCJKLanguage = true
|
||||
} else {
|
||||
pm.isCJKLanguage = false
|
||||
pcfg.IsCJKLanguage = false
|
||||
}
|
||||
}
|
||||
|
||||
pm.params["iscjklanguage"] = p.m.isCJKLanguage
|
||||
params["iscjklanguage"] = pcfg.IsCJKLanguage
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -628,7 +683,7 @@ func (p *pageMeta) shouldList(global bool) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
switch p.buildConfig.List {
|
||||
switch p.pageConfig.Build.List {
|
||||
case pagemeta.Always:
|
||||
return true
|
||||
case pagemeta.Never:
|
||||
@@ -652,56 +707,56 @@ func (p *pageMeta) shouldBeCheckedForMenuDefinitions() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
return p.kind == kinds.KindHome || p.kind == kinds.KindSection || p.kind == kinds.KindPage
|
||||
return p.pageConfig.Kind == kinds.KindHome || p.pageConfig.Kind == kinds.KindSection || p.pageConfig.Kind == kinds.KindPage
|
||||
}
|
||||
|
||||
func (p *pageMeta) noRender() bool {
|
||||
return p.buildConfig.Render != pagemeta.Always
|
||||
return p.pageConfig.Build.Render != pagemeta.Always
|
||||
}
|
||||
|
||||
func (p *pageMeta) noLink() bool {
|
||||
return p.buildConfig.Render == pagemeta.Never
|
||||
return p.pageConfig.Build.Render == pagemeta.Never
|
||||
}
|
||||
|
||||
func (p *pageMeta) applyDefaultValues() error {
|
||||
if p.buildConfig.IsZero() {
|
||||
p.buildConfig, _ = pagemeta.DecodeBuildConfig(nil)
|
||||
if p.pageConfig.Build.IsZero() {
|
||||
p.pageConfig.Build, _ = pagemeta.DecodeBuildConfig(nil)
|
||||
}
|
||||
|
||||
if !p.s.conf.IsKindEnabled(p.Kind()) {
|
||||
(&p.buildConfig).Disable()
|
||||
(&p.pageConfig.Build).Disable()
|
||||
}
|
||||
|
||||
if p.markup == "" {
|
||||
if p.pageConfig.Markup == "" {
|
||||
if p.File() != nil {
|
||||
// Fall back to file extension
|
||||
p.markup = p.s.ContentSpec.ResolveMarkup(p.File().Ext())
|
||||
p.pageConfig.Markup = p.s.ContentSpec.ResolveMarkup(p.File().Ext())
|
||||
}
|
||||
if p.markup == "" {
|
||||
p.markup = "markdown"
|
||||
if p.pageConfig.Markup == "" {
|
||||
p.pageConfig.Markup = "markdown"
|
||||
}
|
||||
}
|
||||
|
||||
if p.title == "" && p.f == nil {
|
||||
if p.pageConfig.Title == "" && p.f == nil {
|
||||
switch p.Kind() {
|
||||
case kinds.KindHome:
|
||||
p.title = p.s.Title()
|
||||
p.pageConfig.Title = p.s.Title()
|
||||
case kinds.KindSection:
|
||||
sectionName := p.pathInfo.Unmormalized().BaseNameNoIdentifier()
|
||||
sectionName := p.pathInfo.Unnormalized().BaseNameNoIdentifier()
|
||||
if p.s.conf.PluralizeListTitles {
|
||||
sectionName = flect.Pluralize(sectionName)
|
||||
}
|
||||
p.title = p.s.conf.C.CreateTitle(sectionName)
|
||||
p.pageConfig.Title = p.s.conf.C.CreateTitle(sectionName)
|
||||
case kinds.KindTerm:
|
||||
if p.term != "" {
|
||||
p.title = p.s.conf.C.CreateTitle(p.term)
|
||||
p.pageConfig.Title = p.s.conf.C.CreateTitle(p.term)
|
||||
} else {
|
||||
panic("term not set")
|
||||
}
|
||||
case kinds.KindTaxonomy:
|
||||
p.title = strings.Replace(p.s.conf.C.CreateTitle(p.pathInfo.Unmormalized().BaseNameNoIdentifier()), "-", " ", -1)
|
||||
p.pageConfig.Title = strings.Replace(p.s.conf.C.CreateTitle(p.pathInfo.Unnormalized().BaseNameNoIdentifier()), "-", " ", -1)
|
||||
case kinds.KindStatus404:
|
||||
p.title = "404 Page not found"
|
||||
p.pageConfig.Title = "404 Page not found"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -714,7 +769,7 @@ func (p *pageMeta) newContentConverter(ps *pageState, markup string) (converter.
|
||||
}
|
||||
cp := p.s.ContentSpec.Converters.Get(markup)
|
||||
if cp == nil {
|
||||
return converter.NopConverter, fmt.Errorf("no content renderer found for markup %q", markup)
|
||||
return converter.NopConverter, fmt.Errorf("no content renderer found for markup %q, page: %s", markup, ps.getPageInfoForError())
|
||||
}
|
||||
|
||||
var id string
|
||||
@@ -752,7 +807,7 @@ func (m *pageMeta) outputFormats() output.Formats {
|
||||
}
|
||||
|
||||
func (p *pageMeta) Slug() string {
|
||||
return p.urlPaths.Slug
|
||||
return p.pageConfig.Slug
|
||||
}
|
||||
|
||||
func getParam(m resource.ResourceParamsProvider, key string, stringToLower bool) any {
|
||||
@@ -790,26 +845,6 @@ func getParamToLower(m resource.ResourceParamsProvider, key string) any {
|
||||
return getParam(m, key, true)
|
||||
}
|
||||
|
||||
type pageMetaDates struct {
|
||||
dates resource.Dates
|
||||
}
|
||||
|
||||
func (d *pageMetaDates) Date() time.Time {
|
||||
return d.dates.Date()
|
||||
}
|
||||
|
||||
func (d *pageMetaDates) Lastmod() time.Time {
|
||||
return d.dates.Lastmod()
|
||||
}
|
||||
|
||||
func (d *pageMetaDates) PublishDate() time.Time {
|
||||
return d.dates.PublishDate()
|
||||
}
|
||||
|
||||
func (d *pageMetaDates) ExpiryDate() time.Time {
|
||||
return d.dates.ExpiryDate()
|
||||
}
|
||||
|
||||
func (ps *pageState) initLazyProviders() error {
|
||||
ps.init.Add(func(ctx context.Context) (any, error) {
|
||||
pp, err := newPagePaths(ps)
|
||||
|
||||
+90
-27
@@ -15,45 +15,111 @@ package hugolib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/gohugoio/hugo/hugofs/files"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/resources"
|
||||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
|
||||
"github.com/gohugoio/hugo/lazy"
|
||||
|
||||
"github.com/gohugoio/hugo/resources/kinds"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/gohugoio/hugo/resources/page/pagemeta"
|
||||
)
|
||||
|
||||
var pageIDCounter atomic.Uint64
|
||||
|
||||
func (h *HugoSites) newPage(m *pageMeta) (*pageState, error) {
|
||||
if m.pathInfo == nil {
|
||||
func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
|
||||
m.Staler = &resources.AtomicStaler{}
|
||||
if m.pageConfig == nil {
|
||||
m.pageMetaParams = pageMetaParams{
|
||||
pageConfig: &pagemeta.PageConfig{
|
||||
Params: maps.Params{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var sourceKey string
|
||||
if m.f != nil {
|
||||
sourceKey = filepath.ToSlash(m.f.Filename())
|
||||
}
|
||||
|
||||
pid := pageIDCounter.Add(1)
|
||||
pi, err := m.parseFrontMatter(h, pid, sourceKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err := m.setMetaPre(pi, h.Log, h.Conf); err != nil {
|
||||
return nil, nil, m.wrapError(err, h.BaseFs.SourceFs)
|
||||
}
|
||||
pcfg := m.pageConfig
|
||||
if pcfg.Lang != "" {
|
||||
if h.Conf.IsLangDisabled(pcfg.Lang) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
if pcfg.Path != "" {
|
||||
s := m.pageConfig.Path
|
||||
if !paths.HasExt(s) {
|
||||
var (
|
||||
isBranch bool
|
||||
ext string = "md"
|
||||
)
|
||||
if pcfg.Kind != "" {
|
||||
isBranch = kinds.IsBranch(pcfg.Kind)
|
||||
} else if m.pathInfo != nil {
|
||||
isBranch = m.pathInfo.IsBranchBundle()
|
||||
if m.pathInfo.Ext() != "" {
|
||||
ext = m.pathInfo.Ext()
|
||||
}
|
||||
} else if m.f != nil {
|
||||
pi := m.f.FileInfo().Meta().PathInfo
|
||||
isBranch = pi.IsBranchBundle()
|
||||
if pi.Ext() != "" {
|
||||
ext = pi.Ext()
|
||||
}
|
||||
}
|
||||
if isBranch {
|
||||
s += "/_index." + ext
|
||||
} else {
|
||||
s += "/index." + ext
|
||||
}
|
||||
}
|
||||
m.pathInfo = h.Conf.PathParser().Parse(files.ComponentFolderContent, s)
|
||||
} else if m.pathInfo == nil {
|
||||
if m.f != nil {
|
||||
m.pathInfo = m.f.FileInfo().Meta().PathInfo
|
||||
}
|
||||
|
||||
if m.pathInfo == nil {
|
||||
panic(fmt.Sprintf("missing pathInfo in %v", m))
|
||||
}
|
||||
}
|
||||
|
||||
m.Staler = &resources.AtomicStaler{}
|
||||
|
||||
ps, err := func() (*pageState, error) {
|
||||
if m.s == nil {
|
||||
// Identify the Site/language to associate this Page with.
|
||||
var lang string
|
||||
if m.f != nil {
|
||||
if pcfg.Lang != "" {
|
||||
lang = pcfg.Lang
|
||||
} else if m.f != nil {
|
||||
meta := m.f.FileInfo().Meta()
|
||||
lang = meta.Lang
|
||||
m.s = h.Sites[meta.LangIndex]
|
||||
} else {
|
||||
lang = m.pathInfo.Lang()
|
||||
}
|
||||
if lang == "" {
|
||||
lang = h.Conf.DefaultContentLanguage()
|
||||
}
|
||||
var found bool
|
||||
for _, ss := range h.Sites {
|
||||
if ss.Lang() == lang {
|
||||
@@ -62,51 +128,49 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return nil, fmt.Errorf("no site found for language %q", lang)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Identify Page Kind.
|
||||
if m.kind == "" {
|
||||
m.kind = kinds.KindSection
|
||||
if m.pageConfig.Kind == "" {
|
||||
m.pageConfig.Kind = kinds.KindSection
|
||||
if m.pathInfo.Base() == "/" {
|
||||
m.kind = kinds.KindHome
|
||||
m.pageConfig.Kind = kinds.KindHome
|
||||
} else if m.pathInfo.IsBranchBundle() {
|
||||
// A section, taxonomy or term.
|
||||
tc := m.s.pageMap.cfg.getTaxonomyConfig(m.Path())
|
||||
if !tc.IsZero() {
|
||||
// Either a taxonomy or a term.
|
||||
if tc.pluralTreeKey == m.Path() {
|
||||
m.kind = kinds.KindTaxonomy
|
||||
m.pageConfig.Kind = kinds.KindTaxonomy
|
||||
} else {
|
||||
m.kind = kinds.KindTerm
|
||||
m.pageConfig.Kind = kinds.KindTerm
|
||||
}
|
||||
}
|
||||
} else if m.f != nil {
|
||||
m.kind = kinds.KindPage
|
||||
m.pageConfig.Kind = kinds.KindPage
|
||||
}
|
||||
}
|
||||
|
||||
if m.kind == kinds.KindPage && !m.s.conf.IsKindEnabled(m.kind) {
|
||||
if m.pageConfig.Kind == kinds.KindPage && !m.s.conf.IsKindEnabled(m.pageConfig.Kind) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
pid := pageIDCounter.Add(1)
|
||||
|
||||
// Parse page content.
|
||||
cachedContent, err := newCachedContent(m, pid)
|
||||
if err != nil {
|
||||
return nil, m.wrapError(err)
|
||||
}
|
||||
|
||||
var dependencyManager identity.Manager = identity.NopManager
|
||||
|
||||
if m.s.conf.Internal.Watch {
|
||||
dependencyManager = identity.NewManager(m.Path())
|
||||
}
|
||||
|
||||
// Parse the rest of the page content.
|
||||
m.content, err = m.newCachedContent(h, pi)
|
||||
if err != nil {
|
||||
return nil, m.wrapError(err, h.SourceFs)
|
||||
}
|
||||
|
||||
ps := &pageState{
|
||||
pid: pid,
|
||||
pageOutput: nopPageOutput,
|
||||
@@ -115,7 +179,6 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, error) {
|
||||
Staler: m,
|
||||
dependencyManager: dependencyManager,
|
||||
pageCommon: &pageCommon{
|
||||
content: cachedContent,
|
||||
FileProvider: m,
|
||||
AuthorProvider: m,
|
||||
Scratcher: maps.NewScratcher(),
|
||||
@@ -168,10 +231,6 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, error) {
|
||||
ps.ShortcodeInfoProvider = ps
|
||||
ps.AlternativeOutputFormatsProvider = ps
|
||||
|
||||
if err := ps.setMetaPre(); err != nil {
|
||||
return nil, ps.wrapError(err)
|
||||
}
|
||||
|
||||
if err := ps.initLazyProviders(); err != nil {
|
||||
return nil, ps.wrapError(err)
|
||||
}
|
||||
@@ -182,5 +241,9 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, error) {
|
||||
m.MarkStale()
|
||||
}
|
||||
|
||||
return ps, err
|
||||
if ps == nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return ps, ps.PathInfo(), err
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
package hugolib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/output"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
@@ -37,12 +39,16 @@ func newPageOutput(
|
||||
targetPathsProvider = ft
|
||||
linksProvider = ft
|
||||
|
||||
var paginatorProvider page.PaginatorProvider = page.NopPage
|
||||
var paginatorProvider page.PaginatorProvider
|
||||
var pag *pagePaginator
|
||||
|
||||
if render && ps.IsNode() {
|
||||
pag = newPagePaginator(ps)
|
||||
paginatorProvider = pag
|
||||
} else {
|
||||
paginatorProvider = page.PaginatorNotSupportedFunc(func() error {
|
||||
return fmt.Errorf("pagination not supported for this page: %s", ps.getPageInfoForError())
|
||||
})
|
||||
}
|
||||
|
||||
var dependencyManager identity.Manager = identity.NopManager
|
||||
|
||||
@@ -116,8 +116,8 @@ func createTargetPathDescriptor(p *pageState) (page.TargetPathDescriptor, error)
|
||||
pageInfoPage := p.PathInfo()
|
||||
pageInfoCurrentSection := p.CurrentSection().PathInfo()
|
||||
if p.s.Conf.DisablePathToLower() {
|
||||
pageInfoPage = pageInfoPage.Unmormalized()
|
||||
pageInfoCurrentSection = pageInfoCurrentSection.Unmormalized()
|
||||
pageInfoPage = pageInfoPage.Unnormalized()
|
||||
pageInfoCurrentSection = pageInfoCurrentSection.Unnormalized()
|
||||
}
|
||||
|
||||
desc := page.TargetPathDescriptor{
|
||||
@@ -127,7 +127,7 @@ func createTargetPathDescriptor(p *pageState) (page.TargetPathDescriptor, error)
|
||||
Section: pageInfoCurrentSection,
|
||||
UglyURLs: s.h.Conf.IsUglyURLs(p.Section()),
|
||||
ForcePrefix: s.h.Conf.IsMultihost() || alwaysInSubDir,
|
||||
URL: pm.urlPaths.URL,
|
||||
URL: pm.pageConfig.URL,
|
||||
}
|
||||
|
||||
if pm.Slug() != "" {
|
||||
|
||||
+29
-13
@@ -104,12 +104,12 @@ func (pco *pageContentOutput) Reset() {
|
||||
}
|
||||
|
||||
func (pco *pageContentOutput) Fragments(ctx context.Context) *tableofcontents.Fragments {
|
||||
return pco.po.p.content.mustContentToC(ctx, pco).tableOfContents
|
||||
return pco.po.p.m.content.mustContentToC(ctx, pco).tableOfContents
|
||||
}
|
||||
|
||||
func (pco *pageContentOutput) RenderShortcodes(ctx context.Context) (template.HTML, error) {
|
||||
content := pco.po.p.content
|
||||
source, err := content.contentSource()
|
||||
content := pco.po.p.m.content
|
||||
source, err := content.pi.contentSource(content)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -125,7 +125,7 @@ func (pco *pageContentOutput) RenderShortcodes(ctx context.Context) (template.HT
|
||||
insertPlaceholders = true
|
||||
}
|
||||
c := make([]byte, 0, len(source)+(len(source)/10))
|
||||
for _, it := range content.parseInfo.itemsStep2 {
|
||||
for _, it := range content.pi.itemsStep2 {
|
||||
switch v := it.(type) {
|
||||
case pageparser.Item:
|
||||
c = append(c, source[v.Pos():v.Pos()+len(v.Val(source))]...)
|
||||
@@ -169,12 +169,12 @@ func (pco *pageContentOutput) RenderShortcodes(ctx context.Context) (template.HT
|
||||
}
|
||||
|
||||
func (pco *pageContentOutput) Content(ctx context.Context) (any, error) {
|
||||
r, err := pco.po.p.content.contentRendered(ctx, pco)
|
||||
r, err := pco.po.p.m.content.contentRendered(ctx, pco)
|
||||
return r.content, err
|
||||
}
|
||||
|
||||
func (pco *pageContentOutput) TableOfContents(ctx context.Context) template.HTML {
|
||||
return pco.po.p.content.mustContentToC(ctx, pco).tableOfContentsHTML
|
||||
return pco.po.p.m.content.mustContentToC(ctx, pco).tableOfContentsHTML
|
||||
}
|
||||
|
||||
func (p *pageContentOutput) Len(ctx context.Context) int {
|
||||
@@ -182,7 +182,7 @@ func (p *pageContentOutput) Len(ctx context.Context) int {
|
||||
}
|
||||
|
||||
func (pco *pageContentOutput) mustContentRendered(ctx context.Context) contentSummary {
|
||||
r, err := pco.po.p.content.contentRendered(ctx, pco)
|
||||
r, err := pco.po.p.m.content.contentRendered(ctx, pco)
|
||||
if err != nil {
|
||||
pco.fail(err)
|
||||
}
|
||||
@@ -190,7 +190,7 @@ func (pco *pageContentOutput) mustContentRendered(ctx context.Context) contentSu
|
||||
}
|
||||
|
||||
func (pco *pageContentOutput) mustContentPlain(ctx context.Context) contentPlainPlainWords {
|
||||
r, err := pco.po.p.content.contentPlain(ctx, pco)
|
||||
r, err := pco.po.p.m.content.contentPlain(ctx, pco)
|
||||
if err != nil {
|
||||
pco.fail(err)
|
||||
}
|
||||
@@ -270,7 +270,7 @@ func (pco *pageContentOutput) RenderString(ctx context.Context, args ...any) (te
|
||||
}
|
||||
|
||||
conv := pco.po.p.getContentConverter()
|
||||
if opts.Markup != "" && opts.Markup != pco.po.p.m.markup {
|
||||
if opts.Markup != "" && opts.Markup != pco.po.p.m.pageConfig.Markup {
|
||||
var err error
|
||||
conv, err = pco.po.p.m.newContentConverter(pco.po.p, opts.Markup)
|
||||
if err != nil {
|
||||
@@ -281,6 +281,7 @@ func (pco *pageContentOutput) RenderString(ctx context.Context, args ...any) (te
|
||||
var rendered []byte
|
||||
|
||||
parseInfo := &contentParseInfo{
|
||||
h: pco.po.p.s.h,
|
||||
pid: pco.po.p.pid,
|
||||
}
|
||||
|
||||
@@ -293,7 +294,7 @@ func (pco *pageContentOutput) RenderString(ctx context.Context, args ...any) (te
|
||||
}
|
||||
|
||||
s := newShortcodeHandler(pco.po.p.pathOrTitle(), pco.po.p.s)
|
||||
if err := parseInfo.mapItems(contentToRenderb, s); err != nil {
|
||||
if err := parseInfo.mapItemsAfterFrontMatter(contentToRenderb, s); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -320,7 +321,7 @@ func (pco *pageContentOutput) RenderString(ctx context.Context, args ...any) (te
|
||||
|
||||
tokenHandler := func(ctx context.Context, token string) ([]byte, error) {
|
||||
if token == tocShortcodePlaceholder {
|
||||
toc, err := pco.po.p.content.contentToC(ctx, pco)
|
||||
toc, err := pco.po.p.m.content.contentToC(ctx, pco)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -350,7 +351,7 @@ func (pco *pageContentOutput) RenderString(ctx context.Context, args ...any) (te
|
||||
}
|
||||
|
||||
// We need a consolidated view in $page.HasShortcode
|
||||
pco.po.p.content.shortcodeState.transferNames(s)
|
||||
pco.po.p.m.content.shortcodeState.transferNames(s)
|
||||
|
||||
} else {
|
||||
c, err := pco.renderContentWithConverter(ctx, conv, []byte(contentToRender), false)
|
||||
@@ -411,7 +412,7 @@ func (pco *pageContentOutput) initRenderHooks() error {
|
||||
var renderCacheMu sync.Mutex
|
||||
|
||||
resolvePosition := func(ctx any) text.Position {
|
||||
source := pco.po.p.content.mustSource()
|
||||
source := pco.po.p.m.content.mustSource()
|
||||
var offset int
|
||||
|
||||
switch v := ctx.(type) {
|
||||
@@ -469,6 +470,21 @@ func (pco *pageContentOutput) initRenderHooks() error {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if found {
|
||||
if isitp, ok := templ.(tpl.IsInternalTemplateProvider); ok && isitp.IsInternalTemplate() {
|
||||
renderHookConfig := pco.po.p.s.conf.Markup.Goldmark.RenderHooks
|
||||
switch templ.Name() {
|
||||
case "_default/_markup/render-link.html":
|
||||
if !renderHookConfig.Link.IsEnableDefault() {
|
||||
return nil, false
|
||||
}
|
||||
case "_default/_markup/render-image.html":
|
||||
if !renderHookConfig.Image.IsEnableDefault() {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return templ, found
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ package hugolib
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hugo"
|
||||
"github.com/gohugoio/hugo/lazy"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
)
|
||||
@@ -52,7 +53,9 @@ func (p pagePosition) Next() page.Page {
|
||||
return p.next()
|
||||
}
|
||||
|
||||
// Deprecated: Use Next instead.
|
||||
func (p pagePosition) NextPage() page.Page {
|
||||
hugo.Deprecate(".Page.NextPage", "Use .Page.Next instead.", "v0.123.0")
|
||||
return p.Next()
|
||||
}
|
||||
|
||||
@@ -60,7 +63,9 @@ func (p pagePosition) Prev() page.Page {
|
||||
return p.prev()
|
||||
}
|
||||
|
||||
// Deprecated: Use Prev instead.
|
||||
func (p pagePosition) PrevPage() page.Page {
|
||||
hugo.Deprecate(".Page.PrevPage", "Use .Page.Prev instead.", "v0.123.0")
|
||||
return p.Prev()
|
||||
}
|
||||
|
||||
|
||||
+2
-33
@@ -698,12 +698,7 @@ title: "empty"
|
||||
|{{ .RawContent }}|
|
||||
`
|
||||
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/basic/index.html", "|**basic**|")
|
||||
b.AssertFileContent("public/empty/index.html", "! title")
|
||||
@@ -1545,32 +1540,6 @@ CONTENT:{{ .Content }}
|
||||
)
|
||||
}
|
||||
|
||||
// https://github.com/gohugoio/hugo/issues/5478
|
||||
func TestPageWithCommentedOutFrontMatter(t *testing.T) {
|
||||
b := newTestSitesBuilder(t)
|
||||
b.WithSimpleConfigFile()
|
||||
|
||||
b.WithContent("page.md", `<!--
|
||||
+++
|
||||
title = "hello"
|
||||
+++
|
||||
-->
|
||||
This is the content.
|
||||
`)
|
||||
|
||||
b.WithTemplatesAdded("layouts/_default/single.html", `
|
||||
Title: {{ .Title }}
|
||||
Content:{{ .Content }}
|
||||
`)
|
||||
|
||||
b.CreateSites().Build(BuildCfg{})
|
||||
|
||||
b.AssertFileContent("public/page/index.html",
|
||||
"Title: hello",
|
||||
"Content:<p>This is the content.</p>",
|
||||
)
|
||||
}
|
||||
|
||||
func TestHomePageWithNoTitle(t *testing.T) {
|
||||
b := newTestSitesBuilder(t).WithConfigFile("toml", `
|
||||
title = "Site Title"
|
||||
@@ -1718,7 +1687,7 @@ Single: {{ .Title}}|{{ .RelPermalink }}|{{ .Path }}|
|
||||
b := Test(t, files)
|
||||
b.AssertFileContent("public/sect/p1/index.html", "Single: Page1|/sect/p1/|/sect/p1")
|
||||
b.AssertFileContent("public/sect/PaGe2/index.html", "Single: Page2|/sect/PaGe2/|/sect/p2")
|
||||
b.AssertFileContent("public/sect2/page3/index.html", "Single: Page3|/sect2/page3/|/sect2/page3|")
|
||||
b.AssertFileContent("public/sect2/PaGe3/index.html", "Single: Page3|/sect2/PaGe3/|/sect2/page3|")
|
||||
b.AssertFileContent("public/sect3/Pag.E4/index.html", "Single: Pag.E4|/sect3/Pag.E4/|/sect3/p4|")
|
||||
}
|
||||
|
||||
|
||||
+100
-7
@@ -150,6 +150,8 @@ defaultContentLanguageInSubdir = true
|
||||
[languages]
|
||||
[languages.en]
|
||||
weight = 1
|
||||
[languages.en.permalinks]
|
||||
"/" = "/enpages/:slug/"
|
||||
[languages.nn]
|
||||
weight = 2
|
||||
-- content/mybundle/index.md --
|
||||
@@ -173,8 +175,8 @@ Resources: {{ range .Resources }}RelPermalink: {{ .RelPermalink }}|Content: {{ .
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/en/mybundle/index.html", "My Bundle|/en/mybundle/|en|\nResources: RelPermalink: /en/mybundle/f1.txt|Content: F1|RelPermalink: /en/mybundle/f2.txt|Content: F2||")
|
||||
b.AssertFileContent("public/nn/mybundle/index.html", "My Bundle NN|/nn/mybundle/|nn|\nResources: RelPermalink: /en/mybundle/f1.txt|Content: F1|RelPermalink: /nn/mybundle/f2.nn.txt|Content: F2 nn.||")
|
||||
b.AssertFileContent("public/en/enpages/my-bundle/index.html", "My Bundle|/en/enpages/my-bundle/|en|\nResources: RelPermalink: /en/enpages/my-bundle/f1.txt|Content: F1|RelPermalink: /en/enpages/my-bundle/f2.txt|Content: F2||")
|
||||
b.AssertFileContent("public/nn/mybundle/index.html", "My Bundle NN|/nn/mybundle/|nn|\nResources: RelPermalink: /en/enpages/my-bundle/f1.txt|Content: F1|RelPermalink: /nn/mybundle/f2.nn.txt|Content: F2 nn.||")
|
||||
}
|
||||
|
||||
func TestMultilingualDisableLanguage(t *testing.T) {
|
||||
@@ -186,30 +188,43 @@ baseURL = "https://example.com"
|
||||
disableKinds = ["taxonomy", "term"]
|
||||
defaultContentLanguage = "en"
|
||||
defaultContentLanguageInSubdir = true
|
||||
disableLanguages = ["nn"]
|
||||
[languages]
|
||||
[languages.en]
|
||||
weight = 1
|
||||
[languages.nn]
|
||||
weight = 2
|
||||
-- content/p1.md --
|
||||
disabled = true
|
||||
-- content/mysect/_index.md --
|
||||
---
|
||||
title: "My Sect En"
|
||||
---
|
||||
-- content/mysect/p1/index.md --
|
||||
---
|
||||
title: "P1"
|
||||
---
|
||||
P1
|
||||
-- content/p1.nn.md --
|
||||
-- content/mysect/_index.nn.md --
|
||||
---
|
||||
title: "My Sect Nn"
|
||||
---
|
||||
-- content/mysect/p1/index.nn.md --
|
||||
---
|
||||
title: "P1nn"
|
||||
---
|
||||
P1nn
|
||||
-- layouts/index.html --
|
||||
Len RegularPages: {{ len .Site.RegularPages }}|RegularPages: {{ range site.RegularPages }}{{ .RelPermalink }}: {{ .Title }}|{{ end }}|
|
||||
Len Pages: {{ len .Site.Pages }}|
|
||||
Len Sites: {{ len .Site.Sites }}|
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Title }}|{{ .Content }}|{{ .Lang }}|
|
||||
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/en/p1/index.html", "P1|<p>P1</p>\n|en|")
|
||||
b.AssertFileExists("public/public/nn/p1/index.html", false)
|
||||
b.AssertFileContent("public/en/index.html", "Len RegularPages: 1|")
|
||||
b.AssertFileContent("public/en/mysect/p1/index.html", "P1|<p>P1</p>\n|en|")
|
||||
b.AssertFileExists("public/public/nn/mysect/p1/index.html", false)
|
||||
b.Assert(len(b.H.Sites), qt.Equals, 1)
|
||||
}
|
||||
|
||||
@@ -742,3 +757,81 @@ func TestPageBundlerHome(t *testing.T) {
|
||||
Title: Home|First Resource: data.json|Content: <p>Hook Len Page Resources 1</p>
|
||||
`)
|
||||
}
|
||||
|
||||
func TestHTMLFilesIsue11999(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ["taxonomy", "term", "rss", "sitemap", "robotsTXT", "404"]
|
||||
[permalinks]
|
||||
posts = "/myposts/:slugorfilename"
|
||||
-- content/posts/markdown-without-frontmatter.md --
|
||||
-- content/posts/html-without-frontmatter.html --
|
||||
<html>hello</html>
|
||||
-- content/posts/html-with-frontmatter.html --
|
||||
---
|
||||
title: "HTML with frontmatter"
|
||||
---
|
||||
<html>hello</html>
|
||||
-- content/posts/html-with-commented-out-frontmatter.html --
|
||||
<!--
|
||||
---
|
||||
title: "HTML with commented out frontmatter"
|
||||
---
|
||||
-->
|
||||
<html>hello</html>
|
||||
-- content/posts/markdown-with-frontmatter.md --
|
||||
---
|
||||
title: "Markdown"
|
||||
---
|
||||
-- content/posts/mybundle/index.md --
|
||||
---
|
||||
title: My Bundle
|
||||
---
|
||||
-- content/posts/mybundle/data.txt --
|
||||
Data.txt
|
||||
-- content/posts/mybundle/html-in-bundle-without-frontmatter.html --
|
||||
<html>hell</html>
|
||||
-- content/posts/mybundle/html-in-bundle-with-frontmatter.html --
|
||||
---
|
||||
title: Hello
|
||||
---
|
||||
<html>hello</html>
|
||||
-- content/posts/mybundle/html-in-bundle-with-commented-out-frontmatter.html --
|
||||
<!--
|
||||
---
|
||||
title: "HTML with commented out frontmatter"
|
||||
---
|
||||
-->
|
||||
<html>hello</html>
|
||||
-- layouts/index.html --
|
||||
{{ range site.RegularPages }}{{ .RelPermalink }}|{{ end }}$
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Title }}|{{ .RelPermalink }}Resources: {{ range .Resources }}{{ .Name }}|{{ end }}$
|
||||
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "/myposts/html-with-commented-out-frontmatter/|/myposts/html-without-frontmatter/|/myposts/markdown-without-frontmatter/|/myposts/html-with-frontmatter/|/myposts/markdown-with-frontmatter/|/myposts/mybundle/|$")
|
||||
|
||||
b.AssertFileContent("public/myposts/mybundle/index.html",
|
||||
"My Bundle|/myposts/mybundle/Resources: html-in-bundle-with-commented-out-frontmatter.html|html-in-bundle-without-frontmatter.html|html-in-bundle-with-frontmatter.html|data.txt|$")
|
||||
|
||||
b.AssertPublishDir(`
|
||||
index.html
|
||||
myposts/html-with-commented-out-frontmatter
|
||||
myposts/html-with-commented-out-frontmatter/index.html
|
||||
myposts/html-with-frontmatter
|
||||
myposts/html-with-frontmatter/index.html
|
||||
myposts/html-without-frontmatter
|
||||
myposts/html-without-frontmatter/index.html
|
||||
myposts/markdown-with-frontmatter
|
||||
myposts/markdown-with-frontmatter/index.html
|
||||
myposts/markdown-without-frontmatter
|
||||
myposts/markdown-without-frontmatter/index.html
|
||||
myposts/mybundle/data.txt
|
||||
myposts/mybundle/index.html
|
||||
! myposts/mybundle/html-in-bundle-with-frontmatter.html
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func (c *pageFinder) getPageRef(context page.Page, ref string) (page.Page, error
|
||||
}
|
||||
|
||||
func (c *pageFinder) getPage(context page.Page, ref string) (page.Page, error) {
|
||||
n, err := c.getContentNode(context, false, filepath.ToSlash(ref))
|
||||
n, err := c.getContentNode(context, false, paths.ToSlashTrimTrailing(ref))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -413,6 +413,10 @@ title: p2
|
||||
func TestPageGetPageVariations(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
-- content/s1/_index.md --
|
||||
---
|
||||
title: s1 section
|
||||
---
|
||||
-- content/s1/p1/index.md --
|
||||
---
|
||||
title: p1
|
||||
@@ -430,6 +434,8 @@ title: p3
|
||||
title: p2_root
|
||||
---
|
||||
-- layouts/index.html --
|
||||
/s1: {{ with .GetPage "/s1" }}{{ .Title }}{{ end }}|
|
||||
/s1/: {{ with .GetPage "/s1/" }}{{ .Title }}{{ end }}|
|
||||
/s1/p2.md: {{ with .GetPage "/s1/p2.md" }}{{ .Title }}{{ end }}|
|
||||
/s1/p2: {{ with .GetPage "/s1/p2" }}{{ .Title }}{{ end }}|
|
||||
/s1/p1/index.md: {{ with .GetPage "/s1/p1/index.md" }}{{ .Title }}{{ end }}|
|
||||
@@ -444,6 +450,8 @@ p1/index.md: {{ with .GetPage "p1/index.md" }}{{ .Title }}{{ end }}|
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", `
|
||||
/s1: s1 section|
|
||||
/s1/: s1 section|
|
||||
/s1/p2.md: p2|
|
||||
/s1/p2: p2|
|
||||
/s1/p1/index.md: p1|
|
||||
|
||||
+15
-41
@@ -15,7 +15,6 @@ package hugolib
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -27,8 +26,6 @@ import (
|
||||
"github.com/bep/logg"
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
"github.com/gohugoio/hugo/common/rungroup"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/parser/pageparser"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/gohugoio/hugo/source"
|
||||
@@ -77,26 +74,6 @@ type pagesCollector struct {
|
||||
g rungroup.Group[hugofs.FileMetaInfo]
|
||||
}
|
||||
|
||||
func (c *pagesCollector) copyFile(fim hugofs.FileMetaInfo) error {
|
||||
meta := fim.Meta()
|
||||
f, err := meta.Open()
|
||||
if err != nil {
|
||||
return fmt.Errorf("copyFile: failed to open: %w", err)
|
||||
}
|
||||
|
||||
s := c.m.s
|
||||
|
||||
target := filepath.Join(s.PathSpec.GetTargetLanguageBasePath(), meta.PathInfo.Path())
|
||||
|
||||
defer f.Close()
|
||||
|
||||
fs := s.PublishFsStatic
|
||||
|
||||
s.PathSpec.ProcessingStats.Incr(&s.PathSpec.ProcessingStats.Files)
|
||||
|
||||
return helpers.WriteToDisk(filepath.Clean(target), f, fs)
|
||||
}
|
||||
|
||||
// Collect collects content by walking the file system and storing
|
||||
// it in the content tree.
|
||||
// It may be restricted by filenames set on the collector (partial build).
|
||||
@@ -136,14 +113,7 @@ func (c *pagesCollector) Collect() (collectErr error) {
|
||||
NumWorkers: numWorkers,
|
||||
Handle: func(ctx context.Context, fi hugofs.FileMetaInfo) error {
|
||||
if err := c.m.AddFi(fi); err != nil {
|
||||
if errors.Is(err, pageparser.ErrPlainHTMLDocumentsNotSupported) {
|
||||
// Reclassify this as a static file.
|
||||
if err := c.copyFile(fi); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return hugofs.AddFileInfoToError(err, fi, c.fs)
|
||||
}
|
||||
return hugofs.AddFileInfoToError(err, fi, c.fs)
|
||||
}
|
||||
numFilesProcessedTotal.Add(1)
|
||||
if numFilesProcessedTotal.Load()%1000 == 0 {
|
||||
@@ -196,6 +166,11 @@ func (c *pagesCollector) Collect() (collectErr error) {
|
||||
|
||||
return id.p.Dir() == fim.Meta().PathInfo.Dir()
|
||||
}
|
||||
|
||||
if fim.Meta().PathInfo.IsLeafBundle() && id.p.BundleType() == paths.PathTypeContentSingle {
|
||||
return id.p.Dir() == fim.Meta().PathInfo.Dir()
|
||||
}
|
||||
|
||||
return id.p.Path() == fim.Meta().PathInfo.Path()
|
||||
})
|
||||
}
|
||||
@@ -249,9 +224,6 @@ func (c *pagesCollector) collectDir(dirPath *paths.Path, isDir bool, inFilter fu
|
||||
|
||||
func (c *pagesCollector) collectDirDir(path string, root hugofs.FileMetaInfo, inFilter func(fim hugofs.FileMetaInfo) bool) error {
|
||||
filter := func(fim hugofs.FileMetaInfo) bool {
|
||||
if c.sp.IgnoreFile(fim.Meta().Filename) {
|
||||
return false
|
||||
}
|
||||
if inFilter != nil {
|
||||
return inFilter(fim)
|
||||
}
|
||||
@@ -330,13 +302,14 @@ func (c *pagesCollector) collectDirDir(path string, root hugofs.FileMetaInfo, in
|
||||
|
||||
w := hugofs.NewWalkway(
|
||||
hugofs.WalkwayConfig{
|
||||
Logger: c.logger,
|
||||
Root: path,
|
||||
Info: root,
|
||||
Fs: c.fs,
|
||||
HookPre: preHook,
|
||||
HookPost: postHook,
|
||||
WalkFn: wfn,
|
||||
Logger: c.logger,
|
||||
Root: path,
|
||||
Info: root,
|
||||
Fs: c.fs,
|
||||
IgnoreFile: c.h.SourceSpec.IgnoreFile,
|
||||
HookPre: preHook,
|
||||
HookPost: postHook,
|
||||
WalkFn: wfn,
|
||||
})
|
||||
|
||||
return w.Walk()
|
||||
@@ -371,6 +344,7 @@ func (c *pagesCollector) handleBundleLeaf(dir, bundle hugofs.FileMetaInfo, inPat
|
||||
Logger: c.logger,
|
||||
Info: dir,
|
||||
DirEntries: readdir,
|
||||
IgnoreFile: c.h.SourceSpec.IgnoreFile,
|
||||
WalkFn: walk,
|
||||
})
|
||||
|
||||
|
||||
@@ -153,12 +153,32 @@ Len: {{ len $empty }}: Type: {{ printf "%T" $empty }}
|
||||
{{ $pag := .Paginate $pgs }}
|
||||
Len Pag: {{ len $pag.Pages }}
|
||||
`
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "Len: 0", "Len Pag: 0")
|
||||
}
|
||||
|
||||
func TestPaginatorNodePagesOnly(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
paginate = 1
|
||||
-- content/p1.md --
|
||||
-- layouts/_default/single.html --
|
||||
Paginator: {{ .Paginator }}
|
||||
`
|
||||
b, err := TestE(t, files)
|
||||
b.Assert(err, qt.IsNotNil)
|
||||
b.Assert(err.Error(), qt.Contains, `error calling Paginator: pagination not supported for this page: kind: "page"`)
|
||||
}
|
||||
|
||||
func TestNilPointerErrorMessage(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
-- content/p1.md --
|
||||
-- layouts/_default/single.html --
|
||||
Home Filename: {{ site.Home.File.Filename }}
|
||||
`
|
||||
b, err := TestE(t, files)
|
||||
b.Assert(err, qt.IsNotNil)
|
||||
b.Assert(err.Error(), qt.Contains, `_default/single.html:1:22: executing "_default/single.html" – File is nil; wrap it in if or with: {{ with site.Home.File }}{{ .Filename }}{{ end }}`)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
// 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 (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestFrontMatterParamsInItsOwnSection(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.org/"
|
||||
-- content/_index.md --
|
||||
+++
|
||||
title = "Home"
|
||||
[[cascade]]
|
||||
background = 'yosemite.jpg'
|
||||
[cascade.params]
|
||||
a = "home-a"
|
||||
b = "home-b"
|
||||
[cascade._target]
|
||||
kind = 'page'
|
||||
+++
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "P1"
|
||||
summary: "frontmatter.summary"
|
||||
params:
|
||||
a: "p1-a"
|
||||
summary: "params.summary"
|
||||
---
|
||||
-- layouts/_default/single.html --
|
||||
Params: {{ range $k, $v := .Params }}{{ $k }}: {{ $v }}|{{ end }}$
|
||||
Summary: {{ .Summary }}|
|
||||
`
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/p1/index.html",
|
||||
"Params: a: p1-a|b: home-b|background: yosemite.jpg|draft: false|iscjklanguage: false|summary: params.summary|title: P1|$",
|
||||
"Summary: frontmatter.summary|",
|
||||
)
|
||||
}
|
||||
|
||||
func TestFrontMatterParamsKindPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.org/"
|
||||
disableKinds = ["taxonomy", "term"]
|
||||
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "P1"
|
||||
date: 2019-08-07
|
||||
path: "/a/b/c"
|
||||
slug: "s1"
|
||||
---
|
||||
-- content/mysection.md --
|
||||
---
|
||||
title: "My Section"
|
||||
kind: "section"
|
||||
date: 2022-08-07
|
||||
path: "/a/b"
|
||||
---
|
||||
-- layouts/index.html --
|
||||
RegularPages: {{ range site.RegularPages }}{{ .Path }}|{{ .RelPermalink }}|{{ .Title }}|{{ .Date.Format "2006-02-01" }}| Slug: {{ .Params.slug }}|{{ end }}$
|
||||
Sections: {{ range site.Sections }}{{ .Path }}|{{ .RelPermalink }}|{{ .Title }}|{{ .Date.Format "2006-02-01" }}| Slug: {{ .Params.slug }}|{{ end }}$
|
||||
{{ $ab := site.GetPage "a/b" }}
|
||||
a/b pages: {{ range $ab.RegularPages }}{{ .Path }}|{{ .RelPermalink }}|{{ end }}$
|
||||
`
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html",
|
||||
"RegularPages: /a/b/c|/a/b/s1/|P1|2019-07-08| Slug: s1|$",
|
||||
"Sections: /a|/a/|As",
|
||||
"a/b pages: /a/b/c|/a/b/s1/|$",
|
||||
)
|
||||
}
|
||||
|
||||
func TestFrontMatterParamsLang(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.org/"
|
||||
disableKinds = ["taxonomy", "term"]
|
||||
defaultContentLanguage = "en"
|
||||
defaultContentLanguageInSubdir = true
|
||||
[languages]
|
||||
[languages.en]
|
||||
weight = 1
|
||||
[languages.nn]
|
||||
weight = 2
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "P1 nn"
|
||||
lang: "nn"
|
||||
---
|
||||
-- content/p2.md --
|
||||
---
|
||||
title: "P2"
|
||||
---
|
||||
-- layouts/index.html --
|
||||
RegularPages: {{ range site.RegularPages }}{{ .Path }}|{{ .RelPermalink }}|{{ .Title }}|{{ end }}$
|
||||
|
||||
`
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/en/index.html",
|
||||
"RegularPages: /p2|/en/p2/|P2|$",
|
||||
)
|
||||
b.AssertFileContent("public/nn/index.html",
|
||||
"RegularPages: /p1|/nn/p1/|P1 nn|$",
|
||||
)
|
||||
}
|
||||
|
||||
func TestFrontMatterTitleOverrideWarn(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.org/"
|
||||
disableKinds = ["taxonomy", "term"]
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "My title"
|
||||
params:
|
||||
title: "My title from params"
|
||||
---
|
||||
|
||||
|
||||
`
|
||||
|
||||
b := Test(t, files, TestOptWarn())
|
||||
|
||||
b.AssertLogContains("ARN Hugo front matter key \"title\" is overridden in params section", "You can suppress this warning")
|
||||
}
|
||||
|
||||
func TestFrontMatterParamsLangNoCascade(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.org/"
|
||||
disableKinds = ["taxonomy", "term"]
|
||||
defaultContentLanguage = "en"
|
||||
defaultContentLanguageInSubdir = true
|
||||
[languages]
|
||||
[languages.en]
|
||||
weight = 1
|
||||
[languages.nn]
|
||||
weight = 2
|
||||
-- content/_index.md --
|
||||
+++
|
||||
[[cascade]]
|
||||
background = 'yosemite.jpg'
|
||||
lang = 'nn'
|
||||
+++
|
||||
|
||||
`
|
||||
|
||||
b, err := TestE(t, files)
|
||||
b.Assert(err, qt.IsNotNil)
|
||||
}
|
||||
|
||||
// Issue 11970.
|
||||
func TestFrontMatterBuildIsHugoKeyword(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.org/"
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "P1"
|
||||
build: "foo"
|
||||
---
|
||||
-- layouts/_default/single.html --
|
||||
Params: {{ range $k, $v := .Params }}{{ $k }}: {{ $v }}|{{ end }}$
|
||||
`
|
||||
b, err := TestE(t, files)
|
||||
|
||||
b.Assert(err, qt.IsNotNil)
|
||||
b.Assert(err.Error(), qt.Contains, "We renamed the _build keyword")
|
||||
}
|
||||
+11
-4
@@ -39,7 +39,7 @@ My Section Bundle Text 2 Content.
|
||||
---
|
||||
title: "My Section Bundle Content"
|
||||
---
|
||||
My Section Bundle Content.
|
||||
My Section Bundle Content Content.
|
||||
-- content/mysection/_index.md --
|
||||
---
|
||||
title: "My Section"
|
||||
@@ -68,7 +68,7 @@ Foo.
|
||||
func TestRebuildEditTextFileInLeafBundle(t *testing.T) {
|
||||
b := TestRunning(t, rebuildFilesSimple)
|
||||
b.AssertFileContent("public/mysection/mysectionbundle/index.html",
|
||||
"Resources: 0:/mysection/mysectionbundle/mysectionbundletext.txt|My Section Bundle Text 2 Content.|1:|<p>My Section Bundle Content.</p>\n|$")
|
||||
"Resources: 0:/mysection/mysectionbundle/mysectionbundletext.txt|My Section Bundle Text 2 Content.|1:|<p>My Section Bundle Content Content.</p>\n|$")
|
||||
|
||||
b.EditFileReplaceAll("content/mysection/mysectionbundle/mysectionbundletext.txt", "Content.", "Content Edited.").Build()
|
||||
b.AssertFileContent("public/mysection/mysectionbundle/index.html",
|
||||
@@ -101,14 +101,21 @@ func TestRebuildEditTextFileInBranchBundle(t *testing.T) {
|
||||
|
||||
func TestRebuildRenameTextFileInLeafBundle(t *testing.T) {
|
||||
b := TestRunning(t, rebuildFilesSimple)
|
||||
b.AssertFileContent("public/mysection/mysectionbundle/index.html", "My Section Bundle Text 2 Content.")
|
||||
b.AssertFileContent("public/mysection/mysectionbundle/index.html", "My Section Bundle Text 2 Content.", "Len Resources: 2|")
|
||||
|
||||
b.RenameFile("content/mysection/mysectionbundle/mysectionbundletext.txt", "content/mysection/mysectionbundle/mysectionbundletext2.txt").Build()
|
||||
b.AssertFileContent("public/mysection/mysectionbundle/index.html", "mysectionbundletext2", "My Section Bundle Text 2 Content.")
|
||||
b.AssertFileContent("public/mysection/mysectionbundle/index.html", "mysectionbundletext2", "My Section Bundle Text 2 Content.", "Len Resources: 2|")
|
||||
b.AssertRenderCountPage(3)
|
||||
b.AssertRenderCountContent(3)
|
||||
}
|
||||
|
||||
func TestRebuilEditContentFileInLeafBundle(t *testing.T) {
|
||||
b := TestRunning(t, rebuildFilesSimple)
|
||||
b.AssertFileContent("public/mysection/mysectionbundle/index.html", "My Section Bundle Content Content.")
|
||||
b.EditFileReplaceAll("content/mysection/mysectionbundle/mysectionbundlecontent.md", "Content Content.", "Content Content Edited.").Build()
|
||||
b.AssertFileContent("public/mysection/mysectionbundle/index.html", "My Section Bundle Content Content Edited.")
|
||||
}
|
||||
|
||||
func TestRebuildRenameTextFileInBranchBundle(t *testing.T) {
|
||||
b := TestRunning(t, rebuildFilesSimple)
|
||||
b.AssertFileContent("public/mysection/index.html", "My Section")
|
||||
|
||||
@@ -67,12 +67,7 @@ HasShortcode not found: {{ .HasShortcode "notfound" }}|
|
||||
Content: {{ .Content }}|
|
||||
`
|
||||
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/p1/index.html",
|
||||
"Fragments: [p1-h1 p2-h1 p2-h2 p2-h3 p2-withmarkdown p3-h1 p3-h2 p3-withmarkdown]|",
|
||||
@@ -118,12 +113,7 @@ JSON: {{ .Content }}
|
||||
|
||||
`
|
||||
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", "Myshort HTML")
|
||||
b.AssertFileContent("public/p1/index.json", "Myshort JSON")
|
||||
|
||||
@@ -172,12 +172,7 @@ Has other: {{ .HasShortcode "other" }}
|
||||
|
||||
`
|
||||
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html",
|
||||
`
|
||||
@@ -213,12 +208,7 @@ title: "P1"
|
||||
{{ .Content }}
|
||||
`
|
||||
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", `TableOfContents`)
|
||||
}
|
||||
|
||||
@@ -315,7 +315,7 @@ func prepareShortcode(
|
||||
isRenderString bool,
|
||||
) (shortcodeRenderer, error) {
|
||||
toParseErr := func(err error) error {
|
||||
source := p.content.mustSource()
|
||||
source := p.m.content.mustSource()
|
||||
return p.parseError(fmt.Errorf("failed to render shortcode %q: %w", sc.name, err), source, sc.pos)
|
||||
}
|
||||
|
||||
@@ -443,7 +443,7 @@ func doRenderShortcode(
|
||||
// unchanged.
|
||||
// 2 If inner does not have a newline, strip the wrapping <p> block and
|
||||
// the newline.
|
||||
switch p.m.markup {
|
||||
switch p.m.pageConfig.Markup {
|
||||
case "", "markdown":
|
||||
if match, _ := regexp.MatchString(innerNewlineRegexp, inner); !match {
|
||||
cleaner, err := regexp.Compile(innerCleanupRegexp)
|
||||
|
||||
@@ -916,12 +916,7 @@ title: "p1"
|
||||
{{ .Content }}
|
||||
`
|
||||
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", `
|
||||
<x
|
||||
@@ -957,12 +952,7 @@ title: "p1"
|
||||
{{ .Content }}
|
||||
`
|
||||
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", "<ol>\n<li>\n<p>List 1</p>\n<ol>\n<li>Item Mark1 1</li>\n<li>Item Mark1 2</li>\n<li>Item Mark2 1</li>\n<li>Item Mark2 2\n<ol>\n<li>Item Mark2 2-1</li>\n</ol>\n</li>\n<li>Item Mark2 3</li>\n</ol>\n</li>\n</ol>")
|
||||
}
|
||||
@@ -987,12 +977,7 @@ echo "foo";
|
||||
{{ .Content }}
|
||||
`
|
||||
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", "<pre><code>echo "foo";\n</code></pre>")
|
||||
}
|
||||
@@ -1023,12 +1008,7 @@ title: "p1"
|
||||
{{ .Content }}
|
||||
`
|
||||
|
||||
b := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
},
|
||||
).Build()
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", `
|
||||
<pre><code> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">line 1<span class="p">;</span>
|
||||
|
||||
+3
-1
@@ -40,6 +40,7 @@ import (
|
||||
"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"
|
||||
@@ -123,7 +124,7 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
|
||||
Stdout: cfg.LogOut,
|
||||
Stderr: cfg.LogOut,
|
||||
StoreErrors: conf.Running(),
|
||||
SuppressStatements: conf.IgnoredErrors(),
|
||||
SuppressStatements: conf.IgnoredLogs(),
|
||||
}
|
||||
logger = loggers.New(logOpts)
|
||||
}
|
||||
@@ -281,6 +282,7 @@ func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []
|
||||
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),
|
||||
|
||||
@@ -330,8 +330,7 @@ func TestGetOutputFormatRel(t *testing.T) {
|
||||
b := newTestSitesBuilder(t).
|
||||
WithSimpleConfigFileAndSettings(map[string]any{
|
||||
"outputFormats": map[string]any{
|
||||
"humansTXT": map[string]any{
|
||||
"name": "HUMANS",
|
||||
"HUMANS": map[string]any{
|
||||
"mediaType": "text/plain",
|
||||
"baseName": "humans",
|
||||
"isPlainText": true,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user