Skip to content

Commit

Permalink
Fix rebuild with resources.Concat
Browse files Browse the repository at this point in the history
Fixes #12017
  • Loading branch information
bep committed Feb 16, 2024
1 parent 21d9057 commit 639073e
Show file tree
Hide file tree
Showing 18 changed files with 229 additions and 120 deletions.
71 changes: 57 additions & 14 deletions common/paths/pathparser.go
Expand Up @@ -18,9 +18,11 @@ import (
"path/filepath"
"runtime"
"strings"
"sync"

"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/identity"
)

var defaultPathParser PathParser
Expand Down Expand Up @@ -50,27 +52,50 @@ func NormalizePathStringBasic(s string) string {
return s
}

// ParseIdentity parses component c with path s into a StringIdentity.
func (pp *PathParser) ParseIdentity(c, s string) identity.StringIdentity {
s = NormalizePathStringBasic(s)
p := getPath()
p.component = c
defer putPath(p)
p, err := pp.doParse(c, s, p)
if err != nil {
panic(err)
}
return identity.StringIdentity(p.IdentifierBase())
}

// Parse parses component c with path s into Path using Hugo's content path rules.
func (parser PathParser) Parse(c, s string) *Path {
p, err := parser.parse(c, s)
func (pp *PathParser) Parse(c, s string) *Path {
p, err := pp.parse(c, s)
if err != nil {
panic(err)
}
return p
}

func (pp *PathParser) newPath(component string) *Path {
return &Path{
component: component,
posContainerLow: -1,
posContainerHigh: -1,
posSectionHigh: -1,
posIdentifierLanguage: -1,
}
}

func (pp *PathParser) parse(component, s string) (*Path, error) {
ss := NormalizePathStringBasic(s)

p, err := pp.doParse(component, ss)
p, err := pp.doParse(component, ss, pp.newPath(component))
if err != nil {
return nil, err
}

if s != ss {
var err error
// Preserve the original case for titles etc.
p.unnormalized, err = pp.doParse(component, s)
p.unnormalized, err = pp.doParse(component, s, pp.newPath(component))

if err != nil {
return nil, err
Expand All @@ -82,15 +107,7 @@ func (pp *PathParser) parse(component, s string) (*Path, error) {
return p, nil
}

func (pp *PathParser) doParse(component, s string) (*Path, error) {
p := &Path{
component: component,
posContainerLow: -1,
posContainerHigh: -1,
posSectionHigh: -1,
posIdentifierLanguage: -1,
}

func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) {
hasLang := pp.LanguageIndex != nil
hasLang = hasLang && (component == files.ComponentFolderContent || component == files.ComponentFolderLayouts)

Expand Down Expand Up @@ -220,6 +237,7 @@ const (
)

type Path struct {
// Note: Any additions to this struct should also be added to the pathPool.
s string

posContainerLow int
Expand All @@ -239,6 +257,31 @@ type Path struct {
unnormalized *Path
}

var pathPool = &sync.Pool{
New: func() any {
return &Path{}
},
}

func getPath() *Path {
return pathPool.Get().(*Path)
}

func putPath(p *Path) {
p.s = ""
p.posContainerLow = -1
p.posContainerHigh = -1
p.posSectionHigh = -1
p.component = ""
p.bundleType = 0
p.identifiers = p.identifiers[:0]
p.posIdentifierLanguage = -1
p.disabled = false
p.trimLeadingSlash = false
p.unnormalized = nil
pathPool.Put(p)
}

// TrimLeadingSlash returns a copy of the Path with the leading slash removed.
func (p Path) TrimLeadingSlash() *Path {
p.trimLeadingSlash = true
Expand All @@ -254,7 +297,7 @@ func (p *Path) norm(s string) string {

// IdentifierBase satifies identity.Identity.
func (p *Path) IdentifierBase() string {
return p.Base()[1:]
return p.Base()
}

// Component returns the component for this path (e.g. "content").
Expand Down
6 changes: 6 additions & 0 deletions common/paths/pathparser_test.go
Expand Up @@ -349,3 +349,9 @@ func TestHasExt(t *testing.T) {
c.Assert(HasExt("/a/b/c"), qt.IsFalse)
c.Assert(HasExt("/a/b.c/d"), qt.IsFalse)
}

func BenchmarkParseIdentity(b *testing.B) {
for i := 0; i < b.N; i++ {
testParser.ParseIdentity(files.ComponentFolderAssets, "/a/b.css")
}
}
4 changes: 2 additions & 2 deletions config/allconfig/allconfig.go
Expand Up @@ -663,7 +663,7 @@ type Configs struct {
// All below is set in Init.
Languages langs.Languages
LanguagesDefaultFirst langs.Languages
ContentPathParser paths.PathParser
ContentPathParser *paths.PathParser

configLangs []config.AllProvider
}
Expand Down Expand Up @@ -735,7 +735,7 @@ func (c *Configs) Init() error {
c.Languages = languages
c.LanguagesDefaultFirst = languagesDefaultFirst

c.ContentPathParser = paths.PathParser{LanguageIndex: languagesDefaultFirst.AsIndexSet(), IsLangDisabled: c.Base.IsLangDisabled}
c.ContentPathParser = &paths.PathParser{LanguageIndex: languagesDefaultFirst.AsIndexSet(), IsLangDisabled: c.Base.IsLangDisabled}

c.configLangs = make([]config.AllProvider, len(c.Languages))
for i, l := range c.LanguagesDefaultFirst {
Expand Down
10 changes: 9 additions & 1 deletion config/allconfig/configlanguage.go
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/common/urls"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/langs"
)

Expand All @@ -42,7 +43,7 @@ func (c ConfigLanguage) LanguagesDefaultFirst() langs.Languages {
return c.m.LanguagesDefaultFirst
}

func (c ConfigLanguage) PathParser() paths.PathParser {
func (c ConfigLanguage) PathParser() *paths.PathParser {
return c.m.ContentPathParser
}

Expand Down Expand Up @@ -133,6 +134,13 @@ func (c ConfigLanguage) Watching() bool {
return c.m.Base.Internal.Watch
}

func (c ConfigLanguage) NewIdentityManager(name string) identity.Manager {
if !c.Watching() {
return identity.NopManager
}
return identity.NewManager(name)
}

// GetConfigSection is mostly used in tests. The switch statement isn't complete, but what's in use.
func (c ConfigLanguage) GetConfigSection(s string) any {
switch s {
Expand Down
4 changes: 3 additions & 1 deletion config/configProvider.go
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/common/urls"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/langs"
)

Expand All @@ -31,7 +32,7 @@ type AllProvider interface {
LanguagePrefix() string
BaseURL() urls.BaseURL
BaseURLLiveReload() urls.BaseURL
PathParser() paths.PathParser
PathParser() *paths.PathParser
Environment() string
IsMultihost() bool
IsMultiLingual() bool
Expand All @@ -57,6 +58,7 @@ type AllProvider interface {
BuildDrafts() bool
Running() bool
Watching() bool
NewIdentityManager(name string) identity.Manager
FastRenderMode() bool
PrintUnusedTemplates() bool
EnableMissingTranslationPlaceholders() bool
Expand Down
2 changes: 1 addition & 1 deletion hugofs/component_fs.go
Expand Up @@ -241,7 +241,7 @@ type ComponentFsOptions struct {
DefaultContentLanguage string

// The parser used to parse paths provided by this filesystem.
PathParser paths.PathParser
PathParser *paths.PathParser
}

func (fs *componentFs) Open(name string) (afero.File, error) {
Expand Down
9 changes: 5 additions & 4 deletions hugolib/content_map.go
Expand Up @@ -93,8 +93,8 @@ func (r *resourceSource) GetIdentity() identity.Identity {
return r.path
}

func (r *resourceSource) ForEeachIdentity(f func(identity.Identity) bool) {
f(r.GetIdentity())
func (r *resourceSource) ForEeachIdentity(f func(identity.Identity) bool) bool {
return f(r.GetIdentity())
}

func (r *resourceSource) Path() string {
Expand Down Expand Up @@ -142,14 +142,15 @@ func (n resourceSources) GetIdentity() identity.Identity {
return nil
}

func (n resourceSources) ForEeachIdentity(f func(identity.Identity) bool) {
func (n resourceSources) ForEeachIdentity(f func(identity.Identity) bool) bool {
for _, r := range n {
if r != nil {
if f(r.GetIdentity()) {
return
return true
}
}
}
return false
}

func (cfg contentMapConfig) getTaxonomyConfig(s string) (v viewName) {
Expand Down
11 changes: 7 additions & 4 deletions hugolib/content_map_page.go
Expand Up @@ -605,12 +605,15 @@ func (n contentNodeIs) GetIdentity() identity.Identity {
return n[0].GetIdentity()
}

func (n contentNodeIs) ForEeachIdentity(f func(identity.Identity) bool) {
func (n contentNodeIs) ForEeachIdentity(f func(identity.Identity) bool) bool {
for _, nn := range n {
if nn != nil {
nn.ForEeachIdentity(f)
if nn.ForEeachIdentity(f) {
return true
}
}
}
return false
}

func (n contentNodeIs) resetBuildState() {
Expand Down Expand Up @@ -1151,7 +1154,7 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context,
// First check the top level dependency manager.
for _, id := range changes {
checkedCounter.Add(1)
if r := depsFinder.Contains(id, p.dependencyManager, 100); r > identity.FinderFoundOneOfManyRepetition {
if r := depsFinder.Contains(id, p.dependencyManager, 2); r > identity.FinderFoundOneOfManyRepetition {
for _, po := range p.pageOutputs {
resetPo(po, r)
}
Expand All @@ -1167,7 +1170,7 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context,
}
for _, id := range changes {
checkedCounter.Add(1)
if r := depsFinder.Contains(id, po.dependencyManagerOutput, 2); r > identity.FinderFoundOneOfManyRepetition {
if r := depsFinder.Contains(id, po.dependencyManagerOutput, 50); r > identity.FinderFoundOneOfManyRepetition {
resetPo(po, r)
continue OUTPUTS
}
Expand Down
4 changes: 2 additions & 2 deletions hugolib/page.go
Expand Up @@ -120,8 +120,8 @@ func (p *pageState) GetIdentity() identity.Identity {
return p
}

func (p *pageState) ForEeachIdentity(f func(identity.Identity) bool) {
f(p)
func (p *pageState) ForEeachIdentity(f func(identity.Identity) bool) bool {
return f(p)
}

func (p *pageState) GetDependencyManager() identity.Manager {
Expand Down
9 changes: 1 addition & 8 deletions hugolib/page__new.go
Expand Up @@ -20,7 +20,6 @@ import (
"sync/atomic"

"github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/resources"

"github.com/gohugoio/hugo/common/maps"
Expand Down Expand Up @@ -160,12 +159,6 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
return nil, nil
}

var dependencyManager identity.Manager = identity.NopManager

if m.s.conf.Internal.Watch {
dependencyManager = identity.NewManager(m.Path())
}

// Parse the rest of the page content.
m.content, err = m.newCachedContent(h, pi)
if err != nil {
Expand All @@ -178,7 +171,7 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
pageOutputTemplateVariationsState: &atomic.Uint32{},
resourcesPublishInit: &sync.Once{},
Staler: m,
dependencyManager: dependencyManager,
dependencyManager: m.s.Conf.NewIdentityManager(m.Path()),
pageCommon: &pageCommon{
FileProvider: m,
AuthorProvider: m,
Expand Down
7 changes: 1 addition & 6 deletions hugolib/page__output.go
Expand Up @@ -51,11 +51,6 @@ func newPageOutput(
})
}

var dependencyManager identity.Manager = identity.NopManager
if ps.s.conf.Internal.Watch {
dependencyManager = identity.NewManager(ps.Path() + "/" + f.Name)
}

providers := struct {
page.PaginatorProvider
resource.ResourceLinksProvider
Expand All @@ -75,7 +70,7 @@ func newPageOutput(
TableOfContentsProvider: page.NopPage,
render: render,
paginator: pag,
dependencyManagerOutput: dependencyManager,
dependencyManagerOutput: ps.s.Conf.NewIdentityManager((ps.Path() + "/" + f.Name)),
}

return po
Expand Down
34 changes: 33 additions & 1 deletion hugolib/rebuild_test.go
Expand Up @@ -1131,7 +1131,7 @@ Single.
Running: true,
NeedsOsFS: true,
NeedsNpmInstall: true,
// LogLevel: logg.LevelTrace,
// LogLevel: logg.LevelDebug,
},
).Build()

Expand Down Expand Up @@ -1261,3 +1261,35 @@ func BenchmarkRebuildContentFileChange(b *testing.B) {
// fmt.Println(bb.LogString())
}
}

func TestRebuildConcat(t *testing.T) {
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableLiveReload = true
disableKinds = ["taxonomy", "term", "sitemap", "robotsTXT", "404", "rss"]
-- assets/a.css --
a
-- assets/b.css --
b
-- assets/c.css --
c
-- assets/common/c1.css --
c1
-- assets/common/c2.css --
c2
-- layouts/index.html --
{{ $a := resources.Get "a.css" }}
{{ $b := resources.Get "b.css" }}
{{ $common := resources.Match "common/*.css" | resources.Concat "common.css" | minify }}
{{ $ab := slice $a $b $common | resources.Concat "ab.css" }}
all: {{ $ab.RelPermalink }}
`
b := TestRunning(t, files)

b.AssertFileContent("public/ab.css", "abc1c2")
b.EditFileReplaceAll("assets/common/c2.css", "c2", "c2 edited").Build()
b.AssertFileContent("public/ab.css", "abc1c2 edited")
b.AddFiles("assets/common/c3.css", "c3").Build()
b.AssertFileContent("public/ab.css", "abc1c2 editedc3")
}

0 comments on commit 639073e

Please sign in to comment.