mirror of
https://github.com/gohugoio/hugo.git
synced 2026-09-02 03:32:38 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 629f84e8ed | |||
| 76ef3f42fa | |||
| 0ccb6cdc04 | |||
| 1f1c62e6c7 | |||
| f1d755965f | |||
| 558f74f009 | |||
| ba03114aa9 | |||
| 3935faa417 | |||
| d4d49e0f0e | |||
| 78178d0c2a | |||
| 68d92ef9dd | |||
| b40f3c7df6 | |||
| 57206e7274 | |||
| b1f8676347 | |||
| 07b2e535be | |||
| f038a51b3e | |||
| b4bff6190c | |||
| d2cebee273 | |||
| be914ff34d | |||
| 48a0fea87a | |||
| 9ca1de09dd | |||
| e62675002e | |||
| 9668759ad8 | |||
| 9e9b1f110c | |||
| dc6a292133 | |||
| 4f92f949ea | |||
| d24ffdde5b | |||
| ca31b95f30 |
@@ -4,7 +4,7 @@ parameters:
|
||||
defaults: &defaults
|
||||
resource_class: large
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser:1.22200.20000
|
||||
- image: bepsays/ci-hugoreleaser:1.22200.20100
|
||||
environment: &buildenv
|
||||
GOMODCACHE: /root/project/gomodcache
|
||||
version: 2
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
environment:
|
||||
<<: [*buildenv]
|
||||
docker:
|
||||
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22200.20000
|
||||
- image: bepsays/ci-hugoreleaser-linux-arm64:1.22200.20100
|
||||
steps:
|
||||
- *restore-cache
|
||||
- &attach-workspace
|
||||
|
||||
@@ -521,6 +521,7 @@ func applyLocalFlagsBuildConfig(cmd *cobra.Command, r *rootCommand) {
|
||||
cmd.Flags().StringP("cacheDir", "", "", "filesystem path to cache directory")
|
||||
_ = cmd.Flags().SetAnnotation("cacheDir", cobra.BashCompSubdirsInDir, []string{})
|
||||
cmd.Flags().StringP("contentDir", "c", "", "filesystem path to content directory")
|
||||
cmd.Flags().StringSliceP("renderSegments", "", []string{}, "named segments to render (configured in the segments config)")
|
||||
_ = cmd.Flags().SetAnnotation("theme", cobra.BashCompSubdirsInDir, []string{"themes"})
|
||||
}
|
||||
|
||||
|
||||
+6
-7
@@ -617,9 +617,9 @@ func (c *serverCommand) setBaseURLsInConfig() error {
|
||||
}
|
||||
return c.withConfE(func(conf *commonConfig) error {
|
||||
for i, language := range conf.configs.Languages {
|
||||
isMultiHost := conf.configs.IsMultihost
|
||||
isMultihost := conf.configs.IsMultihost
|
||||
var serverPort int
|
||||
if isMultiHost {
|
||||
if isMultihost {
|
||||
serverPort = c.serverPorts[i].p
|
||||
} else {
|
||||
serverPort = c.serverPorts[0].p
|
||||
@@ -737,9 +737,9 @@ func (c *serverCommand) createServerPorts(cd *simplecobra.Commandeer) error {
|
||||
flags := cd.CobraCommand.Flags()
|
||||
var cerr error
|
||||
c.withConf(func(conf *commonConfig) {
|
||||
isMultiHost := conf.configs.IsMultihost
|
||||
isMultihost := conf.configs.IsMultihost
|
||||
c.serverPorts = make([]serverPortListener, 1)
|
||||
if isMultiHost {
|
||||
if isMultihost {
|
||||
if !c.serverAppend {
|
||||
cerr = errors.New("--appendPort=false not supported when in multihost mode")
|
||||
return
|
||||
@@ -852,7 +852,7 @@ func (c *serverCommand) serve() error {
|
||||
h *hugolib.HugoSites
|
||||
)
|
||||
err := c.withConfE(func(conf *commonConfig) error {
|
||||
isMultiHost := conf.configs.IsMultihost
|
||||
isMultihost := conf.configs.IsMultihost
|
||||
var err error
|
||||
h, err = c.r.HugFromConfig(conf)
|
||||
if err != nil {
|
||||
@@ -862,7 +862,7 @@ func (c *serverCommand) serve() error {
|
||||
// We need the server to share the same logger as the Hugo build (for error counts etc.)
|
||||
c.r.logger = h.Log
|
||||
|
||||
if isMultiHost {
|
||||
if isMultihost {
|
||||
for _, l := range conf.configs.ConfigLangs() {
|
||||
baseURLs = append(baseURLs, l.BaseURL())
|
||||
roots = append(roots, l.Language().Lang)
|
||||
@@ -1005,7 +1005,6 @@ func (c *serverCommand) serve() error {
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
c.r.Println("Error:", err)
|
||||
}
|
||||
|
||||
+13
-1
@@ -111,17 +111,29 @@ func (i HugoInfo) Deps() []*Dependency {
|
||||
return i.deps
|
||||
}
|
||||
|
||||
// IsMultiHost reports whether each configured language has a unique baseURL.
|
||||
// Deprecated: Use hugo.IsMultihost instead.
|
||||
func (i HugoInfo) IsMultiHost() bool {
|
||||
Deprecate("hugo.IsMultiHost", "Use hugo.IsMultihost instead.", "v0.124.0")
|
||||
return i.conf.IsMultihost()
|
||||
}
|
||||
|
||||
// IsMultihost reports whether each configured language has a unique baseURL.
|
||||
func (i HugoInfo) IsMultihost() bool {
|
||||
return i.conf.IsMultihost()
|
||||
}
|
||||
|
||||
// IsMultilingual reports whether there are two or more configured languages.
|
||||
func (i HugoInfo) IsMultilingual() bool {
|
||||
return i.conf.IsMultilingual()
|
||||
}
|
||||
|
||||
// ConfigProvider represents the config options that are relevant for HugoInfo.
|
||||
type ConfigProvider interface {
|
||||
Environment() string
|
||||
Running() bool
|
||||
WorkingDir() string
|
||||
IsMultihost() bool
|
||||
IsMultilingual() bool
|
||||
}
|
||||
|
||||
// NewInfo creates a new Hugo Info object.
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// 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 hugo_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
)
|
||||
|
||||
func TestIsMultilingualAndIsMultihost(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
||||
defaultContentLanguageInSubdir = true
|
||||
[languages.de]
|
||||
baseURL = 'https://de.example.org/'
|
||||
[languages.en]
|
||||
baseURL = 'https://en.example.org/'
|
||||
-- content/_index.md --
|
||||
---
|
||||
title: home
|
||||
---
|
||||
-- layouts/index.html --
|
||||
multilingual={{ hugo.IsMultilingual }}
|
||||
multihost={{ hugo.IsMultihost }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/de/index.html",
|
||||
"multilingual=true",
|
||||
"multihost=true",
|
||||
)
|
||||
b.AssertFileContent("public/en/index.html",
|
||||
"multilingual=true",
|
||||
"multihost=true",
|
||||
)
|
||||
|
||||
files = strings.ReplaceAll(files, "baseURL = 'https://de.example.org/'", "")
|
||||
files = strings.ReplaceAll(files, "baseURL = 'https://en.example.org/'", "")
|
||||
|
||||
b = hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/de/index.html",
|
||||
"multilingual=true",
|
||||
"multihost=false",
|
||||
)
|
||||
b.AssertFileContent("public/en/index.html",
|
||||
"multilingual=true",
|
||||
"multihost=false",
|
||||
)
|
||||
|
||||
files = strings.ReplaceAll(files, "[languages.de]", "")
|
||||
files = strings.ReplaceAll(files, "[languages.en]", "")
|
||||
|
||||
b = hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/en/index.html",
|
||||
"multilingual=false",
|
||||
"multihost=false",
|
||||
)
|
||||
}
|
||||
@@ -65,10 +65,11 @@ func TestDeprecationLogLevelFromVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
type testConfig struct {
|
||||
environment string
|
||||
running bool
|
||||
workingDir string
|
||||
multihost bool
|
||||
environment string
|
||||
running bool
|
||||
workingDir string
|
||||
multihost bool
|
||||
multilingual bool
|
||||
}
|
||||
|
||||
func (c testConfig) Environment() string {
|
||||
@@ -86,3 +87,7 @@ func (c testConfig) WorkingDir() string {
|
||||
func (c testConfig) IsMultihost() bool {
|
||||
return c.multihost
|
||||
}
|
||||
|
||||
func (c testConfig) IsMultilingual() bool {
|
||||
return c.multilingual
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ package hugo
|
||||
// This should be the only one.
|
||||
var CurrentVersion = Version{
|
||||
Major: 0,
|
||||
Minor: 123,
|
||||
PatchLevel: 8,
|
||||
Minor: 124,
|
||||
PatchLevel: 0,
|
||||
Suffix: "",
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ const (
|
||||
// E.g. /blog/my-post.md
|
||||
PathTypeContentSingle
|
||||
|
||||
// All bewlow are bundled content files.
|
||||
// All below are bundled content files.
|
||||
|
||||
// Leaf bundles, e.g. /blog/my-post/index.md
|
||||
PathTypeLeaf
|
||||
@@ -313,7 +313,7 @@ func (p *Path) norm(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
// IdentifierBase satifies identity.Identity.
|
||||
// IdentifierBase satisfies identity.Identity.
|
||||
func (p *Path) IdentifierBase() string {
|
||||
return p.Base()
|
||||
}
|
||||
@@ -368,7 +368,7 @@ func (p *Path) Name() string {
|
||||
return p.s
|
||||
}
|
||||
|
||||
// Name returns the last element of path withhout any extension.
|
||||
// Name returns the last element of path without any extension.
|
||||
func (p *Path) NameNoExt() string {
|
||||
if i := p.identifierIndex(0); i != -1 {
|
||||
return p.s[p.posContainerHigh : p.identifiers[i].Low-1]
|
||||
@@ -376,7 +376,7 @@ func (p *Path) NameNoExt() string {
|
||||
return p.s[p.posContainerHigh:]
|
||||
}
|
||||
|
||||
// Name returns the last element of path withhout any language identifier.
|
||||
// Name returns the last element of path without any language identifier.
|
||||
func (p *Path) NameNoLang() string {
|
||||
i := p.identifierIndex(p.posIdentifierLanguage)
|
||||
if i == -1 {
|
||||
@@ -386,7 +386,7 @@ func (p *Path) NameNoLang() string {
|
||||
return p.s[p.posContainerHigh:p.identifiers[i].Low-1] + p.s[p.identifiers[i].High:]
|
||||
}
|
||||
|
||||
// BaseNameNoIdentifier returns the logcical base name for a resource without any idenifier (e.g. no extension).
|
||||
// BaseNameNoIdentifier returns the logical base name for a resource without any identifier (e.g. no extension).
|
||||
// For bundles this will be the containing directory's name, e.g. "blog".
|
||||
func (p *Path) BaseNameNoIdentifier() string {
|
||||
if p.IsBundle() {
|
||||
@@ -395,7 +395,7 @@ func (p *Path) BaseNameNoIdentifier() string {
|
||||
return p.NameNoIdentifier()
|
||||
}
|
||||
|
||||
// NameNoIdentifier returns the last element of path withhout any identifier (e.g. no extension).
|
||||
// NameNoIdentifier returns the last element of path without any identifier (e.g. no extension).
|
||||
func (p *Path) NameNoIdentifier() string {
|
||||
if len(p.identifiers) > 0 {
|
||||
return p.s[p.posContainerHigh : p.identifiers[len(p.identifiers)-1].Low-1]
|
||||
@@ -435,7 +435,7 @@ func (p *Path) PathNoIdentifier() string {
|
||||
return p.base(false, false)
|
||||
}
|
||||
|
||||
// PathRel returns the path relativeto the given owner.
|
||||
// PathRel returns the path relative to the given owner.
|
||||
func (p *Path) PathRel(owner *Path) string {
|
||||
ob := owner.Base()
|
||||
if !strings.HasSuffix(ob, "/") {
|
||||
|
||||
@@ -99,6 +99,14 @@ type Unwrapper interface {
|
||||
Unwrapv() any
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying value of v if it implements Unwrapper, otherwise v is returned.
|
||||
func Unwrapv(v any) any {
|
||||
if u, ok := v.(Unwrapper); ok {
|
||||
return u.Unwrapv()
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// LowHigh is typically used to represent a slice boundary.
|
||||
type LowHigh struct {
|
||||
Low int
|
||||
|
||||
@@ -39,6 +39,7 @@ import (
|
||||
"github.com/gohugoio/hugo/config/services"
|
||||
"github.com/gohugoio/hugo/deploy/deployconfig"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugolib/segments"
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
"github.com/gohugoio/hugo/markup/markup_config"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
@@ -103,9 +104,11 @@ type Config struct {
|
||||
RootConfig
|
||||
|
||||
// Author information.
|
||||
// Deprecated: Use taxonomies instead.
|
||||
Author map[string]any
|
||||
|
||||
// Social links.
|
||||
// Deprecated: Use .Site.Params instead.
|
||||
Social map[string]string
|
||||
|
||||
// The build configuration section contains build-related configuration options.
|
||||
@@ -137,6 +140,9 @@ type Config struct {
|
||||
// a slice of page matcher and params to apply to those pages.
|
||||
Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, map[page.PageMatcher]maps.Params] `mapstructure:"-"`
|
||||
|
||||
// The segments defines segments for the site. Used for partial/segmented builds.
|
||||
Segments *config.ConfigNamespace[map[string]segments.SegmentConfig, segments.Segments] `mapstructure:"-"`
|
||||
|
||||
// Menu configuration.
|
||||
// <docsmeta>{"refs": ["config:languages:menus"] }</docsmeta>
|
||||
Menus *config.ConfigNamespace[map[string]navigation.MenuConfig, navigation.Menus] `mapstructure:"-"`
|
||||
@@ -364,6 +370,7 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
|
||||
CreateTitle: helpers.GetTitleFunc(c.TitleCaseStyle),
|
||||
IsUglyURLSection: isUglyURL,
|
||||
IgnoreFile: ignoreFile,
|
||||
SegmentFilter: c.Segments.Config.Get(func(s string) { logger.Warnf("Render segment %q not found in configuration", s) }, c.RootConfig.RenderSegments...),
|
||||
MainSections: c.MainSections,
|
||||
Clock: clock,
|
||||
transientErr: transientErr,
|
||||
@@ -400,6 +407,7 @@ type ConfigCompiled struct {
|
||||
CreateTitle func(s string) string
|
||||
IsUglyURLSection func(section string) bool
|
||||
IgnoreFile func(filename string) bool
|
||||
SegmentFilter segments.SegmentFilter
|
||||
MainSections []string
|
||||
Clock time.Time
|
||||
|
||||
@@ -472,6 +480,10 @@ type RootConfig struct {
|
||||
// A list of languages to disable.
|
||||
DisableLanguages []string
|
||||
|
||||
// The named segments to render.
|
||||
// This needs to match the name of the segment in the segments configuration.
|
||||
RenderSegments []string
|
||||
|
||||
// Disable the injection of the Hugo generator tag on the home page.
|
||||
DisableHugoGeneratorInject bool
|
||||
|
||||
@@ -826,7 +838,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
||||
langConfigMap := make(map[string]*Config)
|
||||
|
||||
languagesConfig := cfg.GetStringMap("languages")
|
||||
var isMultiHost bool
|
||||
var isMultihost bool
|
||||
|
||||
if err := all.CompileConfig(logger); err != nil {
|
||||
return nil, err
|
||||
@@ -863,7 +875,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
||||
}
|
||||
if kk == "baseurl" {
|
||||
// baseURL configure don the language level is a multihost setup.
|
||||
isMultiHost = true
|
||||
isMultihost = true
|
||||
}
|
||||
mergedConfig.Set(kk, vv)
|
||||
rootv := cfg.Get(kk)
|
||||
@@ -913,7 +925,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
||||
}
|
||||
|
||||
// Adjust Goldmark config defaults for multilingual, single-host sites.
|
||||
if len(languagesConfig) > 1 && !isMultiHost && !clone.Markup.Goldmark.DuplicateResourceFiles {
|
||||
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)
|
||||
@@ -943,7 +955,7 @@ func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadCon
|
||||
Base: all,
|
||||
LanguageConfigMap: langConfigMap,
|
||||
LoadingInfo: res,
|
||||
IsMultihost: isMultiHost,
|
||||
IsMultihost: isMultihost,
|
||||
}
|
||||
|
||||
return cm, nil
|
||||
|
||||
@@ -25,11 +25,13 @@ import (
|
||||
"github.com/gohugoio/hugo/config/security"
|
||||
"github.com/gohugoio/hugo/config/services"
|
||||
"github.com/gohugoio/hugo/deploy/deployconfig"
|
||||
"github.com/gohugoio/hugo/hugolib/segments"
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
"github.com/gohugoio/hugo/markup/markup_config"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/gohugoio/hugo/minifiers"
|
||||
"github.com/gohugoio/hugo/modules"
|
||||
|
||||
"github.com/gohugoio/hugo/navigation"
|
||||
"github.com/gohugoio/hugo/output"
|
||||
"github.com/gohugoio/hugo/related"
|
||||
@@ -120,6 +122,14 @@ var allDecoderSetups = map[string]decodeWeight{
|
||||
return err
|
||||
},
|
||||
},
|
||||
"segments": {
|
||||
key: "segments",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
var err error
|
||||
p.c.Segments, err = segments.DecodeSegments(p.p.GetStringMap(d.key))
|
||||
return err
|
||||
},
|
||||
},
|
||||
"server": {
|
||||
key: "server",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
|
||||
@@ -52,7 +52,7 @@ func (c ConfigLanguage) LanguagePrefix() string {
|
||||
return c.Language().Lang
|
||||
}
|
||||
|
||||
if !c.IsMultiLingual() || c.DefaultContentLanguage() == c.Language().Lang {
|
||||
if !c.IsMultilingual() || c.DefaultContentLanguage() == c.Language().Lang {
|
||||
return ""
|
||||
}
|
||||
return c.Language().Lang
|
||||
@@ -78,7 +78,7 @@ func (c ConfigLanguage) FastRenderMode() bool {
|
||||
return c.config.Internal.FastRenderMode
|
||||
}
|
||||
|
||||
func (c ConfigLanguage) IsMultiLingual() bool {
|
||||
func (c ConfigLanguage) IsMultilingual() bool {
|
||||
return len(c.m.Languages) > 1
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ type AllProvider interface {
|
||||
PathParser() *paths.PathParser
|
||||
Environment() string
|
||||
IsMultihost() bool
|
||||
IsMultiLingual() bool
|
||||
IsMultilingual() bool
|
||||
NoBuildLock() bool
|
||||
BaseConfig() BaseConfig
|
||||
Dirs() CommonDirs
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
For a given taxonomy, renders a list of terms assigned to the page.
|
||||
|
||||
@context {page} page The current page.
|
||||
@context {string} taxonomy The taxonony.
|
||||
@context {string} taxonomy The taxonomy.
|
||||
|
||||
@example: {{ partial "terms.html" (dict "taxonomy" "tags" "page" .) }}
|
||||
*/}}
|
||||
|
||||
Vendored
+1
-1
@@ -361,7 +361,7 @@ type BuildState struct {
|
||||
|
||||
mu sync.Mutex // protects state below.
|
||||
|
||||
// A set of ilenames in /public that
|
||||
// A set of filenames in /public that
|
||||
// contains a post-processing prefix.
|
||||
filenamesWithPostPrefix map[string]bool
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ hugo [flags]
|
||||
--printPathWarnings print warnings on duplicate target paths etc.
|
||||
--printUnusedTemplates print warnings on unused templates.
|
||||
--quiet build in quiet mode
|
||||
--renderSegments strings named segments to render (configured in the segments config)
|
||||
--renderToMemory render to memory (mostly useful when running the server)
|
||||
-s, --source string filesystem path to read files relative from
|
||||
--templateMetrics display metrics about template executions
|
||||
|
||||
@@ -18,13 +18,14 @@ hugo config [command] [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
--format string preferred file format (toml, yaml or json) (default "toml")
|
||||
-h, --help help for config
|
||||
--lang string the language to display config for. Defaults to the first language defined.
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
--format string preferred file format (toml, yaml or json) (default "toml")
|
||||
-h, --help help for config
|
||||
--lang string the language to display config for. Defaults to the first language defined.
|
||||
--renderSegments strings named segments to render (configured in the segments config)
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -14,11 +14,12 @@ hugo config mounts [flags] [args]
|
||||
### Options
|
||||
|
||||
```
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for mounts
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for mounts
|
||||
--renderSegments strings named segments to render (configured in the segments config)
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -18,13 +18,14 @@ hugo mod clean [flags] [args]
|
||||
### Options
|
||||
|
||||
```
|
||||
--all clean entire module cache
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for clean
|
||||
--pattern string pattern matching module paths to clean (all if not set), e.g. "**hugo*"
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
--all clean entire module cache
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for clean
|
||||
--pattern string pattern matching module paths to clean (all if not set), e.g. "**hugo*"
|
||||
--renderSegments strings named segments to render (configured in the segments config)
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -20,12 +20,13 @@ hugo mod graph [flags] [args]
|
||||
### Options
|
||||
|
||||
```
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
--clean delete module cache for dependencies that fail verification
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for graph
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
--clean delete module cache for dependencies that fail verification
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for graph
|
||||
--renderSegments strings named segments to render (configured in the segments config)
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -25,11 +25,12 @@ hugo mod init [flags] [args]
|
||||
### Options
|
||||
|
||||
```
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for init
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for init
|
||||
--renderSegments strings named segments to render (configured in the segments config)
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -28,11 +28,12 @@ hugo mod npm pack [flags] [args]
|
||||
### Options
|
||||
|
||||
```
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for pack
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for pack
|
||||
--renderSegments strings named segments to render (configured in the segments config)
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -14,11 +14,12 @@ hugo mod tidy [flags] [args]
|
||||
### Options
|
||||
|
||||
```
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for tidy
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for tidy
|
||||
--renderSegments strings named segments to render (configured in the segments config)
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -20,11 +20,12 @@ hugo mod vendor [flags] [args]
|
||||
### Options
|
||||
|
||||
```
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for vendor
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for vendor
|
||||
--renderSegments strings named segments to render (configured in the segments config)
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -18,12 +18,13 @@ hugo mod verify [flags] [args]
|
||||
### Options
|
||||
|
||||
```
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
--clean delete module cache for dependencies that fail verification
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for verify
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
--clean delete module cache for dependencies that fail verification
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
-h, --help help for verify
|
||||
--renderSegments strings named segments to render (configured in the segments config)
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -25,14 +25,15 @@ hugo new content [path] [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
--editor string edit new content with this editor, if provided
|
||||
-f, --force overwrite file if it already exists
|
||||
-h, --help help for content
|
||||
-k, --kind string content type to create
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--cacheDir string filesystem path to cache directory
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
--editor string edit new content with this editor, if provided
|
||||
-f, --force overwrite file if it already exists
|
||||
-h, --help help for content
|
||||
-k, --kind string content type to create
|
||||
--renderSegments strings named segments to render (configured in the segments config)
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -28,49 +28,50 @@ hugo server [command] [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
--appendPort append port to baseURL (default true)
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--bind string interface to which the server will bind (default "127.0.0.1")
|
||||
-D, --buildDrafts include content marked as draft
|
||||
-E, --buildExpired include expired content
|
||||
-F, --buildFuture include content with publishdate in the future
|
||||
--cacheDir string filesystem path to cache directory
|
||||
--cleanDestinationDir remove files from destination not found in static directories
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
--disableBrowserError do not show build errors in the browser
|
||||
--disableFastRender enables full re-renders on changes
|
||||
--disableKinds strings disable different kind of pages (home, RSS etc.)
|
||||
--disableLiveReload watch without enabling live browser reload on rebuild
|
||||
--enableGitInfo add Git revision, date, author, and CODEOWNERS info to the pages
|
||||
--forceSyncStatic copy all files when static is changed.
|
||||
--gc enable to run some cleanup tasks (remove unused cache files) after the build
|
||||
-h, --help help for server
|
||||
--ignoreCache ignores the cache directory
|
||||
-l, --layoutDir string filesystem path to layout directory
|
||||
--liveReloadPort int port for live reloading (i.e. 443 in HTTPS proxy situations) (default -1)
|
||||
--minify minify any supported output format (HTML, XML etc.)
|
||||
--navigateToChanged navigate to changed content file on live browser reload
|
||||
--noBuildLock don't create .hugo_build.lock file
|
||||
--noChmod don't sync permission mode of files
|
||||
--noHTTPCache prevent HTTP caching
|
||||
--noTimes don't sync modification time of files
|
||||
--panicOnWarning panic on first WARNING log
|
||||
--poll string set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes
|
||||
-p, --port int port on which the server will listen (default 1313)
|
||||
--pprof enable the pprof server (port 8080)
|
||||
--printI18nWarnings print missing translations
|
||||
--printMemoryUsage print memory usage to screen at intervals
|
||||
--printPathWarnings print warnings on duplicate target paths etc.
|
||||
--printUnusedTemplates print warnings on unused templates.
|
||||
--renderStaticToDisk serve static files from disk and dynamic files from memory
|
||||
--templateMetrics display metrics about template executions
|
||||
--templateMetricsHints calculate some improvement hints when combined with --templateMetrics
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
--tlsAuto generate and use locally-trusted certificates.
|
||||
--tlsCertFile string path to TLS certificate file
|
||||
--tlsKeyFile string path to TLS key file
|
||||
--trace file write trace to file (not useful in general)
|
||||
-w, --watch watch filesystem for changes and recreate as needed (default true)
|
||||
--appendPort append port to baseURL (default true)
|
||||
-b, --baseURL string hostname (and path) to the root, e.g. https://spf13.com/
|
||||
--bind string interface to which the server will bind (default "127.0.0.1")
|
||||
-D, --buildDrafts include content marked as draft
|
||||
-E, --buildExpired include expired content
|
||||
-F, --buildFuture include content with publishdate in the future
|
||||
--cacheDir string filesystem path to cache directory
|
||||
--cleanDestinationDir remove files from destination not found in static directories
|
||||
-c, --contentDir string filesystem path to content directory
|
||||
--disableBrowserError do not show build errors in the browser
|
||||
--disableFastRender enables full re-renders on changes
|
||||
--disableKinds strings disable different kind of pages (home, RSS etc.)
|
||||
--disableLiveReload watch without enabling live browser reload on rebuild
|
||||
--enableGitInfo add Git revision, date, author, and CODEOWNERS info to the pages
|
||||
--forceSyncStatic copy all files when static is changed.
|
||||
--gc enable to run some cleanup tasks (remove unused cache files) after the build
|
||||
-h, --help help for server
|
||||
--ignoreCache ignores the cache directory
|
||||
-l, --layoutDir string filesystem path to layout directory
|
||||
--liveReloadPort int port for live reloading (i.e. 443 in HTTPS proxy situations) (default -1)
|
||||
--minify minify any supported output format (HTML, XML etc.)
|
||||
--navigateToChanged navigate to changed content file on live browser reload
|
||||
--noBuildLock don't create .hugo_build.lock file
|
||||
--noChmod don't sync permission mode of files
|
||||
--noHTTPCache prevent HTTP caching
|
||||
--noTimes don't sync modification time of files
|
||||
--panicOnWarning panic on first WARNING log
|
||||
--poll string set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes
|
||||
-p, --port int port on which the server will listen (default 1313)
|
||||
--pprof enable the pprof server (port 8080)
|
||||
--printI18nWarnings print missing translations
|
||||
--printMemoryUsage print memory usage to screen at intervals
|
||||
--printPathWarnings print warnings on duplicate target paths etc.
|
||||
--printUnusedTemplates print warnings on unused templates.
|
||||
--renderSegments strings named segments to render (configured in the segments config)
|
||||
--renderStaticToDisk serve static files from disk and dynamic files from memory
|
||||
--templateMetrics display metrics about template executions
|
||||
--templateMetricsHints calculate some improvement hints when combined with --templateMetrics
|
||||
-t, --theme strings themes to use (located in /themes/THEMENAME/)
|
||||
--tlsAuto generate and use locally-trusted certificates.
|
||||
--tlsCertFile string path to TLS certificate file
|
||||
--tlsKeyFile string path to TLS key file
|
||||
--trace file write trace to file (not useful in general)
|
||||
-w, --watch watch filesystem for changes and recreate as needed (default true)
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
@@ -207,6 +207,10 @@ chroma:
|
||||
- Aliases:
|
||||
- dax
|
||||
Name: Dax
|
||||
- Aliases:
|
||||
- desktop
|
||||
- desktop_entry
|
||||
Name: Desktop file
|
||||
- Aliases:
|
||||
- diff
|
||||
- udiff
|
||||
@@ -443,6 +447,10 @@ chroma:
|
||||
- Aliases:
|
||||
- mason
|
||||
Name: Mason
|
||||
- Aliases:
|
||||
- materialize
|
||||
- mzsql
|
||||
Name: Materialize SQL dialect
|
||||
- Aliases:
|
||||
- mathematica
|
||||
- mma
|
||||
@@ -493,6 +501,9 @@ chroma:
|
||||
- Aliases:
|
||||
- natural
|
||||
Name: Natural
|
||||
- Aliases:
|
||||
- ndisasm
|
||||
Name: NDISASM
|
||||
- Aliases:
|
||||
- newspeak
|
||||
Name: Newspeak
|
||||
@@ -607,6 +618,9 @@ chroma:
|
||||
- Aliases:
|
||||
- prolog
|
||||
Name: Prolog
|
||||
- Aliases:
|
||||
- promela
|
||||
Name: Promela
|
||||
- Aliases:
|
||||
- promql
|
||||
Name: PromQL
|
||||
@@ -673,6 +687,9 @@ chroma:
|
||||
- Aliases:
|
||||
- registry
|
||||
Name: reg
|
||||
- Aliases:
|
||||
- rego
|
||||
Name: Rego
|
||||
- Aliases:
|
||||
- rst
|
||||
- rest
|
||||
@@ -682,6 +699,9 @@ chroma:
|
||||
- rexx
|
||||
- arexx
|
||||
Name: Rexx
|
||||
- Aliases:
|
||||
- spec
|
||||
Name: RPMSpec
|
||||
- Aliases:
|
||||
- rb
|
||||
- ruby
|
||||
@@ -946,6 +966,7 @@ config:
|
||||
dir: :cacheDir/modules
|
||||
maxAge: -1
|
||||
canonifyURLs: false
|
||||
capitalizeListTitles: true
|
||||
cascade: []
|
||||
cleanDestinationDir: false
|
||||
contentDir: content
|
||||
@@ -1585,6 +1606,7 @@ config:
|
||||
toLower: false
|
||||
relativeURLs: false
|
||||
removePathAccents: false
|
||||
renderSegments: null
|
||||
resourceDir: resources
|
||||
sectionPagesMenu: ""
|
||||
security:
|
||||
@@ -1607,6 +1629,7 @@ config:
|
||||
- (?i)GET|POST
|
||||
urls:
|
||||
- .*
|
||||
segments: {}
|
||||
server:
|
||||
headers: null
|
||||
redirects:
|
||||
@@ -1704,6 +1727,8 @@ config_helpers:
|
||||
_merge: none
|
||||
security:
|
||||
_merge: none
|
||||
segments:
|
||||
_merge: none
|
||||
server:
|
||||
_merge: none
|
||||
services:
|
||||
@@ -2965,6 +2990,21 @@ tpl:
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
IsMultiHost:
|
||||
Aliases: null
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
IsMultihost:
|
||||
Aliases: null
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
IsMultilingual:
|
||||
Aliases: null
|
||||
Args: null
|
||||
Description: ""
|
||||
Examples: null
|
||||
IsProduction:
|
||||
Aliases: null
|
||||
Args: null
|
||||
|
||||
@@ -2,7 +2,7 @@ module github.com/gohugoio/hugo
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69
|
||||
github.com/alecthomas/chroma/v2 v2.12.0
|
||||
github.com/alecthomas/chroma/v2 v2.13.0
|
||||
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.32.6
|
||||
@@ -25,7 +25,7 @@ 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.20.1
|
||||
github.com/evanw/esbuild v0.20.2
|
||||
github.com/fatih/color v1.16.0
|
||||
github.com/fortytw2/leaktest v1.3.0
|
||||
github.com/frankban/quicktest v1.14.6
|
||||
@@ -35,7 +35,7 @@ require (
|
||||
github.com/gobuffalo/flect v1.0.2
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.1.0
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.2.0
|
||||
github.com/gohugoio/locales v0.14.0
|
||||
github.com/gohugoio/localescompressed v1.0.1
|
||||
github.com/gohugoio/testmodBuilder/mods v0.0.0-20190520184928-c56af20f2e95
|
||||
@@ -64,7 +64,7 @@ require (
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/fsync v0.10.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/tdewolff/minify/v2 v2.20.17
|
||||
github.com/tdewolff/minify/v2 v2.20.19
|
||||
github.com/tdewolff/parse/v2 v2.7.12
|
||||
github.com/yuin/goldmark v1.7.0
|
||||
github.com/yuin/goldmark-emoji v1.0.2
|
||||
@@ -72,11 +72,11 @@ require (
|
||||
gocloud.dev v0.36.0
|
||||
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326
|
||||
golang.org/x/image v0.15.0
|
||||
golang.org/x/mod v0.15.0
|
||||
golang.org/x/net v0.21.0
|
||||
golang.org/x/mod v0.16.0
|
||||
golang.org/x/net v0.22.0
|
||||
golang.org/x/sync v0.6.0
|
||||
golang.org/x/text v0.14.0
|
||||
golang.org/x/tools v0.18.0
|
||||
golang.org/x/tools v0.19.0
|
||||
google.golang.org/api v0.152.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
@@ -114,7 +114,7 @@ require (
|
||||
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/dlclark/regexp2 v1.11.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
|
||||
@@ -142,9 +142,9 @@ require (
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/crypto v0.19.0 // indirect
|
||||
golang.org/x/crypto v0.21.0 // indirect
|
||||
golang.org/x/oauth2 v0.15.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
|
||||
@@ -66,10 +66,10 @@ github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZd
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink=
|
||||
github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw=
|
||||
github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw=
|
||||
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
|
||||
github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU=
|
||||
github.com/alecthomas/chroma/v2 v2.13.0 h1:VP72+99Fb2zEcYM0MeaWJmV+xQvz5v5cxRHd+ooU1lI=
|
||||
github.com/alecthomas/chroma/v2 v2.13.0/go.mod h1:BUGjjsD+ndS6eX37YgTchSEG+Jg9Jv1GiZs9sqPqztk=
|
||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||
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.50.7 h1:odKb+uneeGgF2jgAerKjFzpljiyZxleV4SHB7oBK+YA=
|
||||
@@ -168,8 +168,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc=
|
||||
github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI=
|
||||
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
|
||||
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
@@ -179,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.20.1 h1:ueyMIL19umCcJTSxiBH/QmPipgGt8hEDM24pdfowgEc=
|
||||
github.com/evanw/esbuild v0.20.1/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
|
||||
github.com/evanw/esbuild v0.20.2 h1:E4Y0iJsothpUCq7y0D+ERfqpJmPWrZpNybJA3x3I4p8=
|
||||
github.com/evanw/esbuild v0.20.2/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=
|
||||
@@ -211,8 +211,8 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e h1:QArsSubW7eDh8APMXkByjQWvuljwPGAGQpJEFn0F0wY=
|
||||
github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e/go.mod h1:3Ltoo9Banwq0gOtcOwxuHG6omk+AwsQPADyw2vQYOJQ=
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.1.0 h1:oFQ3f1M3Ook6amHmbqVu/uBRrQ6yjMDFkIv4HQr0f1Y=
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.1.0/go.mod h1:g9CCh+Ci2IMbPUrVJuXbBTrA+rIIx5+hDQ4EXYaQDoM=
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.2.0 h1:PCtO5l++psZf48yen2LxQ3JiOXxaRC6v0594NeHvGZg=
|
||||
github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.2.0/go.mod h1:g9CCh+Ci2IMbPUrVJuXbBTrA+rIIx5+hDQ4EXYaQDoM=
|
||||
github.com/gohugoio/locales v0.14.0 h1:Q0gpsZwfv7ATHMbcTNepFd59H7GoykzWJIxi113XGDc=
|
||||
github.com/gohugoio/locales v0.14.0/go.mod h1:ip8cCAv/cnmVLzzXtiTpPwgJ4xhKZranqNqtoIu0b/4=
|
||||
github.com/gohugoio/localescompressed v1.0.1 h1:KTYMi8fCWYLswFyJAeOtuk/EkXR/KPTHHNN9OS+RTxo=
|
||||
@@ -427,8 +427,8 @@ 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.17 h1:zGqEDhspr3XjSrQI/56vw9IdAhLAaKTLXWnDBsxNVt8=
|
||||
github.com/tdewolff/minify/v2 v2.20.17/go.mod h1:ulkFoeAVWMLEyjuDz1ZIWOA31g5aWOawCFRp9R/MudM=
|
||||
github.com/tdewolff/minify/v2 v2.20.19 h1:tX0SR0LUrIqGoLjXnkIzRSIbKJ7PaNnSENLD4CyH6Xo=
|
||||
github.com/tdewolff/minify/v2 v2.20.19/go.mod h1:ulkFoeAVWMLEyjuDz1ZIWOA31g5aWOawCFRp9R/MudM=
|
||||
github.com/tdewolff/parse/v2 v2.7.12 h1:tgavkHc2ZDEQVKy1oWxwIyh5bP4F5fEh/JmBwPP/3LQ=
|
||||
github.com/tdewolff/parse/v2 v2.7.12/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
|
||||
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
@@ -466,8 +466,8 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm
|
||||
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.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -508,8 +508,8 @@ 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.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
||||
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -545,8 +545,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
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.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -615,8 +615,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
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.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.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=
|
||||
@@ -685,8 +685,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f
|
||||
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.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
|
||||
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
|
||||
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
|
||||
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
|
||||
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=
|
||||
|
||||
@@ -42,7 +42,7 @@ var LanguageDirsMerger overlayfs.DirsMerger = func(lofi, bofi []fs.DirEntry) []f
|
||||
|
||||
// AppendDirsMerger merges two directories keeping all regular files
|
||||
// with the first slice as the base.
|
||||
// Duplicate directories in the secnond slice will be ignored.
|
||||
// Duplicate directories in the second slice will be ignored.
|
||||
// This strategy is used for the i18n and data fs where we need all entries.
|
||||
var AppendDirsMerger overlayfs.DirsMerger = func(lofi, bofi []fs.DirEntry) []fs.DirEntry {
|
||||
for _, fi1 := range bofi {
|
||||
|
||||
@@ -822,7 +822,7 @@ func (f *rootMappingDir) ReadDir(count int) ([]iofs.DirEntry, error) {
|
||||
return f.fs.collectDirEntries(f.name)
|
||||
}
|
||||
|
||||
// Sentinal error to signal that a file is a directory.
|
||||
// Sentinel error to signal that a file is a directory.
|
||||
var errIsDir = errors.New("isDir")
|
||||
|
||||
func (f *rootMappingDir) Stat() (iofs.FileInfo, error) {
|
||||
|
||||
+21
-21
@@ -508,12 +508,12 @@ func TestLoadConfigFromThemeDir(t *testing.T) {
|
||||
theme = "test-theme"
|
||||
|
||||
[params]
|
||||
m1 = "mv1"
|
||||
m1 = "mv1"
|
||||
`
|
||||
|
||||
themeConfig := `
|
||||
[params]
|
||||
t1 = "tv1"
|
||||
t1 = "tv1"
|
||||
t2 = "tv2"
|
||||
`
|
||||
|
||||
@@ -885,9 +885,9 @@ ThisIsAParam: {{ site.Params.thisIsAParam }}
|
||||
).BuildE()
|
||||
|
||||
b.Assert(err, qt.IsNil)
|
||||
b.AssertFileContent("public/index.html", `
|
||||
b.AssertFileContent("public/index.html", `
|
||||
MyParam: enParamValue
|
||||
ThisIsAParam: thisIsAParamValue
|
||||
ThisIsAParam: thisIsAParamValue
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -919,7 +919,7 @@ title: "My Swedish Section"
|
||||
-- layouts/index.html --
|
||||
LanguageCode: {{ eq site.LanguageCode site.Language.LanguageCode }}|{{ site.Language.LanguageCode }}|
|
||||
{{ range $i, $e := (slice site .Site) }}
|
||||
{{ $i }}|AllPages: {{ len .AllPages }}|Sections: {{ if .Sections }}true{{ end }}| Author: {{ .Authors }}|BuildDrafts: {{ .BuildDrafts }}|IsMultiLingual: {{ .IsMultiLingual }}|Param: {{ .Language.Params.myparam }}|Language string: {{ .Language }}|Languages: {{ .Languages }}
|
||||
{{ $i }}|AllPages: {{ len .AllPages }}|Sections: {{ if .Sections }}true{{ end }}| Author: {{ .Authors }}|BuildDrafts: {{ .BuildDrafts }}|IsMultilingual: {{ .IsMultiLingual }}|Param: {{ .Language.Params.myparam }}|Language string: {{ .Language }}|Languages: {{ .Languages }}
|
||||
{{ end }}
|
||||
|
||||
|
||||
@@ -939,9 +939,9 @@ LanguageCode: {{ eq site.LanguageCode site.Language.LanguageCode }}|{{ site.Lang
|
||||
b.AssertFileContent("public/index.html", `
|
||||
AllPages: 4|
|
||||
Sections: true|
|
||||
Param: enParamValue
|
||||
Param: enParamValue
|
||||
IsMultiLingual: true
|
||||
Param: enParamValue
|
||||
Param: enParamValue
|
||||
IsMultilingual: true
|
||||
LanguageCode: true|en-US|
|
||||
`)
|
||||
|
||||
@@ -1062,7 +1062,7 @@ Home
|
||||
).BuildE()
|
||||
|
||||
b.Assert(err, qt.IsNil)
|
||||
b.AssertFileContent("public/index.html", `
|
||||
b.AssertFileContent("public/index.html", `
|
||||
Home
|
||||
`)
|
||||
|
||||
@@ -1095,7 +1095,7 @@ HTML.
|
||||
HTACCESS.
|
||||
|
||||
|
||||
|
||||
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
@@ -1111,7 +1111,7 @@ languageCode = "en-US"
|
||||
-- layouts/index.html --
|
||||
LanguageCode: {{ .Site.LanguageCode }}|{{ site.Language.LanguageCode }}|
|
||||
|
||||
|
||||
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
@@ -1137,7 +1137,7 @@ suffixes = ["bar"]
|
||||
-- layouts/index.html --
|
||||
Home.
|
||||
|
||||
|
||||
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
@@ -1164,8 +1164,8 @@ func TestConfigMiscPanics(t *testing.T) {
|
||||
params:
|
||||
-- layouts/index.html --
|
||||
Foo: {{ site.Params.foo }}|
|
||||
|
||||
|
||||
|
||||
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
@@ -1188,8 +1188,8 @@ defaultContentLanguage = "en"
|
||||
weight = 1
|
||||
-- layouts/index.html --
|
||||
Foo: {{ site.Params.foo }}|
|
||||
|
||||
|
||||
|
||||
|
||||
`
|
||||
b, err := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
@@ -1215,8 +1215,8 @@ languageCode = "en"
|
||||
languageName = "English"
|
||||
weight = 1
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
`
|
||||
b, err := NewIntegrationTestBuilder(
|
||||
IntegrationTestConfig{
|
||||
@@ -1241,7 +1241,7 @@ contentDir = "mycontent"
|
||||
-- layouts/index.html --
|
||||
Home.
|
||||
|
||||
|
||||
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
@@ -1343,7 +1343,7 @@ disabled = true
|
||||
-- layouts/index.html --
|
||||
Home.
|
||||
|
||||
|
||||
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
@@ -1438,7 +1438,7 @@ home = ["html"]
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT", "page", "section"]
|
||||
|
||||
|
||||
`
|
||||
|
||||
runVariant(t, files, nil)
|
||||
|
||||
@@ -1500,13 +1500,6 @@ func (sa *sitePagesAssembler) assembleTermsAndTranslations() error {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// 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.pageConfig.TranslationKey != "" {
|
||||
sa.s.h.translationKeyPages.Append(ps.m.pageConfig.TranslationKey, ps)
|
||||
}
|
||||
|
||||
if sa.pageMap.cfg.taxonomyTermDisabled {
|
||||
return false, nil
|
||||
}
|
||||
@@ -1587,6 +1580,13 @@ func (sa *sitePagesAssembler) assembleResources() error {
|
||||
Handle: func(s string, n contentNodeI, match doctree.DimensionFlag) (bool, error) {
|
||||
ps := n.(*pageState)
|
||||
|
||||
// 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.pageConfig.TranslationKey != "" {
|
||||
sa.s.h.translationKeyPages.Append(ps.m.pageConfig.TranslationKey, ps)
|
||||
}
|
||||
|
||||
// Prepare resources for this page.
|
||||
ps.shiftToOutputFormat(true, 0)
|
||||
targetPaths := ps.targetPaths()
|
||||
@@ -1778,7 +1778,7 @@ func (sa *sitePagesAssembler) addStandalonePages() error {
|
||||
|
||||
if sitemapEnabled {
|
||||
addStandalone("/_sitemap", kinds.KindSitemap, output.SitemapFormat)
|
||||
skipSitemapIndex := s.Conf.IsMultihost() || !(s.Conf.DefaultContentLanguageInSubdir() || s.Conf.IsMultiLingual())
|
||||
skipSitemapIndex := s.Conf.IsMultihost() || !(s.Conf.DefaultContentLanguageInSubdir() || s.Conf.IsMultilingual())
|
||||
|
||||
if !skipSitemapIndex {
|
||||
addStandalone("/_sitemapindex", kinds.KindSitemapIndex, output.SitemapIndexFormat)
|
||||
|
||||
@@ -315,7 +315,7 @@ func TestStaticFs(t *testing.T) {
|
||||
checkFileContent(sfs, "f2.txt", c, "Hugo Themes Still Rocks!")
|
||||
}
|
||||
|
||||
func TestStaticFsMultiHost(t *testing.T) {
|
||||
func TestStaticFsMultihost(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
v := config.New()
|
||||
workDir := "mywork"
|
||||
@@ -537,7 +537,7 @@ SCSS Match: {{ with resources.Match "**.scss" }}{{ . | len }}|{{ range .}}{{ .Re
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", `
|
||||
b.AssertFileContent("public/index.html", `
|
||||
SCSS: /scss/app.scss|body { color: blue; }|
|
||||
SCSS Match: 2|
|
||||
`)
|
||||
|
||||
@@ -269,7 +269,7 @@ func (h *HugoSites) pickOneAndLogTheRest(errors []error) error {
|
||||
return errors[i]
|
||||
}
|
||||
|
||||
func (h *HugoSites) isMultiLingual() bool {
|
||||
func (h *HugoSites) isMultilingual() bool {
|
||||
return len(h.Sites) > 1
|
||||
}
|
||||
|
||||
@@ -410,6 +410,10 @@ type BuildCfg struct {
|
||||
|
||||
// shouldRender returns whether this output format should be rendered or not.
|
||||
func (cfg *BuildCfg) shouldRender(p *pageState) bool {
|
||||
if p.skipRender() {
|
||||
return false
|
||||
}
|
||||
|
||||
if !p.renderOnce {
|
||||
return true
|
||||
}
|
||||
|
||||
+22
-31
@@ -28,16 +28,16 @@ import (
|
||||
"github.com/bep/logg"
|
||||
"github.com/gohugoio/hugo/cache/dynacache"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/hugofs/files"
|
||||
"github.com/gohugoio/hugo/hugofs/glob"
|
||||
"github.com/gohugoio/hugo/hugolib/segments"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/output"
|
||||
"github.com/gohugoio/hugo/publisher"
|
||||
"github.com/gohugoio/hugo/source"
|
||||
"github.com/gohugoio/hugo/tpl"
|
||||
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
|
||||
"github.com/gohugoio/hugo/common/herrors"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/common/para"
|
||||
@@ -318,9 +318,20 @@ func (h *HugoSites) render(l logg.LevelLogger, config *BuildCfg) error {
|
||||
|
||||
i := 0
|
||||
for _, s := range h.Sites {
|
||||
segmentFilter := s.conf.C.SegmentFilter
|
||||
if segmentFilter.ShouldExcludeCoarse(segments.SegmentMatcherFields{Lang: s.language.Lang}) {
|
||||
l.Logf("skip language %q not matching segments set in --renderSegments", s.language.Lang)
|
||||
continue
|
||||
}
|
||||
|
||||
siteRenderContext.languageIdx = s.languagei
|
||||
h.currentSite = s
|
||||
for siteOutIdx, renderFormat := range s.renderFormats {
|
||||
if segmentFilter.ShouldExcludeCoarse(segments.SegmentMatcherFields{Output: renderFormat.Name, Lang: s.language.Lang}) {
|
||||
l.Logf("skip output format %q for language %q not matching segments set in --renderSegments", renderFormat.Name, s.language.Lang)
|
||||
continue
|
||||
}
|
||||
|
||||
siteRenderContext.outIdx = siteOutIdx
|
||||
siteRenderContext.sitesOutIdx = i
|
||||
i++
|
||||
@@ -595,8 +606,10 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
|
||||
return sb.String()
|
||||
}))
|
||||
|
||||
// For a list of events for the different OSes, see the test output in https://github.com/bep/fsnotifyeventlister/.
|
||||
events = h.fileEventsFilter(events)
|
||||
events = h.fileEventsTranslate(events)
|
||||
eventInfos := h.fileEventsApplyInfo(events)
|
||||
|
||||
logger := h.Log
|
||||
|
||||
@@ -631,36 +644,12 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
|
||||
addedContentPaths []*paths.Path
|
||||
)
|
||||
|
||||
for _, ev := range events {
|
||||
removed := false
|
||||
added := false
|
||||
|
||||
if ev.Op&fsnotify.Remove == fsnotify.Remove {
|
||||
removed = true
|
||||
}
|
||||
|
||||
fi, statErr := h.Fs.Source.Stat(ev.Name)
|
||||
|
||||
// Some editors (Vim) sometimes issue only a Rename operation when writing an existing file
|
||||
// Sometimes a rename operation means that file has been renamed other times it means
|
||||
// it's been updated.
|
||||
if ev.Op.Has(fsnotify.Rename) {
|
||||
// If the file is still on disk, it's only been updated, if it's not, it's been moved
|
||||
if statErr != nil {
|
||||
removed = true
|
||||
}
|
||||
}
|
||||
if ev.Op.Has(fsnotify.Create) {
|
||||
added = true
|
||||
}
|
||||
|
||||
isChangedDir := statErr == nil && fi.IsDir()
|
||||
|
||||
for _, ev := range eventInfos {
|
||||
cpss := h.BaseFs.ResolvePaths(ev.Name)
|
||||
pss := make([]*paths.Path, len(cpss))
|
||||
for i, cps := range cpss {
|
||||
p := cps.Path
|
||||
if removed && !paths.HasExt(p) {
|
||||
if ev.removed && !paths.HasExt(p) {
|
||||
// Assume this is a renamed/removed directory.
|
||||
// For deletes, we walk up the tree to find the container (e.g. branch bundle),
|
||||
// so we will catch this even if it is a file without extension.
|
||||
@@ -671,7 +660,7 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
|
||||
}
|
||||
|
||||
pss[i] = h.Configs.ContentPathParser.Parse(cps.Component, p)
|
||||
if added && !isChangedDir && cps.Component == files.ComponentFolderContent {
|
||||
if ev.added && !ev.isChangedDir && cps.Component == files.ComponentFolderContent {
|
||||
addedContentPaths = append(addedContentPaths, pss[i])
|
||||
}
|
||||
|
||||
@@ -683,9 +672,9 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
|
||||
}
|
||||
}
|
||||
|
||||
if removed {
|
||||
if ev.removed {
|
||||
changedPaths.deleted = append(changedPaths.deleted, pss...)
|
||||
} else if isChangedDir {
|
||||
} else if ev.isChangedDir {
|
||||
changedPaths.changedDirs = append(changedPaths.changedDirs, pss...)
|
||||
} else {
|
||||
changedPaths.changedFiles = append(changedPaths.changedFiles, pss...)
|
||||
@@ -792,6 +781,8 @@ func (h *HugoSites) processPartial(ctx context.Context, l logg.LevelLogger, conf
|
||||
// It's hard to determine the exact change set of this,
|
||||
// so be very coarse grained for now.
|
||||
changes = append(changes, identity.GenghisKhan)
|
||||
case files.ComponentFolderArchetypes:
|
||||
// Ignore for now.
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown component: %q", pathInfo.Component()))
|
||||
}
|
||||
|
||||
@@ -66,12 +66,12 @@ robots|{{ site.Language.Lang }}
|
||||
404|{{ site.Language.Lang }}
|
||||
|
||||
|
||||
|
||||
|
||||
`
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.Assert(b.H.Conf.IsMultiLingual(), qt.Equals, true)
|
||||
b.Assert(b.H.Conf.IsMultilingual(), qt.Equals, true)
|
||||
b.Assert(b.H.Conf.IsMultihost(), qt.Equals, true)
|
||||
|
||||
// helpers.PrintFs(b.H.Fs.PublishDir, "", os.Stdout)
|
||||
|
||||
@@ -112,6 +112,6 @@ Template: false
|
||||
Resource1: /js/include.js:END
|
||||
Resource2: :END
|
||||
Resource3: :END
|
||||
Resources: [include.js]
|
||||
Resources: [/js/include.js]
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/hugolib/doctree"
|
||||
"github.com/gohugoio/hugo/hugolib/segments"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/gohugoio/hugo/output"
|
||||
@@ -36,6 +37,7 @@ import (
|
||||
|
||||
"github.com/gohugoio/hugo/common/herrors"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/common/types"
|
||||
|
||||
"github.com/gohugoio/hugo/source"
|
||||
|
||||
@@ -151,6 +153,19 @@ func (p *pageState) reusePageOutputContent() bool {
|
||||
return p.pageOutputTemplateVariationsState.Load() == 1
|
||||
}
|
||||
|
||||
func (p *pageState) skipRender() bool {
|
||||
b := p.s.conf.C.SegmentFilter.ShouldExcludeFine(
|
||||
segments.SegmentMatcherFields{
|
||||
Path: p.Path(),
|
||||
Kind: p.Kind(),
|
||||
Lang: p.Lang(),
|
||||
Output: p.pageOutput.f.Name,
|
||||
},
|
||||
)
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (po *pageState) isRenderedAny() bool {
|
||||
for _, o := range po.pageOutputs {
|
||||
if o.isRendered() {
|
||||
@@ -731,3 +746,9 @@ func (p pageWithWeight0) Weight0() int {
|
||||
func (p pageWithWeight0) page() page.Page {
|
||||
return p.pageState
|
||||
}
|
||||
|
||||
var _ types.Unwrapper = (*pageWithWeight0)(nil)
|
||||
|
||||
func (p pageWithWeight0) Unwrapv() any {
|
||||
return p.pageState
|
||||
}
|
||||
|
||||
@@ -106,9 +106,9 @@ func (p *pageMeta) Aliases() []string {
|
||||
return p.pageConfig.Aliases
|
||||
}
|
||||
|
||||
// Deprecated: use taxonomies.
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (p *pageMeta) Author() page.Author {
|
||||
hugo.Deprecate(".Author", "Use taxonomies.", "v0.98.0")
|
||||
hugo.Deprecate(".Page.Author", "Use taxonomies instead.", "v0.98.0")
|
||||
authors := p.Authors()
|
||||
|
||||
for _, author := range authors {
|
||||
@@ -117,9 +117,9 @@ func (p *pageMeta) Author() page.Author {
|
||||
return page.Author{}
|
||||
}
|
||||
|
||||
// Deprecated: use taxonomies.
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (p *pageMeta) Authors() page.AuthorList {
|
||||
hugo.Deprecate(".Author", "Use taxonomies.", "v0.112.0")
|
||||
hugo.Deprecate(".Page.Authors", "Use taxonomies instead.", "v0.112.0")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -124,11 +124,16 @@ func (pt pageTree) Parent() page.Page {
|
||||
return pt.p.s.home
|
||||
}
|
||||
|
||||
_, n := pt.p.s.pageMap.treePages.LongestPrefix(dir, true, nil)
|
||||
if n != nil {
|
||||
return n.(page.Page)
|
||||
for {
|
||||
_, n := pt.p.s.pageMap.treePages.LongestPrefix(dir, true, nil)
|
||||
if n == nil {
|
||||
return pt.p.s.home
|
||||
}
|
||||
if pt.p.m.bundled || n.isContentNodeBranch() {
|
||||
return n.(page.Page)
|
||||
}
|
||||
dir = paths.Dir(dir)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pt pageTree) Ancestors() page.Pages {
|
||||
|
||||
+48
-3
@@ -578,7 +578,7 @@ date: 2012-01-12
|
||||
b.Assert(s.getPageOldVersion("/with-index-no-date").Date().IsZero(), qt.Equals, true)
|
||||
checkDate(s.getPageOldVersion("/with-index-date"), 2018)
|
||||
|
||||
b.Assert(s.Site().LastChange().Year(), qt.Equals, 2018)
|
||||
b.Assert(s.Site().Lastmod().Year(), qt.Equals, 2018)
|
||||
}
|
||||
|
||||
func TestCreateNewPage(t *testing.T) {
|
||||
@@ -709,7 +709,7 @@ func TestPageWithShortCodeInSummary(t *testing.T) {
|
||||
assertFunc := func(t *testing.T, ext string, pages page.Pages) {
|
||||
p := pages[0]
|
||||
checkPageTitle(t, p, "Simple")
|
||||
checkPageContent(t, p, normalizeExpected(ext, "<p>Summary Next Line. <figure><img src=\"/not/real\"/> </figure> . More text here.</p><p>Some more text</p>"))
|
||||
checkPageContent(t, p, normalizeExpected(ext, "<p>Summary Next Line. <figure><img src=\"/not/real\"> </figure> . More text here.</p><p>Some more text</p>"))
|
||||
checkPageSummary(t, p, "Summary Next Line. . More text here. Some more text")
|
||||
checkPageType(t, p, "page")
|
||||
}
|
||||
@@ -773,7 +773,7 @@ func TestSummaryManualSplit(t *testing.T) {
|
||||
title: Simple
|
||||
---
|
||||
This is **summary**.
|
||||
<!--more-->
|
||||
<!--more-->
|
||||
This is **content**.
|
||||
-- layouts/_default/single.html --
|
||||
Summary: {{ .Summary }}|Truncated: {{ .Truncated }}|
|
||||
@@ -1350,6 +1350,51 @@ AllTranslations: {{ range .AllTranslations }}{{ .Language.Lang }}|{{ end }}|
|
||||
)
|
||||
}
|
||||
|
||||
func TestTranslationKeyTermPages(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['home','rss','section','sitemap','taxonomy']
|
||||
defaultContentLanguage = 'en'
|
||||
defaultContentLanguageInSubdir = true
|
||||
[languages.en]
|
||||
weight = 1
|
||||
[languages.pt]
|
||||
weight = 2
|
||||
[taxonomies]
|
||||
category = 'categories'
|
||||
-- layouts/_default/list.html --
|
||||
{{ .IsTranslated }}|{{ range .Translations }}{{ .RelPermalink }}|{{ end }}
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Title }}|
|
||||
-- content/p1.en.md --
|
||||
---
|
||||
title: p1 (en)
|
||||
categories: [music]
|
||||
---
|
||||
-- content/p1.pt.md --
|
||||
---
|
||||
title: p1 (pt)
|
||||
categories: [música]
|
||||
---
|
||||
-- content/categories/music/_index.en.md --
|
||||
---
|
||||
title: music
|
||||
translationKey: foo
|
||||
---
|
||||
-- content/categories/música/_index.pt.md --
|
||||
---
|
||||
title: música
|
||||
translationKey: foo
|
||||
---
|
||||
`
|
||||
|
||||
b := Test(t, files)
|
||||
b.AssertFileContent("public/en/categories/music/index.html", "true|/pt/categories/m%C3%BAsica/|")
|
||||
b.AssertFileContent("public/pt/categories/música/index.html", "true|/en/categories/music/|")
|
||||
}
|
||||
|
||||
// Issue #11540.
|
||||
func TestTranslationKeyResourceSharing(t *testing.T) {
|
||||
files := `
|
||||
|
||||
@@ -161,7 +161,7 @@ func (c *pagesCollector) Collect() (collectErr error) {
|
||||
// We always start from a directory.
|
||||
collectErr = c.collectDir(id.p, id.isDir, func(fim hugofs.FileMetaInfo) bool {
|
||||
if id.delete || id.isDir {
|
||||
if id.isDir {
|
||||
if id.isDir && fim.Meta().PathInfo.IsLeafBundle() {
|
||||
return strings.HasPrefix(fim.Meta().PathInfo.Path(), paths.AddTrailingSlash(id.p.Path()))
|
||||
}
|
||||
|
||||
|
||||
@@ -1472,3 +1472,21 @@ all: {{ $ab.RelPermalink }}
|
||||
b.AddFiles("assets/common/c3.css", "c3").Build()
|
||||
b.AssertFileContent("public/ab.css", "abc1c2 editedc3")
|
||||
}
|
||||
|
||||
func TestRebuildEditArchetypeFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
disableLiveReload = true
|
||||
-- archetypes/default.md --
|
||||
---
|
||||
title: "Default"
|
||||
---
|
||||
`
|
||||
|
||||
b := TestRunning(t, files)
|
||||
// Just make sure that it doesn't panic.
|
||||
b.EditFileReplaceAll("archetypes/default.md", "Default", "Default Edited").Build()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
// 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 segments
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gobwas/glob"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/common/predicate"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
hglob "github.com/gohugoio/hugo/hugofs/glob"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
// Segments is a collection of named segments.
|
||||
type Segments struct {
|
||||
s map[string]excludeInclude
|
||||
}
|
||||
|
||||
type excludeInclude struct {
|
||||
exclude predicate.P[SegmentMatcherFields]
|
||||
include predicate.P[SegmentMatcherFields]
|
||||
}
|
||||
|
||||
// ShouldExcludeCoarse returns whether the given fields should be excluded.
|
||||
// This is used for the coarser grained checks, e.g. language and output format.
|
||||
// Note that ShouldExcludeCoarse(fields) == ShouldExcludeFine(fields) may
|
||||
// not always be true, but ShouldExcludeCoarse(fields) == true == ShouldExcludeFine(fields)
|
||||
// will always be truthful.
|
||||
func (e excludeInclude) ShouldExcludeCoarse(fields SegmentMatcherFields) bool {
|
||||
return e.exclude != nil && e.exclude(fields)
|
||||
}
|
||||
|
||||
// ShouldExcludeFine returns whether the given fields should be excluded.
|
||||
// This is used for the finer grained checks, e.g. on invididual pages.
|
||||
func (e excludeInclude) ShouldExcludeFine(fields SegmentMatcherFields) bool {
|
||||
if e.exclude != nil && e.exclude(fields) {
|
||||
return true
|
||||
}
|
||||
return e.include != nil && !e.include(fields)
|
||||
}
|
||||
|
||||
type SegmentFilter interface {
|
||||
// ShouldExcludeCoarse returns whether the given fields should be excluded on a coarse level.
|
||||
ShouldExcludeCoarse(SegmentMatcherFields) bool
|
||||
|
||||
// ShouldExcludeFine returns whether the given fields should be excluded on a fine level.
|
||||
ShouldExcludeFine(SegmentMatcherFields) bool
|
||||
}
|
||||
|
||||
type segmentFilter struct {
|
||||
coarse predicate.P[SegmentMatcherFields]
|
||||
fine predicate.P[SegmentMatcherFields]
|
||||
}
|
||||
|
||||
func (f segmentFilter) ShouldExcludeCoarse(field SegmentMatcherFields) bool {
|
||||
return f.coarse(field)
|
||||
}
|
||||
|
||||
func (f segmentFilter) ShouldExcludeFine(fields SegmentMatcherFields) bool {
|
||||
return f.fine(fields)
|
||||
}
|
||||
|
||||
var (
|
||||
matchAll = func(SegmentMatcherFields) bool { return true }
|
||||
matchNothing = func(SegmentMatcherFields) bool { return false }
|
||||
)
|
||||
|
||||
// Get returns a SegmentFilter for the given segments.
|
||||
func (sms Segments) Get(onNotFound func(s string), ss ...string) SegmentFilter {
|
||||
if ss == nil {
|
||||
return segmentFilter{coarse: matchNothing, fine: matchNothing}
|
||||
}
|
||||
var sf segmentFilter
|
||||
for _, s := range ss {
|
||||
if seg, ok := sms.s[s]; ok {
|
||||
if sf.coarse == nil {
|
||||
sf.coarse = seg.ShouldExcludeCoarse
|
||||
} else {
|
||||
sf.coarse = sf.coarse.Or(seg.ShouldExcludeCoarse)
|
||||
}
|
||||
if sf.fine == nil {
|
||||
sf.fine = seg.ShouldExcludeFine
|
||||
} else {
|
||||
sf.fine = sf.fine.Or(seg.ShouldExcludeFine)
|
||||
}
|
||||
} else if onNotFound != nil {
|
||||
onNotFound(s)
|
||||
}
|
||||
}
|
||||
|
||||
if sf.coarse == nil {
|
||||
sf.coarse = matchAll
|
||||
}
|
||||
if sf.fine == nil {
|
||||
sf.fine = matchAll
|
||||
}
|
||||
|
||||
return sf
|
||||
}
|
||||
|
||||
type SegmentConfig struct {
|
||||
Excludes []SegmentMatcherFields
|
||||
Includes []SegmentMatcherFields
|
||||
}
|
||||
|
||||
// SegmentMatcherFields is a matcher for a segment include or exclude.
|
||||
// All of these are Glob patterns.
|
||||
type SegmentMatcherFields struct {
|
||||
Kind string
|
||||
Path string
|
||||
Lang string
|
||||
Output string
|
||||
}
|
||||
|
||||
func getGlob(s string) (glob.Glob, error) {
|
||||
if s == "" {
|
||||
return nil, nil
|
||||
}
|
||||
g, err := hglob.GetGlob(s)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to compile Glob %q: %w", s, err)
|
||||
}
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func compileSegments(f []SegmentMatcherFields) (predicate.P[SegmentMatcherFields], error) {
|
||||
if f == nil {
|
||||
return func(SegmentMatcherFields) bool { return false }, nil
|
||||
}
|
||||
var (
|
||||
result predicate.P[SegmentMatcherFields]
|
||||
section predicate.P[SegmentMatcherFields]
|
||||
)
|
||||
|
||||
addToSection := func(matcherFields SegmentMatcherFields, f func(fields SegmentMatcherFields) string) error {
|
||||
s1 := f(matcherFields)
|
||||
g, err := getGlob(s1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
matcher := func(fields SegmentMatcherFields) bool {
|
||||
s2 := f(fields)
|
||||
if s2 == "" {
|
||||
return false
|
||||
}
|
||||
return g.Match(s2)
|
||||
}
|
||||
if section == nil {
|
||||
section = matcher
|
||||
} else {
|
||||
section = section.And(matcher)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, fields := range f {
|
||||
if fields.Kind != "" {
|
||||
if err := addToSection(fields, func(fields SegmentMatcherFields) string { return fields.Kind }); err != nil {
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
if fields.Path != "" {
|
||||
if err := addToSection(fields, func(fields SegmentMatcherFields) string { return fields.Path }); err != nil {
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
if fields.Lang != "" {
|
||||
if err := addToSection(fields, func(fields SegmentMatcherFields) string { return fields.Lang }); err != nil {
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
if fields.Output != "" {
|
||||
if err := addToSection(fields, func(fields SegmentMatcherFields) string { return fields.Output }); err != nil {
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
result = section
|
||||
} else {
|
||||
result = result.Or(section)
|
||||
}
|
||||
section = nil
|
||||
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func DecodeSegments(in map[string]any) (*config.ConfigNamespace[map[string]SegmentConfig, Segments], error) {
|
||||
buildConfig := func(in any) (Segments, any, error) {
|
||||
sms := Segments{
|
||||
s: map[string]excludeInclude{},
|
||||
}
|
||||
m, err := maps.ToStringMapE(in)
|
||||
if err != nil {
|
||||
return sms, nil, err
|
||||
}
|
||||
if m == nil {
|
||||
m = map[string]any{}
|
||||
}
|
||||
m = maps.CleanConfigStringMap(m)
|
||||
|
||||
var scfgm map[string]SegmentConfig
|
||||
if err := mapstructure.Decode(m, &scfgm); err != nil {
|
||||
return sms, nil, err
|
||||
}
|
||||
|
||||
for k, v := range scfgm {
|
||||
var (
|
||||
include predicate.P[SegmentMatcherFields]
|
||||
exclude predicate.P[SegmentMatcherFields]
|
||||
err error
|
||||
)
|
||||
if v.Excludes != nil {
|
||||
exclude, err = compileSegments(v.Excludes)
|
||||
if err != nil {
|
||||
return sms, nil, err
|
||||
}
|
||||
}
|
||||
if v.Includes != nil {
|
||||
include, err = compileSegments(v.Includes)
|
||||
if err != nil {
|
||||
return sms, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
ei := excludeInclude{
|
||||
exclude: exclude,
|
||||
include: include,
|
||||
}
|
||||
sms.s[k] = ei
|
||||
|
||||
}
|
||||
|
||||
return sms, nil, nil
|
||||
}
|
||||
|
||||
ns, err := config.DecodeNamespace[map[string]SegmentConfig](in, buildConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode segments: %w", err)
|
||||
}
|
||||
return ns, nil
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// 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 segments_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
)
|
||||
|
||||
func TestSegments(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.org/"
|
||||
renderSegments = ["docs"]
|
||||
[languages]
|
||||
[languages.en]
|
||||
weight = 1
|
||||
[languages.no]
|
||||
weight = 2
|
||||
[languages.nb]
|
||||
weight = 3
|
||||
[segments]
|
||||
[segments.docs]
|
||||
[[segments.docs.includes]]
|
||||
kind = "{home,taxonomy,term}"
|
||||
[[segments.docs.includes]]
|
||||
path = "{/docs,/docs/**}"
|
||||
[[segments.docs.excludes]]
|
||||
path = "/blog/**"
|
||||
[[segments.docs.excludes]]
|
||||
lang = "n*"
|
||||
output = "rss"
|
||||
[[segments.docs.excludes]]
|
||||
output = "json"
|
||||
-- layouts/_default/single.html --
|
||||
Single: {{ .Title }}|{{ .RelPermalink }}|
|
||||
-- layouts/_default/list.html --
|
||||
List: {{ .Title }}|{{ .RelPermalink }}|
|
||||
-- content/docs/_index.md --
|
||||
-- content/docs/section1/_index.md --
|
||||
-- content/docs/section1/page1.md --
|
||||
---
|
||||
title: "Docs Page 1"
|
||||
tags: ["tag1", "tag2"]
|
||||
---
|
||||
-- content/blog/_index.md --
|
||||
-- content/blog/section1/page1.md --
|
||||
---
|
||||
title: "Blog Page 1"
|
||||
tags: ["tag1", "tag2"]
|
||||
---
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
b.Assert(b.H.Configs.Base.RootConfig.RenderSegments, qt.DeepEquals, []string{"docs"})
|
||||
|
||||
b.AssertFileContent("public/docs/section1/page1/index.html", "Docs Page 1")
|
||||
b.AssertFileExists("public/blog/section1/page1/index.html", false)
|
||||
b.AssertFileExists("public/index.html", true)
|
||||
b.AssertFileExists("public/index.xml", true)
|
||||
b.AssertFileExists("public/no/index.html", true)
|
||||
b.AssertFileExists("public/no/index.xml", false)
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package segments
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestCompileSegments(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
c.Run("excludes", func(c *qt.C) {
|
||||
fields := []SegmentMatcherFields{
|
||||
{
|
||||
Lang: "n*",
|
||||
Output: "rss",
|
||||
},
|
||||
}
|
||||
|
||||
match, err := compileSegments(fields)
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
check := func() {
|
||||
c.Assert(match, qt.IsNotNil)
|
||||
c.Assert(match(SegmentMatcherFields{Lang: "no"}), qt.Equals, false)
|
||||
c.Assert(match(SegmentMatcherFields{Lang: "no", Kind: "page"}), qt.Equals, false)
|
||||
c.Assert(match(SegmentMatcherFields{Lang: "no", Output: "rss"}), qt.Equals, true)
|
||||
c.Assert(match(SegmentMatcherFields{Lang: "no", Output: "html"}), qt.Equals, false)
|
||||
c.Assert(match(SegmentMatcherFields{Kind: "page"}), qt.Equals, false)
|
||||
c.Assert(match(SegmentMatcherFields{Lang: "no", Output: "rss", Kind: "page"}), qt.Equals, true)
|
||||
}
|
||||
|
||||
check()
|
||||
|
||||
fields = []SegmentMatcherFields{
|
||||
{
|
||||
Path: "/blog/**",
|
||||
},
|
||||
{
|
||||
Lang: "n*",
|
||||
Output: "rss",
|
||||
},
|
||||
}
|
||||
|
||||
match, err = compileSegments(fields)
|
||||
c.Assert(err, qt.IsNil)
|
||||
check()
|
||||
c.Assert(match(SegmentMatcherFields{Path: "/blog/foo"}), qt.Equals, true)
|
||||
})
|
||||
|
||||
c.Run("includes", func(c *qt.C) {
|
||||
fields := []SegmentMatcherFields{
|
||||
{
|
||||
Path: "/docs/**",
|
||||
},
|
||||
{
|
||||
Lang: "no",
|
||||
Output: "rss",
|
||||
},
|
||||
}
|
||||
|
||||
match, err := compileSegments(fields)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(match, qt.IsNotNil)
|
||||
c.Assert(match(SegmentMatcherFields{Lang: "no"}), qt.Equals, false)
|
||||
c.Assert(match(SegmentMatcherFields{Kind: "page"}), qt.Equals, false)
|
||||
c.Assert(match(SegmentMatcherFields{Kind: "page", Path: "/blog/foo"}), qt.Equals, false)
|
||||
c.Assert(match(SegmentMatcherFields{Lang: "en"}), qt.Equals, false)
|
||||
c.Assert(match(SegmentMatcherFields{Lang: "no", Output: "rss"}), qt.Equals, true)
|
||||
c.Assert(match(SegmentMatcherFields{Lang: "no", Output: "html"}), qt.Equals, false)
|
||||
c.Assert(match(SegmentMatcherFields{Kind: "page", Path: "/docs/foo"}), qt.Equals, true)
|
||||
})
|
||||
|
||||
c.Run("includes variant1", func(c *qt.C) {
|
||||
c.Skip()
|
||||
|
||||
fields := []SegmentMatcherFields{
|
||||
{
|
||||
Kind: "home",
|
||||
},
|
||||
{
|
||||
Path: "{/docs,/docs/**}",
|
||||
},
|
||||
}
|
||||
|
||||
match, err := compileSegments(fields)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(match, qt.IsNotNil)
|
||||
c.Assert(match(SegmentMatcherFields{Path: "/blog/foo"}), qt.Equals, false)
|
||||
c.Assert(match(SegmentMatcherFields{Kind: "page", Path: "/docs/foo"}), qt.Equals, true)
|
||||
c.Assert(match(SegmentMatcherFields{Kind: "home", Path: "/"}), qt.Equals, true)
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkSegmentsMatch(b *testing.B) {
|
||||
fields := []SegmentMatcherFields{
|
||||
{
|
||||
Path: "/docs/**",
|
||||
},
|
||||
{
|
||||
Lang: "no",
|
||||
Output: "rss",
|
||||
},
|
||||
}
|
||||
|
||||
match, err := compileSegments(fields)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
match(SegmentMatcherFields{Lang: "no", Output: "rss"})
|
||||
}
|
||||
}
|
||||
+74
-12
@@ -19,6 +19,7 @@ import (
|
||||
"io"
|
||||
"mime"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
@@ -297,7 +298,6 @@ func (s *siteRefLinker) refLink(ref string, source any, relative bool, outputFor
|
||||
ref = filepath.ToSlash(ref)
|
||||
|
||||
refURL, err = url.Parse(ref)
|
||||
|
||||
if err != nil {
|
||||
return s.notFoundURL, err
|
||||
}
|
||||
@@ -427,6 +427,73 @@ func (h *HugoSites) fileEventsFilter(events []fsnotify.Event) []fsnotify.Event {
|
||||
return events[:n]
|
||||
}
|
||||
|
||||
type fileEventInfo struct {
|
||||
fsnotify.Event
|
||||
fi os.FileInfo
|
||||
added bool
|
||||
removed bool
|
||||
isChangedDir bool
|
||||
}
|
||||
|
||||
func (h *HugoSites) fileEventsApplyInfo(events []fsnotify.Event) []fileEventInfo {
|
||||
var infos []fileEventInfo
|
||||
for _, ev := range events {
|
||||
removed := false
|
||||
added := false
|
||||
|
||||
if ev.Op&fsnotify.Remove == fsnotify.Remove {
|
||||
removed = true
|
||||
}
|
||||
|
||||
fi, statErr := h.Fs.Source.Stat(ev.Name)
|
||||
|
||||
// Some editors (Vim) sometimes issue only a Rename operation when writing an existing file
|
||||
// Sometimes a rename operation means that file has been renamed other times it means
|
||||
// it's been updated.
|
||||
if ev.Op.Has(fsnotify.Rename) {
|
||||
// If the file is still on disk, it's only been updated, if it's not, it's been moved
|
||||
if statErr != nil {
|
||||
removed = true
|
||||
}
|
||||
}
|
||||
if ev.Op.Has(fsnotify.Create) {
|
||||
added = true
|
||||
}
|
||||
|
||||
isChangedDir := statErr == nil && fi.IsDir()
|
||||
|
||||
infos = append(infos, fileEventInfo{
|
||||
Event: ev,
|
||||
fi: fi,
|
||||
added: added,
|
||||
removed: removed,
|
||||
isChangedDir: isChangedDir,
|
||||
})
|
||||
}
|
||||
|
||||
n := 0
|
||||
|
||||
for _, ev := range infos {
|
||||
// Remove any directories that's also represented by a file.
|
||||
keep := true
|
||||
if ev.isChangedDir {
|
||||
for _, ev2 := range infos {
|
||||
if ev2.fi != nil && !ev2.fi.IsDir() && filepath.Dir(ev2.Name) == ev.Name {
|
||||
keep = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if keep {
|
||||
infos[n] = ev
|
||||
n++
|
||||
}
|
||||
}
|
||||
infos = infos[:n]
|
||||
|
||||
return infos
|
||||
}
|
||||
|
||||
func (h *HugoSites) fileEventsTranslate(events []fsnotify.Event) []fsnotify.Event {
|
||||
eventMap := make(map[string][]fsnotify.Event)
|
||||
|
||||
@@ -531,18 +598,13 @@ func (h *HugoSites) fileEventsContentPaths(p []pathChange) []pathChange {
|
||||
return keepers
|
||||
}
|
||||
|
||||
// HomeAbsURL is a convenience method giving the absolute URL to the home page.
|
||||
func (s *Site) HomeAbsURL() string {
|
||||
base := ""
|
||||
if len(s.conf.Languages) > 1 {
|
||||
base = s.Language().Lang
|
||||
}
|
||||
return s.AbsURL(base, false)
|
||||
}
|
||||
|
||||
// SitemapAbsURL is a convenience method giving the absolute URL to the sitemap.
|
||||
func (s *Site) SitemapAbsURL() string {
|
||||
p := s.HomeAbsURL()
|
||||
base := ""
|
||||
if len(s.conf.Languages) > 1 || s.Conf.DefaultContentLanguageInSubdir() {
|
||||
base = s.Language().Lang
|
||||
}
|
||||
p := s.AbsURL(base, false)
|
||||
if !strings.HasSuffix(p, "/") {
|
||||
p += "/"
|
||||
}
|
||||
@@ -681,7 +743,7 @@ func (s *Site) getLanguagePermalinkLang(alwaysInSubDir bool) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
if s.h.Conf.IsMultiLingual() && alwaysInSubDir {
|
||||
if s.h.Conf.IsMultilingual() && alwaysInSubDir {
|
||||
return s.Language().Lang
|
||||
}
|
||||
|
||||
|
||||
+16
-8
@@ -361,8 +361,7 @@ func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// Returns true if we're running in a server.
|
||||
// Deprecated: use hugo.IsServer instead
|
||||
// Deprecated: Use hugo.IsServer instead.
|
||||
func (s *Site) IsServer() bool {
|
||||
hugo.Deprecate(".Site.IsServer", "Use hugo.IsServer instead.", "v0.120.0")
|
||||
return s.conf.Internal.Running
|
||||
@@ -382,8 +381,9 @@ func (s *Site) Copyright() string {
|
||||
return s.conf.Copyright
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
|
||||
func (s *Site) RSSLink() template.URL {
|
||||
hugo.Deprecate("Site.RSSLink", "Use the Output Format's Permalink method instead, e.g. .OutputFormats.Get \"RSS\".Permalink", "v0.114.0")
|
||||
hugo.Deprecate(".Site.RSSLink", "Use the Output Format's Permalink method instead, e.g. .OutputFormats.Get \"RSS\".Permalink", "v0.114.0")
|
||||
rssOutputFormat := s.home.OutputFormats().Get("rss")
|
||||
return template.URL(rssOutputFormat.Permalink())
|
||||
}
|
||||
@@ -431,9 +431,9 @@ func (s *Site) BaseURL() string {
|
||||
return s.conf.C.BaseURL.WithPath
|
||||
}
|
||||
|
||||
// Returns the last modification date of the content.
|
||||
// Deprecated: Use .Lastmod instead.
|
||||
// Deprecated: Use .Site.Lastmod instead.
|
||||
func (s *Site) LastChange() time.Time {
|
||||
hugo.Deprecate(".Site.LastChange", "Use .Site.Lastmod instead.", "v0.123.0")
|
||||
return s.lastmod
|
||||
}
|
||||
|
||||
@@ -447,25 +447,31 @@ func (s *Site) Params() maps.Params {
|
||||
return s.conf.Params
|
||||
}
|
||||
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (s *Site) Author() map[string]any {
|
||||
hugo.Deprecate(".Site.Author", "Use taxonomies instead.", "v0.124.0")
|
||||
return s.conf.Author
|
||||
}
|
||||
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (s *Site) Authors() page.AuthorList {
|
||||
hugo.Deprecate(".Site.Authors", "Use taxonomies instead.", "v0.124.0")
|
||||
return page.AuthorList{}
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Params instead.
|
||||
func (s *Site) Social() map[string]string {
|
||||
hugo.Deprecate(".Site.Social", "Use .Site.Params instead.", "v0.124.0")
|
||||
return s.conf.Social
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead
|
||||
// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead.
|
||||
func (s *Site) DisqusShortname() string {
|
||||
hugo.Deprecate(".Site.DisqusShortname", "Use .Site.Config.Services.Disqus.Shortname instead.", "v0.120.0")
|
||||
return s.Config().Services.Disqus.Shortname
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead
|
||||
// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead.
|
||||
func (s *Site) GoogleAnalytics() string {
|
||||
hugo.Deprecate(".Site.GoogleAnalytics", "Use .Site.Config.Services.GoogleAnalytics.ID instead.", "v0.120.0")
|
||||
return s.Config().Services.GoogleAnalytics.ID
|
||||
@@ -484,8 +490,10 @@ func (s *Site) BuildDrafts() bool {
|
||||
return s.conf.BuildDrafts
|
||||
}
|
||||
|
||||
// Deprecated: Use hugo.IsMultilingual instead.
|
||||
func (s *Site) IsMultiLingual() bool {
|
||||
return s.h.isMultiLingual()
|
||||
hugo.Deprecate(".Site.IsMultiLingual", "Use hugo.IsMultilingual instead.", "v0.124.0")
|
||||
return s.h.isMultilingual()
|
||||
}
|
||||
|
||||
func (s *Site) LanguagePrefix() string {
|
||||
|
||||
@@ -271,7 +271,7 @@ func (s *Site) renderAliases() error {
|
||||
p := n.(*pageState)
|
||||
|
||||
// We cannot alias a page that's not rendered.
|
||||
if p.m.noLink() {
|
||||
if p.m.noLink() || p.skipRender() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ func (s *Site) renderAliases() error {
|
||||
// renderMainLanguageRedirect creates a redirect to the main language home,
|
||||
// depending on if it lives in sub folder (e.g. /en) or not.
|
||||
func (s *Site) renderMainLanguageRedirect() error {
|
||||
if s.h.Conf.IsMultihost() || !(s.h.Conf.DefaultContentLanguageInSubdir() || s.h.Conf.IsMultiLingual()) {
|
||||
if s.h.Conf.IsMultihost() || !(s.h.Conf.DefaultContentLanguageInSubdir() || s.h.Conf.IsMultilingual()) {
|
||||
// No need for a redirect
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -398,3 +398,26 @@ Kind: {{ .Kind }}|RelPermalink: {{ .RelPermalink }}|SectionsPath: {{ .SectionsPa
|
||||
b.AssertFileContent("public/a/b/c/mybundle/index.html", "Kind: page|RelPermalink: /a/b/c/mybundle/|SectionsPath: /a/b/c|SectionsEntries: [a b c]|Len: 3")
|
||||
b.AssertFileContent("public/index.html", "Kind: home|RelPermalink: /|SectionsPath: /|SectionsEntries: []|Len: 0")
|
||||
}
|
||||
|
||||
func TestParentWithPageOverlap(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com/"
|
||||
-- content/docs/_index.md --
|
||||
-- content/docs/logs/_index.md --
|
||||
-- content/docs/logs/sdk.md --
|
||||
-- content/docs/logs/sdk_exporters/stdout.md --
|
||||
-- layouts/_default/list.html --
|
||||
{{ .RelPermalink }}|{{ with .Parent}}{{ .RelPermalink }}{{ end }}|
|
||||
-- layouts/_default/single.html --
|
||||
{{ .RelPermalink }}|{{ with .Parent}}{{ .RelPermalink }}{{ end }}|
|
||||
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "/||")
|
||||
b.AssertFileContent("public/docs/index.html", "/docs/|/|")
|
||||
b.AssertFileContent("public/docs/logs/index.html", "/docs/logs/|/docs/|")
|
||||
b.AssertFileContent("public/docs/logs/sdk/index.html", "/docs/logs/sdk/|/docs/logs/|")
|
||||
b.AssertFileContent("public/docs/logs/sdk_exporters/stdout/index.html", "/docs/logs/sdk_exporters/stdout/|/docs/logs/|")
|
||||
}
|
||||
|
||||
+56
-1
@@ -15,6 +15,7 @@ package hugolib
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
@@ -127,7 +128,7 @@ func TestParseSitemap(t *testing.T) {
|
||||
func TestSitemapShouldNotUseListXML(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
disableKinds = ["term", "taxonomy"]
|
||||
@@ -170,3 +171,57 @@ type: sitemap
|
||||
|
||||
b.AssertFileExists("public/sitemap.xml", true)
|
||||
}
|
||||
|
||||
// Issue 12266
|
||||
func TestSitemapIssue12266(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = 'https://example.org/'
|
||||
disableKinds = ['rss','taxonomy','term']
|
||||
defaultContentLanguage = 'en'
|
||||
defaultContentLanguageInSubdir = true
|
||||
[languages.de]
|
||||
[languages.en]
|
||||
`
|
||||
|
||||
// Test A: multilingual with defaultContentLanguageInSubdir = true
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/sitemap.xml",
|
||||
"<loc>https://example.org/de/sitemap.xml</loc>",
|
||||
"<loc>https://example.org/en/sitemap.xml</loc>",
|
||||
)
|
||||
b.AssertFileContent("public/de/sitemap.xml", "<loc>https://example.org/de/</loc>")
|
||||
b.AssertFileContent("public/en/sitemap.xml", "<loc>https://example.org/en/</loc>")
|
||||
|
||||
// Test B: multilingual with defaultContentLanguageInSubdir = false
|
||||
files = strings.ReplaceAll(files, "defaultContentLanguageInSubdir = true", "defaultContentLanguageInSubdir = false")
|
||||
|
||||
b = Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/sitemap.xml",
|
||||
"<loc>https://example.org/de/sitemap.xml</loc>",
|
||||
"<loc>https://example.org/en/sitemap.xml</loc>",
|
||||
)
|
||||
b.AssertFileContent("public/de/sitemap.xml", "<loc>https://example.org/de/</loc>")
|
||||
b.AssertFileContent("public/en/sitemap.xml", "<loc>https://example.org/</loc>")
|
||||
|
||||
// Test C: monolingual with defaultContentLanguageInSubdir = false
|
||||
files = strings.ReplaceAll(files, "[languages.de]", "")
|
||||
files = strings.ReplaceAll(files, "[languages.en]", "")
|
||||
|
||||
b = Test(t, files)
|
||||
|
||||
b.AssertFileExists("public/en/sitemap.xml", false)
|
||||
b.AssertFileContent("public/sitemap.xml", "<loc>https://example.org/</loc>")
|
||||
|
||||
// Test D: monolingual with defaultContentLanguageInSubdir = true
|
||||
files = strings.ReplaceAll(files, "defaultContentLanguageInSubdir = false", "defaultContentLanguageInSubdir = true")
|
||||
|
||||
b = Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/sitemap.xml", "<loc>https://example.org/en/sitemap.xml</loc>")
|
||||
b.AssertFileContent("public/en/sitemap.xml", "<loc>https://example.org/en/</loc>")
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,7 +1,8 @@
|
||||
# Release env.
|
||||
# These will be replaced by script before release.
|
||||
HUGORELEASER_TAG=v0.123.7
|
||||
HUGORELEASER_COMMITISH=312735366b20d64bd61bff8627f593749f86c964
|
||||
HUGORELEASER_TAG=v0.123.8
|
||||
HUGORELEASER_COMMITISH=5fed9c591b694f314e5939548e11cc3dcb79a79c
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -355,7 +355,7 @@ func (im *identityManager) String() string {
|
||||
}
|
||||
|
||||
func (im *identityManager) forEeachIdentity(fn func(id Identity) bool) bool {
|
||||
// The absense of a lock here is debliberate. This is currently opnly used on server reloads
|
||||
// The absence of a lock here is deliberate. This is currently only used on server reloads
|
||||
// in a single-threaded context.
|
||||
for id := range im.ids {
|
||||
if fn(id) {
|
||||
|
||||
+1
-1
@@ -134,7 +134,7 @@ var commonTestScriptsParam = testscript.Params{
|
||||
fmt.Fprintf(ts.Stdout(), "%s %04o %s %s\n", fi.Mode(), fi.Mode().Perm(), fi.ModTime().Format(time.RFC3339Nano), fi.Name())
|
||||
}
|
||||
},
|
||||
// append appends to a file with a leaading newline.
|
||||
// append appends to a file with a leading newline.
|
||||
"append": func(ts *testscript.TestScript, neg bool, args []string) {
|
||||
if len(args) < 2 {
|
||||
ts.Fatalf("usage: append FILE TEXT")
|
||||
|
||||
@@ -147,7 +147,7 @@ type codeBlockContext struct {
|
||||
ordinal int
|
||||
|
||||
// This is only used in error situations and is expensive to create,
|
||||
// to deleay creation until needed.
|
||||
// to delay creation until needed.
|
||||
pos htext.Position
|
||||
posInit sync.Once
|
||||
createPos func() htext.Position
|
||||
|
||||
@@ -184,9 +184,11 @@ func newMarkdown(pcfg converter.ProviderConfig) goldmark.Markdown {
|
||||
}
|
||||
}
|
||||
|
||||
extensions = append(extensions, passthrough.NewPassthroughWithDelimiters(
|
||||
inlineDelimiters,
|
||||
blockDelimiters,
|
||||
extensions = append(extensions, passthrough.New(
|
||||
passthrough.Config{
|
||||
InlineDelimiters: inlineDelimiters,
|
||||
BlockDelimiters: blockDelimiters,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ func New(astAttributes []ast.Attribute, ownerType AttributesOwnerType) *Attribut
|
||||
case []byte:
|
||||
// Note that we don't do any HTML escaping here.
|
||||
// We used to do that, but that changed in #9558.
|
||||
// Noww it's up to the templates to decide.
|
||||
// Now it's up to the templates to decide.
|
||||
vv = string(vvv)
|
||||
default:
|
||||
panic(fmt.Sprintf("not implemented: %T", vvv))
|
||||
@@ -175,7 +175,7 @@ func (a *AttributesHolder) OptionsSlice() []Attribute {
|
||||
|
||||
// RenderASTAttributes writes the AST attributes to the given as attributes to an HTML element.
|
||||
// This is used by the default HTML renderers, e.g. for headings etc. where no hook template could be found.
|
||||
// This performs HTML esacaping of string attributes.
|
||||
// This performs HTML escaping of string attributes.
|
||||
func RenderASTAttributes(w hugio.FlexiWriter, attributes ...ast.Attribute) {
|
||||
for _, attr := range attributes {
|
||||
|
||||
|
||||
+3
-3
@@ -86,7 +86,7 @@ func ApplyProjectConfigDefaults(mod Module, cfgs ...config.AllProvider) error {
|
||||
|
||||
first := cfgs[0]
|
||||
dirsBase := first.DirsBase()
|
||||
isMultiHost := first.IsMultihost()
|
||||
isMultihost := first.IsMultihost()
|
||||
|
||||
for i, cfg := range cfgs {
|
||||
dirs := cfg.Dirs()
|
||||
@@ -113,7 +113,7 @@ func ApplyProjectConfigDefaults(mod Module, cfgs ...config.AllProvider) error {
|
||||
dir = dirs.AssetDir
|
||||
case files.ComponentFolderStatic:
|
||||
// For static dirs, we only care about the language in multihost setups.
|
||||
dropLang = !isMultiHost
|
||||
dropLang = !isMultihost
|
||||
}
|
||||
|
||||
var perLang bool
|
||||
@@ -270,7 +270,7 @@ type Config struct {
|
||||
|
||||
// When enabled, we will pick the vendored module closest to the module
|
||||
// using it.
|
||||
// The default behaviour is to pick the first.
|
||||
// The default behavior is to pick the first.
|
||||
// Note that there can still be only one dependency of a given module path,
|
||||
// so once it is in use it cannot be redefined.
|
||||
VendorClosest bool
|
||||
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
|
||||
const commitPrefix = "releaser:"
|
||||
|
||||
// New initialises a ReleaseHandler.
|
||||
// New initializes a ReleaseHandler.
|
||||
func New(skipPush, try bool, step int) (*ReleaseHandler, error) {
|
||||
if step < 1 || step > 2 {
|
||||
return nil, fmt.Errorf("step must be 1 or 2")
|
||||
|
||||
@@ -52,9 +52,9 @@ type AlternativeOutputFormatsProvider interface {
|
||||
|
||||
// AuthorProvider provides author information.
|
||||
type AuthorProvider interface {
|
||||
// Deprecated.
|
||||
// Deprecated: Use taxonomies instead.
|
||||
Author() Author
|
||||
// Deprecated.
|
||||
// Deprecated: Use taxonomies instead.
|
||||
Authors() AuthorList
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
package page
|
||||
|
||||
// AuthorList is a list of all authors and their metadata.
|
||||
// Deprecated: Use taxonomies instead.
|
||||
type AuthorList map[string]Author
|
||||
|
||||
// Author contains details about the author of a page.
|
||||
// Deprecated: Use taxonomies instead.
|
||||
type Author struct {
|
||||
GivenName string
|
||||
FamilyName string
|
||||
@@ -41,4 +43,5 @@ type Author struct {
|
||||
// - youtube
|
||||
// - linkedin
|
||||
// - skype
|
||||
// Deprecated: Use taxonomies instead.
|
||||
type AuthorSocial map[string]string
|
||||
|
||||
@@ -157,10 +157,11 @@ func TestDecodeCascadeConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
type testConfig struct {
|
||||
environment string
|
||||
running bool
|
||||
workingDir string
|
||||
multihost bool
|
||||
environment string
|
||||
running bool
|
||||
workingDir string
|
||||
multihost bool
|
||||
multilingual bool
|
||||
}
|
||||
|
||||
func (c testConfig) Environment() string {
|
||||
@@ -179,6 +180,10 @@ func (c testConfig) IsMultihost() bool {
|
||||
return c.multihost
|
||||
}
|
||||
|
||||
func (c testConfig) IsMultilingual() bool {
|
||||
return c.multilingual
|
||||
}
|
||||
|
||||
func TestIsGlobWithExtension(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
|
||||
@@ -79,10 +79,12 @@ func (p *nopPage) RSSLink() template.URL {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (p *nopPage) Author() Author {
|
||||
return Author{}
|
||||
}
|
||||
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (p *nopPage) Authors() AuthorList {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -421,18 +421,22 @@ func (f *frontmatterFieldHandlers) newDateFieldHandler(key string, setter func(d
|
||||
return false, nil
|
||||
}
|
||||
|
||||
date, err := htime.ToTimeInDefaultLocationE(v, d.Location)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
var date time.Time
|
||||
if vt, ok := v.(time.Time); ok && vt.Location() == d.Location {
|
||||
date = vt
|
||||
} else {
|
||||
var err error
|
||||
date, err = htime.ToTimeInDefaultLocationE(v, d.Location)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
d.PageConfig.Params[key] = date
|
||||
}
|
||||
|
||||
// We map several date keys to one, so, for example,
|
||||
// "expirydate", "unpublishdate" will all set .ExpiryDate (first found).
|
||||
setter(d, date)
|
||||
|
||||
// This is the params key as set in front matter.
|
||||
d.PageConfig.Params[key] = date
|
||||
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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 pagemeta_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
)
|
||||
|
||||
func TestLastModEq(t *testing.T) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
timeZone = "Europe/London"
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: p1
|
||||
date: 2024-03-13T06:00:00
|
||||
---
|
||||
-- layouts/_default/single.html --
|
||||
Date: {{ .Date }}
|
||||
Lastmod: {{ .Lastmod }}
|
||||
Eq: {{ eq .Date .Lastmod }}
|
||||
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", `
|
||||
Date: 2024-03-13 06:00:00 +0000 GMT
|
||||
Lastmod: 2024-03-13 06:00:00 +0000 GMT
|
||||
Eq: true
|
||||
`)
|
||||
}
|
||||
+26
-18
@@ -54,8 +54,7 @@ type Site interface {
|
||||
// A shortcut to the home
|
||||
Home() Page
|
||||
|
||||
// Returns true if we're running in a server.
|
||||
// Deprecated: use hugo.IsServer instead
|
||||
// Deprecated: Use hugo.IsServer instead.
|
||||
IsServer() bool
|
||||
|
||||
// Returns the server port.
|
||||
@@ -64,7 +63,6 @@ type Site interface {
|
||||
// Returns the configured title for this Site.
|
||||
Title() string
|
||||
|
||||
// Returns the configured language code for this Site.
|
||||
// Deprecated: Use .Language.LanguageCode instead.
|
||||
LanguageCode() string
|
||||
|
||||
@@ -86,7 +84,6 @@ type Site interface {
|
||||
// Returns a taxonomy map.
|
||||
Taxonomies() TaxonomyList
|
||||
|
||||
// Returns the last modification date of the content.
|
||||
// Deprecated: Use .Lastmod instead.
|
||||
LastChange() time.Time
|
||||
|
||||
@@ -111,13 +108,13 @@ type Site interface {
|
||||
// Returns the site config.
|
||||
Config() SiteConfig
|
||||
|
||||
// Author is deprecated and will be removed in a future release.
|
||||
// Deprecated: Use taxonomies instead.
|
||||
Author() map[string]interface{}
|
||||
|
||||
// Authors is deprecated and will be removed in a future release.
|
||||
// Deprecated: Use taxonomies instead.
|
||||
Authors() AuthorList
|
||||
|
||||
// Returns the social links for this site.
|
||||
// Deprecated: Use .Site.Params instead.
|
||||
Social() map[string]string
|
||||
|
||||
// Deprecated: Use Config().Services.GoogleAnalytics instead.
|
||||
@@ -129,13 +126,13 @@ type Site interface {
|
||||
// BuildDrafts is deprecated and will be removed in a future release.
|
||||
BuildDrafts() bool
|
||||
|
||||
// IsMultiLingual reports whether this site is configured with more than one language.
|
||||
// Deprecated: Use hugo.IsMultilingual instead.
|
||||
IsMultiLingual() bool
|
||||
|
||||
// LanguagePrefix returns the language prefix for this site.
|
||||
LanguagePrefix() string
|
||||
|
||||
// Deprecated. Use site.Home.OutputFormats.Get "rss" instead.
|
||||
// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
|
||||
RSSLink() template.URL
|
||||
}
|
||||
|
||||
@@ -168,19 +165,22 @@ func (s *siteWrapper) Key() string {
|
||||
return s.s.Language().Lang
|
||||
}
|
||||
|
||||
// // Deprecated: Use .Site.Params instead.
|
||||
func (s *siteWrapper) Social() map[string]string {
|
||||
return s.s.Social()
|
||||
}
|
||||
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (s *siteWrapper) Author() map[string]interface{} {
|
||||
return s.s.Author()
|
||||
}
|
||||
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (s *siteWrapper) Authors() AuthorList {
|
||||
return AuthorList{}
|
||||
return s.s.Authors()
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead
|
||||
// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead.
|
||||
func (s *siteWrapper) GoogleAnalytics() string {
|
||||
return s.s.GoogleAnalytics()
|
||||
}
|
||||
@@ -217,7 +217,7 @@ func (s *siteWrapper) Home() Page {
|
||||
return s.s.Home()
|
||||
}
|
||||
|
||||
// Deprecated: use hugo.IsServer instead
|
||||
// Deprecated: Use hugo.IsServer instead.
|
||||
func (s *siteWrapper) IsServer() bool {
|
||||
return s.s.IsServer()
|
||||
}
|
||||
@@ -262,9 +262,9 @@ func (s *siteWrapper) Taxonomies() TaxonomyList {
|
||||
return s.s.Taxonomies()
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Lastmod instead.
|
||||
func (s *siteWrapper) LastChange() time.Time {
|
||||
hugo.Deprecate(".Site.LastChange", "Use .Site.Lastmod instead.", "v0.123.0")
|
||||
return s.s.Lastmod()
|
||||
return s.s.LastChange()
|
||||
}
|
||||
|
||||
func (s *siteWrapper) Lastmod() time.Time {
|
||||
@@ -295,11 +295,12 @@ func (s *siteWrapper) BuildDrafts() bool {
|
||||
return s.s.BuildDrafts()
|
||||
}
|
||||
|
||||
// Deprecated: Use hugo.IsMultilingual instead.
|
||||
func (s *siteWrapper) IsMultiLingual() bool {
|
||||
return s.s.IsMultiLingual()
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead
|
||||
// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead.
|
||||
func (s *siteWrapper) DisqusShortname() string {
|
||||
return s.s.DisqusShortname()
|
||||
}
|
||||
@@ -308,6 +309,7 @@ func (s *siteWrapper) LanguagePrefix() string {
|
||||
return s.s.LanguagePrefix()
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
|
||||
func (s *siteWrapper) RSSLink() template.URL {
|
||||
return s.s.RSSLink()
|
||||
}
|
||||
@@ -322,14 +324,17 @@ type testSite struct {
|
||||
l *langs.Language
|
||||
}
|
||||
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (s testSite) Author() map[string]interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (s testSite) Authors() AuthorList {
|
||||
return AuthorList{}
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Params instead.
|
||||
func (s testSite) Social() map[string]string {
|
||||
return make(map[string]string)
|
||||
}
|
||||
@@ -342,6 +347,7 @@ func (t testSite) ServerPort() int {
|
||||
return 1313
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Lastmod instead.
|
||||
func (testSite) LastChange() (t time.Time) {
|
||||
return
|
||||
}
|
||||
@@ -386,7 +392,7 @@ func (t testSite) Languages() langs.Languages {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead
|
||||
// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead.
|
||||
func (t testSite) GoogleAnalytics() string {
|
||||
return ""
|
||||
}
|
||||
@@ -395,7 +401,7 @@ func (t testSite) MainSections() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: use hugo.IsServer instead
|
||||
// Deprecated: Use hugo.IsServer instead.
|
||||
func (t testSite) IsServer() bool {
|
||||
return false
|
||||
}
|
||||
@@ -444,7 +450,7 @@ func (s testSite) Config() SiteConfig {
|
||||
return SiteConfig{}
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead
|
||||
// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead.
|
||||
func (testSite) DisqusShortname() string {
|
||||
return ""
|
||||
}
|
||||
@@ -453,6 +459,7 @@ func (s testSite) BuildDrafts() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Deprecated: Use hugo.IsMultilingual instead.
|
||||
func (s testSite) IsMultiLingual() bool {
|
||||
return false
|
||||
}
|
||||
@@ -461,6 +468,7 @@ func (s testSite) Param(key any) (any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
|
||||
func (s testSite) RSSLink() template.URL {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -127,10 +127,12 @@ func (p *testPage) AlternativeOutputFormats() OutputFormats {
|
||||
panic("testpage: not implemented")
|
||||
}
|
||||
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (p *testPage) Author() Author {
|
||||
return Author{}
|
||||
}
|
||||
|
||||
// Deprecated: Use taxonomies instead.
|
||||
func (p *testPage) Authors() AuthorList {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
"github.com/gohugoio/hugo/hugofs/glob"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
@@ -61,13 +62,14 @@ func (r Resources) Get(name any) Resource {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
namestr = strings.ToLower(namestr)
|
||||
|
||||
namestr = paths.AddLeadingSlash(namestr)
|
||||
|
||||
// First check the Name.
|
||||
// Note that this can be modified by the user in the front matter,
|
||||
// also, it does not contain any language code.
|
||||
for _, resource := range r {
|
||||
if strings.EqualFold(namestr, resource.Name()) {
|
||||
if strings.EqualFold(namestr, paths.AddLeadingSlash(resource.Name())) {
|
||||
return resource
|
||||
}
|
||||
}
|
||||
@@ -75,7 +77,7 @@ func (r Resources) Get(name any) Resource {
|
||||
// Finally, check the normalized name.
|
||||
for _, resource := range r {
|
||||
if nop, ok := resource.(NameNormalizedProvider); ok {
|
||||
if strings.EqualFold(namestr, nop.NameNormalized()) {
|
||||
if strings.EqualFold(namestr, paths.AddLeadingSlash(nop.NameNormalized())) {
|
||||
return resource
|
||||
}
|
||||
}
|
||||
@@ -92,21 +94,21 @@ func (r Resources) GetMatch(pattern any) Resource {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
g, err := glob.GetGlob(patternstr)
|
||||
g, err := glob.GetGlob(paths.AddLeadingSlash(patternstr))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, resource := range r {
|
||||
if g.Match(resource.Name()) {
|
||||
if g.Match(paths.AddLeadingSlash(resource.Name())) {
|
||||
return resource
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, check the original name.
|
||||
// Finally, check the normalized name.
|
||||
for _, resource := range r {
|
||||
if nop, ok := resource.(NameNormalizedProvider); ok {
|
||||
if g.Match(nop.NameNormalized()) {
|
||||
if g.Match(paths.AddLeadingSlash(nop.NameNormalized())) {
|
||||
return resource
|
||||
}
|
||||
}
|
||||
@@ -130,14 +132,14 @@ func (r Resources) Match(pattern any) Resources {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
g, err := glob.GetGlob(patternstr)
|
||||
g, err := glob.GetGlob(paths.AddLeadingSlash(patternstr))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var matches Resources
|
||||
for _, resource := range r {
|
||||
if g.Match(resource.Name()) {
|
||||
if g.Match(paths.AddLeadingSlash(resource.Name())) {
|
||||
matches = append(matches, resource)
|
||||
}
|
||||
}
|
||||
@@ -145,7 +147,7 @@ func (r Resources) Match(pattern any) Resources {
|
||||
// Fall back to the normalized name.
|
||||
for _, resource := range r {
|
||||
if nop, ok := resource.(NameNormalizedProvider); ok {
|
||||
if g.Match(nop.NameNormalized()) {
|
||||
if g.Match(paths.AddLeadingSlash(nop.NameNormalized())) {
|
||||
matches = append(matches, resource)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,8 +134,8 @@ func (c *Client) match(name, pattern string, matchFunc func(r resource.Resource)
|
||||
OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) {
|
||||
return meta.Open()
|
||||
},
|
||||
NameNormalized: meta.PathInfo.Name(),
|
||||
NameOriginal: meta.PathInfo.Unnormalized().Name(),
|
||||
NameNormalized: meta.PathInfo.Path(),
|
||||
NameOriginal: meta.PathInfo.Unnormalized().Path(),
|
||||
GroupIdentity: meta.PathInfo,
|
||||
TargetPath: meta.PathInfo.Unnormalized().Path(),
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package dartsass integrates with the Dass Sass Embedded protocol to transpile
|
||||
// Package dartsass integrates with the Dart Sass Embedded protocol to transpile
|
||||
// SCSS/SASS.
|
||||
package dartsass
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@ func (t importResolver) CanonicalizeURL(url string) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Not found, let Dart Dass handle it
|
||||
// Not found, let Dart Sass handle it
|
||||
return "", nil
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
// The current is built with 446a5dcf5a3230ce9832682d8f521071d8a34a2b (go 1.22 dev. Thu Oct 5 12:20:11 2023 -0700)
|
||||
// The current is built with db6097f8cb [release-branch.go1.22] go1.22.1
|
||||
// TODO(bep) preserve the staticcheck.conf file.
|
||||
fmt.Println("Forking ...")
|
||||
defer fmt.Println("Done ...")
|
||||
|
||||
|
||||
+29
-25
@@ -1,5 +1,5 @@
|
||||
name: hugo
|
||||
base: core20
|
||||
base: core22
|
||||
confinement: strict
|
||||
adopt-info: hugo
|
||||
title: Hugo
|
||||
@@ -75,19 +75,19 @@ environment:
|
||||
npm_config_userconfig: $SNAP_USER_DATA/.npmrc
|
||||
pandoc_datadir: $SNAP/usr/share/pandoc
|
||||
PYTHONHOME: /usr:$SNAP/usr
|
||||
RUBYLIB: $SNAP/usr/lib/ruby/vendor_ruby/2.7.0:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/ruby/vendor_ruby/2.7.0:$SNAP/usr/lib/ruby/vendor_ruby:$SNAP/usr/lib/ruby/2.7.0:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/ruby/2.7.0
|
||||
RUBYLIB: $SNAP/usr/lib/ruby/vendor_ruby/3.0.0:$SNAP/usr/lib/$CRAFT_ARCH_TRIPLET/ruby/vendor_ruby/3.0.0:$SNAP/usr/lib/ruby/vendor_ruby:$SNAP/usr/lib/ruby/3.0.0:$SNAP/usr/lib/$CRAFT_ARCH_TRIPLET/ruby/3.0.0
|
||||
|
||||
# HUGO_SECURITY_EXEC_OSENV
|
||||
#
|
||||
# Default value:
|
||||
# (?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG)$
|
||||
# (?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|SYSTEMDRIVE)$
|
||||
# Bundled applications require additional access:
|
||||
# git: GIT_EXEC_PATH and LD_LIBRARY_PATH
|
||||
# npx: npm_config_{cache,init_module,userconfig}
|
||||
# pandoc: pandoc_datadir
|
||||
# rst2html: PYTHONHOME and SNAP
|
||||
# asciidoctor: RUBYLIB
|
||||
HUGO_SECURITY_EXEC_OSENV: (?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|GIT_EXEC_PATH|LD_LIBRARY_PATH|npm_config_(cache|init_module|userconfig)|pandoc_datadir|PYTHONHOME|SNAP|RUBYLIB)$
|
||||
|
||||
HUGO_SECURITY_EXEC_OSENV: (?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|SYSTEMDRIVE|GIT_EXEC_PATH|LD_LIBRARY_PATH|npm_config_(cache|init_module|userconfig)|pandoc_datadir|PYTHONHOME|SNAP|RUBYLIB)$
|
||||
apps:
|
||||
hugo:
|
||||
command: bin/hugo
|
||||
@@ -116,7 +116,7 @@ parts:
|
||||
go:
|
||||
plugin: nil
|
||||
stage-snaps:
|
||||
- go/1.21/stable
|
||||
- go/1.22/stable
|
||||
prime:
|
||||
- bin/go
|
||||
- pkg/tool
|
||||
@@ -129,12 +129,12 @@ parts:
|
||||
- git
|
||||
- go
|
||||
override-pull: |
|
||||
snapcraftctl pull
|
||||
snapcraftctl set-version "$(git describe --tags --always --match 'v[0-9]*' | sed 's/^v//; s/-/+git/; s/-g/./')"
|
||||
craftctl default
|
||||
craftctl set version=$(git describe --tags --always --match 'v[0-9]*' | sed 's/^v//; s/-/+git/; s/-g/./')
|
||||
if grep -q 'Suffix:\s*""' common/hugo/version_current.go; then
|
||||
snapcraftctl set-grade "stable"
|
||||
craftctl set grade=stable
|
||||
else
|
||||
snapcraftctl set-grade "devel"
|
||||
craftctl set grade=devel
|
||||
fi
|
||||
override-build: |
|
||||
echo "\nStarting override-build:"
|
||||
@@ -152,15 +152,15 @@ parts:
|
||||
echo " * Building shell completion..."
|
||||
./hugo completion bash > hugo-completion
|
||||
|
||||
echo " * Installing to ${SNAPCRAFT_PART_INSTALL}..."
|
||||
install -d $SNAPCRAFT_PART_INSTALL/bin
|
||||
cp -av hugo $SNAPCRAFT_PART_INSTALL/bin/
|
||||
mv -v hugo-completion $SNAPCRAFT_PART_INSTALL/
|
||||
echo " * Installing to ${CRAFT_PART_INSTALL}..."
|
||||
install -d $CRAFT_PART_INSTALL/bin
|
||||
cp -av hugo $CRAFT_PART_INSTALL/bin/
|
||||
mv -v hugo-completion $CRAFT_PART_INSTALL/
|
||||
|
||||
echo " * Stripping binary..."
|
||||
ls -l $SNAPCRAFT_PART_INSTALL/bin/hugo
|
||||
strip --remove-section=.comment --remove-section=.note $SNAPCRAFT_PART_INSTALL/bin/hugo
|
||||
ls -l $SNAPCRAFT_PART_INSTALL/bin/hugo
|
||||
ls -l $CRAFT_PART_INSTALL/bin/hugo
|
||||
strip --remove-section=.comment --remove-section=.note $CRAFT_PART_INSTALL/bin/hugo
|
||||
ls -l $CRAFT_PART_INSTALL/bin/hugo
|
||||
|
||||
asciidoctor:
|
||||
plugin: nil
|
||||
@@ -168,8 +168,12 @@ parts:
|
||||
- asciidoctor
|
||||
override-build: |
|
||||
set -ex
|
||||
snapcraftctl build
|
||||
sed -i '1s|#!/usr/bin/ruby|#!/usr/bin/env ruby|' $SNAPCRAFT_PART_INSTALL/usr/bin/asciidoctor
|
||||
craftctl default
|
||||
sed -i '1s|#!/usr/bin/ruby|#!/usr/bin/env ruby|' $CRAFT_PART_INSTALL/usr/bin/asciidoctor
|
||||
# don't try and flock() gemspecs since this is blocked by AppArmor - see
|
||||
# https://github.com/rubygems/rubygems/pull/5278 in particular
|
||||
# https://github.com/rubygems/rubygems/pull/5278/commits/27b682c81226838b1254ac5843a3f5b1cb20f076
|
||||
sed -i 's|!solaris_platform|win_platform|' $CRAFT_PART_INSTALL/usr/lib/ruby/vendor_ruby/rubygems.rb
|
||||
|
||||
dart-sass:
|
||||
plugin: nil
|
||||
@@ -177,8 +181,8 @@ parts:
|
||||
- curl
|
||||
override-build: |
|
||||
set -ex
|
||||
snapcraftctl build
|
||||
case "$SNAPCRAFT_TARGET_ARCH" in
|
||||
craftctl default
|
||||
case "$CRAFT_TARGET_ARCH" in
|
||||
amd64) arch=x64 ;;
|
||||
arm64) arch=arm64 ;;
|
||||
armhf) arch=arm ;;
|
||||
@@ -189,8 +193,8 @@ parts:
|
||||
url=$(curl -s https://api.github.com/repos/sass/dart-sass/releases/latest | awk -F\" "/browser_download_url.*-linux-${arch}.tar.gz/{print \$(NF-1)}")
|
||||
curl -LO --retry-connrefused --retry 10 "${url}"
|
||||
tar xf dart-sass-*-linux-${arch}.tar.gz
|
||||
install -d $SNAPCRAFT_PART_INSTALL/bin
|
||||
cp -av dart-sass/* $SNAPCRAFT_PART_INSTALL/bin/
|
||||
install -d $CRAFT_PART_INSTALL/bin
|
||||
cp -av dart-sass/* $CRAFT_PART_INSTALL/bin/
|
||||
fi
|
||||
|
||||
node:
|
||||
@@ -209,7 +213,7 @@ parts:
|
||||
- python3-docutils
|
||||
override-build: |
|
||||
set -ex
|
||||
snapcraftctl build
|
||||
sed -i "s|'/usr/share/docutils/'|os.path.expandvars('\$SNAP/usr/share/docutils/')|" $SNAPCRAFT_PART_INSTALL/usr/lib/python3/dist-packages/docutils/__init__.py
|
||||
craftctl default
|
||||
sed -i "s|'/usr/share/docutils/'|os.path.expandvars('\$SNAP/usr/share/docutils/')|" $CRAFT_PART_INSTALL/usr/lib/python3/dist-packages/docutils/__init__.py
|
||||
organize:
|
||||
usr/share/docutils/scripts/python3: usr/bin
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ func (fi *File) Extension() string {
|
||||
func (fi *File) Ext() string { return fi.p().Ext() }
|
||||
|
||||
// Lang returns a file's language (e.g. "sv").
|
||||
// Deprecated: use .Page.Language.Lang instead.
|
||||
// Deprecated: Use .Page.Language.Lang instead.
|
||||
func (fi *File) Lang() string {
|
||||
hugo.Deprecate(".Page.File.Lang", "Use .Page.Language.Lang instead.", "v0.123.0")
|
||||
return fi.fim.Meta().Lang
|
||||
|
||||
@@ -16,4 +16,4 @@ checkfilecount $NUM_COMMANDS manpages
|
||||
hugo gen chromastyles -h
|
||||
stdout 'Generate CSS stylesheet for the Chroma code highlighter'
|
||||
hugo gen chromastyles --style monokai
|
||||
stdout '/\* LineHighlight \*/ \.chroma \.hl \{ background-color: #3c3d38 \}'
|
||||
stdout '/\* LineHighlight \*/ \.chroma \.hl \{ background-color:#3c3d38 \}'
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
hugo
|
||||
|
||||
stdout 'IsMultiHost: true'
|
||||
|
||||
-- hugo.toml --
|
||||
title = "Hugo IsMultiHost Test"
|
||||
[languages.en]
|
||||
baseURL = "https://example.org"
|
||||
[languages.zh]
|
||||
baseURL = "https://zh.example.org"
|
||||
|
||||
-- layouts/index.html --
|
||||
{{ warnf "IsMultiHost: %v" hugo.IsMultiHost }}
|
||||
@@ -617,10 +617,10 @@ type intersector struct {
|
||||
}
|
||||
|
||||
func (i *intersector) appendIfNotSeen(v reflect.Value) {
|
||||
vi := v.Interface()
|
||||
if !i.seen[vi] {
|
||||
k := normalize(v)
|
||||
if !i.seen[k] {
|
||||
i.r = reflect.Append(i.r, v)
|
||||
i.seen[vi] = true
|
||||
i.seen[k] = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -638,7 +638,7 @@ func (i *intersector) handleValuePair(l1vv, l2vv reflect.Value) {
|
||||
i.appendIfNotSeen(l1vv)
|
||||
}
|
||||
case kind == reflect.Ptr, kind == reflect.Struct:
|
||||
if l1vv.Interface() == l2vv.Interface() {
|
||||
if types.Unwrapv(l1vv.Interface()) == types.Unwrapv(l2vv.Interface()) {
|
||||
i.appendIfNotSeen(l1vv)
|
||||
}
|
||||
case kind == reflect.Interface:
|
||||
|
||||
@@ -230,3 +230,51 @@ boolf = false
|
||||
"false",
|
||||
)
|
||||
}
|
||||
|
||||
func TestTermEntriesCollectionsIssue12254(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
capitalizeListTitles = false
|
||||
disableKinds = ['rss','sitemap']
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: p1
|
||||
categories: [cat-a]
|
||||
tags: ['tag-b','tag-a','tag-c']
|
||||
---
|
||||
-- content/p2.md --
|
||||
---
|
||||
title: p2
|
||||
categories: [cat-a]
|
||||
tags: ['tag-b','tag-a']
|
||||
---
|
||||
-- content/p3.md --
|
||||
---
|
||||
title: p3
|
||||
categories: [cat-a]
|
||||
tags: ['tag-b']
|
||||
---
|
||||
-- layouts/_default/term.html --
|
||||
{{ $list1 := .Pages }}
|
||||
{{ range $i, $e := site.Taxonomies.tags.ByCount }}
|
||||
{{ $list2 := .Pages }}
|
||||
{{ $i }}: List1: {{ len $list1 }}|
|
||||
{{ $i }}: List2: {{ len $list2 }}|
|
||||
{{ $i }}: Intersect: {{ intersect $.Pages .Pages | len }}|
|
||||
{{ $i }}: Union: {{ union $.Pages .Pages | len }}|
|
||||
{{ $i }}: SymDiff: {{ symdiff $.Pages .Pages | len }}|
|
||||
{{ $i }}: Uniq: {{ append $.Pages .Pages | uniq | len }}|
|
||||
{{ end }}
|
||||
|
||||
|
||||
`
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/categories/cat-a/index.html",
|
||||
"0: List1: 3|\n0: List2: 3|\n0: Intersect: 3|\n0: Union: 3|\n0: SymDiff: 0|\n0: Uniq: 3|\n\n\n1: List1: 3|",
|
||||
"1: List2: 2|\n1: Intersect: 2|\n1: Union: 3|\n1: SymDiff: 1|\n1: Uniq: 3|\n\n\n2: List1: 3|\n2: List2: 1|",
|
||||
"2: Intersect: 1|\n2: Union: 3|\n2: SymDiff: 2|\n2: Uniq: 3|",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/gohugoio/hugo/common/types"
|
||||
"github.com/mitchellh/hashstructure"
|
||||
)
|
||||
|
||||
@@ -60,7 +61,7 @@ func normalize(v reflect.Value) any {
|
||||
return f
|
||||
}
|
||||
}
|
||||
return v.Interface()
|
||||
return types.Unwrapv(v.Interface())
|
||||
}
|
||||
|
||||
// collects identities from the slices in seqs into a set. Numeric values are normalized,
|
||||
|
||||
@@ -6,14 +6,14 @@ package fmtsort_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gohugoio/hugo/tpl/internal/go_templates/fmtsort"
|
||||
"math"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"unsafe"
|
||||
|
||||
"github.com/gohugoio/hugo/tpl/internal/go_templates/fmtsort"
|
||||
)
|
||||
|
||||
var compareTests = [][]reflect.Value{
|
||||
@@ -191,14 +191,14 @@ func sprintKey(key reflect.Value) string {
|
||||
var (
|
||||
ints [3]int
|
||||
chans = makeChans()
|
||||
// pin runtime.Pinner
|
||||
pin runtime.Pinner
|
||||
)
|
||||
|
||||
func makeChans() []chan int {
|
||||
cs := []chan int{make(chan int), make(chan int), make(chan int)}
|
||||
// Order channels by address. See issue #49431.
|
||||
for i := range cs {
|
||||
reflect.ValueOf(cs[i]).UnsafePointer()
|
||||
pin.Pin(reflect.ValueOf(cs[i]).UnsafePointer())
|
||||
}
|
||||
sort.Slice(cs, func(i, j int) bool {
|
||||
return uintptr(reflect.ValueOf(cs[i]).UnsafePointer()) < uintptr(reflect.ValueOf(cs[j]).UnsafePointer())
|
||||
|
||||
@@ -172,13 +172,31 @@ func jsValEscaper(args ...any) string {
|
||||
// cyclic data. This may be an unacceptable DoS risk.
|
||||
b, err := json.Marshal(a)
|
||||
if err != nil {
|
||||
// Put a space before comment so that if it is flush against
|
||||
// While the standard JSON marshaller does not include user controlled
|
||||
// information in the error message, if a type has a MarshalJSON method,
|
||||
// the content of the error message is not guaranteed. Since we insert
|
||||
// the error into the template, as part of a comment, we attempt to
|
||||
// prevent the error from either terminating the comment, or the script
|
||||
// block itself.
|
||||
//
|
||||
// In particular we:
|
||||
// * replace "*/" comment end tokens with "* /", which does not
|
||||
// terminate the comment
|
||||
// * replace "</script" with "\x3C/script", and "<!--" with
|
||||
// "\x3C!--", which prevents confusing script block termination
|
||||
// semantics
|
||||
//
|
||||
// We also put a space before the comment so that if it is flush against
|
||||
// a division operator it is not turned into a line comment:
|
||||
// x/{{y}}
|
||||
// turning into
|
||||
// x//* error marshaling y:
|
||||
// second line of error message */null
|
||||
return fmt.Sprintf(" /* %s */null ", strings.ReplaceAll(err.Error(), "*/", "* /"))
|
||||
errStr := err.Error()
|
||||
errStr = strings.ReplaceAll(errStr, "*/", "* /")
|
||||
errStr = strings.ReplaceAll(errStr, "</script", `\x3C/script`)
|
||||
errStr = strings.ReplaceAll(errStr, "<!--", `\x3C!--`)
|
||||
return fmt.Sprintf(" /* %s */null ", errStr)
|
||||
}
|
||||
|
||||
// TODO: maybe post-process output to prevent it from containing
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -106,61 +107,72 @@ func TestNextJsCtx(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type jsonErrType struct{}
|
||||
|
||||
func (e *jsonErrType) MarshalJSON() ([]byte, error) {
|
||||
return nil, errors.New("beep */ boop </script blip <!--")
|
||||
}
|
||||
|
||||
func TestJSValEscaper(t *testing.T) {
|
||||
tests := []struct {
|
||||
x any
|
||||
js string
|
||||
x any
|
||||
js string
|
||||
skipNest bool
|
||||
}{
|
||||
{int(42), " 42 "},
|
||||
{uint(42), " 42 "},
|
||||
{int16(42), " 42 "},
|
||||
{uint16(42), " 42 "},
|
||||
{int32(-42), " -42 "},
|
||||
{uint32(42), " 42 "},
|
||||
{int16(-42), " -42 "},
|
||||
{uint16(42), " 42 "},
|
||||
{int64(-42), " -42 "},
|
||||
{uint64(42), " 42 "},
|
||||
{uint64(1) << 53, " 9007199254740992 "},
|
||||
{int(42), " 42 ", false},
|
||||
{uint(42), " 42 ", false},
|
||||
{int16(42), " 42 ", false},
|
||||
{uint16(42), " 42 ", false},
|
||||
{int32(-42), " -42 ", false},
|
||||
{uint32(42), " 42 ", false},
|
||||
{int16(-42), " -42 ", false},
|
||||
{uint16(42), " 42 ", false},
|
||||
{int64(-42), " -42 ", false},
|
||||
{uint64(42), " 42 ", false},
|
||||
{uint64(1) << 53, " 9007199254740992 ", false},
|
||||
// ulp(1 << 53) > 1 so this loses precision in JS
|
||||
// but it is still a representable integer literal.
|
||||
{uint64(1)<<53 + 1, " 9007199254740993 "},
|
||||
{float32(1.0), " 1 "},
|
||||
{float32(-1.0), " -1 "},
|
||||
{float32(0.5), " 0.5 "},
|
||||
{float32(-0.5), " -0.5 "},
|
||||
{float32(1.0) / float32(256), " 0.00390625 "},
|
||||
{float32(0), " 0 "},
|
||||
{math.Copysign(0, -1), " -0 "},
|
||||
{float64(1.0), " 1 "},
|
||||
{float64(-1.0), " -1 "},
|
||||
{float64(0.5), " 0.5 "},
|
||||
{float64(-0.5), " -0.5 "},
|
||||
{float64(0), " 0 "},
|
||||
{math.Copysign(0, -1), " -0 "},
|
||||
{"", `""`},
|
||||
{"foo", `"foo"`},
|
||||
{uint64(1)<<53 + 1, " 9007199254740993 ", false},
|
||||
{float32(1.0), " 1 ", false},
|
||||
{float32(-1.0), " -1 ", false},
|
||||
{float32(0.5), " 0.5 ", false},
|
||||
{float32(-0.5), " -0.5 ", false},
|
||||
{float32(1.0) / float32(256), " 0.00390625 ", false},
|
||||
{float32(0), " 0 ", false},
|
||||
{math.Copysign(0, -1), " -0 ", false},
|
||||
{float64(1.0), " 1 ", false},
|
||||
{float64(-1.0), " -1 ", false},
|
||||
{float64(0.5), " 0.5 ", false},
|
||||
{float64(-0.5), " -0.5 ", false},
|
||||
{float64(0), " 0 ", false},
|
||||
{math.Copysign(0, -1), " -0 ", false},
|
||||
{"", `""`, false},
|
||||
{"foo", `"foo"`, false},
|
||||
// Newlines.
|
||||
{"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`},
|
||||
{"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`, false},
|
||||
// "\v" == "v" on IE 6 so use "\u000b" instead.
|
||||
{"\t\x0b", `"\t\u000b"`},
|
||||
{struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`},
|
||||
{[]any{}, "[]"},
|
||||
{[]any{42, "foo", nil}, `[42,"foo",null]`},
|
||||
{[]string{"<!--", "</script>", "-->"}, `["\u003c!--","\u003c/script\u003e","--\u003e"]`},
|
||||
{"<!--", `"\u003c!--"`},
|
||||
{"-->", `"--\u003e"`},
|
||||
{"<![CDATA[", `"\u003c![CDATA["`},
|
||||
{"]]>", `"]]\u003e"`},
|
||||
{"</script", `"\u003c/script"`},
|
||||
{"\U0001D11E", "\"\U0001D11E\""}, // or "\uD834\uDD1E"
|
||||
{nil, " null "},
|
||||
{"\t\x0b", `"\t\u000b"`, false},
|
||||
{struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`, false},
|
||||
{[]any{}, "[]", false},
|
||||
{[]any{42, "foo", nil}, `[42,"foo",null]`, false},
|
||||
{[]string{"<!--", "</script>", "-->"}, `["\u003c!--","\u003c/script\u003e","--\u003e"]`, false},
|
||||
{"<!--", `"\u003c!--"`, false},
|
||||
{"-->", `"--\u003e"`, false},
|
||||
{"<![CDATA[", `"\u003c![CDATA["`, false},
|
||||
{"]]>", `"]]\u003e"`, false},
|
||||
{"</script", `"\u003c/script"`, false},
|
||||
{"\U0001D11E", "\"\U0001D11E\"", false}, // or "\uD834\uDD1E"
|
||||
{nil, " null ", false},
|
||||
{&jsonErrType{}, " /* json: error calling MarshalJSON for type *template.jsonErrType: beep * / boop \\x3C/script blip \\x3C!-- */null ", true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if js := jsValEscaper(test.x); js != test.js {
|
||||
t.Errorf("%+v: want\n\t%q\ngot\n\t%q", test.x, test.js, js)
|
||||
}
|
||||
if test.skipNest {
|
||||
continue
|
||||
}
|
||||
// Make sure that escaping corner cases are not broken
|
||||
// by nesting.
|
||||
a := []any{test.x}
|
||||
|
||||
@@ -6,6 +6,7 @@ package testenv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -60,6 +61,13 @@ func tryExec() error {
|
||||
// may as well use the same path so that this branch can be tested without
|
||||
// an ios environment.
|
||||
|
||||
if !testing.Testing() {
|
||||
// This isn't a standard 'go test' binary, so we don't know how to
|
||||
// self-exec in a way that should succeed without side effects.
|
||||
// Just forget it.
|
||||
return errors.New("can't probe for exec support with a non-test executable")
|
||||
}
|
||||
|
||||
// We know that this is a test executable. We should be able to run it with a
|
||||
// no-op flag to check for overall exec support.
|
||||
exe, err := os.Executable()
|
||||
|
||||
@@ -17,5 +17,5 @@ import (
|
||||
var Sigquit = os.Kill
|
||||
|
||||
func syscallIsNotSupported(err error) bool {
|
||||
return errors.Is(err, fs.ErrPermission)
|
||||
return errors.Is(err, fs.ErrPermission) || errors.Is(err, errors.ErrUnsupported)
|
||||
}
|
||||
|
||||
@@ -54,8 +54,8 @@ func TestGoToolLocation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Modified by Hugo.
|
||||
func TestHasGoBuild(t *testing.T) {
|
||||
// Removed by Hugo.
|
||||
}
|
||||
|
||||
func TestMustHaveExec(t *testing.T) {
|
||||
@@ -73,7 +73,7 @@ func TestMustHaveExec(t *testing.T) {
|
||||
t.Errorf("expected MustHaveExec to skip on %v", runtime.GOOS)
|
||||
}
|
||||
case "ios":
|
||||
if b := testenv.Builder(); strings.HasSuffix(b, "-corellium") && !hasExec {
|
||||
if b := testenv.Builder(); isCorelliumBuilder(b) && !hasExec {
|
||||
// Most ios environments can't exec, but the corellium builder can.
|
||||
t.Errorf("expected MustHaveExec not to skip on %v", b)
|
||||
}
|
||||
@@ -106,3 +106,23 @@ func TestCleanCmdEnvPWD(t *testing.T) {
|
||||
}
|
||||
t.Error("PWD not set in cmd.Env")
|
||||
}
|
||||
|
||||
func isCorelliumBuilder(builderName string) bool {
|
||||
// Support both the old infra's builder names and the LUCI builder names.
|
||||
// The former's names are ad-hoc so we could maintain this invariant on
|
||||
// the builder side. The latter's names are structured, and "corellium" will
|
||||
// appear as a "host" suffix after the GOOS and GOARCH, which always begin
|
||||
// with an underscore.
|
||||
return strings.HasSuffix(builderName, "-corellium") || strings.Contains(builderName, "_corellium")
|
||||
}
|
||||
|
||||
func isEmulatedBuilder(builderName string) bool {
|
||||
// Support both the old infra's builder names and the LUCI builder names.
|
||||
// The former's names are ad-hoc so we could maintain this invariant on
|
||||
// the builder side. The latter's names are structured, and the signifier
|
||||
// of emulation "emu" will appear as a "host" suffix after the GOOS and
|
||||
// GOARCH because it modifies the run environment in such a way that it
|
||||
// the target GOOS and GOARCH may not match the host. This suffix always
|
||||
// begins with an underscore.
|
||||
return strings.HasSuffix(builderName, "-emu") || strings.Contains(builderName, "_emu")
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ func syscallIsNotSupported(err error) bool {
|
||||
}
|
||||
}
|
||||
|
||||
if errors.Is(err, fs.ErrPermission) {
|
||||
if errors.Is(err, fs.ErrPermission) || errors.Is(err, errors.ErrUnsupported) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -284,7 +284,6 @@ func (a *ActionNode) tree() *Tree {
|
||||
|
||||
func (a *ActionNode) Copy() Node {
|
||||
return a.tr.newAction(a.Pos, a.Line, a.Pipe.CopyPipe())
|
||||
|
||||
}
|
||||
|
||||
// CommandNode holds a command (a pipeline inside an evaluating action).
|
||||
|
||||
@@ -171,3 +171,70 @@ Home.
|
||||
b.AssertFileExists("public/a.txt", true) // failing test
|
||||
b.AssertFileExists("public/b.txt", true) // failing test
|
||||
}
|
||||
|
||||
func TestGlobalResourcesNotPublishedRegressionIssue12214(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
||||
-- assets/files/a.txt --
|
||||
I am a.txt
|
||||
-- assets/files/b.txt --
|
||||
I am b.txt
|
||||
-- assets/files/c.txt --
|
||||
I am c.txt
|
||||
-- assets/files/C.txt --
|
||||
I am C.txt
|
||||
-- layouts/index.html --
|
||||
Home.
|
||||
{{ with resources.ByType "text" }}
|
||||
{{ with .Get "files/a.txt" }}
|
||||
{{ .Publish }}
|
||||
files/a.txt: {{ .Name }}
|
||||
{{ end }}
|
||||
{{ with .Get "/files/a.txt" }}
|
||||
/files/a.txt: {{ .Name }}
|
||||
{{ end }}
|
||||
{{ with .GetMatch "files/*b*" }}
|
||||
{{ .Publish }}
|
||||
files/*b*: {{ .Name }}
|
||||
{{ end }}
|
||||
{{ with .GetMatch "files/C*" }}
|
||||
{{ .Publish }}
|
||||
files/C*: {{ .Name }}
|
||||
{{ end }}
|
||||
{{ with .GetMatch "files/c*" }}
|
||||
{{ .Publish }}
|
||||
files/c*: {{ .Name }}
|
||||
{{ end }}
|
||||
{{ with .GetMatch "/files/c*" }}
|
||||
/files/c*: {{ .Name }}
|
||||
{{ end }}
|
||||
{{ with .Match "files/C*" }}
|
||||
match files/C*: {{ len . }}|
|
||||
{{ end }}
|
||||
{{ with .Match "/files/C*" }}
|
||||
match /files/C*: {{ len . }}|
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", `
|
||||
files/a.txt: /files/a.txt
|
||||
# There are both C.txt and c.txt in the assets, but the Glob matching is case insensitive, so GetMatch returns the first.
|
||||
files/C*: /files/C.txt
|
||||
files/c*: /files/C.txt
|
||||
files/*b*: /files/b.txt
|
||||
/files/c*: /files/C.txt
|
||||
/files/a.txt: /files/a.txt
|
||||
match files/C*: 2|
|
||||
match /files/C*: 2|
|
||||
`)
|
||||
|
||||
b.AssertFileContent("public/files/a.txt", "I am a.txt")
|
||||
b.AssertFileContent("public/files/b.txt", "I am b.txt")
|
||||
b.AssertFileContent("public/files/C.txt", "I am C.txt")
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
{{ range . }}
|
||||
<sitemap>
|
||||
<loc>{{ .SitemapAbsURL }}</loc>
|
||||
{{ if not .LastChange.IsZero }}
|
||||
<lastmod>{{ .LastChange.Format "2006-01-02T15:04:05-07:00" | safeHTML }}</lastmod>
|
||||
{{ if not .Lastmod.IsZero }}
|
||||
<lastmod>{{ .Lastmod.Format "2006-01-02T15:04:05-07:00" | safeHTML }}</lastmod>
|
||||
{{ end }}
|
||||
</sitemap>
|
||||
{{ end }}
|
||||
|
||||
@@ -34,18 +34,11 @@
|
||||
{{ end }}{{ end }}
|
||||
{{- end }}
|
||||
|
||||
{{- /* Deprecate site.Social.facebook_admin in favor of site.Params.social.facebook_admin */}}
|
||||
{{- $facebookAdmin := "" }}
|
||||
{{- /* Facebook Page Admin ID for Domain Insights */}}
|
||||
{{- with site.Params.social }}
|
||||
{{- if reflect.IsMap . }}
|
||||
{{- $facebookAdmin = .facebook_admin }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
{{- with site.Social.facebook_admin }}
|
||||
{{- $facebookAdmin = . }}
|
||||
{{- warnf "The social key in site configuration is deprecated. Use params.social.facebook_admin instead." }}
|
||||
{{- with .facebook_admin }}
|
||||
<meta property="fb:admins" content="{{ . }}" />
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- /* Facebook Page Admin ID for Domain Insights */}}
|
||||
{{ with $facebookAdmin }}<meta property="fb:admins" content="{{ . }}" />{{ end }}
|
||||
|
||||
@@ -1,29 +1,38 @@
|
||||
<figure{{ with .Get "class" }} class="{{ . }}"{{ end }}>
|
||||
{{- if .Get "link" -}}
|
||||
<a href="{{ .Get "link" }}"{{ with .Get "target" }} target="{{ . }}"{{ end }}{{ with .Get "rel" }} rel="{{ . }}"{{ end }}>
|
||||
{{- if .Get "link" -}}
|
||||
<a href="{{ .Get "link" }}"{{ with .Get "target" }} target="{{ . }}"{{ end }}{{ with .Get "rel" }} rel="{{ . }}"{{ end }}>
|
||||
{{- end -}}
|
||||
|
||||
{{- $u := urls.Parse (.Get "src") -}}
|
||||
{{- $src := $u.String -}}
|
||||
{{- if not $u.IsAbs -}}
|
||||
{{- with or (.Page.Resources.Get $u.Path) (resources.Get $u.Path) -}}
|
||||
{{- $src = .RelPermalink -}}
|
||||
{{- end -}}
|
||||
<img src="{{ .Get "src" }}"
|
||||
{{- if or (.Get "alt") (.Get "caption") }}
|
||||
alt="{{ with .Get "alt" }}{{ . }}{{ else }}{{ .Get "caption" | markdownify| plainify }}{{ end }}"
|
||||
{{- end -}}
|
||||
{{- with .Get "width" }} width="{{ . }}"{{ end -}}
|
||||
{{- with .Get "height" }} height="{{ . }}"{{ end -}}
|
||||
{{- with .Get "loading" }} loading="{{ . }}"{{ end -}}
|
||||
/><!-- Closing img tag -->
|
||||
{{- if .Get "link" }}</a>{{ end -}}
|
||||
{{- if or (or (.Get "title") (.Get "caption")) (.Get "attr") -}}
|
||||
<figcaption>
|
||||
{{ with (.Get "title") -}}
|
||||
<h4>{{ . }}</h4>
|
||||
{{- end -}}
|
||||
{{- if or (.Get "caption") (.Get "attr") -}}<p>
|
||||
{{- .Get "caption" | markdownify -}}
|
||||
{{- with .Get "attrlink" }}
|
||||
<a href="{{ . }}">
|
||||
{{- end -}}
|
||||
{{- .Get "attr" | markdownify -}}
|
||||
{{- if .Get "attrlink" }}</a>{{ end }}</p>
|
||||
{{- end }}
|
||||
</figcaption>
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
<img src="{{ $src }}"
|
||||
{{- if or (.Get "alt") (.Get "caption") }}
|
||||
alt="{{ with .Get "alt" }}{{ . }}{{ else }}{{ .Get "caption" | markdownify| plainify }}{{ end }}"
|
||||
{{- end -}}
|
||||
{{- with .Get "width" }} width="{{ . }}"{{ end -}}
|
||||
{{- with .Get "height" }} height="{{ . }}"{{ end -}}
|
||||
{{- with .Get "loading" }} loading="{{ . }}"{{ end -}}
|
||||
><!-- Closing img tag -->
|
||||
{{- if .Get "link" }}</a>{{ end -}}
|
||||
{{- if or (or (.Get "title") (.Get "caption")) (.Get "attr") -}}
|
||||
<figcaption>
|
||||
{{ with (.Get "title") -}}
|
||||
<h4>{{ . }}</h4>
|
||||
{{- end -}}
|
||||
{{- if or (.Get "caption") (.Get "attr") -}}<p>
|
||||
{{- .Get "caption" | markdownify -}}
|
||||
{{- with .Get "attrlink" }}
|
||||
<a href="{{ . }}">
|
||||
{{- end -}}
|
||||
{{- .Get "attr" | markdownify -}}
|
||||
{{- if .Get "attrlink" }}</a>{{ end }}</p>
|
||||
{{- end }}
|
||||
</figcaption>
|
||||
{{- end }}
|
||||
</figure>
|
||||
|
||||
@@ -8,23 +8,15 @@
|
||||
<meta name="twitter:title" content="{{ .Title }}"/>
|
||||
<meta name="twitter:description" content="{{ with .Description }}{{ . }}{{ else }}{{if .IsPage}}{{ .Summary }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}{{ end -}}"/>
|
||||
|
||||
{{- /* Deprecate site.Social.twitter in favor of site.Params.social.twitter */}}
|
||||
{{- $twitterSite := "" }}
|
||||
{{- with site.Params.social }}
|
||||
{{- if reflect.IsMap . }}
|
||||
{{- $twitterSite = .twitter }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
{{- with site.Social.twitter }}
|
||||
{{- $twitterSite = . }}
|
||||
{{- warnf "The social key in site configuration is deprecated. Use params.social.twitter instead." }}
|
||||
{{- with .twitter }}
|
||||
{{- $content := . }}
|
||||
{{- if not (strings.HasPrefix . "@") }}
|
||||
{{- $content = printf "@%v" . }}
|
||||
{{- end }}
|
||||
<meta name="twitter:site" content="{{ $content }}"/>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- with $twitterSite }}
|
||||
{{- $content := . }}
|
||||
{{- if not (strings.HasPrefix . "@") }}
|
||||
{{- $content = printf "@%v" $twitterSite }}
|
||||
{{- end }}
|
||||
<meta name="twitter:site" content="{{ $content }}"/>
|
||||
{{- end }}
|
||||
|
||||
Reference in New Issue
Block a user