mirror of
https://github.com/gohugoio/hugo.git
synced 2025-06-30 12:49:53 +00:00
Compare commits
81 commits
Author | SHA1 | Date | |
---|---|---|---|
|
dd6e2c8724 | ||
|
762417617c | ||
|
29bdbde19c | ||
|
6a4a3ab8f8 | ||
|
36f6f987a9 | ||
|
18a9ca7d7a | ||
|
b6c8dfa9dc | ||
|
621ea42f3c | ||
|
4ef5720141 | ||
|
34e83789f7 | ||
|
4d3ebe4d21 | ||
|
b5c0383bda | ||
|
4217fee4b0 | ||
|
fad57964aa | ||
|
10da2bd765 | ||
|
01241d5dc9 | ||
|
8e61f1fe12 | ||
|
f37412a575 | ||
|
21a4a9acd7 | ||
|
7a4a4790e5 | ||
|
54065b7ef8 | ||
|
e333836f49 | ||
|
cc7bfeea32 | ||
|
32eb1a8ad4 | ||
|
32af02cd3e | ||
|
189453612e | ||
|
5273a884d4 | ||
|
6334948515 | ||
|
75259636c8 | ||
|
0df9f3510f | ||
|
302e6a726b | ||
|
202fe0d45c | ||
|
843ffeb48d | ||
|
bff5d19121 | ||
|
da370d30de | ||
|
6bd328c584 | ||
|
766a2e7868 | ||
|
13e1617557 | ||
|
463e440c7a | ||
|
0a5fd8ebb8 | ||
|
e57dcd3795 | ||
|
eaf5ace30d | ||
|
9ad26b69ad | ||
|
f47193669d | ||
|
013c8cfb25 | ||
|
e25db38467 | ||
|
7766fc6241 | ||
|
4a48facef4 | ||
|
0c7b1a3f26 | ||
|
970b887ba1 | ||
|
b9b95e5aec | ||
|
84c8426f32 | ||
|
a03a245f0c | ||
|
5a81a3a4cf | ||
|
61317821e4 | ||
|
6142bc701c | ||
|
e6574cf7a7 | ||
|
05417512bd | ||
|
81426998b8 | ||
|
6def5a1ba9 | ||
|
bc98e7a80d | ||
|
c745a3e108 | ||
|
9d1d8c8899 | ||
|
84d7a108e8 | ||
|
325a0dba63 | ||
|
d70f828e2b | ||
|
c7feb15d10 | ||
|
363ab48a24 | ||
|
80f0595311 | ||
|
b39b249623 | ||
|
d799c045fd | ||
|
95666fc5a4 | ||
|
620fc87b56 | ||
|
8b2124e7c3 | ||
|
5fec7829b1 | ||
|
927d1ec6c1 | ||
|
be93d5218b | ||
|
a1cb15e1cf | ||
|
673a4d00eb | ||
|
31db7edf6d | ||
|
5857b60cbc |
90 changed files with 2331 additions and 687 deletions
|
@ -2,8 +2,8 @@
|
|||
# Twitter: https://twitter.com/gohugoio
|
||||
# Website: https://gohugo.io/
|
||||
|
||||
ARG GO_VERSION="1.23.2"
|
||||
ARG ALPINE_VERSION="3.20"
|
||||
ARG GO_VERSION="1.24"
|
||||
ARG ALPINE_VERSION="3.22"
|
||||
ARG DART_SASS_VERSION="1.79.3"
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.5.0 AS xx
|
||||
|
@ -19,7 +19,7 @@ RUN apk add clang lld
|
|||
COPY --from=xx / /
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
RUN xx-apk add musl-dev gcc g++
|
||||
RUN xx-apk add musl-dev gcc g++
|
||||
|
||||
# Optionally set HUGO_BUILD_TAGS to "none" or "withdeploy" when building like so:
|
||||
# docker build --build-arg HUGO_BUILD_TAGS=withdeploy .
|
||||
|
@ -72,7 +72,7 @@ RUN mkdir -p /var/hugo/bin /cache && \
|
|||
adduser -Sg hugo -u 1000 -h /var/hugo hugo && \
|
||||
chown -R hugo: /var/hugo /cache && \
|
||||
# For the Hugo's Git integration to work.
|
||||
runuser -u hugo -- git config --global --add safe.directory /project && \
|
||||
runuser -u hugo -- git config --global --add safe.directory /project && \
|
||||
# See https://github.com/gohugoio/hugo/issues/9810
|
||||
runuser -u hugo -- git config --global core.quotepath false
|
||||
|
||||
|
|
|
@ -65,9 +65,11 @@ See the [features] section of the documentation for a comprehensive summary of H
|
|||
|
||||
<p> </p>
|
||||
<p float="left">
|
||||
<a href="https://www.linode.com/?utm_campaign=hugosponsor&utm_medium=banner&utm_source=hugogithub" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/assets/images/sponsors/linode-logo_standard_light_medium.png" width="200" alt="Linode"></a>
|
||||
<a href="https://www.linode.com/?utm_campaign=hugosponsor&utm_medium=banner&utm_source=hugogithub" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/hugoDocs/master/assets/images/sponsors/linode-logo_standard_light_medium.png" width="200" alt="Linode"></a>
|
||||
|
||||
<a href="https://www.jetbrains.com/go/?utm_source=OSS&utm_medium=referral&utm_campaign=hugo" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/gohugoioTheme/master/assets/images/sponsors/goland.svg" width="200" alt="The complete IDE crafted for professional Go developers."></a>
|
||||
<a href="https://www.jetbrains.com/go/?utm_source=OSS&utm_medium=referral&utm_campaign=hugo" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/hugoDocs/master/assets/images/sponsors/goland.svg" width="200" alt="The complete IDE crafted for professional Go developers."></a>
|
||||
|
||||
<a href="https://pinme.eth.limo/?s=hugo" target="_blank"><img src="https://raw.githubusercontent.com/gohugoio/hugoDocs/master/assets/images/sponsors/logo-pinme.svg" width="200" alt="PinMe."></a>
|
||||
</p>
|
||||
|
||||
## Editions
|
||||
|
|
|
@ -972,6 +972,9 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
|
|||
lrl.Logf("force refresh")
|
||||
livereload.ForceRefresh()
|
||||
}
|
||||
} else {
|
||||
lrl.Logf("force refresh")
|
||||
livereload.ForceRefresh()
|
||||
}
|
||||
|
||||
if len(cssChanges) > 0 {
|
||||
|
|
|
@ -76,10 +76,8 @@ Ensure you run this within the root directory of your site.`,
|
|||
&simpleCommand{
|
||||
name: "site",
|
||||
use: "site [path]",
|
||||
short: "Create a new site (skeleton)",
|
||||
long: `Create a new site in the provided directory.
|
||||
The new site will have the correct structure, but no content or theme yet.
|
||||
Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
||||
short: "Create a new site",
|
||||
long: `Create a new site at the specified path.`,
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return newUserError("path needs to be provided")
|
||||
|
@ -124,11 +122,9 @@ Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
|||
&simpleCommand{
|
||||
name: "theme",
|
||||
use: "theme [name]",
|
||||
short: "Create a new theme (skeleton)",
|
||||
long: `Create a new theme (skeleton) called [name] in ./themes.
|
||||
New theme is a skeleton. Please add content to the touched files. Add your
|
||||
name to the copyright line in the license and adjust the theme.toml file
|
||||
according to your needs.`,
|
||||
short: "Create a new theme",
|
||||
long: `Create a new theme with the specified name in the ./themes directory.
|
||||
This generates a functional theme including template examples and sample content.`,
|
||||
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return newUserError("theme name needs to be provided")
|
||||
|
|
|
@ -627,7 +627,7 @@ func (c *serverCommand) setServerInfoInConfig() error {
|
|||
panic("no server ports set")
|
||||
}
|
||||
return c.withConfE(func(conf *commonConfig) error {
|
||||
for i, language := range conf.configs.Languages {
|
||||
for i, language := range conf.configs.LanguagesDefaultFirst {
|
||||
isMultihost := conf.configs.IsMultihost
|
||||
var serverPort int
|
||||
if isMultihost {
|
||||
|
|
|
@ -15,6 +15,7 @@ package collections
|
|||
|
||||
import (
|
||||
"html/template"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
|
@ -77,6 +78,7 @@ func TestAppend(t *testing.T) {
|
|||
{[]string{"a", "b"}, []any{nil}, []any{"a", "b", nil}},
|
||||
{[]string{"a", "b"}, []any{nil, "d", nil}, []any{"a", "b", nil, "d", nil}},
|
||||
{[]any{"a", nil, "c"}, []any{"d", nil, "f"}, []any{"a", nil, "c", "d", nil, "f"}},
|
||||
{[]string{"a", "b"}, []any{}, []string{"a", "b"}},
|
||||
} {
|
||||
|
||||
result, err := Append(test.start, test.addend...)
|
||||
|
@ -146,3 +148,66 @@ func TestAppendShouldMakeACopyOfTheInputSlice(t *testing.T) {
|
|||
c.Assert(result, qt.DeepEquals, []string{"a", "b", "c"})
|
||||
c.Assert(slice, qt.DeepEquals, []string{"d", "b"})
|
||||
}
|
||||
|
||||
func TestIndirect(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
type testStruct struct {
|
||||
Field string
|
||||
}
|
||||
|
||||
var (
|
||||
nilPtr *testStruct
|
||||
nilIface interface{} = nil
|
||||
nonNilIface interface{} = &testStruct{Field: "hello"}
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input any
|
||||
wantKind reflect.Kind
|
||||
wantNil bool
|
||||
}{
|
||||
{
|
||||
name: "nil pointer",
|
||||
input: nilPtr,
|
||||
wantKind: reflect.Ptr,
|
||||
wantNil: true,
|
||||
},
|
||||
{
|
||||
name: "nil interface",
|
||||
input: nilIface,
|
||||
wantKind: reflect.Invalid,
|
||||
wantNil: false,
|
||||
},
|
||||
{
|
||||
name: "non-nil pointer to struct",
|
||||
input: &testStruct{Field: "abc"},
|
||||
wantKind: reflect.Struct,
|
||||
wantNil: false,
|
||||
},
|
||||
{
|
||||
name: "non-nil interface holding pointer",
|
||||
input: nonNilIface,
|
||||
wantKind: reflect.Struct,
|
||||
wantNil: false,
|
||||
},
|
||||
{
|
||||
name: "plain value",
|
||||
input: testStruct{Field: "xyz"},
|
||||
wantKind: reflect.Struct,
|
||||
wantNil: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
v := reflect.ValueOf(tt.input)
|
||||
got, isNil := indirect(v)
|
||||
|
||||
c.Assert(got.Kind(), qt.Equals, tt.wantKind)
|
||||
c.Assert(isNil, qt.Equals, tt.wantNil)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -136,3 +136,37 @@ func TestSortedStringSlice(t *testing.T) {
|
|||
c.Assert(s.Count("z"), qt.Equals, 0)
|
||||
c.Assert(s.Count("a"), qt.Equals, 1)
|
||||
}
|
||||
|
||||
func TestStringSliceToInterfaceSlice(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
in []string
|
||||
want []any
|
||||
}{
|
||||
{
|
||||
name: "empty slice",
|
||||
in: []string{},
|
||||
want: []any{},
|
||||
},
|
||||
{
|
||||
name: "single element",
|
||||
in: []string{"hello"},
|
||||
want: []any{"hello"},
|
||||
},
|
||||
{
|
||||
name: "multiple elements",
|
||||
in: []string{"a", "b", "c"},
|
||||
want: []any{"a", "b", "c"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := StringSliceToInterfaceSlice(tt.in)
|
||||
c.Assert(got, qt.DeepEquals, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
77
common/collections/stack_test.go
Normal file
77
common/collections/stack_test.go
Normal file
|
@ -0,0 +1,77 @@
|
|||
package collections
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestNewStack(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
s := NewStack[int]()
|
||||
|
||||
c.Assert(s, qt.IsNotNil)
|
||||
}
|
||||
|
||||
func TestStackBasic(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
s := NewStack[int]()
|
||||
|
||||
c.Assert(s.Len(), qt.Equals, 0)
|
||||
|
||||
s.Push(1)
|
||||
s.Push(2)
|
||||
s.Push(3)
|
||||
|
||||
c.Assert(s.Len(), qt.Equals, 3)
|
||||
|
||||
top, ok := s.Peek()
|
||||
c.Assert(ok, qt.Equals, true)
|
||||
c.Assert(top, qt.Equals, 3)
|
||||
|
||||
popped, ok := s.Pop()
|
||||
c.Assert(ok, qt.Equals, true)
|
||||
c.Assert(popped, qt.Equals, 3)
|
||||
|
||||
c.Assert(s.Len(), qt.Equals, 2)
|
||||
|
||||
_, _ = s.Pop()
|
||||
_, _ = s.Pop()
|
||||
_, ok = s.Pop()
|
||||
|
||||
c.Assert(ok, qt.Equals, false)
|
||||
}
|
||||
|
||||
func TestStackDrain(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
s := NewStack[string]()
|
||||
s.Push("a")
|
||||
s.Push("b")
|
||||
|
||||
got := s.Drain()
|
||||
|
||||
c.Assert(got, qt.DeepEquals, []string{"a", "b"})
|
||||
c.Assert(s.Len(), qt.Equals, 0)
|
||||
}
|
||||
|
||||
func TestStackDrainMatching(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
s := NewStack[int]()
|
||||
s.Push(1)
|
||||
s.Push(2)
|
||||
s.Push(3)
|
||||
s.Push(4)
|
||||
|
||||
got := s.DrainMatching(func(v int) bool { return v%2 == 0 })
|
||||
|
||||
c.Assert(got, qt.DeepEquals, []int{4, 2})
|
||||
c.Assert(s.Drain(), qt.DeepEquals, []int{1, 3})
|
||||
}
|
|
@ -24,6 +24,7 @@ const (
|
|||
WarnRenderShortcodesInHTML = "warning-rendershortcodes-in-html"
|
||||
WarnGoldmarkRawHTML = "warning-goldmark-raw-html"
|
||||
WarnPartialSuperfluousPrefix = "warning-partial-superfluous-prefix"
|
||||
WarnHomePageIsLeafBundle = "warning-home-page-is-leaf-bundle"
|
||||
)
|
||||
|
||||
// Field/method names with special meaning.
|
||||
|
|
|
@ -17,7 +17,7 @@ package hugo
|
|||
// This should be the only one.
|
||||
var CurrentVersion = Version{
|
||||
Major: 0,
|
||||
Minor: 147,
|
||||
Minor: 148,
|
||||
PatchLevel: 0,
|
||||
Suffix: "",
|
||||
Suffix: "-DEV",
|
||||
}
|
||||
|
|
|
@ -120,15 +120,20 @@ func (pp *PathParser) parse(component, s string) (*Path, error) {
|
|||
return p, nil
|
||||
}
|
||||
|
||||
func (pp *PathParser) parseIdentifier(component, s string, p *Path, i, lastDot int) {
|
||||
func (pp *PathParser) parseIdentifier(component, s string, p *Path, i, lastDot, numDots int, isLast bool) {
|
||||
if p.posContainerHigh != -1 {
|
||||
return
|
||||
}
|
||||
mayHaveLang := p.posIdentifierLanguage == -1 && pp.LanguageIndex != nil
|
||||
mayHaveLang := numDots > 1 && p.posIdentifierLanguage == -1 && pp.LanguageIndex != nil
|
||||
mayHaveLang = mayHaveLang && (component == files.ComponentFolderContent || component == files.ComponentFolderLayouts)
|
||||
mayHaveOutputFormat := component == files.ComponentFolderLayouts
|
||||
mayHaveKind := p.posIdentifierKind == -1 && mayHaveOutputFormat
|
||||
mayHaveLayout := component == files.ComponentFolderLayouts
|
||||
var mayHaveLayout bool
|
||||
if p.pathType == TypeShortcode {
|
||||
mayHaveLayout = !isLast && component == files.ComponentFolderLayouts
|
||||
} else {
|
||||
mayHaveLayout = component == files.ComponentFolderLayouts
|
||||
}
|
||||
|
||||
var found bool
|
||||
var high int
|
||||
|
@ -167,7 +172,6 @@ func (pp *PathParser) parseIdentifier(component, s string, p *Path, i, lastDot i
|
|||
if langFound {
|
||||
p.identifiersKnown = append(p.identifiersKnown, id)
|
||||
p.posIdentifierLanguage = len(p.identifiersKnown) - 1
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,19 +238,24 @@ func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) {
|
|||
p.s = s
|
||||
slashCount := 0
|
||||
lastDot := 0
|
||||
lastSlashIdx := strings.LastIndex(s, "/")
|
||||
numDots := strings.Count(s[lastSlashIdx+1:], ".")
|
||||
if strings.Contains(s, "/_shortcodes/") {
|
||||
p.pathType = TypeShortcode
|
||||
}
|
||||
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
c := s[i]
|
||||
|
||||
switch c {
|
||||
case '.':
|
||||
pp.parseIdentifier(component, s, p, i, lastDot)
|
||||
pp.parseIdentifier(component, s, p, i, lastDot, numDots, false)
|
||||
lastDot = i
|
||||
case '/':
|
||||
slashCount++
|
||||
if p.posContainerHigh == -1 {
|
||||
if lastDot > 0 {
|
||||
pp.parseIdentifier(component, s, p, i, lastDot)
|
||||
pp.parseIdentifier(component, s, p, i, lastDot, numDots, true)
|
||||
}
|
||||
p.posContainerHigh = i + 1
|
||||
} else if p.posContainerLow == -1 {
|
||||
|
@ -282,10 +291,9 @@ func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) {
|
|||
p.pathType = TypeContentData
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if component == files.ComponentFolderLayouts {
|
||||
if p.pathType < TypeMarkup && component == files.ComponentFolderLayouts {
|
||||
if p.posIdentifierBaseof != -1 {
|
||||
p.pathType = TypeBaseof
|
||||
} else {
|
||||
|
@ -301,12 +309,10 @@ func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) {
|
|||
}
|
||||
|
||||
if p.pathType == TypeShortcode && p.posIdentifierLayout != -1 {
|
||||
// myshortcode or myshortcode.html, no layout.
|
||||
if len(p.identifiersKnown) <= 2 {
|
||||
id := p.identifiersKnown[p.posIdentifierLayout]
|
||||
if id.Low == p.posContainerHigh {
|
||||
// First identifier is shortcode name.
|
||||
p.posIdentifierLayout = -1
|
||||
} else {
|
||||
// First is always the name.
|
||||
p.posIdentifierLayout--
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -434,12 +434,12 @@ func TestParseLayouts(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"Layout multiple",
|
||||
"/maylayout.list.section.no.html",
|
||||
"/mylayout.list.section.no.html",
|
||||
func(c *qt.C, p *Path) {
|
||||
c.Assert(p.Layout(), qt.Equals, "maylayout")
|
||||
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "no", "section", "list", "maylayout"})
|
||||
c.Assert(p.Layout(), qt.Equals, "mylayout")
|
||||
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "no", "section", "list", "mylayout"})
|
||||
c.Assert(p.IdentifiersUnknown(), qt.DeepEquals, []string{})
|
||||
c.Assert(p.Base(), qt.Equals, "/maylayout.html")
|
||||
c.Assert(p.Base(), qt.Equals, "/mylayout.html")
|
||||
c.Assert(p.Lang(), qt.Equals, "no")
|
||||
},
|
||||
},
|
||||
|
@ -487,7 +487,8 @@ func TestParseLayouts(t *testing.T) {
|
|||
func(c *qt.C, p *Path) {
|
||||
c.Assert(p.Base(), qt.Equals, "/_shortcodes/myshortcode.html")
|
||||
c.Assert(p.Type(), qt.Equals, TypeShortcode)
|
||||
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "list", "myshortcode"})
|
||||
c.Assert(p.Identifiers(), qt.DeepEquals, []string{"html", "list"})
|
||||
c.Assert(p.Layout(), qt.Equals, "list")
|
||||
c.Assert(p.PathNoIdentifier(), qt.Equals, "/_shortcodes/myshortcode")
|
||||
c.Assert(p.PathBeforeLangAndOutputFormatAndExt(), qt.Equals, "/_shortcodes/myshortcode.list")
|
||||
c.Assert(p.Lang(), qt.Equals, "")
|
||||
|
@ -563,11 +564,30 @@ func TestParseLayouts(t *testing.T) {
|
|||
c.Assert(p.Type(), qt.Equals, TypePartial)
|
||||
},
|
||||
},
|
||||
{
|
||||
"Shortcode lang in root",
|
||||
"/_shortcodes/no.html",
|
||||
func(c *qt.C, p *Path) {
|
||||
c.Assert(p.Type(), qt.Equals, TypeShortcode)
|
||||
c.Assert(p.Lang(), qt.Equals, "")
|
||||
c.Assert(p.NameNoIdentifier(), qt.Equals, "no")
|
||||
},
|
||||
},
|
||||
{
|
||||
"Shortcode lang layout",
|
||||
"/_shortcodes/myshortcode.no.html",
|
||||
func(c *qt.C, p *Path) {
|
||||
c.Assert(p.Type(), qt.Equals, TypeShortcode)
|
||||
c.Assert(p.Lang(), qt.Equals, "no")
|
||||
c.Assert(p.Layout(), qt.Equals, "")
|
||||
c.Assert(p.NameNoIdentifier(), qt.Equals, "myshortcode")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
c.Run(test.name, func(c *qt.C) {
|
||||
if test.name != "Baseof" {
|
||||
if test.name != "Shortcode lang layout" {
|
||||
// return
|
||||
}
|
||||
test.assert(c, testParser.Parse(files.ComponentFolderLayouts, test.path))
|
||||
|
|
|
@ -17,7 +17,6 @@ package terminal
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
isatty "github.com/mattn/go-isatty"
|
||||
|
@ -41,10 +40,6 @@ func PrintANSIColors(f *os.File) bool {
|
|||
// IsTerminal return true if the file descriptor is terminal and the TERM
|
||||
// environment variable isn't a dumb one.
|
||||
func IsTerminal(f *os.File) bool {
|
||||
if runtime.GOOS == "windows" {
|
||||
return false
|
||||
}
|
||||
|
||||
fd := f.Fd()
|
||||
return os.Getenv("TERM") != "dumb" && (isatty.IsTerminal(fd) || isatty.IsCygwinTerminal(fd))
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ type Config struct {
|
|||
|
||||
// The cascade configuration section contains the top level front matter cascade configuration options,
|
||||
// a slice of page matcher and params to apply to those pages.
|
||||
Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, *maps.Ordered[page.PageMatcher, maps.Params]] `mapstructure:"-"`
|
||||
Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]] `mapstructure:"-"`
|
||||
|
||||
// The segments defines segments for the site. Used for partial/segmented builds.
|
||||
Segments *config.ConfigNamespace[map[string]segments.SegmentConfig, segments.Segments] `mapstructure:"-"`
|
||||
|
@ -776,7 +776,7 @@ type Configs struct {
|
|||
}
|
||||
|
||||
func (c *Configs) Validate(logger loggers.Logger) error {
|
||||
c.Base.Cascade.Config.Range(func(p page.PageMatcher, params maps.Params) bool {
|
||||
c.Base.Cascade.Config.Range(func(p page.PageMatcher, cfg page.PageMatcherParamsConfig) bool {
|
||||
page.CheckCascadePattern(logger, p)
|
||||
return true
|
||||
})
|
||||
|
|
|
@ -273,6 +273,69 @@ GA ID: {{ site.Config.Services.GoogleAnalytics.ID }}.
|
|||
b.AssertFileContent("public/index.html", "GA ID: foo bar.")
|
||||
}
|
||||
|
||||
func TestMergeDeepBuildStats(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
title = "Theme 1"
|
||||
_merge = "deep"
|
||||
[module]
|
||||
[module.hugoVersion]
|
||||
[[module.imports]]
|
||||
path = "theme1"
|
||||
-- themes/theme1/hugo.toml --
|
||||
[build]
|
||||
[build.buildStats]
|
||||
disableIDs = true
|
||||
enable = true
|
||||
-- layouts/home.html --
|
||||
Home.
|
||||
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files, hugolib.TestOptOsFs())
|
||||
|
||||
conf := b.H.Configs
|
||||
base := conf.Base
|
||||
|
||||
b.Assert(base.Title, qt.Equals, "Theme 1")
|
||||
b.Assert(len(base.Module.Imports), qt.Equals, 1)
|
||||
b.Assert(base.Build.BuildStats.Enable, qt.Equals, true)
|
||||
b.AssertFileExists("/hugo_stats.json", true)
|
||||
}
|
||||
|
||||
func TestMergeDeepBuildStatsTheme(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
_merge = "deep"
|
||||
theme = ["theme1"]
|
||||
-- themes/theme1/hugo.toml --
|
||||
title = "Theme 1"
|
||||
[build]
|
||||
[build.buildStats]
|
||||
disableIDs = true
|
||||
enable = true
|
||||
-- layouts/home.html --
|
||||
Home.
|
||||
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files, hugolib.TestOptOsFs())
|
||||
|
||||
conf := b.H.Configs
|
||||
base := conf.Base
|
||||
|
||||
b.Assert(base.Title, qt.Equals, "Theme 1")
|
||||
b.Assert(len(base.Module.Imports), qt.Equals, 1)
|
||||
b.Assert(base.Build.BuildStats.Enable, qt.Equals, true)
|
||||
b.AssertFileExists("/hugo_stats.json", true)
|
||||
}
|
||||
|
||||
func TestDefaultConfigLanguageBlankWhenNoEnglishExists(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
@ -294,3 +357,25 @@ All.
|
|||
|
||||
b.Assert(b.H.Conf.DefaultContentLanguage(), qt.Equals, "sv")
|
||||
}
|
||||
|
||||
func TestDefaultConfigEnvDisableLanguagesIssue13707(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableLanguages = []
|
||||
[languages]
|
||||
[languages.en]
|
||||
weight = 1
|
||||
[languages.nn]
|
||||
weight = 2
|
||||
[languages.sv]
|
||||
weight = 3
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files, hugolib.TestOptWithConfig(func(conf *hugolib.IntegrationTestConfig) {
|
||||
conf.Environ = []string{`HUGO_DISABLELANGUAGES=sv nn`}
|
||||
}))
|
||||
|
||||
b.Assert(len(b.H.Sites), qt.Equals, 1)
|
||||
}
|
||||
|
|
|
@ -330,7 +330,7 @@ var allDecoderSetups = map[string]decodeWeight{
|
|||
key: "cascade",
|
||||
decode: func(d decodeWeight, p decodeConfig) error {
|
||||
var err error
|
||||
p.c.Cascade, err = page.DecodeCascadeConfig(nil, p.p.Get(d.key))
|
||||
p.c.Cascade, err = page.DecodeCascadeConfig(nil, true, p.p.Get(d.key))
|
||||
return err
|
||||
},
|
||||
},
|
||||
|
|
|
@ -233,40 +233,51 @@ func (l configLoader) applyOsEnvOverrides(environ []string) error {
|
|||
|
||||
if existing != nil {
|
||||
val, err := metadecoders.Default.UnmarshalStringTo(env.Value, existing)
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
val = l.envValToVal(env.Key, val)
|
||||
if owner != nil {
|
||||
owner[nestedKey] = val
|
||||
} else {
|
||||
l.cfg.Set(env.Key, val)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if owner != nil {
|
||||
owner[nestedKey] = val
|
||||
} else {
|
||||
l.cfg.Set(env.Key, val)
|
||||
}
|
||||
} else {
|
||||
if nestedKey != "" {
|
||||
owner[nestedKey] = env.Value
|
||||
} else {
|
||||
var val any
|
||||
key := strings.ReplaceAll(env.Key, delim, ".")
|
||||
_, ok := allDecoderSetups[key]
|
||||
if ok {
|
||||
// A map.
|
||||
if v, err := metadecoders.Default.UnmarshalStringTo(env.Value, map[string]any{}); err == nil {
|
||||
val = v
|
||||
}
|
||||
}
|
||||
if val == nil {
|
||||
// A string.
|
||||
val = l.envStringToVal(key, env.Value)
|
||||
}
|
||||
l.cfg.Set(key, val)
|
||||
}
|
||||
}
|
||||
|
||||
if owner != nil && nestedKey != "" {
|
||||
owner[nestedKey] = env.Value
|
||||
} else {
|
||||
var val any
|
||||
key := strings.ReplaceAll(env.Key, delim, ".")
|
||||
_, ok := allDecoderSetups[key]
|
||||
if ok {
|
||||
// A map.
|
||||
if v, err := metadecoders.Default.UnmarshalStringTo(env.Value, map[string]any{}); err == nil {
|
||||
val = v
|
||||
}
|
||||
}
|
||||
|
||||
if val == nil {
|
||||
// A string.
|
||||
val = l.envStringToVal(key, env.Value)
|
||||
}
|
||||
l.cfg.Set(key, val)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *configLoader) envValToVal(k string, v any) any {
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
return l.envStringToVal(k, v)
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
func (l *configLoader) envStringToVal(k, v string) any {
|
||||
switch k {
|
||||
case "disablekinds", "disablelanguages":
|
||||
|
|
|
@ -36,6 +36,7 @@ import (
|
|||
"github.com/dustin/go-humanize"
|
||||
"github.com/gobwas/glob"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/common/para"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/deploy/deployconfig"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
|
@ -487,7 +488,12 @@ func knownHiddenDirectory(name string) bool {
|
|||
// walkLocal walks the source directory and returns a flat list of files,
|
||||
// using localFile.SlashPath as the map keys.
|
||||
func (d *Deployer) walkLocal(fs afero.Fs, matchers []*deployconfig.Matcher, include, exclude glob.Glob, mediaTypes media.Types, mappath func(string) string) (map[string]*localFile, error) {
|
||||
retval := map[string]*localFile{}
|
||||
retval := make(map[string]*localFile)
|
||||
var mu sync.Mutex
|
||||
|
||||
workers := para.New(d.cfg.Workers)
|
||||
g, _ := workers.Start(context.Background())
|
||||
|
||||
err := afero.Walk(fs, "", func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -508,45 +514,54 @@ func (d *Deployer) walkLocal(fs afero.Fs, matchers []*deployconfig.Matcher, incl
|
|||
return nil
|
||||
}
|
||||
|
||||
// When a file system is HFS+, its filepath is in NFD form.
|
||||
if runtime.GOOS == "darwin" {
|
||||
path = norm.NFC.String(path)
|
||||
}
|
||||
|
||||
// Check include/exclude matchers.
|
||||
slashpath := filepath.ToSlash(path)
|
||||
if include != nil && !include.Match(slashpath) {
|
||||
d.logger.Infof(" dropping %q due to include\n", slashpath)
|
||||
return nil
|
||||
}
|
||||
if exclude != nil && exclude.Match(slashpath) {
|
||||
d.logger.Infof(" dropping %q due to exclude\n", slashpath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Find the first matching matcher (if any).
|
||||
var m *deployconfig.Matcher
|
||||
for _, cur := range matchers {
|
||||
if cur.Matches(slashpath) {
|
||||
m = cur
|
||||
break
|
||||
// Process each file in a worker
|
||||
g.Run(func() error {
|
||||
// When a file system is HFS+, its filepath is in NFD form.
|
||||
if runtime.GOOS == "darwin" {
|
||||
path = norm.NFC.String(path)
|
||||
}
|
||||
}
|
||||
// Apply any additional modifications to the local path, to map it to
|
||||
// the remote path.
|
||||
if mappath != nil {
|
||||
slashpath = mappath(slashpath)
|
||||
}
|
||||
lf, err := newLocalFile(fs, path, slashpath, m, mediaTypes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
retval[lf.SlashPath] = lf
|
||||
|
||||
// Check include/exclude matchers.
|
||||
slashpath := filepath.ToSlash(path)
|
||||
if include != nil && !include.Match(slashpath) {
|
||||
d.logger.Infof(" dropping %q due to include\n", slashpath)
|
||||
return nil
|
||||
}
|
||||
if exclude != nil && exclude.Match(slashpath) {
|
||||
d.logger.Infof(" dropping %q due to exclude\n", slashpath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Find the first matching matcher (if any).
|
||||
var m *deployconfig.Matcher
|
||||
for _, cur := range matchers {
|
||||
if cur.Matches(slashpath) {
|
||||
m = cur
|
||||
break
|
||||
}
|
||||
}
|
||||
// Apply any additional modifications to the local path, to map it to
|
||||
// the remote path.
|
||||
if mappath != nil {
|
||||
slashpath = mappath(slashpath)
|
||||
}
|
||||
lf, err := newLocalFile(fs, path, slashpath, m, mediaTypes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mu.Lock()
|
||||
retval[lf.SlashPath] = lf
|
||||
mu.Unlock()
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := g.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return retval, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -623,7 +623,7 @@ func TestEndToEndSync(t *testing.T) {
|
|||
localFs: test.fs,
|
||||
bucket: test.bucket,
|
||||
mediaTypes: media.DefaultTypes,
|
||||
cfg: deployconfig.DeployConfig{MaxDeletes: -1},
|
||||
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1},
|
||||
}
|
||||
|
||||
// Initial deployment should sync remote with local.
|
||||
|
@ -706,7 +706,7 @@ func TestMaxDeletes(t *testing.T) {
|
|||
localFs: test.fs,
|
||||
bucket: test.bucket,
|
||||
mediaTypes: media.DefaultTypes,
|
||||
cfg: deployconfig.DeployConfig{MaxDeletes: -1},
|
||||
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1},
|
||||
}
|
||||
|
||||
// Sync remote with local.
|
||||
|
@ -836,7 +836,7 @@ func TestIncludeExclude(t *testing.T) {
|
|||
}
|
||||
deployer := &Deployer{
|
||||
localFs: fsTest.fs,
|
||||
cfg: deployconfig.DeployConfig{MaxDeletes: -1}, bucket: fsTest.bucket,
|
||||
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1}, bucket: fsTest.bucket,
|
||||
target: tgt,
|
||||
mediaTypes: media.DefaultTypes,
|
||||
}
|
||||
|
@ -893,7 +893,7 @@ func TestIncludeExcludeRemoteDelete(t *testing.T) {
|
|||
}
|
||||
deployer := &Deployer{
|
||||
localFs: fsTest.fs,
|
||||
cfg: deployconfig.DeployConfig{MaxDeletes: -1}, bucket: fsTest.bucket,
|
||||
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1}, bucket: fsTest.bucket,
|
||||
mediaTypes: media.DefaultTypes,
|
||||
}
|
||||
|
||||
|
@ -945,7 +945,7 @@ func TestCompression(t *testing.T) {
|
|||
deployer := &Deployer{
|
||||
localFs: test.fs,
|
||||
bucket: test.bucket,
|
||||
cfg: deployconfig.DeployConfig{MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: ".*", Gzip: true, Re: regexp.MustCompile(".*")}}},
|
||||
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: ".*", Gzip: true, Re: regexp.MustCompile(".*")}}},
|
||||
mediaTypes: media.DefaultTypes,
|
||||
}
|
||||
|
||||
|
@ -1000,7 +1000,7 @@ func TestMatching(t *testing.T) {
|
|||
deployer := &Deployer{
|
||||
localFs: test.fs,
|
||||
bucket: test.bucket,
|
||||
cfg: deployconfig.DeployConfig{MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: "^subdir/aaa$", Force: true, Re: regexp.MustCompile("^subdir/aaa$")}}},
|
||||
cfg: deployconfig.DeployConfig{Workers: 2, MaxDeletes: -1, Matchers: []*deployconfig.Matcher{{Pattern: "^subdir/aaa$", Force: true, Re: regexp.MustCompile("^subdir/aaa$")}}},
|
||||
mediaTypes: media.DefaultTypes,
|
||||
}
|
||||
|
||||
|
@ -1097,5 +1097,6 @@ func verifyRemote(ctx context.Context, bucket *blob.Bucket, local []*fileData) (
|
|||
func newDeployer() *Deployer {
|
||||
return &Deployer{
|
||||
logger: loggers.NewDefault(),
|
||||
cfg: deployconfig.DeployConfig{Workers: 2},
|
||||
}
|
||||
}
|
||||
|
|
98
go.mod
98
go.mod
|
@ -2,9 +2,9 @@ module github.com/gohugoio/hugo
|
|||
|
||||
require (
|
||||
github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69
|
||||
github.com/alecthomas/chroma/v2 v2.17.0
|
||||
github.com/alecthomas/chroma/v2 v2.18.0
|
||||
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.1
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.4
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudfront v1.44.10
|
||||
github.com/bep/clocks v0.5.0
|
||||
github.com/bep/debounce v1.2.0
|
||||
|
@ -26,12 +26,12 @@ require (
|
|||
github.com/clbanning/mxj/v2 v2.7.0
|
||||
github.com/disintegration/gift v1.2.1
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/evanw/esbuild v0.25.3
|
||||
github.com/evanw/esbuild v0.25.5
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/fortytw2/leaktest v1.3.0
|
||||
github.com/frankban/quicktest v1.14.6
|
||||
github.com/fsnotify/fsnotify v1.9.0
|
||||
github.com/getkin/kin-openapi v0.131.0
|
||||
github.com/getkin/kin-openapi v0.132.0
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/gobuffalo/flect v1.0.3
|
||||
github.com/gobwas/glob v0.2.3
|
||||
|
@ -54,44 +54,46 @@ require (
|
|||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c
|
||||
github.com/muesli/smartcrop v0.3.0
|
||||
github.com/niklasfasching/go-org v1.7.0
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/niklasfasching/go-org v1.8.0
|
||||
github.com/olekukonko/tablewriter v1.0.7
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
|
||||
github.com/pelletier/go-toml/v2 v2.2.4
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
|
||||
github.com/rogpeppe/go-internal v1.14.1
|
||||
github.com/sanity-io/litter v1.5.8
|
||||
github.com/spf13/afero v1.14.0
|
||||
github.com/spf13/cast v1.7.1
|
||||
github.com/spf13/cast v1.9.2
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/fsync v0.10.1
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/tdewolff/minify/v2 v2.20.37
|
||||
github.com/tdewolff/parse/v2 v2.7.15
|
||||
github.com/tdewolff/minify/v2 v2.23.8
|
||||
github.com/tdewolff/parse/v2 v2.8.1
|
||||
github.com/tetratelabs/wazero v1.9.0
|
||||
github.com/yuin/goldmark v1.7.10
|
||||
github.com/yuin/goldmark v1.7.12
|
||||
github.com/yuin/goldmark-emoji v1.0.6
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
gocloud.dev v0.40.0
|
||||
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326
|
||||
golang.org/x/image v0.26.0
|
||||
golang.org/x/mod v0.24.0
|
||||
golang.org/x/net v0.39.0
|
||||
golang.org/x/sync v0.13.0
|
||||
golang.org/x/text v0.24.0
|
||||
golang.org/x/tools v0.32.0
|
||||
google.golang.org/api v0.221.0
|
||||
golang.org/x/image v0.28.0
|
||||
golang.org/x/mod v0.25.0
|
||||
golang.org/x/net v0.41.0
|
||||
golang.org/x/sync v0.15.0
|
||||
golang.org/x/text v0.26.0
|
||||
golang.org/x/tools v0.33.0
|
||||
google.golang.org/api v0.237.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
rsc.io/qr v0.2.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.116.0 // indirect
|
||||
cloud.google.com/go/auth v0.14.1 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
cloud.google.com/go/iam v1.2.2 // indirect
|
||||
cloud.google.com/go/storage v1.43.0 // indirect
|
||||
cel.dev/expr v0.23.0 // indirect
|
||||
cloud.google.com/go v0.120.0 // indirect
|
||||
cloud.google.com/go/auth v0.16.2 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.7.0 // indirect
|
||||
cloud.google.com/go/iam v1.5.2 // indirect
|
||||
cloud.google.com/go/monitoring v1.24.2 // indirect
|
||||
cloud.google.com/go/storage v1.50.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
||||
|
@ -99,6 +101,9 @@ require (
|
|||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 // indirect
|
||||
github.com/aws/aws-sdk-go v1.55.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect
|
||||
|
@ -118,9 +123,13 @@ require (
|
|||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
|
||||
github.com/aws/smithy-go v1.22.2 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
|
@ -130,8 +139,8 @@ require (
|
|||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/google/wire v0.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.2 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
|
@ -141,31 +150,40 @@ require (
|
|||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
|
||||
github.com/olekukonko/errors v0.0.0-20250405072817-4e6d85265da6 // indirect
|
||||
github.com/olekukonko/ll v0.0.8 // indirect
|
||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
|
||||
github.com/zeebo/errs v1.4.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
|
||||
go.opentelemetry.io/otel v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.34.0 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/oauth2 v0.26.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/time v0.10.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.35.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect
|
||||
google.golang.org/genproto v0.0.0-20241104194629-dd2ea8efbc28 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect
|
||||
google.golang.org/grpc v1.70.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
howett.net/plist v1.0.0 // indirect
|
||||
software.sslmate.com/src/go-pkcs12 v0.2.0 // indirect
|
||||
|
|
221
go.sum
221
go.sum
|
@ -1,3 +1,5 @@
|
|||
cel.dev/expr v0.23.0 h1:wUb94w6OYQS4uXraxo9U+wUAs9jT47Xvl4iPgAwM2ss=
|
||||
cel.dev/expr v0.23.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
|
@ -17,26 +19,30 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb
|
|||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
|
||||
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
|
||||
cloud.google.com/go/auth v0.14.1 h1:AwoJbzUdxA/whv1qj3TLKwh3XX5sikny2fc40wUl+h0=
|
||||
cloud.google.com/go/auth v0.14.1/go.mod h1:4JHUxlGXisL0AW8kXPtUF6ztuOksyfUQNFjfsOCXkPM=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
|
||||
cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA=
|
||||
cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q=
|
||||
cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4=
|
||||
cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||
cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU=
|
||||
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA=
|
||||
cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY=
|
||||
cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc=
|
||||
cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI=
|
||||
cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8=
|
||||
cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE=
|
||||
cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc=
|
||||
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
|
||||
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
|
||||
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
|
||||
cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM=
|
||||
cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
|
@ -47,8 +53,10 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
|
|||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs=
|
||||
cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0=
|
||||
cloud.google.com/go/storage v1.50.0 h1:3TbVkzTooBvnZsk7WaAQfOsNrdoM8QHusXA1cpk6QJs=
|
||||
cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv482poyafY=
|
||||
cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4=
|
||||
cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0=
|
||||
|
@ -71,18 +79,26 @@ 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/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 h1:5IT7xOdq17MtcdtL/vtl6mGfzhaq4m4vpollPRmlsBQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0/go.mod h1:ZV4VOm0/eHR06JLrXWe09068dHpr3TRpY9Uo7T+anuA=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.50.0 h1:nNMpRpnkWDAaqcpxMJvxa/Ud98gjbYwayJY4/9bdjiU=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.50.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 h1:ig/FpDD2JofP/NExKQUbn7uOSZzJAQqogfqluZK4ed4=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.17.0 h1:3r2Cgk+nXNICMBxIFGnTRTbQFUwMiLisW+9uos0TtUI=
|
||||
github.com/alecthomas/chroma/v2 v2.17.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
|
||||
github.com/alecthomas/chroma/v2 v2.18.0 h1:6h53Q4hW83SuF+jcsp7CVhLsMozzvQvO8HBbKQW+gn4=
|
||||
github.com/alecthomas/chroma/v2 v2.18.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
|
||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
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.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
|
||||
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.4 h1:GySzjhVvx0ERP6eyfAbAuAXLtAda5TEy19E5q5W8I9E=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.4/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90=
|
||||
|
@ -167,6 +183,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
|||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k=
|
||||
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
|
@ -185,9 +203,17 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
|
|||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
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/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M=
|
||||
github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanw/esbuild v0.25.3 h1:4JKyUsm/nHDhpxis4IyWXAi8GiyTwG1WdEp6OhGVE8U=
|
||||
github.com/evanw/esbuild v0.25.3/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
|
||||
github.com/evanw/esbuild v0.25.5 h1:E+JpeY5S/1LFmnX1vtuZqUKT7qDVcfXdhzMhM3uIKFs=
|
||||
github.com/evanw/esbuild v0.25.5/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
|
@ -201,13 +227,15 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
|||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/getkin/kin-openapi v0.131.0 h1:NO2UeHnFKRYhZ8wg6Nyh5Cq7dHk4suQQr72a4pMrDxE=
|
||||
github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58=
|
||||
github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk=
|
||||
github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
|
@ -317,12 +345,12 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
|
||||
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
|
||||
github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0=
|
||||
github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
|
@ -376,8 +404,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
|||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE=
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
|
@ -388,14 +416,18 @@ github.com/muesli/smartcrop v0.3.0/go.mod h1:i2fCI/UorTfgEpPPLWiFBv4pye+YAG78Rwc
|
|||
github.com/neurosnap/sentences v1.0.6/go.mod h1:pg1IapvYpWCJJm/Etxeh0+gtMf1rI1STY9S7eUCPbDc=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/niklasfasching/go-org v1.7.0 h1:vyMdcMWWTe/XmANk19F4k8XGBYg0GQ/gJGMimOjGMek=
|
||||
github.com/niklasfasching/go-org v1.7.0/go.mod h1:WuVm4d45oePiE0eX25GqTDQIt/qPW1T9DGkRscqLW5o=
|
||||
github.com/niklasfasching/go-org v1.8.0 h1:WyGLaajLLp8JbQzkmapZ1y0MOzKuKV47HkZRloi+HGY=
|
||||
github.com/niklasfasching/go-org v1.8.0/go.mod h1:e2A9zJs7cdONrEGs3gvxCcaAEpwwPNPG7csDpXckMNg=
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/olekukonko/errors v0.0.0-20250405072817-4e6d85265da6 h1:r3FaAI0NZK3hSmtTDrBVREhKULp8oUeqLT5Eyl2mSPo=
|
||||
github.com/olekukonko/errors v0.0.0-20250405072817-4e6d85265da6/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||
github.com/olekukonko/ll v0.0.8 h1:sbGZ1Fx4QxJXEqL/6IG8GEFnYojUSQ45dJVwN2FH2fc=
|
||||
github.com/olekukonko/ll v0.0.8/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g=
|
||||
github.com/olekukonko/tablewriter v1.0.7 h1:HCC2e3MM+2g72M81ZcJU11uciw6z/p82aEnm4/ySDGw=
|
||||
github.com/olekukonko/tablewriter v1.0.7/go.mod h1:H428M+HzoUXC6JU2Abj9IT9ooRmdq9CxuDmKMtrOCMs=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
|
@ -408,12 +440,16 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK
|
|||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
|
@ -428,14 +464,16 @@ github.com/shogo82148/go-shuffle v0.0.0-20180218125048-27e6095f230d/go.mod h1:2h
|
|||
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
|
||||
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
|
||||
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/fsync v0.10.1 h1:JRnB7G72b+gIBaBcpn5ibJSd7ww1iEahXSX2B8G6dSE=
|
||||
github.com/spf13/fsync v0.10.1/go.mod h1:y+B41vYq5i6Boa3Z+BVoPbDeOvxVkNU5OBXhoT8i4TQ=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE=
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
|
@ -449,13 +487,12 @@ 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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tdewolff/minify/v2 v2.20.37 h1:Q97cx4STXCh1dlWDlNHZniE8BJ2EBL0+2b0n92BJQhw=
|
||||
github.com/tdewolff/minify/v2 v2.20.37/go.mod h1:L1VYef/jwKw6Wwyk5A+T0mBjjn3mMPgmjjA688RNsxU=
|
||||
github.com/tdewolff/parse/v2 v2.7.15 h1:hysDXtdGZIRF5UZXwpfn3ZWRbm+ru4l53/ajBRGpCTw=
|
||||
github.com/tdewolff/parse/v2 v2.7.15/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
|
||||
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo=
|
||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
github.com/tdewolff/minify/v2 v2.23.8 h1:tvjHzRer46kwOfpdCBCWsDblCw3QtnLJRd61pTVkyZ8=
|
||||
github.com/tdewolff/minify/v2 v2.23.8/go.mod h1:VW3ISUd3gDOZuQ/jwZr4sCzsuX+Qvsx87FDMjk6Rvno=
|
||||
github.com/tdewolff/parse/v2 v2.8.1 h1:J5GSHru6o3jF1uLlEKVXkDxxcVx6yzOlIVIotK4w2po=
|
||||
github.com/tdewolff/parse/v2 v2.8.1/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=
|
||||
github.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=
|
||||
github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
|
||||
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
|
@ -466,10 +503,12 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.7.10 h1:S+LrtBjRmqMac2UdtB6yyCEJm+UILZ2fefI4p7o0QpI=
|
||||
github.com/yuin/goldmark v1.7.10/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY=
|
||||
github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9HTHs=
|
||||
github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA=
|
||||
github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM=
|
||||
github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
|
@ -480,20 +519,24 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
|||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
|
||||
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
|
||||
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF2rS0bdD3aJYEnmzA=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
|
||||
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I=
|
||||
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
|
||||
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
||||
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
|
||||
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
|
||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
gocloud.dev v0.40.0 h1:f8LgP+4WDqOG/RXoUcyLpeIAGOcAbZrZbDQCUee10ng=
|
||||
|
@ -509,8 +552,8 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y
|
|||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
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=
|
||||
|
@ -526,8 +569,8 @@ golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZ
|
|||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
|
||||
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
|
||||
golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE=
|
||||
golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
|
@ -554,8 +597,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
|
|||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
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=
|
||||
|
@ -595,8 +638,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
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=
|
||||
|
@ -606,8 +649,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
|
|||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
|
||||
golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -623,8 +666,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -672,8 +715,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
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/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
|
@ -692,13 +735,13 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
||||
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
|
@ -751,8 +794,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
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=
|
||||
|
@ -778,8 +821,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
|
|||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/api v0.221.0 h1:qzaJfLhDsbMeFee8zBRdt/Nc+xmOuafD/dbdgGfutOU=
|
||||
google.golang.org/api v0.221.0/go.mod h1:7sOU2+TL4TxUTdbi0gWgAIg7tH5qBXxoyhtL+9x3biQ=
|
||||
google.golang.org/api v0.237.0 h1:MP7XVsGZesOsx3Q8WVa4sUdbrsTvDSOERd3Vh4xj/wc=
|
||||
google.golang.org/api v0.237.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
|
@ -823,12 +866,12 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
|
|||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20241104194629-dd2ea8efbc28 h1:KJjNNclfpIkVqrZlTWcgOOaVQ00LdBnoEaRfkUx760s=
|
||||
google.golang.org/genproto v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:mt9/MofW7AWQ+Gy179ChOnvmJatV8YHUmrcedo9CIFI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
|
@ -845,8 +888,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
|
|||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
|
||||
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
@ -857,8 +900,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
|||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
|
|
|
@ -19,6 +19,8 @@ import (
|
|||
"sync/atomic"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/olekukonko/tablewriter/renderer"
|
||||
"github.com/olekukonko/tablewriter/tw"
|
||||
)
|
||||
|
||||
// ProcessingStats represents statistics about a site build.
|
||||
|
@ -66,23 +68,6 @@ func (s *ProcessingStats) Add(counter *uint64, amount int) {
|
|||
atomic.AddUint64(counter, uint64(amount))
|
||||
}
|
||||
|
||||
// Table writes a table-formatted representation of the stats in a
|
||||
// ProcessingStats instance to w.
|
||||
func (s *ProcessingStats) Table(w io.Writer) {
|
||||
titleVals := s.toVals()
|
||||
data := make([][]string, len(titleVals))
|
||||
for i, tv := range titleVals {
|
||||
data[i] = []string{tv.name, strconv.Itoa(int(tv.val))}
|
||||
}
|
||||
|
||||
table := tablewriter.NewWriter(w)
|
||||
|
||||
table.AppendBulk(data)
|
||||
table.SetHeader([]string{"", s.Name})
|
||||
table.SetBorder(false)
|
||||
table.Render()
|
||||
}
|
||||
|
||||
// ProcessingStatsTable writes a table-formatted representation of stats to w.
|
||||
func ProcessingStatsTable(w io.Writer, stats ...*ProcessingStats) {
|
||||
names := make([]string, len(stats)+1)
|
||||
|
@ -106,13 +91,26 @@ func ProcessingStatsTable(w io.Writer, stats ...*ProcessingStats) {
|
|||
data[j] = append(data[j], strconv.Itoa(int(tv.val)))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
table := tablewriter.NewWriter(w)
|
||||
table := tablewriter.NewTable(
|
||||
w,
|
||||
tablewriter.WithRenderer(renderer.NewBlueprint(tw.Rendition{
|
||||
Borders: tw.BorderNone,
|
||||
Symbols: tw.NewSymbols(tw.StyleLight),
|
||||
Settings: tw.Settings{
|
||||
Separators: tw.Separators{BetweenRows: tw.Off},
|
||||
Lines: tw.Lines{ShowFooterLine: tw.On},
|
||||
},
|
||||
})),
|
||||
tablewriter.WithConfig(
|
||||
tablewriter.Config{
|
||||
MaxWidth: 70,
|
||||
Row: tw.CellConfig{Alignment: tw.CellAlignment{Global: tw.AlignRight, PerColumn: []tw.Align{tw.AlignLeft}}},
|
||||
}),
|
||||
)
|
||||
|
||||
table.AppendBulk(data)
|
||||
table.SetHeader(names)
|
||||
table.SetBorder(false)
|
||||
table.Bulk(data)
|
||||
table.Header(names)
|
||||
table.Render()
|
||||
}
|
||||
|
|
|
@ -892,7 +892,7 @@ disableKinds = ['home','rss','sitemap','taxonomy','term']
|
|||
---
|
||||
title: s
|
||||
cascade:
|
||||
_build:
|
||||
build:
|
||||
render: never
|
||||
---
|
||||
-- content/s/p1.md --
|
||||
|
@ -917,3 +917,51 @@ title: p2
|
|||
b.AssertFileExists("public/sx/index.html", true) // failing
|
||||
b.AssertFileExists("public/sx/p2/index.html", true) // failing
|
||||
}
|
||||
|
||||
func TestCascadeGotmplIssue13743(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
|
||||
[cascade.params]
|
||||
foo = 'bar'
|
||||
[cascade.target]
|
||||
path = '/p1'
|
||||
-- content/_content.gotmpl --
|
||||
{{ .AddPage (dict "title" "p1" "path" "p1") }}
|
||||
-- layouts/all.html --
|
||||
{{ .Title }}|{{ .Params.foo }}
|
||||
`
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", "p1|bar") // actual content is "p1|"
|
||||
}
|
||||
|
||||
func TestCascadeWarnOverrideIssue13806(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
|
||||
[[cascade]]
|
||||
[cascade.params]
|
||||
searchable = true
|
||||
[cascade.target]
|
||||
kind = 'page'
|
||||
-- content/something.md --
|
||||
---
|
||||
title: Something
|
||||
params:
|
||||
searchable: false
|
||||
---
|
||||
-- layouts/all.html --
|
||||
All.
|
||||
|
||||
`
|
||||
|
||||
b := Test(t, files, TestOptWarn())
|
||||
|
||||
b.AssertLogContains("! WARN")
|
||||
}
|
||||
|
|
|
@ -1406,7 +1406,7 @@ home = ["html"]
|
|||
"home": {"html"},
|
||||
"page": {"html"},
|
||||
"rss": {"rss"},
|
||||
"section": nil,
|
||||
"section": {},
|
||||
"taxonomy": {"html", "rss"},
|
||||
"term": {"html", "rss"},
|
||||
})
|
||||
|
|
|
@ -114,7 +114,7 @@ func (f ContentFactory) CreateContentPlaceHolder(filename string, force bool) (s
|
|||
// the paths correct.
|
||||
placeholder := `---
|
||||
title: "Content Placeholder"
|
||||
_build:
|
||||
build:
|
||||
render: never
|
||||
list: never
|
||||
publishResources: false
|
||||
|
|
|
@ -356,7 +356,7 @@ func (m *pageMap) addPagesFromGoTmplFi(fi hugofs.FileMetaInfo, buildConfig *Buil
|
|||
Watching: s.Conf.Watching(),
|
||||
HandlePage: func(pt *pagesfromdata.PagesFromTemplate, pc *pagemeta.PageConfig) error {
|
||||
s := pt.Site.(*Site)
|
||||
if err := pc.Compile(pt.GoTmplFi.Meta().PathInfo.Base(), true, "", s.Log, s.conf.MediaTypes.Config); err != nil {
|
||||
if err := pc.CompileForPagesFromDataPre(pt.GoTmplFi.Meta().PathInfo.Base(), m.s.Log, s.conf.MediaTypes.Config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ import (
|
|||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
|
@ -1310,7 +1309,6 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context,
|
|||
}
|
||||
|
||||
po.renderState = 0
|
||||
po.p.resourcesPublishInit = &sync.Once{}
|
||||
if r == identity.FinderFoundOneOfMany || po.f.Name == output.HTTPStatus404HTMLFormat.Name {
|
||||
// Will force a re-render even in fast render mode.
|
||||
po.renderOnce = false
|
||||
|
@ -1330,6 +1328,7 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context,
|
|||
// This needs no reset, so no need to check it.
|
||||
return nil
|
||||
}
|
||||
|
||||
// First check the top level dependency manager.
|
||||
for _, id := range changes {
|
||||
checkedCounter.Add(1)
|
||||
|
@ -1411,7 +1410,7 @@ func (sa *sitePagesAssembler) applyAggregates() error {
|
|||
}
|
||||
|
||||
// Handle cascades first to get any default dates set.
|
||||
var cascade *maps.Ordered[page.PageMatcher, maps.Params]
|
||||
var cascade *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]
|
||||
if keyPage == "" {
|
||||
// Home page gets it's cascade from the site config.
|
||||
cascade = sa.conf.Cascade.Config
|
||||
|
@ -1423,7 +1422,7 @@ func (sa *sitePagesAssembler) applyAggregates() error {
|
|||
} else {
|
||||
_, data := pw.WalkContext.Data().LongestPrefix(paths.Dir(keyPage))
|
||||
if data != nil {
|
||||
cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params])
|
||||
cascade = data.(*maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1505,11 +1504,11 @@ func (sa *sitePagesAssembler) applyAggregates() error {
|
|||
pageResource := rs.r.(*pageState)
|
||||
relPath := pageResource.m.pathInfo.BaseRel(pageBundle.m.pathInfo)
|
||||
pageResource.m.resourcePath = relPath
|
||||
var cascade *maps.Ordered[page.PageMatcher, maps.Params]
|
||||
var cascade *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]
|
||||
// Apply cascade (if set) to the page.
|
||||
_, data := pw.WalkContext.Data().LongestPrefix(resourceKey)
|
||||
if data != nil {
|
||||
cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params])
|
||||
cascade = data.(*maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig])
|
||||
}
|
||||
if err := pageResource.setMetaPost(cascade); err != nil {
|
||||
return false, err
|
||||
|
@ -1573,10 +1572,10 @@ func (sa *sitePagesAssembler) applyAggregatesToTaxonomiesAndTerms() error {
|
|||
const eventName = "dates"
|
||||
|
||||
if p.Kind() == kinds.KindTerm {
|
||||
var cascade *maps.Ordered[page.PageMatcher, maps.Params]
|
||||
var cascade *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]
|
||||
_, data := pw.WalkContext.Data().LongestPrefix(s)
|
||||
if data != nil {
|
||||
cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params])
|
||||
cascade = data.(*maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig])
|
||||
}
|
||||
if err := p.setMetaPost(cascade); err != nil {
|
||||
return false, err
|
||||
|
@ -1645,6 +1644,8 @@ func (sa *sitePagesAssembler) assembleTermsAndTranslations() error {
|
|||
views = sa.pageMap.cfg.taxonomyConfig.views
|
||||
)
|
||||
|
||||
rebuild := sa.s.h.isRebuild()
|
||||
|
||||
lockType := doctree.LockTypeWrite
|
||||
w := &doctree.NodeShiftTreeWalker[contentNodeI]{
|
||||
Tree: pages,
|
||||
|
@ -1677,6 +1678,14 @@ func (sa *sitePagesAssembler) assembleTermsAndTranslations() error {
|
|||
pi := sa.Site.Conf.PathParser().Parse(files.ComponentFolderContent, viewTermKey+"/_index.md")
|
||||
term := pages.Get(pi.Base())
|
||||
if term == nil {
|
||||
if rebuild {
|
||||
// A new tag was added in server mode.
|
||||
taxonomy := pages.Get(viewName.pluralTreeKey)
|
||||
if taxonomy != nil {
|
||||
sa.assembleChanges.Add(taxonomy.GetIdentity())
|
||||
}
|
||||
}
|
||||
|
||||
m := &pageMeta{
|
||||
term: v,
|
||||
singular: viewName.singular,
|
||||
|
@ -1684,7 +1693,9 @@ func (sa *sitePagesAssembler) assembleTermsAndTranslations() error {
|
|||
pathInfo: pi,
|
||||
pageMetaParams: &pageMetaParams{
|
||||
pageConfig: &pagemeta.PageConfig{
|
||||
Kind: kinds.KindTerm,
|
||||
PageConfigEarly: pagemeta.PageConfigEarly{
|
||||
Kind: kinds.KindTerm,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1945,7 +1956,9 @@ func (sa *sitePagesAssembler) addStandalonePages() error {
|
|||
pathInfo: s.Conf.PathParser().Parse(files.ComponentFolderContent, key+f.MediaType.FirstSuffix.FullSuffix),
|
||||
pageMetaParams: &pageMetaParams{
|
||||
pageConfig: &pagemeta.PageConfig{
|
||||
Kind: kind,
|
||||
PageConfigEarly: pagemeta.PageConfigEarly{
|
||||
Kind: kind,
|
||||
},
|
||||
},
|
||||
},
|
||||
standaloneOutputFormat: f,
|
||||
|
@ -2071,7 +2084,9 @@ func (sa *sitePagesAssembler) addMissingRootSections() error {
|
|||
pathInfo: p,
|
||||
pageMetaParams: &pageMetaParams{
|
||||
pageConfig: &pagemeta.PageConfig{
|
||||
Kind: kinds.KindHome,
|
||||
PageConfigEarly: pagemeta.PageConfigEarly{
|
||||
Kind: kinds.KindHome,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -2104,7 +2119,9 @@ func (sa *sitePagesAssembler) addMissingTaxonomies() error {
|
|||
pathInfo: sa.Conf.PathParser().Parse(files.ComponentFolderContent, key+"/_index.md"),
|
||||
pageMetaParams: &pageMetaParams{
|
||||
pageConfig: &pagemeta.PageConfig{
|
||||
Kind: kinds.KindTaxonomy,
|
||||
PageConfigEarly: pagemeta.PageConfigEarly{
|
||||
Kind: kinds.KindTaxonomy,
|
||||
},
|
||||
},
|
||||
},
|
||||
singular: viewName.singular,
|
||||
|
|
|
@ -44,14 +44,14 @@ tags: ["mytag"]
|
|||
`, "sect/no-list.md", `
|
||||
---
|
||||
title: No List
|
||||
_build:
|
||||
build:
|
||||
list: false
|
||||
---
|
||||
|
||||
`, "sect/no-render.md", `
|
||||
---
|
||||
title: No List
|
||||
_build:
|
||||
build:
|
||||
render: false
|
||||
---
|
||||
`,
|
||||
|
@ -59,14 +59,14 @@ _build:
|
|||
---
|
||||
title: No Render Link
|
||||
aliases: ["/link-alias"]
|
||||
_build:
|
||||
build:
|
||||
render: link
|
||||
---
|
||||
`,
|
||||
"sect/no-publishresources/index.md", `
|
||||
---
|
||||
title: No Publish Resources
|
||||
_build:
|
||||
build:
|
||||
publishResources: false
|
||||
---
|
||||
|
||||
|
@ -81,7 +81,7 @@ headless: true
|
|||
---
|
||||
title: Headless Local Lists
|
||||
cascade:
|
||||
_build:
|
||||
build:
|
||||
render: false
|
||||
list: local
|
||||
publishResources: false
|
||||
|
@ -365,7 +365,7 @@ Data1: {{ $data1.RelPermalink }}
|
|||
`)
|
||||
|
||||
b.WithContent("section/bundle-false/index.md", `---\ntitle: BundleFalse
|
||||
_build:
|
||||
build:
|
||||
publishResources: false
|
||||
---`,
|
||||
"section/bundle-false/data1.json", "Some data1",
|
||||
|
@ -388,7 +388,7 @@ func TestNoRenderAndNoPublishResources(t *testing.T) {
|
|||
noRenderPage := `
|
||||
---
|
||||
title: %s
|
||||
_build:
|
||||
build:
|
||||
render: false
|
||||
publishResources: false
|
||||
---
|
||||
|
|
|
@ -324,6 +324,14 @@ func (h *HugoSites) assemble(ctx context.Context, l logg.LevelLogger, bcfg *Buil
|
|||
}
|
||||
}
|
||||
|
||||
// Handle new terms from assemblePagesStep2.
|
||||
changes = bcfg.WhatChanged.Drain()
|
||||
if len(changes) > 0 {
|
||||
if err := h.resolveAndClearStateForIdentities(ctx, l, nil, changes); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
h.renderFormats = output.Formats{}
|
||||
for _, s := range h.Sites {
|
||||
s.s.initRenderFormats()
|
||||
|
|
|
@ -263,7 +263,7 @@ func (s *IntegrationTestBuilder) AssertLogContains(els ...string) {
|
|||
}
|
||||
}
|
||||
|
||||
// AssertLogNotContains asserts that the last build log does matches the given regular expressions.
|
||||
// AssertLogMatches asserts that the last build log matches the given regular expressions.
|
||||
// The regular expressions can be negated with a "! " prefix.
|
||||
func (s *IntegrationTestBuilder) AssertLogMatches(expression string) {
|
||||
s.Helper()
|
||||
|
|
|
@ -19,7 +19,6 @@ import (
|
|||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
|
@ -29,6 +28,7 @@ import (
|
|||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/gohugoio/hugo/output"
|
||||
"github.com/gohugoio/hugo/related"
|
||||
"github.com/gohugoio/hugo/resources"
|
||||
"github.com/gohugoio/hugo/tpl/tplimpl"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
|
@ -110,8 +110,7 @@ type pageState struct {
|
|||
*pageCommon
|
||||
|
||||
resource.Staler
|
||||
dependencyManager identity.Manager
|
||||
resourcesPublishInit *sync.Once
|
||||
dependencyManager identity.Manager
|
||||
}
|
||||
|
||||
func (p *pageState) incrPageOutputTemplateVariation() {
|
||||
|
@ -522,39 +521,35 @@ func (p *pageState) initPage() error {
|
|||
}
|
||||
|
||||
func (p *pageState) renderResources() error {
|
||||
var initErr error
|
||||
for _, r := range p.Resources() {
|
||||
|
||||
p.resourcesPublishInit.Do(func() {
|
||||
for _, r := range p.Resources() {
|
||||
if _, ok := r.(page.Page); ok {
|
||||
if _, ok := r.(page.Page); ok {
|
||||
if p.s.h.buildCounter.Load() == 0 {
|
||||
// Pages gets rendered with the owning page but we count them here.
|
||||
p.s.PathSpec.ProcessingStats.Incr(&p.s.PathSpec.ProcessingStats.Pages)
|
||||
continue
|
||||
}
|
||||
|
||||
if _, isWrapper := r.(resource.ResourceWrapper); isWrapper {
|
||||
// Skip resources that are wrapped.
|
||||
// These gets published on its own.
|
||||
continue
|
||||
}
|
||||
|
||||
src, ok := r.(resource.Source)
|
||||
if !ok {
|
||||
initErr = fmt.Errorf("resource %T does not support resource.Source", r)
|
||||
return
|
||||
}
|
||||
|
||||
if err := src.Publish(); err != nil {
|
||||
if !herrors.IsNotExist(err) {
|
||||
p.s.Log.Errorf("Failed to publish Resource for page %q: %s", p.pathOrTitle(), err)
|
||||
}
|
||||
} else {
|
||||
p.s.PathSpec.ProcessingStats.Incr(&p.s.PathSpec.ProcessingStats.Files)
|
||||
}
|
||||
continue
|
||||
}
|
||||
})
|
||||
|
||||
return initErr
|
||||
if resources.IsPublished(r) {
|
||||
continue
|
||||
}
|
||||
|
||||
src, ok := r.(resource.Source)
|
||||
if !ok {
|
||||
return fmt.Errorf("resource %T does not support resource.Source", r)
|
||||
}
|
||||
|
||||
if err := src.Publish(); err != nil {
|
||||
if !herrors.IsNotExist(err) {
|
||||
p.s.Log.Errorf("Failed to publish Resource for page %q: %s", p.pathOrTitle(), err)
|
||||
}
|
||||
} else {
|
||||
p.s.PathSpec.ProcessingStats.Incr(&p.s.PathSpec.ProcessingStats.Files)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *pageState) AlternativeOutputFormats() page.OutputFormats {
|
||||
|
|
|
@ -29,7 +29,6 @@ import (
|
|||
|
||||
"github.com/gohugoio/hugo/source"
|
||||
|
||||
"github.com/gohugoio/hugo/common/constants"
|
||||
"github.com/gohugoio/hugo/common/hashing"
|
||||
"github.com/gohugoio/hugo/common/hugo"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
|
@ -54,7 +53,6 @@ type pageMeta struct {
|
|||
|
||||
resource.Staler
|
||||
*pageMetaParams
|
||||
pageMetaFrontMatter
|
||||
|
||||
// Set for standalone pages, e.g. robotsTXT.
|
||||
standaloneOutputFormat output.Format
|
||||
|
@ -73,13 +71,7 @@ type pageMeta struct {
|
|||
// Prepare for a rebuild of the data passed in from front matter.
|
||||
func (m *pageMeta) setMetaPostPrepareRebuild() {
|
||||
params := xmaps.Clone(m.paramsOriginal)
|
||||
m.pageMetaParams.pageConfig = &pagemeta.PageConfig{
|
||||
Kind: m.pageConfig.Kind,
|
||||
Lang: m.pageConfig.Lang,
|
||||
Path: m.pageConfig.Path,
|
||||
Params: params,
|
||||
}
|
||||
m.pageMetaFrontMatter = pageMetaFrontMatter{}
|
||||
m.pageMetaParams.pageConfig = pagemeta.ClonePageConfigForRebuild(m.pageMetaParams.pageConfig, params)
|
||||
}
|
||||
|
||||
type pageMetaParams struct {
|
||||
|
@ -90,18 +82,17 @@ type pageMetaParams struct {
|
|||
|
||||
// These are only set in watch mode.
|
||||
datesOriginal pagemeta.Dates
|
||||
paramsOriginal map[string]any // contains the original params as defined in the front matter.
|
||||
cascadeOriginal *maps.Ordered[page.PageMatcher, maps.Params] // contains the original cascade as defined in the front matter.
|
||||
}
|
||||
|
||||
// From page front matter.
|
||||
type pageMetaFrontMatter struct {
|
||||
configuredOutputFormats output.Formats // outputs defined in front matter.
|
||||
paramsOriginal map[string]any // contains the original params as defined in the front matter.
|
||||
cascadeOriginal *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig] // contains the original cascade as defined in the front matter.
|
||||
}
|
||||
|
||||
func (m *pageMetaParams) init(preserveOriginal bool) {
|
||||
if preserveOriginal {
|
||||
m.paramsOriginal = xmaps.Clone[maps.Params](m.pageConfig.Params)
|
||||
if m.pageConfig.IsFromContentAdapter {
|
||||
m.paramsOriginal = xmaps.Clone(m.pageConfig.ContentAdapterData)
|
||||
} else {
|
||||
m.paramsOriginal = xmaps.Clone(m.pageConfig.Params)
|
||||
}
|
||||
m.cascadeOriginal = m.pageConfig.CascadeCompiled.Clone()
|
||||
}
|
||||
}
|
||||
|
@ -261,7 +252,7 @@ func (p *pageMeta) setMetaPre(pi *contentParseInfo, logger loggers.Logger, conf
|
|||
// Check for any cascade define on itself.
|
||||
if cv, found := frontmatter["cascade"]; found {
|
||||
var err error
|
||||
cascade, err := page.DecodeCascade(logger, cv)
|
||||
cascade, err := page.DecodeCascade(logger, true, cv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -299,7 +290,7 @@ func (p *pageMeta) setMetaPre(pi *contentParseInfo, logger loggers.Logger, conf
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ps *pageState) setMetaPost(cascade *maps.Ordered[page.PageMatcher, maps.Params]) error {
|
||||
func (ps *pageState) setMetaPost(cascade *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig]) error {
|
||||
ps.m.setMetaPostCount++
|
||||
var cascadeHashPre uint64
|
||||
if ps.m.setMetaPostCount > 1 {
|
||||
|
@ -311,15 +302,20 @@ func (ps *pageState) setMetaPost(cascade *maps.Ordered[page.PageMatcher, maps.Pa
|
|||
// Apply cascades first so they can be overridden later.
|
||||
if cascade != nil {
|
||||
if ps.m.pageConfig.CascadeCompiled != nil {
|
||||
cascade.Range(func(k page.PageMatcher, v maps.Params) bool {
|
||||
cascade.Range(func(k page.PageMatcher, v page.PageMatcherParamsConfig) bool {
|
||||
vv, found := ps.m.pageConfig.CascadeCompiled.Get(k)
|
||||
if !found {
|
||||
ps.m.pageConfig.CascadeCompiled.Set(k, v)
|
||||
} else {
|
||||
// Merge
|
||||
for ck, cv := range v {
|
||||
if _, found := vv[ck]; !found {
|
||||
vv[ck] = cv
|
||||
for ck, cv := range v.Params {
|
||||
if _, found := vv.Params[ck]; !found {
|
||||
vv.Params[ck] = cv
|
||||
}
|
||||
}
|
||||
for ck, cv := range v.Fields {
|
||||
if _, found := vv.Fields[ck]; !found {
|
||||
vv.Fields[ck] = cv
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -348,18 +344,35 @@ func (ps *pageState) setMetaPost(cascade *maps.Ordered[page.PageMatcher, maps.Pa
|
|||
}
|
||||
|
||||
// Cascade is also applied to itself.
|
||||
cascade.Range(func(k page.PageMatcher, v maps.Params) bool {
|
||||
var err error
|
||||
cascade.Range(func(k page.PageMatcher, v page.PageMatcherParamsConfig) bool {
|
||||
if !k.Matches(ps) {
|
||||
return true
|
||||
}
|
||||
for kk, vv := range v {
|
||||
for kk, vv := range v.Params {
|
||||
if _, found := ps.m.pageConfig.Params[kk]; !found {
|
||||
ps.m.pageConfig.Params[kk] = vv
|
||||
}
|
||||
}
|
||||
|
||||
for kk, vv := range v.Fields {
|
||||
if ps.m.pageConfig.IsFromContentAdapter {
|
||||
if _, found := ps.m.pageConfig.ContentAdapterData[kk]; !found {
|
||||
ps.m.pageConfig.ContentAdapterData[kk] = vv
|
||||
}
|
||||
} else {
|
||||
if _, found := ps.m.pageConfig.Params[kk]; !found {
|
||||
ps.m.pageConfig.Params[kk] = vv
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ps.setMetaPostParams(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -405,6 +418,12 @@ func (p *pageState) setMetaPostParams() error {
|
|||
PathOrTitle: p.pathOrTitle(),
|
||||
}
|
||||
|
||||
if isContentAdapter {
|
||||
if err := pm.pageConfig.Compile(ext, p.s.Log, p.s.conf.OutputFormats.Config, p.s.conf.MediaTypes.Config); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the date separately
|
||||
// TODO(bep) we need to "do more" in this area so this can be split up and
|
||||
// more easily tested without the Page, but the coupling is strong.
|
||||
|
@ -531,16 +550,7 @@ params:
|
|||
for i, s := range o {
|
||||
o[i] = strings.ToLower(s)
|
||||
}
|
||||
if len(o) > 0 {
|
||||
// Output formats are explicitly set in front matter, use those.
|
||||
outFormats, err := p.s.conf.OutputFormats.Config.GetByNames(o...)
|
||||
if err != nil {
|
||||
p.s.Log.Errorf("Failed to resolve output formats: %s", err)
|
||||
} else {
|
||||
pm.configuredOutputFormats = outFormats
|
||||
params[loki] = outFormats
|
||||
}
|
||||
}
|
||||
pm.pageConfig.Outputs = o
|
||||
case "draft":
|
||||
draft = new(bool)
|
||||
*draft = cast.ToBool(v)
|
||||
|
@ -636,9 +646,6 @@ params:
|
|||
}
|
||||
|
||||
for k, v := range userParams {
|
||||
if _, found := params[k]; found {
|
||||
p.s.Log.Warnidf(constants.WarnFrontMatterParamsOverrides, "Hugo front matter key %q is overridden in params section.", k)
|
||||
}
|
||||
params[strings.ToLower(k)] = v
|
||||
}
|
||||
|
||||
|
@ -672,7 +679,7 @@ params:
|
|||
return err
|
||||
}
|
||||
|
||||
if err := pcfg.Compile("", false, ext, p.s.Log, p.s.conf.MediaTypes.Config); err != nil {
|
||||
if err := pcfg.Compile(ext, p.s.Log, p.s.conf.OutputFormats.Config, p.s.conf.MediaTypes.Config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -829,8 +836,8 @@ func (p *pageMeta) newContentConverter(ps *pageState, markup string) (converter.
|
|||
|
||||
// The output formats this page will be rendered to.
|
||||
func (m *pageMeta) outputFormats() output.Formats {
|
||||
if len(m.configuredOutputFormats) > 0 {
|
||||
return m.configuredOutputFormats
|
||||
if len(m.pageConfig.ConfiguredOutputFormats) > 0 {
|
||||
return m.pageConfig.ConfiguredOutputFormats
|
||||
}
|
||||
return m.s.conf.C.KindOutputFormats[m.Kind()]
|
||||
}
|
||||
|
|
|
@ -16,12 +16,12 @@ package hugolib
|
|||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/gohugoio/hugo/hugofs/files"
|
||||
"github.com/gohugoio/hugo/resources"
|
||||
|
||||
"github.com/gohugoio/hugo/common/constants"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
|
||||
|
@ -40,6 +40,14 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
|
|||
// Make sure that any partially created page part is marked as stale.
|
||||
m.MarkStale()
|
||||
}
|
||||
|
||||
if p != nil && pth != nil && p.IsHome() && pth.IsLeafBundle() {
|
||||
msg := "Using %s in your content's root directory is usually incorrect for your home page. "
|
||||
msg += "You should use %s instead. If you don't rename this file, your home page will be "
|
||||
msg += "treated as a leaf bundle, meaning it won't be able to have any child pages or sections."
|
||||
h.Log.Warnidf(constants.WarnHomePageIsLeafBundle, msg, pth.PathNoLeadingSlash(), strings.ReplaceAll(pth.PathNoLeadingSlash(), "index", "_index"))
|
||||
}
|
||||
|
||||
return p, pth, err
|
||||
}
|
||||
|
||||
|
@ -190,7 +198,6 @@ func (h *HugoSites) doNewPage(m *pageMeta) (*pageState, *paths.Path, error) {
|
|||
pid: pid,
|
||||
pageOutput: nopPageOutput,
|
||||
pageOutputTemplateVariationsState: &atomic.Uint32{},
|
||||
resourcesPublishInit: &sync.Once{},
|
||||
Staler: m,
|
||||
dependencyManager: m.s.Conf.NewIdentityManager(m.Path()),
|
||||
pageCommon: &pageCommon{
|
||||
|
|
|
@ -1968,3 +1968,35 @@ Title: {{ .Title }}
|
|||
"deprecated: path in front matter was deprecated",
|
||||
)
|
||||
}
|
||||
|
||||
// Issue 13538
|
||||
func TestHomePageIsLeafBundle(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
defaultContentLanguage = 'de'
|
||||
defaultContentLanguageInSubdir = true
|
||||
[languages.de]
|
||||
weight = 1
|
||||
[languages.en]
|
||||
weight = 2
|
||||
-- layouts/all.html --
|
||||
{{ .Title }}
|
||||
-- content/index.de.md --
|
||||
---
|
||||
title: home de
|
||||
---
|
||||
-- content/index.en.org --
|
||||
---
|
||||
title: home en
|
||||
---
|
||||
`
|
||||
|
||||
b := Test(t, files, TestOptWarn())
|
||||
|
||||
b.AssertFileContent("public/de/index.html", "home de")
|
||||
b.AssertFileContent("public/en/index.html", "home en")
|
||||
b.AssertLogContains("Using index.de.md in your content's root directory is usually incorrect for your home page. You should use _index.de.md instead.")
|
||||
b.AssertLogContains("Using index.en.org in your content's root directory is usually incorrect for your home page. You should use _index.en.org instead.")
|
||||
}
|
||||
|
|
|
@ -91,17 +91,20 @@ func (p *pagesFromDataTemplateContext) AddPage(v any) (string, error) {
|
|||
|
||||
pd := pagemeta.DefaultPageConfig
|
||||
pd.IsFromContentAdapter = true
|
||||
pd.ContentAdapterData = m
|
||||
|
||||
if err := mapstructure.WeakDecode(m, &pd); err != nil {
|
||||
return "", fmt.Errorf("failed to decode page map: %w", err)
|
||||
// The rest will be handled after the cascade is calculated and applied.
|
||||
if err := mapstructure.WeakDecode(pd.ContentAdapterData, &pd.PageConfigEarly); err != nil {
|
||||
err = fmt.Errorf("failed to decode page map: %w", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
p.p.buildState.NumPagesAdded++
|
||||
|
||||
if err := pd.Validate(true); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
p.p.buildState.NumPagesAdded++
|
||||
|
||||
return "", p.p.HandlePage(p.p, &pd)
|
||||
}
|
||||
|
||||
|
|
|
@ -779,3 +779,139 @@ Single.
|
|||
|
||||
b.AssertFileContent("public/tags/index.html", "Terms: mytag: 1|§s")
|
||||
}
|
||||
|
||||
func TestContentAdapterOutputsIssue13689(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
|
||||
[outputs]
|
||||
page = ['html','json']
|
||||
-- layouts/page.html --
|
||||
html: {{ .Title }}
|
||||
-- layouts/page.json --
|
||||
json: {{ .Title }}
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: p1
|
||||
---
|
||||
-- content/p2.md --
|
||||
---
|
||||
title: p2
|
||||
outputs:
|
||||
- html
|
||||
---
|
||||
-- content/_content.gotmpl --
|
||||
{{ $page := dict "path" "p3" "title" "p3" }}
|
||||
{{ $.AddPage $page }}
|
||||
|
||||
{{ $page := dict "path" "p4" "title" "p4" "outputs" (slice "html") }}
|
||||
{{ $.AddPage $page }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileExists("public/p1/index.html", true)
|
||||
b.AssertFileExists("public/p1/index.json", true)
|
||||
b.AssertFileExists("public/p2/index.html", true)
|
||||
b.AssertFileExists("public/p2/index.json", false)
|
||||
b.AssertFileExists("public/p3/index.html", true)
|
||||
b.AssertFileExists("public/p3/index.json", true)
|
||||
b.AssertFileExists("public/p4/index.html", true)
|
||||
b.AssertFileExists("public/p4/index.json", false) // currently returns true
|
||||
}
|
||||
|
||||
func TestContentAdapterOutputsIssue13692(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','home','sitemap','taxonomy','term']
|
||||
[[cascade]]
|
||||
outputs = ['html','json']
|
||||
[cascade.target]
|
||||
path = '{/s2,/s4}'
|
||||
-- layouts/section.html --
|
||||
html: {{ .Title }}
|
||||
-- layouts/section.json --
|
||||
json: {{ .Title }}
|
||||
-- content/s1/_index.md --
|
||||
---
|
||||
title: s1
|
||||
---
|
||||
-- content/s2/_index.md --
|
||||
---
|
||||
title: s2
|
||||
---
|
||||
-- content/_content.gotmpl --
|
||||
{{ $page := dict "path" "s3" "title" "s3" "kind" "section" }}
|
||||
{{ $.AddPage $page }}
|
||||
|
||||
{{ $page := dict "path" "s4" "title" "s4" "kind" "section" }}
|
||||
{{ $.AddPage $page }}
|
||||
|
||||
{{ $page := dict "path" "s5" "title" "s5" "kind" "section" "outputs" (slice "html") }}
|
||||
{{ $.AddPage $page }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileExists("public/s1/index.html", true)
|
||||
b.AssertFileExists("public/s1/index.json", false)
|
||||
b.AssertFileExists("public/s1/index.xml", true)
|
||||
|
||||
b.AssertFileExists("public/s2/index.html", true)
|
||||
b.AssertFileExists("public/s2/index.json", true)
|
||||
b.AssertFileExists("public/s2/index.xml", false)
|
||||
|
||||
b.AssertFileExists("public/s3/index.html", true)
|
||||
b.AssertFileExists("public/s3/index.json", false)
|
||||
b.AssertFileExists("public/s3/index.xml", true)
|
||||
|
||||
b.AssertFileExists("public/s4/index.html", true)
|
||||
b.AssertFileExists("public/s4/index.json", true)
|
||||
b.AssertFileExists("public/s4/index.xml", false)
|
||||
|
||||
b.AssertFileExists("public/s5/index.html", true)
|
||||
b.AssertFileExists("public/s5/index.json", false)
|
||||
b.AssertFileExists("public/s5/index.xml", false)
|
||||
}
|
||||
|
||||
func TestContentAdapterCascadeBasic(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableLiveReload = true
|
||||
-- content/_index.md --
|
||||
---
|
||||
cascade:
|
||||
- title: foo
|
||||
target:
|
||||
path: "**"
|
||||
---
|
||||
-- layouts/all.html --
|
||||
Title: {{ .Title }}|Content: {{ .Content }}|
|
||||
-- content/_content.gotmpl --
|
||||
{{ $content := dict
|
||||
"mediaType" "text/markdown"
|
||||
"value" "The _Hunchback of Notre Dame_ was written by Victor Hugo."
|
||||
}}
|
||||
|
||||
{{ $page := dict "path" "s1" "kind" "page" }}
|
||||
{{ $.AddPage $page }}
|
||||
{{ $page := dict "path" "s2" "kind" "page" "title" "bar" "content" $content }}
|
||||
{{ $.AddPage $page }}
|
||||
|
||||
`
|
||||
|
||||
b := hugolib.TestRunning(t, files)
|
||||
|
||||
b.AssertFileContent("public/s1/index.html", "Title: foo|")
|
||||
b.AssertFileContent("public/s2/index.html", "Title: bar|", "Content: <p>The <em>Hunchback of Notre Dame</em> was written by Victor Hugo.</p>")
|
||||
|
||||
b.EditFileReplaceAll("content/_index.md", "foo", "baz").Build()
|
||||
|
||||
b.AssertFileContent("public/s1/index.html", "Title: baz|")
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ Summary: {{ .Summary }}|
|
|||
)
|
||||
}
|
||||
|
||||
func TestFrontMatterParamsKindPath(t *testing.T) {
|
||||
func TestFrontMatterParamsPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
|
@ -72,10 +72,9 @@ date: 2019-08-07
|
|||
path: "/a/b/c"
|
||||
slug: "s1"
|
||||
---
|
||||
-- content/mysection.md --
|
||||
-- content/mysection/_index.md --
|
||||
---
|
||||
title: "My Section"
|
||||
kind: "section"
|
||||
date: 2022-08-07
|
||||
path: "/a/b"
|
||||
---
|
||||
|
@ -95,66 +94,6 @@ a/b pages: {{ range $ab.RegularPages }}{{ .Path }}|{{ .RelPermalink }}|{{ end }}
|
|||
)
|
||||
}
|
||||
|
||||
func TestFrontMatterParamsLang(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.org/"
|
||||
disableKinds = ["taxonomy", "term"]
|
||||
defaultContentLanguage = "en"
|
||||
defaultContentLanguageInSubdir = true
|
||||
[languages]
|
||||
[languages.en]
|
||||
weight = 1
|
||||
[languages.nn]
|
||||
weight = 2
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "P1 nn"
|
||||
lang: "nn"
|
||||
---
|
||||
-- content/p2.md --
|
||||
---
|
||||
title: "P2"
|
||||
---
|
||||
-- layouts/index.html --
|
||||
RegularPages: {{ range site.RegularPages }}{{ .Path }}|{{ .RelPermalink }}|{{ .Title }}|{{ end }}$
|
||||
|
||||
`
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/en/index.html",
|
||||
"RegularPages: /p2|/en/p2/|P2|$",
|
||||
)
|
||||
b.AssertFileContent("public/nn/index.html",
|
||||
"RegularPages: /p1|/nn/p1/|P1 nn|$",
|
||||
)
|
||||
}
|
||||
|
||||
func TestFrontMatterTitleOverrideWarn(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.org/"
|
||||
disableKinds = ["taxonomy", "term"]
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "My title"
|
||||
params:
|
||||
title: "My title from params"
|
||||
---
|
||||
|
||||
|
||||
`
|
||||
|
||||
b := Test(t, files, TestOptWarn())
|
||||
|
||||
b.AssertLogContains("ARN Hugo front matter key \"title\" is overridden in params section", "You can suppress this warning")
|
||||
}
|
||||
|
||||
func TestFrontMatterParamsLangNoCascade(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -1766,6 +1766,60 @@ MyTemplate: {{ partial "MyTemplate.html" . }}|
|
|||
b.AssertFileContent("public/index.html", "MyTemplate: MyTemplate Edited")
|
||||
}
|
||||
|
||||
func TestRebuildEditInlinePartial13723(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
disableLiveReload = true
|
||||
title = "Foo"
|
||||
-- layouts/baseof.html --
|
||||
{{ block "main" . }}Main.{{ end }}
|
||||
{{ partial "myinlinepartialinbaseof.html" . }}|
|
||||
{{- define "_partials/myinlinepartialinbaseof.html" }}
|
||||
My inline partial in baseof.
|
||||
{{ end }}
|
||||
-- layouts/_partials/mypartial.html --
|
||||
Mypartial.
|
||||
{{ partial "myinlinepartial.html" . }}|
|
||||
{{- define "_partials/myinlinepartial.html" }}
|
||||
Mypartial Inline.|{{ .Title }}|
|
||||
{{ end }}
|
||||
-- layouts/_partials/myotherpartial.html --
|
||||
Myotherpartial.
|
||||
{{ partial "myotherinlinepartial.html" . }}|
|
||||
{{- define "_partials/myotherinlinepartial.html" }}
|
||||
Myotherpartial Inline.|{{ .Title }}|
|
||||
{{ return "myotherinlinepartial" }}
|
||||
{{ end }}
|
||||
-- layouts/all.html --
|
||||
{{ define "main" }}
|
||||
{{ partial "mypartial.html" . }}|
|
||||
{{ partial "myotherpartial.html" . }}|
|
||||
{{ partial "myinlinepartialinall.html" . }}|
|
||||
{{ end }}
|
||||
{{- define "_partials/myinlinepartialinall.html" }}
|
||||
My inline partial in all.
|
||||
{{ end }}
|
||||
|
||||
`
|
||||
b := TestRunning(t, files)
|
||||
b.AssertFileContent("public/index.html", "Mypartial.", "Mypartial Inline.|Foo")
|
||||
|
||||
// Edit inline partial in partial.
|
||||
b.EditFileReplaceAll("layouts/_partials/mypartial.html", "Mypartial Inline.", "Mypartial Inline Edited.").Build()
|
||||
b.AssertFileContent("public/index.html", "Mypartial Inline Edited.|Foo")
|
||||
|
||||
// Edit inline partial in baseof.
|
||||
b.EditFileReplaceAll("layouts/baseof.html", "My inline partial in baseof.", "My inline partial in baseof Edited.").Build()
|
||||
b.AssertFileContent("public/index.html", "My inline partial in baseof Edited.")
|
||||
|
||||
// Edit inline partial in all.
|
||||
b.EditFileReplaceAll("layouts/all.html", "My inline partial in all.", "My inline partial in all Edited.").Build()
|
||||
b.AssertFileContent("public/index.html", "My inline partial in all Edited.")
|
||||
}
|
||||
|
||||
func TestRebuildEditAsciidocContentFile(t *testing.T) {
|
||||
if !asciidocext.Supports() {
|
||||
t.Skip("skip asciidoc")
|
||||
|
@ -1865,3 +1919,50 @@ p1-content|
|
|||
b.EditFileReplaceAll("content/p1/index.md", "p1-content", "p1-content-foo").Build()
|
||||
b.AssertFileContent("public/p1/index.html", "p1-content-foo")
|
||||
}
|
||||
|
||||
func TestRebuildEditTagIssue13648(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
disableLiveReload = true
|
||||
-- layouts/all.html --
|
||||
All. {{ range .Pages }}{{ .Title }}|{{ end }}
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "P1"
|
||||
tags: ["tag1"]
|
||||
---
|
||||
|
||||
`
|
||||
b := TestRunning(t, files)
|
||||
|
||||
b.AssertFileContent("public/tags/index.html", "All. Tag1|")
|
||||
b.EditFileReplaceAll("content/p1.md", "tag1", "tag2").Build()
|
||||
|
||||
// Note that the below is still not correct, as this is effectively a rename, and
|
||||
// Tag2 should be removed from the list.
|
||||
// But that is a harder problem to tackle.
|
||||
b.AssertFileContent("public/tags/index.html", "All. Tag1|Tag2|")
|
||||
}
|
||||
|
||||
func TestRebuildEditNonReferencedResourceIssue13748(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = "https://example.com"
|
||||
disableLiveReload = true
|
||||
-- content/mybundle/index.md --
|
||||
-- content/mybundle/resource.txt --
|
||||
This is a resource file.
|
||||
-- layouts/all.html --
|
||||
All.
|
||||
`
|
||||
b := TestRunning(t, files)
|
||||
|
||||
b.AssertFileContent("public/mybundle/resource.txt", "This is a resource file.")
|
||||
b.EditFileReplaceAll("content/mybundle/resource.txt", "This is a resource file.", "This is an edited resource file.").Build()
|
||||
b.AssertFileContent("public/mybundle/resource.txt", "This is an edited resource file.")
|
||||
}
|
||||
|
|
|
@ -398,6 +398,10 @@ func doRenderShortcode(
|
|||
return true
|
||||
}
|
||||
base, layoutDescriptor := po.GetInternalTemplateBasePathAndDescriptor()
|
||||
|
||||
// With shortcodes/mymarkdown.md (only), this allows {{% mymarkdown %}} when rendering HTML,
|
||||
// but will not resolve any template when doing {{< mymarkdown >}}.
|
||||
layoutDescriptor.AlwaysAllowPlainText = sc.doMarkup
|
||||
q := tplimpl.TemplateQuery{
|
||||
Path: base,
|
||||
Name: sc.name,
|
||||
|
@ -405,10 +409,9 @@ func doRenderShortcode(
|
|||
Desc: layoutDescriptor,
|
||||
Consider: include,
|
||||
}
|
||||
v := s.TemplateStore.LookupShortcode(q)
|
||||
v, err := s.TemplateStore.LookupShortcode(q)
|
||||
if v == nil {
|
||||
s.Log.Errorf("Unable to locate template for shortcode %q in page %q", sc.name, p.File().Path())
|
||||
return zeroShortcode, nil
|
||||
return zeroShortcode, err
|
||||
}
|
||||
tmpl = v
|
||||
hasVariants = hasVariants || len(ofCount) > 1
|
||||
|
|
|
@ -918,7 +918,7 @@ func TestShortcodeMarkdownOutputFormat(t *testing.T) {
|
|||
---
|
||||
title: "p1"
|
||||
---
|
||||
{{< foo >}}
|
||||
{{% foo %}}
|
||||
# The below would have failed using the HTML template parser.
|
||||
-- layouts/shortcodes/foo.md --
|
||||
§§§
|
||||
|
@ -930,9 +930,7 @@ title: "p1"
|
|||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", `
|
||||
<x
|
||||
`)
|
||||
b.AssertFileContent("public/p1/index.html", "<code><x")
|
||||
}
|
||||
|
||||
func TestShortcodePreserveIndentation(t *testing.T) {
|
||||
|
|
|
@ -204,6 +204,7 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
|
|||
// Katex is relatively slow.
|
||||
PoolSize: 8,
|
||||
Infof: logger.InfoCommand("wasm").Logf,
|
||||
Warnf: logger.WarnCommand("wasm").Logf,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
@ -804,7 +805,7 @@ func (s *Site) initRenderFormats() {
|
|||
Tree: s.pageMap.treePages,
|
||||
Handle: func(key string, n contentNodeI, match doctree.DimensionFlag) (bool, error) {
|
||||
if p, ok := n.(*pageState); ok {
|
||||
for _, f := range p.m.configuredOutputFormats {
|
||||
for _, f := range p.m.pageConfig.ConfiguredOutputFormats {
|
||||
if !formatSet[f.Name] {
|
||||
formats = append(formats, f)
|
||||
formatSet[f.Name] = true
|
||||
|
|
|
@ -16,7 +16,6 @@ package hugolib
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
|
@ -89,14 +88,11 @@ aliases: [/Ali%d]
|
|||
h.Sites[1].PathSpec.ProcessingStats,
|
||||
}
|
||||
|
||||
stats[0].Table(io.Discard)
|
||||
stats[1].Table(io.Discard)
|
||||
|
||||
var buff bytes.Buffer
|
||||
|
||||
helpers.ProcessingStatsTable(&buff, stats...)
|
||||
|
||||
c.Assert(buff.String(), qt.Contains, "Pages | 21 | 7")
|
||||
c.Assert(buff.String(), qt.Contains, "Pages │ 21 │ 7")
|
||||
}
|
||||
|
||||
func TestSiteLastmod(t *testing.T) {
|
||||
|
|
|
@ -615,7 +615,7 @@ var weightedPage5 = `+++
|
|||
weight = "5"
|
||||
title = "Five"
|
||||
|
||||
[_build]
|
||||
[build]
|
||||
render = "never"
|
||||
+++
|
||||
Front Matter with Ordered Pages 5`
|
||||
|
|
|
@ -1,7 +1,17 @@
|
|||
# Release env.
|
||||
# These will be replaced by script before release.
|
||||
HUGORELEASER_TAG=v0.146.7
|
||||
HUGORELEASER_COMMITISH=1ad3d39dc4693434505fc81f91eed57333017e93
|
||||
HUGORELEASER_TAG=v0.147.9
|
||||
HUGORELEASER_COMMITISH=29bdbde19c288d190e889294a862103c6efb70bf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -4,6 +4,15 @@ export function readInput(handle) {
|
|||
let currentLine = [];
|
||||
const buffer = new Uint8Array(buffSize);
|
||||
|
||||
// These are not implemented by QuickJS.
|
||||
console.warn = (value) => {
|
||||
console.log(value);
|
||||
};
|
||||
|
||||
console.error = (value) => {
|
||||
throw new Error(value);
|
||||
};
|
||||
|
||||
// Read all the available bytes
|
||||
while (true) {
|
||||
// Stdin file descriptor
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
(()=>{function l(r){let e=[],a=new Uint8Array(1024);for(;;){let n=0;try{n=Javy.IO.readSync(0,a)}catch(o){if(o.message.includes("os error 29"))break;throw new Error("Error reading from stdin")}if(n<0)throw new Error("Error reading from stdin");if(n===0)break;if(e=[...e,...a.subarray(0,n)],!e.includes(10))continue;let t=0;for(let o=0;t<e.length;t++)if(e[t]===10){let w=e.splice(o,t+1),f=new Uint8Array(w),c;try{c=JSON.parse(new TextDecoder().decode(f))}catch(d){throw new Error(`Error parsing JSON '${new TextDecoder().decode(f)}' from stdin: ${d.message}`)}try{r(c)}catch(d){let u=c.header;u.err=d.message,i({header:u})}o=t+1}e=e.slice(t)}}function i(r){let s=new TextEncoder().encode(JSON.stringify(r)+`
|
||||
`),e=new Uint8Array(s);Javy.IO.writeSync(1,e)}var h=function(r){i({header:r.header,data:{greeting:"Hello "+r.data.name+"!"}})};console.log("Greet module loaded");l(h);})();
|
||||
(()=>{function w(r){let e=[],c=new Uint8Array(1024);for(console.warn=n=>{console.log(n)},console.error=n=>{throw new Error(n)};;){let o=0;try{o=Javy.IO.readSync(0,c)}catch(a){if(a.message.includes("os error 29"))break;throw new Error("Error reading from stdin")}if(o<0)throw new Error("Error reading from stdin");if(o===0)break;if(e=[...e,...c.subarray(0,o)],!e.includes(10))continue;let t=0;for(let a=0;t<e.length;t++)if(e[t]===10){let h=e.splice(a,t+1),l=new Uint8Array(h),d;try{d=JSON.parse(new TextDecoder().decode(l))}catch(s){throw new Error(`Error parsing JSON '${new TextDecoder().decode(l)}' from stdin: ${s.message}`)}try{r(d)}catch(s){let u=d.header;u.err=s.message,i({header:u})}a=t+1}e=e.slice(t)}}function i(r){let f=new TextEncoder().encode(JSON.stringify(r)+`
|
||||
`),e=new Uint8Array(f);Javy.IO.writeSync(1,e)}var g=function(r){i({header:r.header,data:{greeting:"Hello "+r.data.name+"!"}})};console.log("Greet module loaded");w(g);})();
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -7,6 +7,16 @@ const render = function (input) {
|
|||
const expression = data.expression;
|
||||
const options = data.options;
|
||||
const header = input.header;
|
||||
header.warnings = [];
|
||||
|
||||
if (options.strict == 'warn') {
|
||||
// By default, KaTeX will write to console.warn, that's a little hard to handle.
|
||||
options.strict = (errorCode, errorMsg) => {
|
||||
header.warnings.push(
|
||||
`katex: LaTeX-incompatible input and strict mode is set to 'warn': ${errorMsg} [${errorCode}]`,
|
||||
);
|
||||
};
|
||||
}
|
||||
// Any error thrown here will be caught by the common.js readInput function.
|
||||
const output = katex.renderToString(expression, options);
|
||||
writeOutput({ header: header, data: { output: output } });
|
||||
|
|
|
@ -45,7 +45,7 @@ type KatexOptions struct {
|
|||
// A color string given in the format "#XXX" or "#XXXXXX"
|
||||
ErrorColor string `json:"errorColor"`
|
||||
|
||||
// A collection of custom macros.
|
||||
// A collection of custom macros.
|
||||
Macros map[string]string `json:"macros,omitempty"`
|
||||
|
||||
// Specifies a minimum thickness, in ems, for fraction lines.
|
||||
|
@ -53,6 +53,22 @@ type KatexOptions struct {
|
|||
|
||||
// If true, KaTeX will throw a ParseError when it encounters an unsupported command.
|
||||
ThrowOnError bool `json:"throwOnError"`
|
||||
|
||||
// Controls how KaTeX handles LaTeX features that offer convenience but
|
||||
// aren't officially supported, one of error (default), ignore, or warn.
|
||||
//
|
||||
// - error: Throws an error when convenient, unsupported LaTeX features
|
||||
// are encountered.
|
||||
// - ignore: Allows convenient, unsupported LaTeX features without any
|
||||
// feedback.
|
||||
// - warn: Emits a warning when convenient, unsupported LaTeX features are
|
||||
// encountered.
|
||||
//
|
||||
// The "newLineInDisplayMode" error code, which flags the use of \\
|
||||
// or \newline in display mode outside an array or tabular environment, is
|
||||
// intentionally designed not to throw an error, despite this behavior
|
||||
// being questionable.
|
||||
Strict string `json:"strict"`
|
||||
}
|
||||
|
||||
type KatexOutput struct {
|
||||
|
|
|
@ -51,6 +51,9 @@ type Header struct {
|
|||
|
||||
// Set in the response if there was an error.
|
||||
Err string `json:"err"`
|
||||
|
||||
// Warnings is a list of warnings that may be returned in the response.
|
||||
Warnings []string `json:"warnings,omitempty"`
|
||||
}
|
||||
|
||||
type Message[T any] struct {
|
||||
|
@ -155,6 +158,7 @@ func (p *dispatcherPool[Q, R]) Execute(ctx context.Context, q Message[Q]) (Messa
|
|||
}
|
||||
|
||||
resp, err := call.response, p.Err()
|
||||
|
||||
if err == nil && resp.Header.Err != "" {
|
||||
err = errors.New(resp.Header.Err)
|
||||
}
|
||||
|
@ -270,6 +274,8 @@ type Options struct {
|
|||
|
||||
Infof func(format string, v ...any)
|
||||
|
||||
Warnf func(format string, v ...any)
|
||||
|
||||
// E.g. quickjs wasm. May be omitted if not needed.
|
||||
Runtime Binary
|
||||
|
||||
|
@ -325,6 +331,7 @@ type dispatcherPool[Q, R any] struct {
|
|||
counter atomic.Uint32
|
||||
dispatchers []*dispatcher[Q, R]
|
||||
close func() error
|
||||
opts Options
|
||||
|
||||
errc chan error
|
||||
donec chan struct{}
|
||||
|
@ -355,6 +362,11 @@ func newDispatcher[Q, R any](opts Options) (*dispatcherPool[Q, R], error) {
|
|||
// noop
|
||||
}
|
||||
}
|
||||
if opts.Warnf == nil {
|
||||
opts.Warnf = func(format string, v ...any) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
if opts.Memory <= 0 {
|
||||
// 32 MiB
|
||||
|
@ -466,6 +478,7 @@ func newDispatcher[Q, R any](opts Options) (*dispatcherPool[Q, R], error) {
|
|||
|
||||
dp := &dispatcherPool[Q, R]{
|
||||
dispatchers: make([]*dispatcher[Q, R], len(inOuts)),
|
||||
opts: opts,
|
||||
|
||||
errc: make(chan error, 10),
|
||||
donec: make(chan struct{}),
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -104,7 +104,7 @@ func (i Item) ValTyped(source []byte) any {
|
|||
}
|
||||
|
||||
func (i Item) IsText() bool {
|
||||
return i.Type == tText || i.Type == tIndentation
|
||||
return i.Type == tText || i.IsIndentation()
|
||||
}
|
||||
|
||||
func (i Item) IsIndentation() bool {
|
||||
|
@ -152,7 +152,7 @@ func (i Item) IsFrontMatter() bool {
|
|||
}
|
||||
|
||||
func (i Item) IsDone() bool {
|
||||
return i.Type == tError || i.Type == tEOF
|
||||
return i.IsError() || i.IsEOF()
|
||||
}
|
||||
|
||||
func (i Item) IsEOF() bool {
|
||||
|
@ -166,18 +166,19 @@ func (i Item) IsError() bool {
|
|||
func (i Item) ToString(source []byte) string {
|
||||
val := i.Val(source)
|
||||
switch {
|
||||
case i.Type == tEOF:
|
||||
case i.IsEOF():
|
||||
return "EOF"
|
||||
case i.Type == tError:
|
||||
case i.IsError():
|
||||
return string(val)
|
||||
case i.Type == tIndentation:
|
||||
case i.IsIndentation():
|
||||
return fmt.Sprintf("%s:[%s]", i.Type, util.VisualizeSpaces(val))
|
||||
case i.Type > tKeywordMarker:
|
||||
return fmt.Sprintf("<%s>", val)
|
||||
case len(val) > 50:
|
||||
return fmt.Sprintf("%v:%.20q...", i.Type, val)
|
||||
default:
|
||||
return fmt.Sprintf("%v:[%s]", i.Type, val)
|
||||
}
|
||||
return fmt.Sprintf("%v:[%s]", i.Type, val)
|
||||
}
|
||||
|
||||
type ItemType int
|
||||
|
|
|
@ -47,3 +47,217 @@ func TestItemValTyped(t *testing.T) {
|
|||
source = []byte("xtrue")
|
||||
c.Assert(Item{low: 0, high: len(source)}.ValTyped(source), qt.Equals, "xtrue")
|
||||
}
|
||||
|
||||
func TestItemBoolMethods(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
source := []byte(" shortcode ")
|
||||
tests := []struct {
|
||||
name string
|
||||
item Item
|
||||
source []byte
|
||||
want bool
|
||||
call func(Item, []byte) bool
|
||||
}{
|
||||
{
|
||||
name: "IsText true",
|
||||
item: Item{Type: tText},
|
||||
call: func(i Item, _ []byte) bool { return i.IsText() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsIndentation false",
|
||||
item: Item{Type: tText},
|
||||
call: func(i Item, _ []byte) bool { return i.IsIndentation() },
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "IsShortcodeName",
|
||||
item: Item{Type: tScName},
|
||||
call: func(i Item, _ []byte) bool { return i.IsShortcodeName() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsNonWhitespace true",
|
||||
item: Item{
|
||||
Type: tText,
|
||||
low: 2,
|
||||
high: 11,
|
||||
},
|
||||
source: source,
|
||||
call: func(i Item, src []byte) bool { return i.IsNonWhitespace(src) },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsShortcodeParam false",
|
||||
item: Item{Type: tScParamVal},
|
||||
call: func(i Item, _ []byte) bool { return i.IsShortcodeParam() },
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "IsInlineShortcodeName",
|
||||
item: Item{Type: tScNameInline},
|
||||
call: func(i Item, _ []byte) bool { return i.IsInlineShortcodeName() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsLeftShortcodeDelim tLeftDelimScWithMarkup",
|
||||
item: Item{Type: tLeftDelimScWithMarkup},
|
||||
call: func(i Item, _ []byte) bool { return i.IsLeftShortcodeDelim() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsLeftShortcodeDelim tLeftDelimScNoMarkup",
|
||||
item: Item{Type: tLeftDelimScNoMarkup},
|
||||
call: func(i Item, _ []byte) bool { return i.IsLeftShortcodeDelim() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsRightShortcodeDelim tRightDelimScWithMarkup",
|
||||
item: Item{Type: tRightDelimScWithMarkup},
|
||||
call: func(i Item, _ []byte) bool { return i.IsRightShortcodeDelim() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsRightShortcodeDelim tRightDelimScNoMarkup",
|
||||
item: Item{Type: tRightDelimScNoMarkup},
|
||||
call: func(i Item, _ []byte) bool { return i.IsRightShortcodeDelim() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsShortcodeClose",
|
||||
item: Item{Type: tScClose},
|
||||
call: func(i Item, _ []byte) bool { return i.IsShortcodeClose() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsShortcodeParamVal",
|
||||
item: Item{Type: tScParamVal},
|
||||
call: func(i Item, _ []byte) bool { return i.IsShortcodeParamVal() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsShortcodeMarkupDelimiter tLeftDelimScWithMarkup",
|
||||
item: Item{Type: tLeftDelimScWithMarkup},
|
||||
call: func(i Item, _ []byte) bool { return i.IsShortcodeMarkupDelimiter() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsShortcodeMarkupDelimiter tRightDelimScWithMarkup",
|
||||
item: Item{Type: tRightDelimScWithMarkup},
|
||||
call: func(i Item, _ []byte) bool { return i.IsShortcodeMarkupDelimiter() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsFrontMatter TypeFrontMatterYAML",
|
||||
item: Item{Type: TypeFrontMatterYAML},
|
||||
call: func(i Item, _ []byte) bool { return i.IsFrontMatter() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsFrontMatter TypeFrontMatterTOML",
|
||||
item: Item{Type: TypeFrontMatterTOML},
|
||||
call: func(i Item, _ []byte) bool { return i.IsFrontMatter() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsFrontMatter TypeFrontMatterJSON",
|
||||
item: Item{Type: TypeFrontMatterJSON},
|
||||
call: func(i Item, _ []byte) bool { return i.IsFrontMatter() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsFrontMatter TypeFrontMatterORG",
|
||||
item: Item{Type: TypeFrontMatterORG},
|
||||
call: func(i Item, _ []byte) bool { return i.IsFrontMatter() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsDone tError",
|
||||
item: Item{Type: tError},
|
||||
call: func(i Item, _ []byte) bool { return i.IsDone() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsDone tEOF",
|
||||
item: Item{Type: tEOF},
|
||||
call: func(i Item, _ []byte) bool { return i.IsDone() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsEOF",
|
||||
item: Item{Type: tEOF},
|
||||
call: func(i Item, _ []byte) bool { return i.IsEOF() },
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "IsError",
|
||||
item: Item{Type: tError},
|
||||
call: func(i Item, _ []byte) bool { return i.IsError() },
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := tt.call(tt.item, tt.source)
|
||||
c.Assert(got, qt.Equals, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestItem_ToString(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
source := []byte("src")
|
||||
long := make([]byte, 100)
|
||||
for i := range long {
|
||||
long[i] = byte(i)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
item Item
|
||||
source []byte
|
||||
want string
|
||||
call func(Item, []byte) string
|
||||
}{
|
||||
{
|
||||
name: "EOF",
|
||||
item: Item{Type: tEOF},
|
||||
call: func(i Item, _ []byte) string { return i.ToString(source) },
|
||||
want: "EOF",
|
||||
},
|
||||
{
|
||||
name: "Error",
|
||||
item: Item{Type: tError},
|
||||
call: func(i Item, _ []byte) string { return i.ToString(source) },
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "Indentation",
|
||||
item: Item{Type: tIndentation},
|
||||
call: func(i Item, _ []byte) string { return i.ToString(source) },
|
||||
want: "tIndentation:[]",
|
||||
},
|
||||
{
|
||||
name: "Long",
|
||||
item: Item{Type: tKeywordMarker + 1, low: 0, high: 100},
|
||||
call: func(i Item, _ []byte) string { return i.ToString(long) },
|
||||
want: "<" + string(long) + ">",
|
||||
},
|
||||
{
|
||||
name: "Empty",
|
||||
item: Item{Type: tKeywordMarker + 1},
|
||||
call: func(i Item, _ []byte) string { return i.ToString([]byte("")) },
|
||||
want: "<>",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := tt.call(tt.item, tt.source)
|
||||
c.Assert(got, qt.Equals, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ package page
|
|||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
|
@ -24,7 +25,6 @@ import (
|
|||
"github.com/gohugoio/hugo/hugofs/glob"
|
||||
"github.com/gohugoio/hugo/resources/kinds"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// A PageMatcher can be used to match a Page with Glob patterns.
|
||||
|
@ -105,9 +105,9 @@ func CheckCascadePattern(logger loggers.Logger, m PageMatcher) {
|
|||
}
|
||||
}
|
||||
|
||||
func DecodeCascadeConfig(logger loggers.Logger, in any) (*config.ConfigNamespace[[]PageMatcherParamsConfig, *maps.Ordered[PageMatcher, maps.Params]], error) {
|
||||
buildConfig := func(in any) (*maps.Ordered[PageMatcher, maps.Params], any, error) {
|
||||
cascade := maps.NewOrdered[PageMatcher, maps.Params]()
|
||||
func DecodeCascadeConfig(logger loggers.Logger, handleLegacyFormat bool, in any) (*config.ConfigNamespace[[]PageMatcherParamsConfig, *maps.Ordered[PageMatcher, PageMatcherParamsConfig]], error) {
|
||||
buildConfig := func(in any) (*maps.Ordered[PageMatcher, PageMatcherParamsConfig], any, error) {
|
||||
cascade := maps.NewOrdered[PageMatcher, PageMatcherParamsConfig]()
|
||||
if in == nil {
|
||||
return cascade, []map[string]any{}, nil
|
||||
}
|
||||
|
@ -120,7 +120,11 @@ func DecodeCascadeConfig(logger loggers.Logger, in any) (*config.ConfigNamespace
|
|||
|
||||
for _, m := range ms {
|
||||
m = maps.CleanConfigStringMap(m)
|
||||
c, err := mapToPageMatcherParamsConfig(m)
|
||||
var (
|
||||
c PageMatcherParamsConfig
|
||||
err error
|
||||
)
|
||||
c, err = mapToPageMatcherParamsConfig(m)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -139,24 +143,29 @@ func DecodeCascadeConfig(logger loggers.Logger, in any) (*config.ConfigNamespace
|
|||
if found {
|
||||
// Merge
|
||||
for k, v := range cfg.Params {
|
||||
if _, found := c[k]; !found {
|
||||
c[k] = v
|
||||
if _, found := c.Params[k]; !found {
|
||||
c.Params[k] = v
|
||||
}
|
||||
}
|
||||
for k, v := range cfg.Fields {
|
||||
if _, found := c.Fields[k]; !found {
|
||||
c.Fields[k] = v
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cascade.Set(m, cfg.Params)
|
||||
cascade.Set(m, cfg)
|
||||
}
|
||||
}
|
||||
|
||||
return cascade, cfgs, nil
|
||||
}
|
||||
|
||||
return config.DecodeNamespace[[]PageMatcherParamsConfig, *maps.Ordered[PageMatcher, maps.Params]](in, buildConfig)
|
||||
return config.DecodeNamespace[[]PageMatcherParamsConfig, *maps.Ordered[PageMatcher, PageMatcherParamsConfig]](in, buildConfig)
|
||||
}
|
||||
|
||||
// DecodeCascade decodes in which could be either a map or a slice of maps.
|
||||
func DecodeCascade(logger loggers.Logger, in any) (*maps.Ordered[PageMatcher, maps.Params], error) {
|
||||
conf, err := DecodeCascadeConfig(logger, in)
|
||||
func DecodeCascade(logger loggers.Logger, handleLegacyFormat bool, in any) (*maps.Ordered[PageMatcher, PageMatcherParamsConfig], error) {
|
||||
conf, err := DecodeCascadeConfig(logger, handleLegacyFormat, in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -165,36 +174,30 @@ func DecodeCascade(logger loggers.Logger, in any) (*maps.Ordered[PageMatcher, ma
|
|||
|
||||
func mapToPageMatcherParamsConfig(m map[string]any) (PageMatcherParamsConfig, error) {
|
||||
var pcfg PageMatcherParamsConfig
|
||||
if pcfg.Fields == nil {
|
||||
pcfg.Fields = make(maps.Params)
|
||||
}
|
||||
for k, v := range m {
|
||||
switch strings.ToLower(k) {
|
||||
case "params":
|
||||
// We simplified the structure of the cascade config in Hugo 0.111.0.
|
||||
// There is a small chance that someone has used the old structure with the params keyword,
|
||||
// those values will now be moved to the top level.
|
||||
// This should be very unlikely as it would lead to constructs like .Params.params.foo,
|
||||
// and most people see params as an Hugo internal keyword.
|
||||
params := maps.ToStringMap(v)
|
||||
if pcfg.Params == nil {
|
||||
pcfg.Params = params
|
||||
} else {
|
||||
for k, v := range params {
|
||||
if _, found := pcfg.Params[k]; !found {
|
||||
pcfg.Params[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
case "_target", "target":
|
||||
var target PageMatcher
|
||||
if err := decodePageMatcher(v, &target); err != nil {
|
||||
return pcfg, err
|
||||
}
|
||||
pcfg.Target = target
|
||||
default:
|
||||
// Legacy config.
|
||||
case "params":
|
||||
if pcfg.Params == nil {
|
||||
pcfg.Params = make(maps.Params)
|
||||
}
|
||||
pcfg.Params[k] = v
|
||||
params := maps.ToStringMap(v)
|
||||
for k, v := range params {
|
||||
if _, found := pcfg.Params[k]; !found {
|
||||
pcfg.Params[k] = v
|
||||
}
|
||||
}
|
||||
default:
|
||||
|
||||
pcfg.Fields[k] = v
|
||||
}
|
||||
}
|
||||
return pcfg, pcfg.init()
|
||||
|
@ -223,10 +226,14 @@ func decodePageMatcher(m any, v *PageMatcher) error {
|
|||
type PageMatcherParamsConfig struct {
|
||||
// Apply Params to all Pages matching Target.
|
||||
Params maps.Params
|
||||
// Fields holds all fields but Params.
|
||||
Fields maps.Params
|
||||
// Target is the PageMatcher that this config applies to.
|
||||
Target PageMatcher
|
||||
}
|
||||
|
||||
func (p *PageMatcherParamsConfig) init() error {
|
||||
maps.PrepareParams(p.Params)
|
||||
maps.PrepareParams(p.Fields)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -88,19 +88,18 @@ func TestPageMatcher(t *testing.T) {
|
|||
c.Assert(err, qt.IsNil)
|
||||
return v
|
||||
}
|
||||
// Legacy.
|
||||
c.Assert(fn(map[string]any{"_target": map[string]any{"kind": "page"}, "foo": "bar"}), qt.DeepEquals, PageMatcherParamsConfig{
|
||||
Params: maps.Params{
|
||||
Fields: maps.Params{
|
||||
"foo": "bar",
|
||||
},
|
||||
Target: PageMatcher{Path: "", Kind: "page", Lang: "", Environment: ""},
|
||||
})
|
||||
|
||||
// Current format.
|
||||
c.Assert(fn(map[string]any{"target": map[string]any{"kind": "page"}, "params": map[string]any{"foo": "bar"}}), qt.DeepEquals, PageMatcherParamsConfig{
|
||||
Params: maps.Params{
|
||||
"foo": "bar",
|
||||
},
|
||||
Fields: maps.Params{},
|
||||
Target: PageMatcher{Path: "", Kind: "page", Lang: "", Environment: ""},
|
||||
})
|
||||
})
|
||||
|
@ -129,21 +128,22 @@ func TestDecodeCascadeConfig(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
got, err := DecodeCascadeConfig(loggers.NewDefault(), in)
|
||||
got, err := DecodeCascadeConfig(loggers.NewDefault(), true, in)
|
||||
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(got, qt.IsNotNil)
|
||||
c.Assert(got.Config.Keys(), qt.DeepEquals, []PageMatcher{{Kind: "page", Environment: "production"}, {Kind: "page"}})
|
||||
c.Assert(got.Config.Values(), qt.DeepEquals, []maps.Params{{"a": string("av")}, {"b": string("bv")}})
|
||||
|
||||
c.Assert(got.SourceStructure, qt.DeepEquals, []PageMatcherParamsConfig{
|
||||
{
|
||||
Params: maps.Params{"a": string("av")},
|
||||
Fields: maps.Params{},
|
||||
Target: PageMatcher{Kind: "page", Environment: "production"},
|
||||
},
|
||||
{Params: maps.Params{"b": string("bv")}, Target: PageMatcher{Kind: "page"}},
|
||||
{Params: maps.Params{"b": string("bv")}, Fields: maps.Params{}, Target: PageMatcher{Kind: "page"}},
|
||||
})
|
||||
|
||||
got, err = DecodeCascadeConfig(loggers.NewDefault(), nil)
|
||||
got, err = DecodeCascadeConfig(loggers.NewDefault(), true, nil)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(got, qt.IsNotNil)
|
||||
}
|
||||
|
|
|
@ -29,9 +29,11 @@ import (
|
|||
"github.com/gohugoio/hugo/hugofs/files"
|
||||
"github.com/gohugoio/hugo/markup"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/gohugoio/hugo/output"
|
||||
"github.com/gohugoio/hugo/resources/kinds"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
|
||||
|
@ -74,35 +76,43 @@ func (d Dates) IsAllDatesZero() bool {
|
|||
return d.Date.IsZero() && d.Lastmod.IsZero() && d.PublishDate.IsZero() && d.ExpiryDate.IsZero()
|
||||
}
|
||||
|
||||
// Page config that needs to be set early. These cannot be modified by cascade.
|
||||
type PageConfigEarly struct {
|
||||
Kind string // The kind of page, e.g. "page", "section", "home" etc. This is usually derived from the content path.
|
||||
Path string // The canonical path to the page, e.g. /sect/mypage. Note: Leading slash, no trailing slash, no extensions or language identifiers.
|
||||
Lang string // The language code for this page. This is usually derived from the module mount or filename.
|
||||
Cascade []map[string]any
|
||||
|
||||
// Content holds the content for this page.
|
||||
Content Source
|
||||
}
|
||||
|
||||
// PageConfig configures a Page, typically from front matter.
|
||||
// Note that all the top level fields are reserved Hugo keywords.
|
||||
// Any custom configuration needs to be set in the Params map.
|
||||
type PageConfig struct {
|
||||
Dates Dates `json:"-"` // Dates holds the four core dates for this page.
|
||||
DatesStrings
|
||||
Title string // The title of the page.
|
||||
LinkTitle string // The link title of the page.
|
||||
Type string // The content type of the page.
|
||||
Layout string // The layout to use for to render this page.
|
||||
Weight int // The weight of the page, used in sorting if set to a non-zero value.
|
||||
Kind string // The kind of page, e.g. "page", "section", "home" etc. This is usually derived from the content path.
|
||||
Path string // The canonical path to the page, e.g. /sect/mypage. Note: Leading slash, no trailing slash, no extensions or language identifiers.
|
||||
Lang string // The language code for this page. This is usually derived from the module mount or filename.
|
||||
URL string // The URL to the rendered page, e.g. /sect/mypage.html.
|
||||
Slug string // The slug for this page.
|
||||
Description string // The description for this page.
|
||||
Summary string // The summary for this page.
|
||||
Draft bool // Whether or not the content is a draft.
|
||||
Headless bool `json:"-"` // Whether or not the page should be rendered.
|
||||
IsCJKLanguage bool // Whether or not the content is in a CJK language.
|
||||
TranslationKey string // The translation key for this page.
|
||||
Keywords []string // The keywords for this page.
|
||||
Aliases []string // The aliases for this page.
|
||||
Outputs []string // The output formats to render this page in. If not set, the site's configured output formats for this page kind will be used.
|
||||
PageConfigEarly `mapstructure:",squash"`
|
||||
Title string // The title of the page.
|
||||
LinkTitle string // The link title of the page.
|
||||
Type string // The content type of the page.
|
||||
Layout string // The layout to use for to render this page.
|
||||
Weight int // The weight of the page, used in sorting if set to a non-zero value.
|
||||
URL string // The URL to the rendered page, e.g. /sect/mypage.html.
|
||||
Slug string // The slug for this page.
|
||||
Description string // The description for this page.
|
||||
Summary string // The summary for this page.
|
||||
Draft bool // Whether or not the content is a draft.
|
||||
Headless bool `json:"-"` // Whether or not the page should be rendered.
|
||||
IsCJKLanguage bool // Whether or not the content is in a CJK language.
|
||||
TranslationKey string // The translation key for this page.
|
||||
Keywords []string // The keywords for this page.
|
||||
Aliases []string // The aliases for this page.
|
||||
Outputs []string // The output formats to render this page in. If not set, the site's configured output formats for this page kind will be used.
|
||||
|
||||
FrontMatterOnlyValues `mapstructure:"-" json:"-"`
|
||||
|
||||
Cascade []map[string]any
|
||||
Sitemap config.SitemapConfig
|
||||
Build BuildConfig
|
||||
Menus any // Can be a string, []string or map[string]any.
|
||||
|
@ -110,13 +120,29 @@ type PageConfig struct {
|
|||
// User defined params.
|
||||
Params maps.Params
|
||||
|
||||
// Content holds the content for this page.
|
||||
Content Source
|
||||
// The raw data from the content adapter.
|
||||
// TODO(bep) clean up the ContentAdapterData vs Params.
|
||||
ContentAdapterData map[string]any `mapstructure:"-" json:"-"`
|
||||
|
||||
// Compiled values.
|
||||
CascadeCompiled *maps.Ordered[page.PageMatcher, maps.Params] `mapstructure:"-" json:"-"`
|
||||
ContentMediaType media.Type `mapstructure:"-" json:"-"`
|
||||
IsFromContentAdapter bool `mapstructure:"-" json:"-"`
|
||||
CascadeCompiled *maps.Ordered[page.PageMatcher, page.PageMatcherParamsConfig] `mapstructure:"-" json:"-"`
|
||||
ContentMediaType media.Type `mapstructure:"-" json:"-"`
|
||||
ConfiguredOutputFormats output.Formats `mapstructure:"-" json:"-"`
|
||||
IsFromContentAdapter bool `mapstructure:"-" json:"-"`
|
||||
}
|
||||
|
||||
func ClonePageConfigForRebuild(p *PageConfig, params map[string]any) *PageConfig {
|
||||
pp := &PageConfig{
|
||||
PageConfigEarly: p.PageConfigEarly,
|
||||
IsFromContentAdapter: p.IsFromContentAdapter,
|
||||
}
|
||||
if pp.IsFromContentAdapter {
|
||||
pp.ContentAdapterData = params
|
||||
} else {
|
||||
pp.Params = params
|
||||
}
|
||||
|
||||
return pp
|
||||
}
|
||||
|
||||
var DefaultPageConfig = PageConfig{
|
||||
|
@ -149,8 +175,7 @@ func (p *PageConfig) Validate(pagesFromData bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Compile sets up the page configuration after all fields have been set.
|
||||
func (p *PageConfig) Compile(basePath string, pagesFromData bool, ext string, logger loggers.Logger, mediaTypes media.Types) error {
|
||||
func (p *PageConfig) CompileForPagesFromDataPre(basePath string, logger loggers.Logger, mediaTypes media.Types) error {
|
||||
// In content adapters, we always get relative paths.
|
||||
if basePath != "" {
|
||||
p.Path = path.Join(basePath, p.Path)
|
||||
|
@ -158,12 +183,32 @@ func (p *PageConfig) Compile(basePath string, pagesFromData bool, ext string, lo
|
|||
|
||||
if p.Params == nil {
|
||||
p.Params = make(maps.Params)
|
||||
} else if pagesFromData {
|
||||
p.Params = maps.PrepareParamsClone(p.Params)
|
||||
} else {
|
||||
maps.PrepareParams(p.Params)
|
||||
p.Params = maps.PrepareParamsClone(p.Params)
|
||||
}
|
||||
|
||||
if p.Kind == "" {
|
||||
p.Kind = kinds.KindPage
|
||||
}
|
||||
|
||||
if p.Cascade != nil {
|
||||
cascade, err := page.DecodeCascade(logger, false, p.Cascade)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode cascade: %w", err)
|
||||
}
|
||||
p.CascadeCompiled = cascade
|
||||
}
|
||||
|
||||
// Note that NormalizePathStringBasic will make sure that we don't preserve the unnormalized path.
|
||||
// We do that when we create pages from the file system; mostly for backward compatibility,
|
||||
// but also because people tend to use use the filename to name their resources (with spaces and all),
|
||||
// and this isn't relevant when creating resources from an API where it's easy to add textual meta data.
|
||||
p.Path = paths.NormalizePathStringBasic(p.Path)
|
||||
|
||||
return p.compilePrePost("", mediaTypes)
|
||||
}
|
||||
|
||||
func (p *PageConfig) compilePrePost(ext string, mediaTypes media.Types) error {
|
||||
if p.Content.Markup == "" && p.Content.MediaType == "" {
|
||||
if ext == "" {
|
||||
ext = "md"
|
||||
|
@ -194,25 +239,37 @@ func (p *PageConfig) Compile(basePath string, pagesFromData bool, ext string, lo
|
|||
if p.Content.Markup == "" {
|
||||
p.Content.Markup = p.ContentMediaType.SubType
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if pagesFromData {
|
||||
if p.Kind == "" {
|
||||
p.Kind = kinds.KindPage
|
||||
// Compile sets up the page configuration after all fields have been set.
|
||||
func (p *PageConfig) Compile(ext string, logger loggers.Logger, outputFormats output.Formats, mediaTypes media.Types) error {
|
||||
if p.IsFromContentAdapter {
|
||||
if err := mapstructure.WeakDecode(p.ContentAdapterData, p); err != nil {
|
||||
err = fmt.Errorf("failed to decode page map: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Note that NormalizePathStringBasic will make sure that we don't preserve the unnormalized path.
|
||||
// We do that when we create pages from the file system; mostly for backward compatibility,
|
||||
// but also because people tend to use use the filename to name their resources (with spaces and all),
|
||||
// and this isn't relevant when creating resources from an API where it's easy to add textual meta data.
|
||||
p.Path = paths.NormalizePathStringBasic(p.Path)
|
||||
// Not needed anymore.
|
||||
p.ContentAdapterData = nil
|
||||
}
|
||||
|
||||
if p.Cascade != nil {
|
||||
cascade, err := page.DecodeCascade(logger, p.Cascade)
|
||||
if p.Params == nil {
|
||||
p.Params = make(maps.Params)
|
||||
} else {
|
||||
maps.PrepareParams(p.Params)
|
||||
}
|
||||
|
||||
if err := p.compilePrePost(ext, mediaTypes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(p.Outputs) > 0 {
|
||||
outFormats, err := outputFormats.GetByNames(p.Outputs...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode cascade: %w", err)
|
||||
return fmt.Errorf("failed to resolve output formats %v: %w", p.Outputs, err)
|
||||
} else {
|
||||
p.ConfiguredOutputFormats = outFormats
|
||||
}
|
||||
p.CascadeCompiled = cascade
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/config/testconfig"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/gohugoio/hugo/output"
|
||||
|
||||
"github.com/gohugoio/hugo/resources/page/pagemeta"
|
||||
|
||||
|
@ -175,7 +176,7 @@ func TestContentMediaTypeFromMarkup(t *testing.T) {
|
|||
} {
|
||||
var pc pagemeta.PageConfig
|
||||
pc.Content.Markup = test.in
|
||||
c.Assert(pc.Compile("", true, "", logger, media.DefaultTypes), qt.IsNil)
|
||||
c.Assert(pc.Compile("", logger, output.DefaultFormats, media.DefaultTypes), qt.IsNil)
|
||||
c.Assert(pc.ContentMediaType.Type, qt.Equals, test.expected)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ func TestDecodeBuildConfig(t *testing.T) {
|
|||
c := qt.New(t)
|
||||
|
||||
configTempl := `
|
||||
[_build]
|
||||
[build]
|
||||
render = %s
|
||||
list = %s
|
||||
publishResources = true`
|
||||
|
@ -82,7 +82,7 @@ publishResources = true`
|
|||
} {
|
||||
cfg, err := config.FromConfigString(fmt.Sprintf(configTempl, test.args...), "toml")
|
||||
c.Assert(err, qt.IsNil)
|
||||
bcfg, err := DecodeBuildConfig(cfg.Get("_build"))
|
||||
bcfg, err := DecodeBuildConfig(cfg.Get("build"))
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
eq := qt.CmpEquals(hqt.DeepAllowUnexported(BuildConfig{}))
|
||||
|
|
|
@ -311,7 +311,7 @@ func (l PermalinkExpander) pageToPermalinkSections(p Page, _ string) (string, er
|
|||
|
||||
// pageToPermalinkContentBaseName returns the URL-safe form of the content base name.
|
||||
func (l PermalinkExpander) pageToPermalinkContentBaseName(p Page, _ string) (string, error) {
|
||||
return l.urlize(p.PathInfo().BaseNameNoIdentifier()), nil
|
||||
return l.urlize(p.PathInfo().Unnormalized().BaseNameNoIdentifier()), nil
|
||||
}
|
||||
|
||||
// pageToPermalinkSlugOrContentBaseName returns the URL-safe form of the slug, content base name.
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
package page_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/bep/logg"
|
||||
|
@ -343,3 +344,29 @@ slug: "c2slug"
|
|||
b.AssertFileContent("public/myc/c1/index.html", "C1|/myc/c1/|term|")
|
||||
b.AssertFileContent("public/myc/c2slug/index.html", "C2|/myc/c2slug/|term|")
|
||||
}
|
||||
|
||||
func TestIssue13755(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
|
||||
disablePathToLower = false
|
||||
[permalinks.page]
|
||||
s1 = "/:contentbasename"
|
||||
-- content/s1/aBc.md --
|
||||
---
|
||||
title: aBc
|
||||
---
|
||||
-- layouts/all.html --
|
||||
{{ .Title }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
b.AssertFileExists("public/abc/index.html", true)
|
||||
|
||||
files = strings.ReplaceAll(files, "disablePathToLower = false", "disablePathToLower = true")
|
||||
|
||||
b = hugolib.Test(t, files)
|
||||
b.AssertFileExists("public/aBc/index.html", true)
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"sync/atomic"
|
||||
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/lazy"
|
||||
"github.com/gohugoio/hugo/resources/internal"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hashing"
|
||||
|
@ -54,6 +55,7 @@ var (
|
|||
_ identity.DependencyManagerProvider = (*genericResource)(nil)
|
||||
_ identity.Identity = (*genericResource)(nil)
|
||||
_ fileInfo = (*genericResource)(nil)
|
||||
_ isPublishedProvider = (*genericResource)(nil)
|
||||
)
|
||||
|
||||
type ResourceSourceDescriptor struct {
|
||||
|
@ -242,6 +244,7 @@ type baseResourceInternal interface {
|
|||
fileInfo
|
||||
mediaTypeAssigner
|
||||
targetPather
|
||||
isPublishedProvider
|
||||
|
||||
ReadSeekCloser() (hugio.ReadSeekCloser, error)
|
||||
|
||||
|
@ -355,7 +358,7 @@ func GetTestInfoForResource(r resource.Resource) GenericResourceTestInfo {
|
|||
|
||||
// genericResource represents a generic linkable resource.
|
||||
type genericResource struct {
|
||||
publishInit *sync.Once
|
||||
publishInit *lazy.OnceMore
|
||||
|
||||
key string
|
||||
keyInit *sync.Once
|
||||
|
@ -536,6 +539,10 @@ func (l *genericResource) Publish() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (l *genericResource) isPublished() bool {
|
||||
return l.publishInit.Done()
|
||||
}
|
||||
|
||||
func (l *genericResource) RelPermalink() string {
|
||||
return l.spec.PathSpec.GetBasePath(false) + paths.PathEscape(l.paths.TargetLink())
|
||||
}
|
||||
|
@ -629,7 +636,7 @@ func (rc *genericResource) cloneWithUpdates(u *transformationUpdate) (baseResour
|
|||
}
|
||||
|
||||
func (l genericResource) clone() *genericResource {
|
||||
l.publishInit = &sync.Once{}
|
||||
l.publishInit = &lazy.OnceMore{}
|
||||
l.keyInit = &sync.Once{}
|
||||
return &l
|
||||
}
|
||||
|
@ -643,6 +650,10 @@ type targetPather interface {
|
|||
TargetPath() string
|
||||
}
|
||||
|
||||
type isPublishedProvider interface {
|
||||
isPublished() bool
|
||||
}
|
||||
|
||||
type resourceHash struct {
|
||||
value uint64
|
||||
size int64
|
||||
|
@ -702,6 +713,11 @@ func InternalResourceSourcePathBestEffort(r resource.Resource) string {
|
|||
return InternalResourceTargetPath(r)
|
||||
}
|
||||
|
||||
// isPublished returns true if the resource is published.
|
||||
func IsPublished(r resource.Resource) bool {
|
||||
return r.(isPublishedProvider).isPublished()
|
||||
}
|
||||
|
||||
type targetPathProvider interface {
|
||||
// targetPath is the relative path to this resource.
|
||||
// In most cases this will be the same as the RelPermalink(),
|
||||
|
|
|
@ -81,11 +81,6 @@ type ResourceWithoutMeta interface {
|
|||
ResourceDataProvider
|
||||
}
|
||||
|
||||
type ResourceWrapper interface {
|
||||
UnwrappedResource() Resource
|
||||
WrapResource(Resource) ResourceWrapper
|
||||
}
|
||||
|
||||
type ResourceTypeProvider interface {
|
||||
// ResourceType is the resource type. For most file types, this is the main
|
||||
// part of the MIME type, e.g. "image", "application", "text" etc.
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/config/allconfig"
|
||||
"github.com/gohugoio/hugo/lazy"
|
||||
"github.com/gohugoio/hugo/output"
|
||||
"github.com/gohugoio/hugo/resources/internal"
|
||||
"github.com/gohugoio/hugo/resources/jsconfig"
|
||||
|
@ -189,7 +190,7 @@ func (r *Spec) NewResource(rd ResourceSourceDescriptor) (resource.Resource, erro
|
|||
gr := &genericResource{
|
||||
Staler: &AtomicStaler{},
|
||||
h: &resourceHash{},
|
||||
publishInit: &sync.Once{},
|
||||
publishInit: &lazy.OnceMore{},
|
||||
keyInit: &sync.Once{},
|
||||
includeHashInKey: isImage,
|
||||
paths: rp,
|
||||
|
|
|
@ -69,7 +69,6 @@ func (c *PostCSSClient) Process(res resources.ResourceTransformer, options map[s
|
|||
}
|
||||
|
||||
type InlineImports struct {
|
||||
// Service `mapstructure:",squash"`
|
||||
// Enable inlining of @import statements.
|
||||
// Does so recursively, but currently once only per file;
|
||||
// that is, it's not possible to import the same file in
|
||||
|
@ -78,6 +77,11 @@ type InlineImports struct {
|
|||
// so you can have @import anywhere in the file.
|
||||
InlineImports bool
|
||||
|
||||
// See issue https://github.com/gohugoio/hugo/issues/13719
|
||||
// Disable inlining of @import statements
|
||||
// This is currenty only used for css.TailwindCSS.
|
||||
DisableInlineImports bool
|
||||
|
||||
// When InlineImports is enabled, we fail the build if an import cannot be resolved.
|
||||
// You can enable this to allow the build to continue and leave the import statement in place.
|
||||
// Note that the inline importer does not process url location or imports with media queries,
|
||||
|
|
|
@ -129,9 +129,11 @@ func (t *tailwindcssTransformation) Transform(ctx *resources.ResourceTransformat
|
|||
t.rs.Assets.Fs, t.rs.Logger, ctx.DependencyManager,
|
||||
)
|
||||
|
||||
src, err = imp.resolve()
|
||||
if err != nil {
|
||||
return err
|
||||
if !options.InlineImports.DisableInlineImports {
|
||||
src, err = imp.resolve()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
|
@ -146,7 +148,11 @@ func (t *tailwindcssTransformation) Transform(ctx *resources.ResourceTransformat
|
|||
Cause: err,
|
||||
}
|
||||
}
|
||||
return imp.toFileError(errBuf.String())
|
||||
s := errBuf.String()
|
||||
if options.InlineImports.DisableInlineImports && strings.Contains(s, "Can't resolve") {
|
||||
s += "You may want to set the 'disableInlineImports' option to false to inline imports, see https://gohugo.io/functions/css/tailwindcss/#disableinlineimports"
|
||||
}
|
||||
return imp.toFileError(s)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/bep/logg"
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/htesting"
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
)
|
||||
|
@ -70,3 +71,66 @@ CSS: {{ $css.Content | safeCSS }}|
|
|||
|
||||
b.AssertFileContent("public/index.html", "/*! tailwindcss v4.")
|
||||
}
|
||||
|
||||
func TestTailwindCSSNoInlineImportsIssue13719(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
||||
theme = 'my-theme'
|
||||
|
||||
[[module.mounts]]
|
||||
source = 'assets'
|
||||
target = 'assets'
|
||||
|
||||
[[module.mounts]]
|
||||
source = 'other'
|
||||
target = 'assets/css'
|
||||
-- assets/css/main.css --
|
||||
@import "tailwindcss";
|
||||
|
||||
@import "colors/red.css";
|
||||
@import "colors/blue.css";
|
||||
@import "colors/purple.css";
|
||||
-- assets/css/colors/red.css --
|
||||
@import "green.css";
|
||||
|
||||
.red {color: red;}
|
||||
-- assets/css/colors/green.css --
|
||||
.green {color: green;}
|
||||
-- themes/my-theme/assets/css/colors/blue.css --
|
||||
.blue {color: blue;}
|
||||
-- other/colors/purple.css --
|
||||
.purple {color: purple;}
|
||||
-- layouts/home.html --
|
||||
{{ with (templates.Defer (dict "key" "global")) }}
|
||||
{{ with resources.Get "css/main.css" }}
|
||||
{{ $opts := dict "disableInlineImports" true }}
|
||||
{{ with . | css.TailwindCSS $opts }}
|
||||
<link rel="stylesheet" href="{{ .RelPermalink }}">
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
-- package.json --
|
||||
{
|
||||
"devDependencies": {
|
||||
"@tailwindcss/cli": "^4.1.7",
|
||||
"tailwindcss": "^4.1.7"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
b, err := hugolib.NewIntegrationTestBuilder(
|
||||
hugolib.IntegrationTestConfig{
|
||||
T: t,
|
||||
TxtarString: files,
|
||||
NeedsOsFS: true,
|
||||
NeedsNpmInstall: true,
|
||||
LogLevel: logg.LevelInfo,
|
||||
}).BuildE()
|
||||
|
||||
b.Assert(err, qt.IsNotNil)
|
||||
b.Assert(err.Error(), qt.Contains, "Can't resolve 'colors/red.css'")
|
||||
b.Assert(err.Error(), qt.Contains, "You may want to set the 'disableInlineImports' option to false")
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ var (
|
|||
_ identity.DependencyManagerProvider = (*resourceAdapter)(nil)
|
||||
_ identity.IdentityGroupProvider = (*resourceAdapter)(nil)
|
||||
_ resource.NameNormalizedProvider = (*resourceAdapter)(nil)
|
||||
_ isPublishedProvider = (*resourceAdapter)(nil)
|
||||
)
|
||||
|
||||
// These are transformations that need special support in Hugo that may not
|
||||
|
@ -325,6 +326,11 @@ func (r *resourceAdapter) Publish() error {
|
|||
return r.target.Publish()
|
||||
}
|
||||
|
||||
func (r *resourceAdapter) isPublished() bool {
|
||||
r.init(false, false)
|
||||
return r.target.isPublished()
|
||||
}
|
||||
|
||||
func (r *resourceAdapter) ReadSeekCloser() (hugio.ReadSeekCloser, error) {
|
||||
r.init(false, false)
|
||||
return r.target.ReadSeekCloser()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Test the new command.
|
||||
|
||||
hugo new site -h
|
||||
stdout 'Create a new site in the provided directory'
|
||||
stdout 'Create a new site at the specified path.'
|
||||
hugo new site my-yaml-site --format yml
|
||||
checkfile my-yaml-site/hugo.yml
|
||||
hugo new site mysite -f
|
||||
|
@ -19,7 +19,7 @@ exists themes
|
|||
! exists resources
|
||||
|
||||
hugo new theme -h
|
||||
stdout 'Create a new theme \(skeleton\) called \[name\] in ./themes'
|
||||
stdout 'Create a new theme with the specified name in the ./themes directory.'
|
||||
hugo new theme mytheme --format yml
|
||||
stdout 'Creating new theme'
|
||||
! exists resources
|
||||
|
|
|
@ -15,6 +15,7 @@ package template
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"iter"
|
||||
|
||||
"github.com/gohugoio/hugo/common/types"
|
||||
template "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
|
||||
|
@ -38,6 +39,19 @@ func (t *Template) Prepare() (*template.Template, error) {
|
|||
return t.text, nil
|
||||
}
|
||||
|
||||
func (t *Template) All() iter.Seq[*Template] {
|
||||
return func(yield func(t *Template) bool) {
|
||||
ns := t.nameSpace
|
||||
ns.mu.Lock()
|
||||
defer ns.mu.Unlock()
|
||||
for _, v := range ns.set {
|
||||
if !yield(v) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See https://github.com/golang/go/issues/5884
|
||||
func StripTags(html string) string {
|
||||
return stripTags(html)
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"iter"
|
||||
"reflect"
|
||||
|
||||
"github.com/gohugoio/hugo/common/herrors"
|
||||
|
@ -433,3 +434,18 @@ func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node
|
|||
func isTrue(val reflect.Value) (truth, ok bool) {
|
||||
return hreflect.IsTruthfulValue(val), true
|
||||
}
|
||||
|
||||
func (t *Template) All() iter.Seq[*Template] {
|
||||
return func(yield func(t *Template) bool) {
|
||||
if t.common == nil {
|
||||
return
|
||||
}
|
||||
t.muTmpl.RLock()
|
||||
defer t.muTmpl.RUnlock()
|
||||
for _, v := range t.tmpl {
|
||||
if !yield(v) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -533,7 +533,7 @@ func (t *Tree) parseControl(context string) (pos Pos, line int, pipe *PipeNode,
|
|||
t.rangeDepth--
|
||||
}
|
||||
switch next.Type() {
|
||||
case nodeEnd: //done
|
||||
case nodeEnd: // done
|
||||
case nodeElse:
|
||||
// Special case for "else if" and "else with".
|
||||
// If the "else" is followed immediately by an "if" or "with",
|
||||
|
|
|
@ -115,6 +115,13 @@ func init() {
|
|||
},
|
||||
)
|
||||
|
||||
ns.AddMethodMapping(ctx.MaxInt64,
|
||||
nil,
|
||||
[][2]string{
|
||||
{"{{ math.MaxInt64 }}", "9223372036854775807"},
|
||||
},
|
||||
)
|
||||
|
||||
ns.AddMethodMapping(ctx.Min,
|
||||
nil,
|
||||
[][2]string{
|
||||
|
|
|
@ -147,6 +147,11 @@ func (ns *Namespace) Max(inputs ...any) (maximum float64, err error) {
|
|||
return ns.applyOpToScalarsOrSlices("Max", math.Max, inputs...)
|
||||
}
|
||||
|
||||
// MaxInt64 returns the maximum value for a signed 64-bit integer.
|
||||
func (ns *Namespace) MaxInt64() int64 {
|
||||
return math.MaxInt64
|
||||
}
|
||||
|
||||
// Min returns the smaller of all numbers in inputs. Any slices in inputs are flattened.
|
||||
func (ns *Namespace) Min(inputs ...any) (minimum float64, err error) {
|
||||
return ns.applyOpToScalarsOrSlices("Min", math.Min, inputs...)
|
||||
|
|
|
@ -258,7 +258,7 @@ func TestMod(t *testing.T) {
|
|||
{int32(3), int32(2), int64(1)},
|
||||
{int64(3), int64(2), int64(1)},
|
||||
{"3", "2", int64(1)},
|
||||
{"3.1", "2", false},
|
||||
{"3.1", "2", int64(1)},
|
||||
{"aaa", "0", false},
|
||||
{"3", "aaa", false},
|
||||
} {
|
||||
|
@ -304,7 +304,7 @@ func TestModBool(t *testing.T) {
|
|||
{int64(3), int64(2), false},
|
||||
{"3", "3", true},
|
||||
{"3", "2", false},
|
||||
{"3.1", "2", nil},
|
||||
{"3.1", "2", false},
|
||||
{"aaa", "0", nil},
|
||||
{"3", "aaa", nil},
|
||||
} {
|
||||
|
@ -879,3 +879,14 @@ func TestToRadians(t *testing.T) {
|
|||
c.Assert(result, qt.Equals, test.expect)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxInt64(t *testing.T) {
|
||||
t.Parallel()
|
||||
ns := New(nil)
|
||||
|
||||
var want int64 = 9223372036854775807
|
||||
got := ns.MaxInt64()
|
||||
if want != got {
|
||||
t.Errorf("want %d, got %d", want, got)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -133,7 +133,7 @@ func (ns *Namespace) lookup(name string) (*tplimpl.TemplInfo, error) {
|
|||
if strings.HasPrefix(name, "partials/") {
|
||||
// This is most likely not what the user intended.
|
||||
// This worked before Hugo 0.146.0.
|
||||
ns.deps.Log.Warnidf(constants.WarnPartialSuperfluousPrefix, "Partial name %q starting with 'partials/' (as in {{ partial \"%s\"}}) is most likely not what you want. Before 0.146.0 we did a double lookup in this situation.", name, name)
|
||||
ns.deps.Log.Warnidf(constants.WarnPartialSuperfluousPrefix, "Doubtful use of partial function in {{ partial \"%s\"}}), this is most likely not what you want. Consider removing superfluous prefix \"partials/\" from template name given as first function argument.", name)
|
||||
}
|
||||
v := ns.deps.TemplateStore.LookupPartial(name)
|
||||
if v == nil {
|
||||
|
|
|
@ -299,3 +299,19 @@ P2.
|
|||
b := hugolib.Test(t, files)
|
||||
b.AssertFileContent("public/index.html", "P1: P1.\nP2: foo bar")
|
||||
}
|
||||
|
||||
func TestTemplateExistsCaseIssue13684(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
-- layouts/home.html --
|
||||
P1: {{ templates.Exists "_partials/MyPartial.html" }}|P1: {{ templates.Exists "_partials/mypartial.html" }}|
|
||||
-- layouts/_partials/MyPartial.html --
|
||||
MyPartial.
|
||||
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
b.AssertFileContent("public/index.html", "P1: true|P1: true|")
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{{- $pc := site.Config.Privacy.Twitter -}}
|
||||
{{- if not $pc.Disable -}}
|
||||
{{- if $pc.Simple -}}
|
||||
{{- template "_internal/shortcodes/twitter_simple.html" . -}}
|
||||
{{- template "_shortcodes/twitter_simple.html" . -}}
|
||||
{{- else -}}
|
||||
{{- $id := or (.Get "id") "" -}}
|
||||
{{- $user := or (.Get "user") "" -}}
|
||||
|
@ -20,7 +20,7 @@
|
|||
{{- $request := printf "https://publish.twitter.com/oembed?%s" $query -}}
|
||||
{{- with try (resources.GetRemote $request) -}}
|
||||
{{- with .Err -}}
|
||||
{{- errorf "%s" . -}}
|
||||
{{- warnidf "shortcode-twitter-getremote" "The %q shortcode was unable to retrieve the remote data: %s. See %s" $.ctx.Name . $.ctx.Position -}}
|
||||
{{- else with .Value -}}
|
||||
{{- (. | transform.Unmarshal).html | safeHTML -}}
|
||||
{{- else -}}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
{{- $request := printf "https://publish.twitter.com/oembed?%s" $query -}}
|
||||
{{- with try (resources.GetRemote $request) -}}
|
||||
{{- with .Err -}}
|
||||
{{- errorf "%s" . -}}
|
||||
{{- warnidf "shortcode-twitter-simple-getremote" "The %q shortcode was unable to retrieve the remote data: %s. See %s" $.ctx.Name . $.ctx.Position -}}
|
||||
{{- else with .Value -}}
|
||||
{{- if not site.Config.Services.Twitter.DisableInlineCSS }}
|
||||
{{- template "__h_simple_twitter_css" (dict "ctx" $.ctx) }}
|
||||
|
|
|
@ -18,7 +18,7 @@ title, then loading.
|
|||
{{- $pc := site.Config.Privacy.Vimeo }}
|
||||
{{- if not $pc.Disable }}
|
||||
{{- if $pc.Simple }}
|
||||
{{- template "_internal/shortcodes/vimeo_simple.html" . }}
|
||||
{{- template "_shortcodes/vimeo_simple.html" . }}
|
||||
{{- else }}
|
||||
{{- $dnt := cond $pc.EnableDNT 1 0 }}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
{{- $request := printf "https://vimeo.com/api/oembed.json?%s" $query -}}
|
||||
{{- with try (resources.GetRemote $request) -}}
|
||||
{{- with .Err -}}
|
||||
{{- errorf "%s" . -}}
|
||||
{{- warnidf "shortcode-vimeo-simple" "The %q shortcode was unable to retrieve the remote data: %s. See %s" $.ctx.Name . $.ctx.Position -}}
|
||||
{{- else with .Value -}}
|
||||
{{- with . | transform.Unmarshal -}}
|
||||
{{- $class := printf "%s %s" "s_video_simple" "__h_video" -}}
|
||||
|
@ -37,7 +37,7 @@
|
|||
{{- $thumbnail := .thumbnail_url -}}
|
||||
{{- $original := $thumbnail | replaceRE "(_.*\\.)" "." -}}
|
||||
<div class="{{ $class }}">
|
||||
<a href="{{ .provider_url }}{{ .video_id }}" rel="noopener" target="_blank">
|
||||
<a href="{{ .provider_url }}{{ .video_id | string }}" rel="noopener" target="_blank">
|
||||
<img src="{{ $thumbnail }}" srcset="{{ $thumbnail }} 1x, {{ $original }} 2x" alt="{{ .title }}">
|
||||
<div class="play">
|
||||
{{ template "__h_simple_icon_play" $.ctx.Page }}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{- $pc := site.Config.Privacy.X -}}
|
||||
{{- if not $pc.Disable -}}
|
||||
{{- if $pc.Simple -}}
|
||||
{{- template "_internal/shortcodes/x_simple.html" . -}}
|
||||
{{- template "_shortcodes/x_simple.html" . -}}
|
||||
{{- else -}}
|
||||
{{- $id := or (.Get "id") "" -}}
|
||||
{{- $user := or (.Get "user") "" -}}
|
||||
|
@ -19,7 +19,7 @@
|
|||
{{- $request := printf "https://publish.x.com/oembed?%s" $query -}}
|
||||
{{- with try (resources.GetRemote $request) -}}
|
||||
{{- with .Err -}}
|
||||
{{- errorf "%s" . -}}
|
||||
{{- warnidf "shortcode-x-getremote" "The %q shortcode was unable to retrieve the remote data: %s. See %s" $.ctx.Name . $.ctx.Position -}}
|
||||
{{- else with .Value -}}
|
||||
{{- (. | transform.Unmarshal).html | safeHTML -}}
|
||||
{{- else -}}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
{{- $request := printf "https://publish.x.com/oembed?%s" $query -}}
|
||||
{{- with try (resources.GetRemote $request) -}}
|
||||
{{- with .Err -}}
|
||||
{{- errorf "%s" . -}}
|
||||
{{- warnidf "shortcode-x-simple-getremote" "The %q shortcode was unable to retrieve the remote data: %s. See %s" $.ctx.Name . $.ctx.Position -}}
|
||||
{{- else with .Value -}}
|
||||
{{- if not site.Config.Services.X.DisableInlineCSS }}
|
||||
{{- template "__h_simple_x_css" (dict "ctx" $.ctx) }}
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/htesting/hqt"
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
)
|
||||
|
@ -495,7 +496,7 @@ Content: {{ .Content }}
|
|||
// Simple mode
|
||||
files = strings.ReplaceAll(files, "privacy.vimeo.simple = false", "privacy.vimeo.simple = true")
|
||||
b = hugolib.Test(t, files)
|
||||
b.AssertFileContent("public/p1/index.html", "c5bf16d87e2a370b")
|
||||
b.AssertFileContent("public/p1/index.html", "04d861fc957ee638")
|
||||
|
||||
// Simple mode with non-existent id
|
||||
files = strings.ReplaceAll(files, "{{< vimeo 55073825 >}}", "{{< vimeo __id_does_not_exist__ >}}")
|
||||
|
@ -696,3 +697,100 @@ title: p2
|
|||
b.AssertFileContent("public/p1/index.html", "78eb19b5c6f3768f")
|
||||
b.AssertFileContent("public/p2/index.html", "a6db910a9cf54bc1")
|
||||
}
|
||||
|
||||
func TestShortcodePlainTextVsHTMLTemplateIssue13698(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
filesTemplate := `
|
||||
-- hugo.toml --
|
||||
markup.goldmark.renderer.unsafe = true
|
||||
-- layouts/all.html --
|
||||
Content: {{ .Content }}|
|
||||
-- layouts/_shortcodes/mymarkdown.md --
|
||||
<div>Foo bar</div>
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: p1
|
||||
---
|
||||
## A shortcode
|
||||
|
||||
SHORTCODE
|
||||
|
||||
`
|
||||
|
||||
files := strings.ReplaceAll(filesTemplate, "SHORTCODE", "{{% mymarkdown %}}")
|
||||
b := hugolib.Test(t, files)
|
||||
b.AssertFileContent("public/p1/index.html", "<div>Foo bar</div>")
|
||||
|
||||
files = strings.ReplaceAll(filesTemplate, "SHORTCODE", "{{< mymarkdown >}}")
|
||||
|
||||
var err error
|
||||
b, err = hugolib.TestE(t, files)
|
||||
b.Assert(err, qt.IsNotNil)
|
||||
b.Assert(err.Error(), qt.Contains, `no compatible template found for shortcode "mymarkdown" in [/_shortcodes/mymarkdown.md]; note that to use plain text template shortcodes in HTML you need to use the shortcode {{% delimiter`)
|
||||
}
|
||||
|
||||
func TestShortcodeOnlyLanguageInBaseIssue13699And13740(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = 'https://example.org/'
|
||||
disableLanguages = ['de']
|
||||
[languages]
|
||||
[languages.en]
|
||||
weight = 1
|
||||
[languages.de]
|
||||
weight = 2
|
||||
-- layouts/_shortcodes/de.html --
|
||||
de.html
|
||||
-- layouts/all.html --
|
||||
{{ .Content }}
|
||||
-- content/_index.md --
|
||||
---
|
||||
title: home
|
||||
---
|
||||
{{< de >}}
|
||||
|
||||
`
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "de.html")
|
||||
}
|
||||
|
||||
func TestShortcodeLanguage13767(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
defaultContentLanguage = 'pl'
|
||||
defaultContentLanguageInSubdir = true
|
||||
[languages.pl]
|
||||
weight = 1
|
||||
[languages.en]
|
||||
weight = 2
|
||||
-- content/_index.md --
|
||||
---
|
||||
title: dom
|
||||
---
|
||||
{{< myshortcode >}}
|
||||
-- content/_index.en.md --
|
||||
---
|
||||
title: home
|
||||
---
|
||||
{{< myshortcode >}}
|
||||
-- layouts/_shortcodes/myshortcode.html --
|
||||
myshortcode.html
|
||||
-- layouts/_shortcodes/myshortcode.en.html --
|
||||
myshortcode.en.html
|
||||
-- layouts/all.html --
|
||||
{{ .Content }}
|
||||
|
||||
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/pl/index.html", "myshortcode.html")
|
||||
b.AssertFileContent("public/en/index.html", "myshortcode.en.html")
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ type TemplateDescriptor struct {
|
|||
// Misc.
|
||||
LayoutFromUserMustMatch bool // If set, we only look for the exact layout.
|
||||
IsPlainText bool // Whether this is a plain text template.
|
||||
AlwaysAllowPlainText bool // Whether to e.g. allow plain text templates to be rendered in HTML.
|
||||
}
|
||||
|
||||
func (d *TemplateDescriptor) normalizeFromFile() {
|
||||
|
@ -64,7 +65,7 @@ func (s descriptorHandler) compareDescriptors(category Category, isEmbedded bool
|
|||
return weightNoMatch
|
||||
}
|
||||
|
||||
w := this.doCompare(category, isEmbedded, s.opts.DefaultContentLanguage, other)
|
||||
w := this.doCompare(category, s.opts.DefaultContentLanguage, other)
|
||||
|
||||
if w.w1 <= 0 {
|
||||
if category == CategoryMarkup && (this.Variant1 == other.Variant1) && (this.Variant2 == other.Variant2 || this.Variant2 != "" && other.Variant2 == "") {
|
||||
|
@ -74,7 +75,12 @@ func (s descriptorHandler) compareDescriptors(category Category, isEmbedded bool
|
|||
}
|
||||
|
||||
w.w1 = 1
|
||||
return w
|
||||
}
|
||||
|
||||
if category == CategoryShortcode {
|
||||
if (this.IsPlainText == other.IsPlainText || !other.IsPlainText) || this.AlwaysAllowPlainText {
|
||||
w.w1 = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,13 +88,16 @@ func (s descriptorHandler) compareDescriptors(category Category, isEmbedded bool
|
|||
}
|
||||
|
||||
//lint:ignore ST1006 this vs other makes it easier to reason about.
|
||||
func (this TemplateDescriptor) doCompare(category Category, isEmbedded bool, defaultContentLanguage string, other TemplateDescriptor) weight {
|
||||
func (this TemplateDescriptor) doCompare(category Category, defaultContentLanguage string, other TemplateDescriptor) weight {
|
||||
w := weightNoMatch
|
||||
|
||||
// HTML in plain text is OK, but not the other way around.
|
||||
if other.IsPlainText && !this.IsPlainText {
|
||||
return w
|
||||
if !this.AlwaysAllowPlainText {
|
||||
// HTML in plain text is OK, but not the other way around.
|
||||
if other.IsPlainText && !this.IsPlainText {
|
||||
return w
|
||||
}
|
||||
}
|
||||
|
||||
if other.Kind != "" && other.Kind != this.Kind {
|
||||
return w
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package tplimpl
|
|||
|
||||
import (
|
||||
"io"
|
||||
"iter"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -44,16 +45,15 @@ var embeddedTemplatesAliases = map[string][]string{
|
|||
"_shortcodes/twitter.html": {"_shortcodes/tweet.html"},
|
||||
}
|
||||
|
||||
func (s *TemplateStore) parseTemplate(ti *TemplInfo) error {
|
||||
err := s.tns.doParseTemplate(ti)
|
||||
func (s *TemplateStore) parseTemplate(ti *TemplInfo, replace bool) error {
|
||||
err := s.tns.doParseTemplate(ti, replace)
|
||||
if err != nil {
|
||||
return s.addFileContext(ti, "parse of template failed", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *templateNamespace) doParseTemplate(ti *TemplInfo) error {
|
||||
func (t *templateNamespace) doParseTemplate(ti *TemplInfo, replace bool) error {
|
||||
if !ti.noBaseOf || ti.category == CategoryBaseof {
|
||||
// Delay parsing until we have the base template.
|
||||
return nil
|
||||
|
@ -68,7 +68,7 @@ func (t *templateNamespace) doParseTemplate(ti *TemplInfo) error {
|
|||
|
||||
if ti.D.IsPlainText {
|
||||
prototype := t.parseText
|
||||
if prototype.Lookup(name) != nil {
|
||||
if !replace && prototype.Lookup(name) != nil {
|
||||
name += "-" + strconv.FormatUint(t.nameCounter.Add(1), 10)
|
||||
}
|
||||
templ, err = prototype.New(name).Parse(ti.content)
|
||||
|
@ -77,7 +77,7 @@ func (t *templateNamespace) doParseTemplate(ti *TemplInfo) error {
|
|||
}
|
||||
} else {
|
||||
prototype := t.parseHTML
|
||||
if prototype.Lookup(name) != nil {
|
||||
if !replace && prototype.Lookup(name) != nil {
|
||||
name += "-" + strconv.FormatUint(t.nameCounter.Add(1), 10)
|
||||
}
|
||||
templ, err = prototype.New(name).Parse(ti.content)
|
||||
|
@ -181,19 +181,24 @@ func (t *templateNamespace) applyBaseTemplate(overlay *TemplInfo, base keyTempla
|
|||
return nil
|
||||
}
|
||||
|
||||
func (t *templateNamespace) templatesIn(in tpl.Template) []tpl.Template {
|
||||
var templs []tpl.Template
|
||||
if textt, ok := in.(*texttemplate.Template); ok {
|
||||
for _, t := range textt.Templates() {
|
||||
templs = append(templs, t)
|
||||
func (t *templateNamespace) templatesIn(in tpl.Template) iter.Seq[tpl.Template] {
|
||||
return func(yield func(t tpl.Template) bool) {
|
||||
switch in := in.(type) {
|
||||
case *htmltemplate.Template:
|
||||
for t := range in.All() {
|
||||
if !yield(t) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
case *texttemplate.Template:
|
||||
for t := range in.All() {
|
||||
if !yield(t) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if htmlt, ok := in.(*htmltemplate.Template); ok {
|
||||
for _, t := range htmlt.Templates() {
|
||||
templs = append(templs, t)
|
||||
}
|
||||
}
|
||||
return templs
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -337,8 +342,6 @@ func (t *templateNamespace) createPrototypes(init bool) error {
|
|||
t.prototypeHTML = htmltemplate.Must(t.parseHTML.Clone())
|
||||
t.prototypeText = texttemplate.Must(t.parseText.Clone())
|
||||
}
|
||||
// t.execHTML = htmltemplate.Must(t.parseHTML.Clone())
|
||||
// t.execText = texttemplate.Must(t.parseText.Clone())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -350,3 +353,14 @@ func newTemplateNamespace(funcs map[string]any) *templateNamespace {
|
|||
standaloneText: texttemplate.New("").Funcs(funcs),
|
||||
}
|
||||
}
|
||||
|
||||
func isText(t tpl.Template) bool {
|
||||
switch t.(type) {
|
||||
case *texttemplate.Template:
|
||||
return true
|
||||
case *htmltemplate.Template:
|
||||
return false
|
||||
default:
|
||||
panic("unknown template type")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
|
@ -113,17 +114,18 @@ func NewStore(opts StoreOptions, siteOpts SiteOptions) (*TemplateStore, error) {
|
|||
panic("HTML output format not found")
|
||||
}
|
||||
s := &TemplateStore{
|
||||
opts: opts,
|
||||
siteOpts: siteOpts,
|
||||
optsOrig: opts,
|
||||
siteOptsOrig: siteOpts,
|
||||
htmlFormat: html,
|
||||
storeSite: configureSiteStorage(siteOpts, opts.Watching),
|
||||
treeMain: doctree.NewSimpleTree[map[nodeKey]*TemplInfo](),
|
||||
treeShortcodes: doctree.NewSimpleTree[map[string]map[TemplateDescriptor]*TemplInfo](),
|
||||
templatesByPath: maps.NewCache[string, *TemplInfo](),
|
||||
shortcodesByName: maps.NewCache[string, *TemplInfo](),
|
||||
cacheLookupPartials: maps.NewCache[string, *TemplInfo](),
|
||||
opts: opts,
|
||||
siteOpts: siteOpts,
|
||||
optsOrig: opts,
|
||||
siteOptsOrig: siteOpts,
|
||||
htmlFormat: html,
|
||||
storeSite: configureSiteStorage(siteOpts, opts.Watching),
|
||||
treeMain: doctree.NewSimpleTree[map[nodeKey]*TemplInfo](),
|
||||
treeShortcodes: doctree.NewSimpleTree[map[string]map[TemplateDescriptor]*TemplInfo](),
|
||||
templatesByPath: maps.NewCache[string, *TemplInfo](),
|
||||
shortcodesByName: maps.NewCache[string, *TemplInfo](),
|
||||
cacheLookupPartials: maps.NewCache[string, *TemplInfo](),
|
||||
templatesSnapshotSet: maps.NewCache[*parse.Tree, struct{}](),
|
||||
|
||||
// Note that the funcs passed below is just for name validation.
|
||||
tns: newTemplateNamespace(siteOpts.TemplateFuncs),
|
||||
|
@ -142,10 +144,10 @@ func NewStore(opts StoreOptions, siteOpts SiteOptions) (*TemplateStore, error) {
|
|||
if err := s.insertEmbedded(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.parseTemplates(); err != nil {
|
||||
if err := s.parseTemplates(false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.extractInlinePartials(); err != nil {
|
||||
if err := s.extractInlinePartials(false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.transformTemplates(); err != nil {
|
||||
|
@ -323,6 +325,9 @@ func (ti *TemplInfo) findBestMatchBaseof(s *TemplateStore, d1 TemplateDescriptor
|
|||
}
|
||||
|
||||
ti.baseVariants.WalkPath(k1, func(k2 string, v map[TemplateDescriptor]*TemplWithBaseApplied) (bool, error) {
|
||||
if !s.inPath(k1, k2) {
|
||||
return false, nil
|
||||
}
|
||||
slashCountK2 := strings.Count(k2, "/")
|
||||
distance := slashCountK1 - slashCountK2
|
||||
|
||||
|
@ -420,10 +425,11 @@ type TemplateStore struct {
|
|||
siteOpts SiteOptions
|
||||
htmlFormat output.Format
|
||||
|
||||
treeMain *doctree.SimpleTree[map[nodeKey]*TemplInfo]
|
||||
treeShortcodes *doctree.SimpleTree[map[string]map[TemplateDescriptor]*TemplInfo]
|
||||
templatesByPath *maps.Cache[string, *TemplInfo]
|
||||
shortcodesByName *maps.Cache[string, *TemplInfo]
|
||||
treeMain *doctree.SimpleTree[map[nodeKey]*TemplInfo]
|
||||
treeShortcodes *doctree.SimpleTree[map[string]map[TemplateDescriptor]*TemplInfo]
|
||||
templatesByPath *maps.Cache[string, *TemplInfo]
|
||||
shortcodesByName *maps.Cache[string, *TemplInfo]
|
||||
templatesSnapshotSet *maps.Cache[*parse.Tree, struct{}]
|
||||
|
||||
dh descriptorHandler
|
||||
|
||||
|
@ -605,7 +611,7 @@ func (s *TemplateStore) LookupShortcodeByName(name string) *TemplInfo {
|
|||
return ti
|
||||
}
|
||||
|
||||
func (s *TemplateStore) LookupShortcode(q TemplateQuery) *TemplInfo {
|
||||
func (s *TemplateStore) LookupShortcode(q TemplateQuery) (*TemplInfo, error) {
|
||||
q.init()
|
||||
k1 := s.key(q.Path)
|
||||
|
||||
|
@ -615,6 +621,9 @@ func (s *TemplateStore) LookupShortcode(q TemplateQuery) *TemplInfo {
|
|||
defer s.putBest(best)
|
||||
|
||||
s.treeShortcodes.WalkPath(k1, func(k2 string, m map[string]map[TemplateDescriptor]*TemplInfo) (bool, error) {
|
||||
if !s.inPath(k1, k2) {
|
||||
return false, nil
|
||||
}
|
||||
slashCountK2 := strings.Count(k2, "/")
|
||||
distance := slashCountK1 - slashCountK2
|
||||
|
||||
|
@ -624,13 +633,15 @@ func (s *TemplateStore) LookupShortcode(q TemplateQuery) *TemplInfo {
|
|||
}
|
||||
|
||||
for k, vv := range v {
|
||||
best.candidates = append(best.candidates, vv)
|
||||
if !q.Consider(vv) {
|
||||
continue
|
||||
}
|
||||
|
||||
weight := s.dh.compareDescriptors(q.Category, vv.subCategory == SubCategoryEmbedded, q.Desc, k)
|
||||
weight.distance = distance
|
||||
if best.isBetter(weight, vv) {
|
||||
isBetter := best.isBetter(weight, vv)
|
||||
if isBetter {
|
||||
best.updateValues(weight, k2, k, vv)
|
||||
}
|
||||
}
|
||||
|
@ -638,8 +649,21 @@ func (s *TemplateStore) LookupShortcode(q TemplateQuery) *TemplInfo {
|
|||
return false, nil
|
||||
})
|
||||
|
||||
// Any match will do.
|
||||
return best.templ
|
||||
if best.w.w1 <= 0 {
|
||||
var err error
|
||||
if s := best.candidatesAsStringSlice(); s != nil {
|
||||
msg := fmt.Sprintf("no compatible template found for shortcode %q in %s", q.Name, s)
|
||||
if !q.Desc.IsPlainText {
|
||||
msg += "; note that to use plain text template shortcodes in HTML you need to use the shortcode {{% delimiter"
|
||||
}
|
||||
err = errors.New(msg)
|
||||
} else {
|
||||
err = fmt.Errorf("no template found for shortcode %q", q.Name)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return best.templ, nil
|
||||
}
|
||||
|
||||
// PrintDebug is for testing/debugging only.
|
||||
|
@ -687,12 +711,16 @@ func (s *TemplateStore) RefreshFiles(include func(fi hugofs.FileMetaInfo) bool)
|
|||
if err := s.insertTemplates(include, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.parseTemplates(); err != nil {
|
||||
if err := s.createTemplatesSnapshot(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.extractInlinePartials(); err != nil {
|
||||
if err := s.parseTemplates(true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.extractInlinePartials(true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.transformTemplates(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -706,6 +734,7 @@ func (s *TemplateStore) RefreshFiles(include func(fi hugofs.FileMetaInfo) bool)
|
|||
}
|
||||
|
||||
func (s *TemplateStore) HasTemplate(templatePath string) bool {
|
||||
templatePath = strings.ToLower(templatePath)
|
||||
templatePath = paths.AddLeadingSlash(templatePath)
|
||||
return s.templatesByPath.Contains(templatePath)
|
||||
}
|
||||
|
@ -780,8 +809,18 @@ func (s *TemplateStore) findBestMatchGet(key string, category Category, consider
|
|||
}
|
||||
}
|
||||
|
||||
func (s *TemplateStore) inPath(k1, k2 string) bool {
|
||||
if k1 != k2 && !strings.HasPrefix(k1, k2+"/") {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *TemplateStore) findBestMatchWalkPath(q TemplateQuery, k1 string, slashCountK1 int, best *bestMatch) {
|
||||
s.treeMain.WalkPath(k1, func(k2 string, v map[nodeKey]*TemplInfo) (bool, error) {
|
||||
if !s.inPath(k1, k2) {
|
||||
return false, nil
|
||||
}
|
||||
slashCountK2 := strings.Count(k2, "/")
|
||||
distance := slashCountK1 - slashCountK2
|
||||
|
||||
|
@ -907,61 +946,79 @@ func (s *TemplateStore) extractIdentifiers(line string) []string {
|
|||
return identifiers
|
||||
}
|
||||
|
||||
func (s *TemplateStore) extractInlinePartials() error {
|
||||
func (s *TemplateStore) extractInlinePartials(rebuild bool) error {
|
||||
isPartialName := func(s string) bool {
|
||||
return strings.HasPrefix(s, "partials/") || strings.HasPrefix(s, "_partials/")
|
||||
}
|
||||
|
||||
p := s.tns
|
||||
// We may find both inline and external partials in the current template namespaces,
|
||||
// so only add the ones we have not seen before.
|
||||
addIfNotSeen := func(isText bool, templs ...tpl.Template) error {
|
||||
for _, templ := range templs {
|
||||
if templ.Name() == "" || !isPartialName(templ.Name()) {
|
||||
continue
|
||||
}
|
||||
name := templ.Name()
|
||||
if !paths.HasExt(name) {
|
||||
// Assume HTML. This in line with how the lookup works.
|
||||
name = name + s.htmlFormat.MediaType.FirstSuffix.FullSuffix
|
||||
}
|
||||
if !strings.HasPrefix(name, "_") {
|
||||
name = "_" + name
|
||||
}
|
||||
pi := s.opts.PathParser.Parse(files.ComponentFolderLayouts, name)
|
||||
ti, err := s.insertTemplate(pi, nil, false, s.treeMain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ti != nil {
|
||||
ti.Template = templ
|
||||
ti.noBaseOf = true
|
||||
ti.subCategory = SubCategoryInline
|
||||
ti.D.IsPlainText = isText
|
||||
}
|
||||
|
||||
for templ := range s.allRawTemplates() {
|
||||
if templ.Name() == "" || !isPartialName(templ.Name()) {
|
||||
continue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
addIfNotSeen(false, p.templatesIn(p.parseHTML)...)
|
||||
addIfNotSeen(true, p.templatesIn(p.parseText)...)
|
||||
|
||||
for _, t := range p.baseofHtmlClones {
|
||||
if err := addIfNotSeen(false, p.templatesIn(t)...); err != nil {
|
||||
if rebuild && s.templatesSnapshotSet.Contains(getParseTree(templ)) {
|
||||
// This partial was not created during this build.
|
||||
continue
|
||||
}
|
||||
name := templ.Name()
|
||||
if !paths.HasExt(name) {
|
||||
// Assume HTML. This in line with how the lookup works.
|
||||
name = name + s.htmlFormat.MediaType.FirstSuffix.FullSuffix
|
||||
}
|
||||
if !strings.HasPrefix(name, "_") {
|
||||
name = "_" + name
|
||||
}
|
||||
pi := s.opts.PathParser.Parse(files.ComponentFolderLayouts, name)
|
||||
ti, err := s.insertTemplate(pi, nil, SubCategoryInline, false, s.treeMain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, t := range p.baseofTextClones {
|
||||
if err := addIfNotSeen(true, p.templatesIn(t)...); err != nil {
|
||||
return err
|
||||
|
||||
if ti != nil {
|
||||
ti.Template = templ
|
||||
ti.noBaseOf = true
|
||||
ti.subCategory = SubCategoryInline
|
||||
ti.D.IsPlainText = isText(templ)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TemplateStore) allRawTemplates() iter.Seq[tpl.Template] {
|
||||
p := s.tns
|
||||
return func(yield func(tpl.Template) bool) {
|
||||
for t := range p.templatesIn(p.parseHTML) {
|
||||
if !yield(t) {
|
||||
return
|
||||
}
|
||||
}
|
||||
for t := range p.templatesIn(p.parseText) {
|
||||
if !yield(t) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, tt := range p.baseofHtmlClones {
|
||||
for t := range p.templatesIn(tt) {
|
||||
if !yield(t) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, tt := range p.baseofTextClones {
|
||||
for t := range p.templatesIn(tt) {
|
||||
if !yield(t) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TemplateStore) insertEmbedded() error {
|
||||
return fs.WalkDir(embeddedTemplatesFs, ".", func(path string, d fs.DirEntry, err error) error {
|
||||
return fs.WalkDir(embeddedTemplatesFs, ".", func(tpath string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -969,7 +1026,7 @@ func (s *TemplateStore) insertEmbedded() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
templb, err := embeddedTemplatesFs.ReadFile(path)
|
||||
templb, err := embeddedTemplatesFs.ReadFile(tpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -977,7 +1034,7 @@ func (s *TemplateStore) insertEmbedded() error {
|
|||
// Get the newlines on Windows in line with how we had it back when we used Go Generate
|
||||
// to write the templates to Go files.
|
||||
templ := string(bytes.ReplaceAll(templb, []byte("\r\n"), []byte("\n")))
|
||||
name := strings.TrimPrefix(filepath.ToSlash(path), "embedded/templates/")
|
||||
name := strings.TrimPrefix(filepath.ToSlash(tpath), "embedded/templates/")
|
||||
|
||||
insertOne := func(name, content string) error {
|
||||
pi := s.opts.PathParser.Parse(files.ComponentFolderLayouts, name)
|
||||
|
@ -991,7 +1048,7 @@ func (s *TemplateStore) insertEmbedded() error {
|
|||
return err
|
||||
}
|
||||
} else {
|
||||
ti, err = s.insertTemplate(pi, nil, false, s.treeMain)
|
||||
ti, err = s.insertTemplate(pi, nil, SubCategoryEmbedded, false, s.treeMain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1007,6 +1064,19 @@ func (s *TemplateStore) insertEmbedded() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Copy the embedded HTML table render hook to each output format.
|
||||
// See https://github.com/gohugoio/hugo/issues/13351.
|
||||
if name == path.Join(containerMarkup, "render-table.html") {
|
||||
for _, of := range s.opts.OutputFormats {
|
||||
path := paths.TrimExt(name) + "." + of.Name + of.MediaType.FirstSuffix.FullSuffix
|
||||
if err := insertOne(path, templ); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := insertOne(name, templ); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1072,7 +1142,7 @@ func (s *TemplateStore) insertShortcode(pi *paths.Path, fi hugofs.FileMetaInfo,
|
|||
return ti, nil
|
||||
}
|
||||
|
||||
func (s *TemplateStore) insertTemplate(pi *paths.Path, fi hugofs.FileMetaInfo, replace bool, tree doctree.Tree[map[nodeKey]*TemplInfo]) (*TemplInfo, error) {
|
||||
func (s *TemplateStore) insertTemplate(pi *paths.Path, fi hugofs.FileMetaInfo, subCategory SubCategory, replace bool, tree doctree.Tree[map[nodeKey]*TemplInfo]) (*TemplInfo, error) {
|
||||
key, _, category, d, err := s.toKeyCategoryAndDescriptor(pi)
|
||||
// See #13577. Warn for now.
|
||||
if err != nil {
|
||||
|
@ -1086,7 +1156,7 @@ func (s *TemplateStore) insertTemplate(pi *paths.Path, fi hugofs.FileMetaInfo, r
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
return s.insertTemplate2(pi, fi, key, category, d, replace, false, tree)
|
||||
return s.insertTemplate2(pi, fi, key, category, subCategory, d, replace, false, tree)
|
||||
}
|
||||
|
||||
func (s *TemplateStore) insertTemplate2(
|
||||
|
@ -1094,6 +1164,7 @@ func (s *TemplateStore) insertTemplate2(
|
|||
fi hugofs.FileMetaInfo,
|
||||
key string,
|
||||
category Category,
|
||||
subCategory SubCategory,
|
||||
d TemplateDescriptor,
|
||||
replace, isLegacyMapped bool,
|
||||
tree doctree.Tree[map[nodeKey]*TemplInfo],
|
||||
|
@ -1116,12 +1187,26 @@ func (s *TemplateStore) insertTemplate2(
|
|||
tree.Insert(key, m)
|
||||
}
|
||||
|
||||
if !replace {
|
||||
if v, found := m[nk]; found {
|
||||
if len(pi.Identifiers()) >= len(v.PathInfo.Identifiers()) {
|
||||
// e.g. /pages/home.foo.html and /pages/home.html where foo may be a valid language name in another site.
|
||||
return nil, nil
|
||||
}
|
||||
nkExisting, existingFound := m[nk]
|
||||
if !replace && existingFound && fi != nil && nkExisting.Fi != nil {
|
||||
// See issue #13715.
|
||||
// We do the merge on the file system level, but from Hugo v0.146.0 we have a situation where
|
||||
// the project may well have a different layouts layout compared to the theme(s) it uses.
|
||||
// We could possibly have fixed that on a lower (file system) level, but since this is just
|
||||
// a temporary situation (until all projects are updated),
|
||||
// do a replace here if the file comes from higher up in the module chain.
|
||||
replace = fi.Meta().ModuleOrdinal < nkExisting.Fi.Meta().ModuleOrdinal
|
||||
}
|
||||
|
||||
if !replace && existingFound {
|
||||
// Always replace inline partials to allow for reloading.
|
||||
replace = subCategory == SubCategoryInline && nkExisting.subCategory == SubCategoryInline
|
||||
}
|
||||
|
||||
if !replace && existingFound {
|
||||
if len(pi.Identifiers()) >= len(nkExisting.PathInfo.Identifiers()) {
|
||||
// e.g. /pages/home.foo.html and /pages/home.html where foo may be a valid language name in another site.
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1148,7 +1233,7 @@ func (s *TemplateStore) insertTemplate2(
|
|||
return ti, nil
|
||||
}
|
||||
|
||||
func (s *TemplateStore) insertTemplates(include func(fi hugofs.FileMetaInfo) bool, replace bool) error {
|
||||
func (s *TemplateStore) insertTemplates(include func(fi hugofs.FileMetaInfo) bool, partialRebuild bool) error {
|
||||
if include == nil {
|
||||
include = func(fi hugofs.FileMetaInfo) bool {
|
||||
return true
|
||||
|
@ -1330,7 +1415,7 @@ func (s *TemplateStore) insertTemplates(include func(fi hugofs.FileMetaInfo) boo
|
|||
|
||||
}
|
||||
|
||||
if replace && pi.NameNoIdentifier() == baseNameBaseof {
|
||||
if partialRebuild && pi.NameNoIdentifier() == baseNameBaseof {
|
||||
// A baseof file has changed.
|
||||
resetBaseVariants = true
|
||||
}
|
||||
|
@ -1338,12 +1423,12 @@ func (s *TemplateStore) insertTemplates(include func(fi hugofs.FileMetaInfo) boo
|
|||
var ti *TemplInfo
|
||||
var err error
|
||||
if pi.Type() == paths.TypeShortcode {
|
||||
ti, err = s.insertShortcode(pi, fi, replace, s.treeShortcodes)
|
||||
ti, err = s.insertShortcode(pi, fi, partialRebuild, s.treeShortcodes)
|
||||
if err != nil || ti == nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
ti, err = s.insertTemplate(pi, fi, replace, s.treeMain)
|
||||
ti, err = s.insertTemplate(pi, fi, SubCategoryMain, partialRebuild, s.treeMain)
|
||||
if err != nil || ti == nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1377,7 +1462,7 @@ func (s *TemplateStore) insertTemplates(include func(fi hugofs.FileMetaInfo) boo
|
|||
desc.IsPlainText = outputFormat.IsPlainText
|
||||
desc.MediaType = mediaType.Type
|
||||
|
||||
ti, err := s.insertTemplate2(pi, fi, targetPath, category, desc, true, true, s.treeMain)
|
||||
ti, err := s.insertTemplate2(pi, fi, targetPath, category, SubCategoryMain, desc, true, true, s.treeMain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1388,6 +1473,7 @@ func (s *TemplateStore) insertTemplates(include func(fi hugofs.FileMetaInfo) boo
|
|||
if err := s.tns.readTemplateInto(ti); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if resetBaseVariants {
|
||||
|
@ -1414,7 +1500,15 @@ func (s *TemplateStore) key(dir string) string {
|
|||
return paths.TrimTrailing(dir)
|
||||
}
|
||||
|
||||
func (s *TemplateStore) parseTemplates() error {
|
||||
func (s *TemplateStore) createTemplatesSnapshot() error {
|
||||
s.templatesSnapshotSet.Reset()
|
||||
for t := range s.allRawTemplates() {
|
||||
s.templatesSnapshotSet.Set(getParseTree(t), struct{}{})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TemplateStore) parseTemplates(replace bool) error {
|
||||
if err := func() error {
|
||||
// Read and parse all templates.
|
||||
for _, v := range s.treeMain.All() {
|
||||
|
@ -1422,7 +1516,7 @@ func (s *TemplateStore) parseTemplates() error {
|
|||
if vv.state == processingStateTransformed {
|
||||
continue
|
||||
}
|
||||
if err := s.parseTemplate(vv); err != nil {
|
||||
if err := s.parseTemplate(vv, replace); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -1442,7 +1536,7 @@ func (s *TemplateStore) parseTemplates() error {
|
|||
// The regular expression used to detect if a template needs a base template has some
|
||||
// rare false positives. Assume we don't need one.
|
||||
vv.noBaseOf = true
|
||||
if err := s.parseTemplate(vv); err != nil {
|
||||
if err := s.parseTemplate(vv, replace); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
|
@ -1471,7 +1565,7 @@ func (s *TemplateStore) parseTemplates() error {
|
|||
if vvv.state == processingStateTransformed {
|
||||
continue
|
||||
}
|
||||
if err := s.parseTemplate(vvv); err != nil {
|
||||
if err := s.parseTemplate(vvv, replace); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -1791,10 +1885,11 @@ type TextTemplatHandler interface {
|
|||
}
|
||||
|
||||
type bestMatch struct {
|
||||
templ *TemplInfo
|
||||
desc TemplateDescriptor
|
||||
w weight
|
||||
key string
|
||||
templ *TemplInfo
|
||||
desc TemplateDescriptor
|
||||
w weight
|
||||
key string
|
||||
candidates []*TemplInfo
|
||||
|
||||
// settings.
|
||||
defaultOutputformat string
|
||||
|
@ -1805,6 +1900,18 @@ func (best *bestMatch) reset() {
|
|||
best.w = weight{}
|
||||
best.desc = TemplateDescriptor{}
|
||||
best.key = ""
|
||||
best.candidates = nil
|
||||
}
|
||||
|
||||
func (best *bestMatch) candidatesAsStringSlice() []string {
|
||||
if len(best.candidates) == 0 {
|
||||
return nil
|
||||
}
|
||||
candidates := make([]string, len(best.candidates))
|
||||
for i, v := range best.candidates {
|
||||
candidates[i] = v.PathInfo.Path()
|
||||
}
|
||||
return candidates
|
||||
}
|
||||
|
||||
func (best *bestMatch) isBetter(w weight, ti *TemplInfo) bool {
|
||||
|
@ -1814,7 +1921,6 @@ func (best *bestMatch) isBetter(w weight, ti *TemplInfo) bool {
|
|||
}
|
||||
|
||||
if w.w1 <= 0 {
|
||||
|
||||
if best.w.w1 <= 0 {
|
||||
return ti.PathInfo.Path() < best.templ.PathInfo.Path()
|
||||
}
|
||||
|
|
|
@ -920,6 +920,26 @@ func TestPartialHTML(t *testing.T) {
|
|||
b.AssertFileContent("public/index.html", "<link rel=\"stylesheet\" href=\"/css/style.css\">")
|
||||
}
|
||||
|
||||
func TestPartialPlainTextInHTML(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
-- layouts/all.html --
|
||||
<html>
|
||||
<head>
|
||||
{{ partial "mypartial.txt" . }}
|
||||
</head>
|
||||
</html>
|
||||
-- layouts/partials/mypartial.txt --
|
||||
My <div>partial</div>.
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html", "My <div>partial</div>.")
|
||||
}
|
||||
|
||||
// Issue #13593.
|
||||
func TestGoatAndNoGoat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
@ -1103,6 +1123,35 @@ All.
|
|||
b.AssertLogContains("unrecognized render hook")
|
||||
}
|
||||
|
||||
func TestLayoutNotFound(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
-- layouts/single.html --
|
||||
Single.
|
||||
`
|
||||
b := hugolib.Test(t, files, hugolib.TestOptWarn())
|
||||
b.AssertLogContains("WARN found no layout file for \"html\" for kind \"home\"")
|
||||
}
|
||||
|
||||
func TestLayoutOverrideThemeWhenThemeOnOldFormatIssue13715(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
theme = "mytheme"
|
||||
-- layouts/list.html --
|
||||
layouts/list.html
|
||||
-- themes/mytheme/layouts/_default/list.html --
|
||||
mytheme/layouts/_default/list.html
|
||||
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
b.AssertFileContent("public/index.html", "layouts/list.html")
|
||||
}
|
||||
|
||||
func BenchmarkExecuteWithContext(b *testing.B) {
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
|
@ -1197,8 +1246,8 @@ s2.
|
|||
Category: tplimpl.CategoryShortcode,
|
||||
Desc: desc,
|
||||
}
|
||||
v := store.LookupShortcode(q)
|
||||
if v == nil {
|
||||
v, err := store.LookupShortcode(q)
|
||||
if v == nil || err != nil {
|
||||
b.Fatal("not found")
|
||||
}
|
||||
}
|
||||
|
@ -1396,3 +1445,106 @@ layouts/taxononmy.html.html
|
|||
b.AssertFileExists("public/p1/index.html", false)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateLoopBlogVsBlogrollIssue13672(t *testing.T) {
|
||||
t.Parallel()
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
-- layouts/blog/_shortcodes/myshortcode.html --
|
||||
layouts/blog/_shortcodes/myshortcode.html
|
||||
-- layouts/blog/baseof.html --
|
||||
blog/baseof.html {{ block "main" . }}{{ end }}
|
||||
-- layouts/blog/all.html --
|
||||
{{ define "main" }}blog/all.html|{{ .Content}}{{ end }}
|
||||
-- layouts/blogroll/_shortcodes/myshortcode.html --
|
||||
layouts/blogroll/myshortcode.html
|
||||
-- layouts/blogroll/baseof.html --
|
||||
{{ block "main" . }}blogroll/baseof.html{{ end }}
|
||||
-- layouts/blogroll/all.html --
|
||||
{{ define "main" }}blogroll/all.html|{{ .Content}}{{ end }}
|
||||
-- content/blog/p1.md --
|
||||
---
|
||||
title: p1
|
||||
---
|
||||
{{< myshortcode >}}
|
||||
-- content/blogroll/p1.md --
|
||||
---
|
||||
title: p1
|
||||
---
|
||||
{{< myshortcode >}}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/blog/p1/index.html", "blog/baseof.html blog/all.html|layouts/blog/_shortcodes/myshortcode.html")
|
||||
b.AssertFileContent("public/blogroll/p1/index.html", "blogroll/all.html|layouts/blogroll/myshortcode.html")
|
||||
}
|
||||
|
||||
// See issue #13668.
|
||||
func TestPartialPlainTextVsHTML(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
/*
|
||||
Note that in the below, there's no output format named txt,
|
||||
so the isPlainText is fetched from the only output format with that extension.
|
||||
*/
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
-- layouts/_partials/myhtml.html --
|
||||
<div>myhtml</div>
|
||||
-- layouts/_partials/mytext.txt --
|
||||
<div>mytext</div>
|
||||
-- layouts/all.html --
|
||||
myhtml: {{ partial "myhtml.html" . }}
|
||||
mytext: {{ partial "mytext.txt" . }}
|
||||
mytexts|safeHTML: {{ partial "mytext.txt" . | safeHTML }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.html",
|
||||
"myhtml: <div>myhtml</div>",
|
||||
"mytext: <div>mytext</div>",
|
||||
"mytexts|safeHTML: <div>mytext</div>",
|
||||
)
|
||||
}
|
||||
|
||||
func TestIssue13351(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
||||
[outputs]
|
||||
home = ['html','json']
|
||||
[outputFormats.html]
|
||||
weight = 1
|
||||
[outputFormats.json]
|
||||
weight = 2
|
||||
-- content/_index.md --
|
||||
---
|
||||
title: home
|
||||
---
|
||||
a|b
|
||||
:--|:--
|
||||
1|2
|
||||
-- layouts/index.html --
|
||||
{{ .Content }}
|
||||
-- layouts/index.json --
|
||||
{{ .Content }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
b.AssertFileContent("public/index.html", "<table>")
|
||||
b.AssertFileContent("public/index.json", "<table>")
|
||||
|
||||
f := strings.ReplaceAll(files, "weight = 1", "weight = 0")
|
||||
b = hugolib.Test(t, f)
|
||||
b.AssertFileContent("public/index.html", "<table>")
|
||||
b.AssertFileContent("public/index.json", "<table>")
|
||||
|
||||
f = strings.ReplaceAll(files, "weight = 1", "")
|
||||
b = hugolib.Test(t, f)
|
||||
b.AssertFileContent("public/index.html", "<table>")
|
||||
b.AssertFileContent("public/index.json", "<table>")
|
||||
}
|
||||
|
|
|
@ -17,8 +17,10 @@ package transform
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html"
|
||||
"html/template"
|
||||
"io"
|
||||
|
@ -234,6 +236,7 @@ func (ns *Namespace) ToMath(ctx context.Context, args ...any) (template.HTML, er
|
|||
MinRuleThickness: 0.04,
|
||||
ErrorColor: "#cc0000",
|
||||
ThrowOnError: true,
|
||||
Strict: "error",
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -243,8 +246,23 @@ func (ns *Namespace) ToMath(ctx context.Context, args ...any) (template.HTML, er
|
|||
}
|
||||
}
|
||||
|
||||
switch katexInput.Options.Strict {
|
||||
case "error", "ignore", "warn":
|
||||
// Valid strict mode, continue
|
||||
default:
|
||||
return "", fmt.Errorf("invalid strict mode; expected one of error, ignore, or warn; received %s", katexInput.Options.Strict)
|
||||
}
|
||||
|
||||
type fileCacheEntry struct {
|
||||
Version string `json:"version"`
|
||||
Output string `json:"output"`
|
||||
Warnings []string `json:"warnings,omitempty"`
|
||||
}
|
||||
|
||||
const fileCacheEntryVersion = "v1" // Increment on incompatible changes.
|
||||
|
||||
s := hashing.HashString(args...)
|
||||
key := "tomath/" + s[:2] + "/" + s[2:]
|
||||
key := "tomath/" + fileCacheEntryVersion + "/" + s[:2] + "/" + s[2:]
|
||||
fileCache := ns.deps.ResourceSpec.FileCaches.MiscCache()
|
||||
|
||||
v, err := ns.cacheMath.GetOrCreate(key, func(string) (template.HTML, error) {
|
||||
|
@ -265,15 +283,35 @@ func (ns *Namespace) ToMath(ctx context.Context, args ...any) (template.HTML, er
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hugio.NewReadSeekerNoOpCloserFromString(result.Data.Output), nil
|
||||
|
||||
e := fileCacheEntry{
|
||||
Version: fileCacheEntryVersion,
|
||||
Output: result.Data.Output,
|
||||
Warnings: result.Header.Warnings,
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
enc := json.NewEncoder(buf)
|
||||
enc.SetEscapeHTML(false)
|
||||
if err := enc.Encode(e); err != nil {
|
||||
return nil, fmt.Errorf("failed to encode file cache entry: %w", err)
|
||||
}
|
||||
return hugio.NewReadSeekerNoOpCloserFromBytes(buf.Bytes()), nil
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
s, err := hugio.ReadString(r)
|
||||
var e fileCacheEntry
|
||||
if err := json.NewDecoder(r).Decode(&e); err != nil {
|
||||
return "", fmt.Errorf("failed to decode file cache entry: %w", err)
|
||||
}
|
||||
|
||||
return template.HTML(s), err
|
||||
for _, warning := range e.Warnings {
|
||||
ns.deps.Log.Warnf("transform.ToMath: %s", warning)
|
||||
}
|
||||
|
||||
return template.HTML(e.Output), err
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
|
@ -495,3 +495,43 @@ DATA
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 13729
|
||||
func TestToMathStrictMode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
||||
-- layouts/all.html --
|
||||
{{ transform.ToMath "a %" dict }}
|
||||
-- foo --
|
||||
`
|
||||
|
||||
// strict mode: default
|
||||
f := strings.ReplaceAll(files, "dict", "")
|
||||
b, err := hugolib.TestE(t, f)
|
||||
b.Assert(err.Error(), qt.Contains, "[commentAtEnd]")
|
||||
|
||||
// strict mode: error
|
||||
f = strings.ReplaceAll(files, "dict", `(dict "strict" "error")`)
|
||||
b, err = hugolib.TestE(t, f)
|
||||
b.Assert(err.Error(), qt.Contains, "[commentAtEnd]")
|
||||
|
||||
// strict mode: ignore
|
||||
f = strings.ReplaceAll(files, "dict", `(dict "strict" "ignore")`)
|
||||
b = hugolib.Test(t, f, hugolib.TestOptWarn())
|
||||
b.AssertLogMatches("")
|
||||
b.AssertFileContent("public/index.html", `<annotation encoding="application/x-tex">a %</annotation>`)
|
||||
|
||||
// strict: warn
|
||||
f = strings.ReplaceAll(files, "dict", `(dict "strict" "warn")`)
|
||||
b = hugolib.Test(t, f, hugolib.TestOptWarn())
|
||||
b.AssertLogMatches("[commentAtEnd]")
|
||||
b.AssertFileContent("public/index.html", `<annotation encoding="application/x-tex">a %</annotation>`)
|
||||
|
||||
// strict mode: invalid value
|
||||
f = strings.ReplaceAll(files, "dict", `(dict "strict" "foo")`)
|
||||
b, err = hugolib.TestE(t, f)
|
||||
b.Assert(err.Error(), qt.Contains, "invalid strict mode")
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue