Skip to content

Commit

Permalink
Add js.Batch
Browse files Browse the repository at this point in the history
Fixes #12626
Closes #7499
Closes #12874
  • Loading branch information
bep committed Nov 20, 2024
1 parent 59a55a1 commit 6c58482
Show file tree
Hide file tree
Showing 45 changed files with 3,720 additions and 935 deletions.
1 change: 1 addition & 0 deletions commands/commandeer.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
r.hugoSites = lazycache.New(lazycache.Options[configKey, *hugolib.HugoSites]{
MaxEntries: 1,
OnEvict: func(key configKey, value *hugolib.HugoSites) {
fmt.Println("Evicting HugoSites", key) // TODO1 remove me.
value.Close()
runtime.GC()
},
Expand Down
15 changes: 15 additions & 0 deletions common/herrors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,21 @@ func IsNotExist(err error) bool {
return false
}

// IsExist returns true if the error is a file exists error.
// Unlike os.IsExist, this also considers wrapped errors.
func IsExist(err error) bool {
if os.IsExist(err) {
return true
}

// os.IsExist does not consider wrapped errors.
if os.IsExist(errors.Unwrap(err)) {
return true
}

return false
}

var nilPointerErrRe = regexp.MustCompile(`at <(.*)>: error calling (.*?): runtime error: invalid memory address or nil pointer dereference`)

const deferredPrefix = "__hdeferred/"
Expand Down
7 changes: 5 additions & 2 deletions common/maps/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,14 @@ func (c *Cache[K, T]) set(key K, value T) {
}

// ForEeach calls the given function for each key/value pair in the cache.
func (c *Cache[K, T]) ForEeach(f func(K, T)) {
// If the function returns false, the iteration stops.
func (c *Cache[K, T]) ForEeach(f func(K, T) bool) {
c.RLock()
defer c.RUnlock()
for k, v := range c.m {
f(k, v)
if !f(k, v) {
return
}
}
}

Expand Down
41 changes: 38 additions & 3 deletions common/maps/scratch.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ type StoreProvider interface {

// Scratch is a writable context used for stateful build operations
type Scratch struct {
values map[string]any
mu sync.RWMutex
values map[string]any
namespaces map[string]*Scratch
mu sync.RWMutex
}

// Add will, for single values, add (using the + operator) the addend to the existing addend (if found).
Expand Down Expand Up @@ -94,6 +95,40 @@ func (c *Scratch) Get(key string) any {
return val
}

// Namespace returns a new Scratch namespace with the given name.
// An empty name is the default namespace.
// Note that Hugo may create namespaces for internal use prefixed with "_".
func (c *Scratch) Namespace(name string) *Scratch {
c.mu.Lock()
defer c.mu.Unlock()
if name == "" {
return c
}
ns, found := c.namespaces[name]
if !found {
ns = NewScratch()
c.namespaces[name] = ns
}
return ns
}

// GetOrCreate returns the value for the given key if it exists, or creates it
// using the given func and stores that value in the map.
// For internal use.
func (c *Scratch) GetOrCreate(key string, create func() (any, error)) (any, error) {
c.mu.Lock()
defer c.mu.Unlock()
if val, found := c.values[key]; found {
return val, nil
}
val, err := create()
if err != nil {
return nil, err
}
c.values[key] = val
return val, nil
}

// Values returns the raw backing map. Note that you should just use
// this method on the locally scoped Scratch instances you obtain via newScratch, not
// .Page.Scratch etc., as that will lead to concurrency issues.
Expand Down Expand Up @@ -156,5 +191,5 @@ func (c *Scratch) GetSortedMapValues(key string) any {

// NewScratch returns a new instance of Scratch.
func NewScratch() *Scratch {
return &Scratch{values: make(map[string]any)}
return &Scratch{values: make(map[string]any), namespaces: make(map[string]*Scratch)}
}
17 changes: 17 additions & 0 deletions common/maps/scratch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,23 @@ func TestScratchAdd(t *testing.T) {
}
}

func TestNamespace(t *testing.T) {
t.Parallel()
c := qt.New(t)

scratch := NewScratch()
scratch.Add("int1", 10)
scratch.Add("int1", 20)

ns := scratch.Namespace("ns1")
ns.Add("int1", 20)
ns.Add("int1", 30)

c.Assert(scratch.Get("int1"), qt.Equals, int64(30))
c.Assert(ns.Get("int1"), qt.Equals, int64(50))
c.Assert(scratch.Namespace("ns1").Get("int1"), qt.Equals, int64(50))
}

func TestScratchAddSlice(t *testing.T) {
t.Parallel()
c := qt.New(t)
Expand Down
7 changes: 7 additions & 0 deletions common/types/closer.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ type Closer interface {
Close() error
}

// CloserFunc is a convenience type to create a Closer from a function.
type CloserFunc func() error

func (f CloserFunc) Close() error {
return f()
}

type CloseAdder interface {
Add(Closer)
}
Expand Down
4 changes: 2 additions & 2 deletions config/allconfig/configlanguage.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,11 @@ func (c ConfigLanguage) Watching() bool {
return c.m.Base.Internal.Watch
}

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

func (c ConfigLanguage) ContentTypes() config.ContentTypesProvider {
Expand Down
2 changes: 1 addition & 1 deletion config/configProvider.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ type AllProvider interface {
BuildDrafts() bool
Running() bool
Watching() bool
NewIdentityManager(name string) identity.Manager
NewIdentityManager(name string, opts ...identity.ManagerOption) identity.Manager
FastRenderMode() bool
PrintUnusedTemplates() bool
EnableMissingTranslationPlaceholders() bool
Expand Down
15 changes: 15 additions & 0 deletions deps/deps.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
Expand Down Expand Up @@ -268,6 +269,20 @@ func (d *Deps) Compile(prototype *Deps) error {
return nil
}

// MkdirTemp returns a temporary directory path that will be cleaned up on exit.
func (d Deps) MkdirTemp(pattern string) (string, error) {
filename, err := os.MkdirTemp("", pattern)
if err != nil {
return "", err
}
d.BuildClosers.Add(types.CloserFunc(
func() error {
return os.RemoveAll(filename)
}))

return filename, nil
}

type globalErrHandler struct {
logger loggers.Logger

Expand Down
4 changes: 4 additions & 0 deletions hugolib/hugo_sites.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ func (h *HugoSites) ShouldSkipFileChangeEvent(ev fsnotify.Event) bool {
return h.skipRebuildForFilenames[ev.Name]
}

func (h *HugoSites) Close() error {
return h.Deps.Close()
}

func (h *HugoSites) isRebuild() bool {
return h.buildCounter.Load() > 0
}
Expand Down
3 changes: 2 additions & 1 deletion hugolib/hugo_sites_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,8 +520,9 @@ func (s *Site) executeDeferredTemplates(de *deps.DeferredExecutions) error {
},
})

de.FilenamesWithPostPrefix.ForEeach(func(filename string, _ bool) {
de.FilenamesWithPostPrefix.ForEeach(func(filename string, _ bool) bool {
g.Enqueue(filename)
return true
})

return g.Wait()
Expand Down
21 changes: 18 additions & 3 deletions hugolib/integrationtest_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ func TestOptWithNFDOnDarwin() TestOpt {
}
}

// TestOptWithOSFs enables the real file system.
func TestOptWithOSFs() TestOpt {
return func(c *IntegrationTestConfig) {
c.NeedsOsFS = true
}
}

// TestOptWithWorkingDir allows setting any config optiona as a function al option.
func TestOptWithConfig(fn func(c *IntegrationTestConfig)) TestOpt {
return func(c *IntegrationTestConfig) {
Expand Down Expand Up @@ -284,8 +291,9 @@ func (s *IntegrationTestBuilder) negate(match string) (string, bool) {
func (s *IntegrationTestBuilder) AssertFileContent(filename string, matches ...string) {
s.Helper()
content := strings.TrimSpace(s.FileContent(filename))

for _, m := range matches {
cm := qt.Commentf("File: %s Match %s", filename, m)
cm := qt.Commentf("File: %s Match %s\nContent:\n%s", filename, m, content)
lines := strings.Split(m, "\n")
for _, match := range lines {
match = strings.TrimSpace(match)
Expand All @@ -295,7 +303,8 @@ func (s *IntegrationTestBuilder) AssertFileContent(filename string, matches ...s
var negate bool
match, negate = s.negate(match)
if negate {
s.Assert(content, qt.Not(qt.Contains), match, cm)
if !s.Assert(content, qt.Not(qt.Contains), match, cm) {
}
continue
}
s.Assert(content, qt.Contains, match, cm)
Expand All @@ -313,7 +322,8 @@ func (s *IntegrationTestBuilder) AssertFileContentExact(filename string, matches
s.Helper()
content := s.FileContent(filename)
for _, m := range matches {
s.Assert(content, qt.Contains, m, qt.Commentf(m))
cm := qt.Commentf("File: %s Match %s\nContent:\n%s", filename, m, content)
s.Assert(content, qt.Contains, m, cm)
}
}

Expand Down Expand Up @@ -450,6 +460,11 @@ func (s *IntegrationTestBuilder) Build() *IntegrationTestBuilder {
return s
}

func (s *IntegrationTestBuilder) Close() {
s.Helper()
s.Assert(s.H.Close(), qt.IsNil)
}

func (s *IntegrationTestBuilder) LogString() string {
return s.lastBuildLog
}
Expand Down
10 changes: 9 additions & 1 deletion hugolib/pages_capture.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,15 @@ func (c *pagesCollector) Collect() (collectErr error) {
id.p,
false,
func(fim hugofs.FileMetaInfo) bool {
return true
if id.isStructuralChange() {
return true
}
fimp := fim.Meta().PathInfo
if fimp == nil {
return true
}

return fimp.Path() == id.p.Path()
},
)
} else if id.p.IsBranchBundle() {
Expand Down
3 changes: 2 additions & 1 deletion hugolib/pagesfromdata/pagesfromgotmpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,11 @@ func (b *BuildState) resolveDeletedPaths() {
return
}
var paths []string
b.sourceInfosPrevious.ForEeach(func(k string, _ *sourceInfo) {
b.sourceInfosPrevious.ForEeach(func(k string, _ *sourceInfo) bool {
if _, found := b.sourceInfosCurrent.Get(k); !found {
paths = append(paths, k)
}
return true
})

b.DeletedPaths = paths
Expand Down
12 changes: 12 additions & 0 deletions hugolib/rebuild_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,18 @@ Foo.
`

func TestRebuildEditLeafBundleHeaderOnly(t *testing.T) {
b := TestRunning(t, rebuildFilesSimple)
b.AssertFileContent("public/mysection/mysectionbundle/index.html",
"My Section Bundle Content Content.")

b.EditFileReplaceAll("content/mysection/mysectionbundle/index.md", "My Section Bundle Content.", "My Section Bundle Content Edited.").Build()
b.AssertFileContent("public/mysection/mysectionbundle/index.html",
"My Section Bundle Content Edited.")
b.AssertRenderCountPage(1)
b.AssertRenderCountContent(1)
}

func TestRebuildEditTextFileInLeafBundle(t *testing.T) {
b := TestRunning(t, rebuildFilesSimple)
b.AssertFileContent("public/mysection/mysectionbundle/index.html",
Expand Down
6 changes: 5 additions & 1 deletion hugolib/site.go
Original file line number Diff line number Diff line change
Expand Up @@ -1493,7 +1493,11 @@ func (s *Site) renderForTemplate(ctx context.Context, name, outputFormat string,
}

if err = s.Tmpl().ExecuteWithContext(ctx, templ, w, d); err != nil {
return fmt.Errorf("render of %q failed: %w", name, err)
filename := name
if p, ok := d.(*pageState); ok {
filename = p.pathOrTitle()
}
return fmt.Errorf("render of %q failed: %w", filename, err)
}
return
}
Expand Down
6 changes: 3 additions & 3 deletions identity/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,8 @@ func FirstIdentity(v any) Identity {
var result Identity = Anonymous
WalkIdentitiesShallow(v, func(level int, id Identity) bool {
result = id
return true
return result != Anonymous
})

return result
}

Expand Down Expand Up @@ -308,19 +307,20 @@ type identityManager struct {

func (im *identityManager) AddIdentity(ids ...Identity) {
im.mu.Lock()
defer im.mu.Unlock()

for _, id := range ids {
if id == nil || id == Anonymous {
continue
}

if _, found := im.ids[id]; !found {
if im.onAddIdentity != nil {
im.onAddIdentity(id)
}
im.ids[id] = true
}
}
im.mu.Unlock()
}

func (im *identityManager) AddIdentityForEach(ids ...ForEeachIdentityProvider) {
Expand Down
22 changes: 22 additions & 0 deletions internal/js/esbuild/batch-esm-runner.gotmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{{ range $i, $e := .Scripts -}}
{{ if eq .Export "*" }}
{{ printf "import %s as Script%d from %q;" .Export $i .Import }}
{{ else }}
{{ printf "import { %s as Script%d } from %q;" .Export $i .Import }}
{{ end }}
{{ end -}}
{{ range $i, $e := .Runners }}
{{ printf "import { %s as Run%d } from %q;" .Export $i .Import }}
{{ end }}
{{/* */}}
{{ if .Runners }}
let scripts = [];
{{ range $i, $e := .Scripts -}}
scripts.push({{ .RunnerJSON $i }});
{{ end -}}
{{/* */}}
{{ range $i, $e := .Runners }}
{{ $id := printf "Run%d" $i }}
{{ $id }}(scripts);
{{ end }}
{{ end }}
Loading

0 comments on commit 6c58482

Please sign in to comment.