mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-30 18:22:37 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0270364a34 | |||
| 760c13a7ac | |||
| 3bd73d262d | |||
| 7104de83ce | |||
| 835579b338 | |||
| 05e067ced8 | |||
| ee48d9692a | |||
| db28695ff5 | |||
| 778f0d9002 | |||
| 13b208e2f7 | |||
| 329b2342f0 | |||
| 33b46d8a41 | |||
| 6c68142cc1 |
Vendored
+11
-5
@@ -176,11 +176,12 @@ func (c *Cache) ClearMatching(predicatePartition func(k string, p PartitionManag
|
||||
}
|
||||
|
||||
// ClearOnRebuild prepares the cache for a new rebuild taking the given changeset into account.
|
||||
func (c *Cache) ClearOnRebuild(changeset ...identity.Identity) {
|
||||
// predicate is optional and will clear any entry for which it returns true.
|
||||
func (c *Cache) ClearOnRebuild(predicate func(k, v any) bool, changeset ...identity.Identity) {
|
||||
g := rungroup.Run[PartitionManager](context.Background(), rungroup.Config[PartitionManager]{
|
||||
NumWorkers: len(c.partitions),
|
||||
Handle: func(ctx context.Context, partition PartitionManager) error {
|
||||
partition.clearOnRebuild(changeset...)
|
||||
partition.clearOnRebuild(predicate, changeset...)
|
||||
return nil
|
||||
},
|
||||
})
|
||||
@@ -479,7 +480,12 @@ func (p *Partition[K, V]) clearMatching(predicate func(k, v any) bool) {
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Partition[K, V]) clearOnRebuild(changeset ...identity.Identity) {
|
||||
func (p *Partition[K, V]) clearOnRebuild(predicate func(k, v any) bool, changeset ...identity.Identity) {
|
||||
if predicate == nil {
|
||||
predicate = func(k, v any) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
opts := p.getOptions()
|
||||
if opts.ClearWhen == ClearNever {
|
||||
return
|
||||
@@ -525,7 +531,7 @@ func (p *Partition[K, V]) clearOnRebuild(changeset ...identity.Identity) {
|
||||
// Second pass needs to be done in a separate loop to catch any
|
||||
// elements marked as stale in the other partitions.
|
||||
p.c.DeleteFunc(func(key K, v V) bool {
|
||||
if shouldDelete(key, v) {
|
||||
if predicate(key, v) || shouldDelete(key, v) {
|
||||
p.trace.Log(
|
||||
logg.StringFunc(
|
||||
func() string {
|
||||
@@ -601,7 +607,7 @@ type PartitionManager interface {
|
||||
adjustMaxSize(addend int) int
|
||||
getMaxSize() int
|
||||
getOptions() OptionsPartition
|
||||
clearOnRebuild(changeset ...identity.Identity)
|
||||
clearOnRebuild(predicate func(k, v any) bool, changeset ...identity.Identity)
|
||||
clearMatching(predicate func(k, v any) bool)
|
||||
clearStale()
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -147,13 +147,13 @@ func TestClear(t *testing.T) {
|
||||
|
||||
c.Assert(cache.Keys(predicateAll), qt.HasLen, 4)
|
||||
|
||||
cache.ClearOnRebuild()
|
||||
cache.ClearOnRebuild(nil)
|
||||
|
||||
// Stale items are always cleared.
|
||||
c.Assert(cache.Keys(predicateAll), qt.HasLen, 2)
|
||||
|
||||
cache = newTestCache(t)
|
||||
cache.ClearOnRebuild(identity.StringIdentity("changed"))
|
||||
cache.ClearOnRebuild(nil, identity.StringIdentity("changed"))
|
||||
|
||||
c.Assert(cache.Keys(nil), qt.HasLen, 1)
|
||||
|
||||
|
||||
+5
-5
@@ -418,7 +418,7 @@ func Deprecate(item, alternative string, version string) {
|
||||
func DeprecateLevel(item, alternative, version string, level logg.Level) {
|
||||
var msg string
|
||||
if level == logg.LevelError {
|
||||
msg = fmt.Sprintf("%s was deprecated in Hugo %s and will be removed in Hugo %s. %s", item, version, CurrentVersion.Next().ReleaseVersion(), alternative)
|
||||
msg = fmt.Sprintf("%s was deprecated in Hugo %s and subsequently removed. %s", item, version, alternative)
|
||||
} else {
|
||||
msg = fmt.Sprintf("%s was deprecated in Hugo %s and will be removed in a future release. %s", item, version, alternative)
|
||||
}
|
||||
@@ -434,11 +434,11 @@ func deprecationLogLevelFromVersion(ver string) logg.Level {
|
||||
to := CurrentVersion
|
||||
minorDiff := to.Minor - from.Minor
|
||||
switch {
|
||||
case minorDiff >= 12:
|
||||
// Start failing the build after about a year.
|
||||
case minorDiff >= 15:
|
||||
// Start failing the build after about 15 months.
|
||||
return logg.LevelError
|
||||
case minorDiff >= 6:
|
||||
// Start printing warnings after about six months.
|
||||
case minorDiff >= 3:
|
||||
// Start printing warnings after about 3 months.
|
||||
return logg.LevelWarn
|
||||
default:
|
||||
return logg.LevelInfo
|
||||
|
||||
@@ -57,11 +57,11 @@ func TestDeprecationLogLevelFromVersion(t *testing.T) {
|
||||
c.Assert(deprecationLogLevelFromVersion("0.55.0"), qt.Equals, logg.LevelError)
|
||||
ver := CurrentVersion
|
||||
c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelInfo)
|
||||
ver.Minor -= 1
|
||||
c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelInfo)
|
||||
ver.Minor -= 6
|
||||
ver.Minor -= 3
|
||||
c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelWarn)
|
||||
ver.Minor -= 6
|
||||
ver.Minor -= 4
|
||||
c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelWarn)
|
||||
ver.Minor -= 13
|
||||
c.Assert(deprecationLogLevelFromVersion(ver.String()), qt.Equals, logg.LevelError)
|
||||
|
||||
// Added just to find the threshold for where we can remove deprecated items.
|
||||
|
||||
@@ -18,6 +18,6 @@ package hugo
|
||||
var CurrentVersion = Version{
|
||||
Major: 0,
|
||||
Minor: 143,
|
||||
PatchLevel: 0,
|
||||
Suffix: "-DEV",
|
||||
PatchLevel: 1,
|
||||
Suffix: "",
|
||||
}
|
||||
|
||||
@@ -73,10 +73,14 @@ func TestPrepareParams(t *testing.T) {
|
||||
for i, test := range tests {
|
||||
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||
// PrepareParams modifies input.
|
||||
prepareClone := PrepareParamsClone(test.input)
|
||||
PrepareParams(test.input)
|
||||
if !reflect.DeepEqual(test.expected, test.input) {
|
||||
t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, test.input)
|
||||
}
|
||||
if !reflect.DeepEqual(test.expected, prepareClone) {
|
||||
t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, prepareClone)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+40
-1
@@ -303,7 +303,7 @@ func toMergeStrategy(v any) ParamsMergeStrategy {
|
||||
}
|
||||
|
||||
// PrepareParams
|
||||
// * makes all the keys in the given map lower cased and will do so
|
||||
// * makes all the keys in the given map lower cased and will do so recursively.
|
||||
// * This will modify the map given.
|
||||
// * Any nested map[interface{}]interface{}, map[string]interface{},map[string]string will be converted to Params.
|
||||
// * Any _merge value will be converted to proper type and value.
|
||||
@@ -343,3 +343,42 @@ func PrepareParams(m Params) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PrepareParamsClone is like PrepareParams, but it does not modify the input.
|
||||
func PrepareParamsClone(m Params) Params {
|
||||
m2 := make(Params)
|
||||
for k, v := range m {
|
||||
var retyped bool
|
||||
lKey := strings.ToLower(k)
|
||||
if lKey == MergeStrategyKey {
|
||||
v = toMergeStrategy(v)
|
||||
retyped = true
|
||||
} else {
|
||||
switch vv := v.(type) {
|
||||
case map[any]any:
|
||||
var p Params = cast.ToStringMap(v)
|
||||
v = PrepareParamsClone(p)
|
||||
retyped = true
|
||||
case map[string]any:
|
||||
var p Params = v.(map[string]any)
|
||||
v = PrepareParamsClone(p)
|
||||
retyped = true
|
||||
case map[string]string:
|
||||
p := make(Params)
|
||||
for k, v := range vv {
|
||||
p[k] = v
|
||||
}
|
||||
v = p
|
||||
PrepareParams(p)
|
||||
retyped = true
|
||||
}
|
||||
}
|
||||
|
||||
if retyped || k != lKey {
|
||||
m2[lKey] = v
|
||||
} else {
|
||||
m2[k] = v
|
||||
}
|
||||
}
|
||||
return m2
|
||||
}
|
||||
|
||||
+48
-26
@@ -1123,6 +1123,9 @@ func (h *HugoSites) resolveAndClearStateForIdentities(
|
||||
l logg.LevelLogger,
|
||||
cachebuster func(s string) bool, changes []identity.Identity,
|
||||
) error {
|
||||
// Drain the cache eviction stack to start fresh.
|
||||
evictedStart := h.Deps.MemCache.DrainEvictedIdentities()
|
||||
|
||||
h.Log.Debug().Log(logg.StringFunc(
|
||||
func() string {
|
||||
var sb strings.Builder
|
||||
@@ -1163,17 +1166,32 @@ func (h *HugoSites) resolveAndClearStateForIdentities(
|
||||
}
|
||||
|
||||
// The order matters here:
|
||||
// 1. Handle the cache busters first, as those may produce identities for the page reset step.
|
||||
// 1. Then GC the cache, which may produce changes.
|
||||
// 2. Then reset the page outputs, which may mark some resources as stale.
|
||||
// 3. Then GC the cache.
|
||||
if cachebuster != nil {
|
||||
if err := loggers.TimeTrackfn(func() (logg.LevelLogger, error) {
|
||||
ll := l.WithField("substep", "gc dynacache cachebuster")
|
||||
h.dynacacheGCCacheBuster(cachebuster)
|
||||
return ll, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
if err := loggers.TimeTrackfn(func() (logg.LevelLogger, error) {
|
||||
ll := l.WithField("substep", "gc dynacache")
|
||||
|
||||
predicate := func(k any, v any) bool {
|
||||
if cachebuster != nil {
|
||||
if s, ok := k.(string); ok {
|
||||
return cachebuster(s)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
h.MemCache.ClearOnRebuild(predicate, changes...)
|
||||
h.Log.Trace(logg.StringFunc(func() string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString("dynacache keys:\n")
|
||||
for _, key := range h.MemCache.Keys(nil) {
|
||||
sb.WriteString(fmt.Sprintf(" %s\n", key))
|
||||
}
|
||||
return sb.String()
|
||||
}))
|
||||
return ll, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Drain the cache eviction stack.
|
||||
@@ -1182,6 +1200,27 @@ func (h *HugoSites) resolveAndClearStateForIdentities(
|
||||
for _, c := range evicted {
|
||||
changes = append(changes, c.Identity)
|
||||
}
|
||||
|
||||
if len(evictedStart) > 0 {
|
||||
// In low memory situations and/or very big sites, there can be a lot of unrelated evicted items,
|
||||
// but there's a chance that some of them are related to the changes we are about to process,
|
||||
// so check.
|
||||
depsFinder := identity.NewFinder(identity.FinderConfig{})
|
||||
var addends []identity.Identity
|
||||
for _, ev := range evictedStart {
|
||||
for _, id := range changes {
|
||||
if cachebuster != nil && cachebuster(ev.Key.(string)) {
|
||||
addends = append(addends, ev.Identity)
|
||||
break
|
||||
}
|
||||
if r := depsFinder.Contains(id, ev.Identity, -1); r > 0 {
|
||||
addends = append(addends, ev.Identity)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
changes = append(changes, addends...)
|
||||
}
|
||||
} else {
|
||||
// Mass eviction, we might as well invalidate everything.
|
||||
changes = []identity.Identity{identity.GenghisKhan}
|
||||
@@ -1238,23 +1277,6 @@ func (h *HugoSites) resolveAndClearStateForIdentities(
|
||||
return err
|
||||
}
|
||||
|
||||
if err := loggers.TimeTrackfn(func() (logg.LevelLogger, error) {
|
||||
ll := l.WithField("substep", "gc dynacache")
|
||||
|
||||
h.MemCache.ClearOnRebuild(changes...)
|
||||
h.Log.Trace(logg.StringFunc(func() string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString("dynacache keys:\n")
|
||||
for _, key := range h.MemCache.Keys(nil) {
|
||||
sb.WriteString(fmt.Sprintf(" %s\n", key))
|
||||
}
|
||||
return sb.String()
|
||||
}))
|
||||
return ll, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -827,6 +827,11 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo
|
||||
addedContentPaths []*paths.Path
|
||||
)
|
||||
|
||||
var (
|
||||
addedOrChangedContent []pathChange
|
||||
changes []identity.Identity
|
||||
)
|
||||
|
||||
for _, ev := range eventInfos {
|
||||
cpss := h.BaseFs.ResolvePaths(ev.Name)
|
||||
pss := make([]*paths.Path, len(cpss))
|
||||
@@ -853,6 +858,13 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo
|
||||
if err == nil && g != nil {
|
||||
cacheBusters = append(cacheBusters, g)
|
||||
}
|
||||
|
||||
if ev.added {
|
||||
changes = append(changes, identity.StructuralChangeAdd)
|
||||
}
|
||||
if ev.removed {
|
||||
changes = append(changes, identity.StructuralChangeRemove)
|
||||
}
|
||||
}
|
||||
|
||||
if ev.removed {
|
||||
@@ -864,11 +876,6 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
addedOrChangedContent []pathChange
|
||||
changes []identity.Identity
|
||||
)
|
||||
|
||||
// Find the most specific identity possible.
|
||||
handleChange := func(pathInfo *paths.Path, delete, isDir bool) {
|
||||
switch pathInfo.Component() {
|
||||
|
||||
@@ -96,3 +96,51 @@ Figure:
|
||||
|
||||
b.AssertFileContent("public/index.xml", "img src="http://example.com/images/sunset.jpg")
|
||||
}
|
||||
|
||||
// Issue 13332.
|
||||
func TestRSSCanonifyURLsSubDir(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
baseURL = 'https://example.org/subdir'
|
||||
disableKinds = ['section','sitemap','taxonomy','term']
|
||||
[markup.goldmark.renderHooks.image]
|
||||
enableDefault = true
|
||||
[markup.goldmark.renderHooks.link]
|
||||
enableDefault = true
|
||||
-- layouts/_default/_markup/render-image.html --
|
||||
{{- $u := urls.Parse .Destination -}}
|
||||
{{- $src := $u.String | relURL -}}
|
||||
<img srcset="{{ $src }}" src="{{ $src }} 2x">
|
||||
<img src="{{ $src }}">
|
||||
{{- /**/ -}}
|
||||
-- layouts/_default/home.html --
|
||||
{{ .Content }}|
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Content }}|
|
||||
-- layouts/_default/rss.xml --
|
||||
{{ with site.GetPage "/s1/p2" }}
|
||||
{{ .Content | transform.XMLEscape | safeHTML }}
|
||||
{{ end }}
|
||||
-- content/s1/p1.md --
|
||||
---
|
||||
title: p1
|
||||
---
|
||||
-- content/s1/p2/index.md --
|
||||
---
|
||||
title: p2
|
||||
---
|
||||

|
||||
|
||||
[p1](/s1/p1)
|
||||
-- content/s1/p2/a.jpg --
|
||||
`
|
||||
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/index.xml", "https://example.org/subdir/s1/p1/")
|
||||
b.AssertFileContent("public/index.xml",
|
||||
"img src="https://example.org/subdir/a.jpg",
|
||||
"img srcset="https://example.org/subdir/a.jpg" src="https://example.org/subdir/a.jpg 2x")
|
||||
}
|
||||
|
||||
+1
-1
@@ -1542,7 +1542,7 @@ func (s *Site) render(ctx *siteRenderContext) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
if ctx.outIdx == 0 {
|
||||
if ctx.outIdx == 0 && s.h.buildCounter.Load() == 0 {
|
||||
// Note that even if disableAliases is set, the aliases themselves are
|
||||
// preserved on page. The motivation with this is to be able to generate
|
||||
// 301 redirects in a .htaccess file and similar using a custom output format.
|
||||
|
||||
+3
-2
@@ -1,7 +1,8 @@
|
||||
# Release env.
|
||||
# These will be replaced by script before release.
|
||||
HUGORELEASER_TAG=v0.142.0
|
||||
HUGORELEASER_COMMITISH=1f746a872442e66b6afd47c8c04ac42dc92cdb6f
|
||||
HUGORELEASER_TAG=v0.143.0
|
||||
HUGORELEASER_COMMITISH=ee48d9692af281180aea00645d86f3231a5231df
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -33,6 +33,9 @@ const (
|
||||
|
||||
// GenghisKhan is an Identity everyone relates to.
|
||||
GenghisKhan = StringIdentity("__genghiskhan")
|
||||
|
||||
StructuralChangeAdd = StringIdentity("__structural_change_add")
|
||||
StructuralChangeRemove = StringIdentity("__structural_change_remove")
|
||||
)
|
||||
|
||||
var NopManager = new(nopManager)
|
||||
|
||||
+17
-56
@@ -1,11 +1,9 @@
|
||||
//go:build mage
|
||||
// +build mage
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
@@ -36,10 +34,6 @@ func init() {
|
||||
if exe := os.Getenv("GOEXE"); exe != "" {
|
||||
goexe = exe
|
||||
}
|
||||
|
||||
// We want to use Go 1.11 modules even if the source lives inside GOPATH.
|
||||
// The default is "auto".
|
||||
os.Setenv("GO111MODULE", "on")
|
||||
}
|
||||
|
||||
func runWith(env map[string]string, cmd string, inArgs ...any) error {
|
||||
@@ -122,10 +116,10 @@ func HugoNoGitInfo() error {
|
||||
return Hugo()
|
||||
}
|
||||
|
||||
var docker = sh.RunCmd("docker")
|
||||
|
||||
// Build hugo Docker container
|
||||
func Docker() error {
|
||||
docker := sh.RunCmd("docker")
|
||||
|
||||
if err := docker("build", "-t", "hugo", "."); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -148,7 +142,7 @@ func Check() {
|
||||
fmt.Printf("Skip Test386 on %s and/or %s\n", runtime.GOARCH, runtime.GOOS)
|
||||
}
|
||||
|
||||
if isCi() && isDarwin() {
|
||||
if isCI() && isDarwin() {
|
||||
// Skip on macOS in CI (disk space issues)
|
||||
} else {
|
||||
mg.Deps(Fmt, Vet)
|
||||
@@ -200,56 +194,19 @@ func Fmt() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
pkgPrefixLen = len("github.com/gohugoio/hugo")
|
||||
pkgs []string
|
||||
pkgsInit sync.Once
|
||||
)
|
||||
const pkgPrefixLen = len("github.com/gohugoio/hugo")
|
||||
|
||||
func hugoPackages() ([]string, error) {
|
||||
var err error
|
||||
pkgsInit.Do(func() {
|
||||
var s string
|
||||
s, err = sh.Output(goexe, "list", "./...")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pkgs = strings.Split(s, "\n")
|
||||
for i := range pkgs {
|
||||
pkgs[i] = "." + pkgs[i][pkgPrefixLen:]
|
||||
}
|
||||
})
|
||||
return pkgs, err
|
||||
}
|
||||
|
||||
// Run golint linter
|
||||
func Lint() error {
|
||||
pkgs, err := hugoPackages()
|
||||
var hugoPackages = sync.OnceValues(func() ([]string, error) {
|
||||
s, err := sh.Output(goexe, "list", "./...")
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
failed := false
|
||||
for _, pkg := range pkgs {
|
||||
// We don't actually want to fail this target if we find golint errors,
|
||||
// so we don't pass -set_exit_status, but we still print out any failures.
|
||||
if _, err := sh.Exec(nil, os.Stderr, nil, "golint", pkg); err != nil {
|
||||
fmt.Printf("ERROR: running go lint on %q: %v\n", pkg, err)
|
||||
failed = true
|
||||
}
|
||||
pkgs := strings.Split(s, "\n")
|
||||
for i := range pkgs {
|
||||
pkgs[i] = "." + pkgs[i][pkgPrefixLen:]
|
||||
}
|
||||
if failed {
|
||||
return errors.New("errors running golint")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isCi() bool {
|
||||
return os.Getenv("CI") != ""
|
||||
}
|
||||
|
||||
func isDarwin() bool {
|
||||
return runtime.GOOS == "darwin"
|
||||
}
|
||||
return pkgs, nil
|
||||
})
|
||||
|
||||
// Run go vet linter
|
||||
func Vet() error {
|
||||
@@ -270,7 +227,7 @@ func TestCoverHTML() error {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
if _, err := f.Write([]byte("mode: count")); err != nil {
|
||||
if _, err := f.WriteString("mode: count"); err != nil {
|
||||
return err
|
||||
}
|
||||
pkgs, err := hugoPackages()
|
||||
@@ -320,6 +277,10 @@ func isUnix() bool {
|
||||
return runtime.GOOS != "windows"
|
||||
}
|
||||
|
||||
func isDarwin() bool {
|
||||
return runtime.GOOS == "darwin"
|
||||
}
|
||||
|
||||
func isCI() bool {
|
||||
return os.Getenv("CI") != ""
|
||||
}
|
||||
|
||||
@@ -158,8 +158,11 @@ 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)
|
||||
}
|
||||
maps.PrepareParams(p.Params)
|
||||
|
||||
if p.Content.Markup == "" && p.Content.MediaType == "" {
|
||||
if ext == "" {
|
||||
|
||||
@@ -95,6 +95,10 @@ func (c *Client) Concat(targetPath string, r resource.Resources) (resource.Resou
|
||||
}
|
||||
|
||||
idm := c.rs.Cfg.NewIdentityManager("concat")
|
||||
|
||||
// Re-create on structural changes.
|
||||
idm.AddIdentity(identity.StructuralChangeAdd, identity.StructuralChangeRemove)
|
||||
|
||||
// Add the concatenated resources as dependencies to the composite resource
|
||||
// so that we can track changes to the individual resources.
|
||||
idm.AddIdentityForEach(identity.ForEeachIdentityProviderFunc(
|
||||
|
||||
@@ -36,8 +36,8 @@ func TestTailwindV4Basic(t *testing.T) {
|
||||
"url": "https://github.com/bep/hugo-starter-tailwind-basic.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/cli": "^4.0.0-alpha.26",
|
||||
"tailwindcss": "^4.0.0-alpha.26"
|
||||
"@tailwindcss/cli": "^4.0.1",
|
||||
"tailwindcss": "^4.0.1"
|
||||
},
|
||||
"name": "hugo-starter-tailwind-basic",
|
||||
"version": "0.1.0"
|
||||
@@ -68,5 +68,5 @@ CSS: {{ $css.Content | safeCSS }}|
|
||||
LogLevel: logg.LevelInfo,
|
||||
}).Build()
|
||||
|
||||
b.AssertFileContent("public/index.html", "/*! tailwindcss v4.0.0")
|
||||
b.AssertFileContent("public/index.html", "/*! tailwindcss v4.")
|
||||
}
|
||||
|
||||
@@ -388,7 +388,6 @@ func (r *resourceAdapter) getImageOps() images.ImageResourceOps {
|
||||
if r.MediaType().SubType == "svg" {
|
||||
panic("this method is only available for raster images. To determine if an image is SVG, you can do {{ if eq .MediaType.SubType \"svg\" }}{{ end }}")
|
||||
}
|
||||
fmt.Println(r.MediaType().SubType)
|
||||
panic("this method is only available for image resources")
|
||||
}
|
||||
r.init(false, false)
|
||||
|
||||
+2
-2
@@ -171,7 +171,7 @@ func (ns *Namespace) TestDeprecationInfo(item, alternative string) string {
|
||||
// Internal template func, used in tests only.
|
||||
func (ns *Namespace) TestDeprecationWarn(item, alternative string) string {
|
||||
v := hugo.CurrentVersion
|
||||
v.Minor -= 6
|
||||
v.Minor -= 3
|
||||
hugo.Deprecate(item, alternative, v.String())
|
||||
return ""
|
||||
}
|
||||
@@ -179,7 +179,7 @@ func (ns *Namespace) TestDeprecationWarn(item, alternative string) string {
|
||||
// Internal template func, used in tests only.
|
||||
func (ns *Namespace) TestDeprecationErr(item, alternative string) string {
|
||||
v := hugo.CurrentVersion
|
||||
v.Minor -= 12
|
||||
v.Minor -= 15
|
||||
hugo.Deprecate(item, alternative, v.String())
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ Renders an embedded YouTube video.
|
||||
{{- $allow := "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" }}
|
||||
{{- $referrerpolicy := "strict-origin-when-cross-origin" }}
|
||||
|
||||
{{- /* Render. */}}
|
||||
{{- /* Render. */ -}}
|
||||
<div
|
||||
{{- with $class }} class="{{ . }}" {{- end }}
|
||||
{{- with $divStyle }} style="{{ . | safeCSS }}" {{- end -}}
|
||||
|
||||
@@ -675,12 +675,12 @@ title: p2
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/p1/index.html", "515600e76b272f51")
|
||||
b.AssertFileContent("public/p2/index.html", "b5ceeace7dfa797a")
|
||||
b.AssertFileContent("public/p1/index.html", "a0a6f5ade9cc3a9f")
|
||||
b.AssertFileContent("public/p2/index.html", "289c655e727e596c")
|
||||
|
||||
files = strings.ReplaceAll(files, "privacy.youtube.privacyEnhanced = false", "privacy.youtube.privacyEnhanced = true")
|
||||
|
||||
b = hugolib.Test(t, files)
|
||||
b.AssertFileContent("public/p1/index.html", "e92c7f4b768d7e23")
|
||||
b.AssertFileContent("public/p2/index.html", "c384e83e035b71d9")
|
||||
b.AssertFileContent("public/p1/index.html", "b76d790c20d2bd04")
|
||||
b.AssertFileContent("public/p2/index.html", "a6db910a9cf54bc1")
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
|
||||
package urlreplacers
|
||||
|
||||
import "github.com/gohugoio/hugo/transform"
|
||||
import (
|
||||
"github.com/gohugoio/hugo/transform"
|
||||
)
|
||||
|
||||
var ar = newAbsURLReplacer()
|
||||
|
||||
|
||||
@@ -16,9 +16,11 @@ package urlreplacers
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/url"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/gohugoio/hugo/common/paths"
|
||||
"github.com/gohugoio/hugo/transform"
|
||||
)
|
||||
|
||||
@@ -31,6 +33,9 @@ type absurllexer struct {
|
||||
// path may be set to a "." relative path
|
||||
path []byte
|
||||
|
||||
// The root path, without leading slash.
|
||||
root []byte
|
||||
|
||||
pos int // input position
|
||||
start int // item start position
|
||||
|
||||
@@ -119,6 +124,9 @@ func checkCandidateBase(l *absurllexer) {
|
||||
}
|
||||
l.pos += relURLPrefixLen
|
||||
l.w.Write(l.path)
|
||||
if len(l.root) > 0 && bytes.HasPrefix(l.content[l.pos:], l.root) {
|
||||
l.pos += len(l.root)
|
||||
}
|
||||
l.start = l.pos
|
||||
}
|
||||
|
||||
@@ -174,7 +182,11 @@ func checkCandidateSrcset(l *absurllexer) {
|
||||
for i, f := range fields {
|
||||
if f[0] == '/' {
|
||||
l.w.Write(l.path)
|
||||
l.w.Write(f[1:])
|
||||
n := 1
|
||||
if len(l.root) > 0 && bytes.HasPrefix(f[n:], l.root) {
|
||||
n += len(l.root)
|
||||
}
|
||||
l.w.Write(f[n:])
|
||||
|
||||
} else {
|
||||
l.w.Write(f)
|
||||
@@ -229,10 +241,15 @@ func (l *absurllexer) replace() {
|
||||
}
|
||||
|
||||
func doReplace(path string, ct transform.FromTo, quotes [][]byte) {
|
||||
var root string
|
||||
if u, err := url.Parse(path); err == nil {
|
||||
root = paths.TrimLeading(u.Path)
|
||||
}
|
||||
lexer := &absurllexer{
|
||||
content: ct.From().Bytes(),
|
||||
w: ct.To(),
|
||||
path: []byte(path),
|
||||
root: []byte(root),
|
||||
quotes: quotes,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user