Skip to content

Commit

Permalink
Add js.Batch
Browse files Browse the repository at this point in the history
  • Loading branch information
bep committed Oct 13, 2024
1 parent ffb41d1 commit c91b32b
Show file tree
Hide file tree
Showing 24 changed files with 2,334 additions and 207 deletions.
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
14 changes: 14 additions & 0 deletions common/maps/scratch.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,20 @@ func (c *Scratch) Get(key string) any {
return val
}

// 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) any {
c.mu.Lock()
defer c.mu.Unlock()
if val, found := c.values[key]; found {
return val
}
val := create()
c.values[key] = val
return val
}

// 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
Empty file added debug.log
Empty file.
16 changes: 13 additions & 3 deletions hugolib/integrationtest_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,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 @@ -280,8 +287,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 @@ -291,7 +299,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 @@ -303,7 +312,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
6 changes: 5 additions & 1 deletion hugolib/site.go
Original file line number Diff line number Diff line change
Expand Up @@ -1522,7 +1522,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
2 changes: 1 addition & 1 deletion lazy/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type Init struct {
prev *Init
children []*Init

init onceMore
init OnceMore
out any
err error
f func(context.Context) (any, error)
Expand Down
10 changes: 5 additions & 5 deletions lazy/once.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ import (
// * it can be reset, so the action can be repeated if needed
// * it has methods to check if it's done or in progress

type onceMore struct {
type OnceMore struct {
mu sync.Mutex
lock uint32
done uint32
}

func (t *onceMore) Do(f func()) {
func (t *OnceMore) Do(f func()) {
if atomic.LoadUint32(&t.done) == 1 {
return
}
Expand All @@ -53,15 +53,15 @@ func (t *onceMore) Do(f func()) {
f()
}

func (t *onceMore) InProgress() bool {
func (t *OnceMore) InProgress() bool {
return atomic.LoadUint32(&t.lock) == 1
}

func (t *onceMore) Done() bool {
func (t *OnceMore) Done() bool {
return atomic.LoadUint32(&t.done) == 1
}

func (t *onceMore) ResetWithLock() *sync.Mutex {
func (t *OnceMore) ResetWithLock() *sync.Mutex {
t.mu.Lock()
defer atomic.StoreUint32(&t.done, 0)
return &t.mu
Expand Down
12 changes: 8 additions & 4 deletions media/mediaType.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,13 @@ func (t Types) GetByType(tp string) (Type, bool) {
return Type{}, false
}

func (t Types) normalizeSuffix(s string) string {
return strings.ToLower(strings.TrimPrefix(s, "."))
}

// BySuffix will return all media types matching a suffix.
func (t Types) BySuffix(suffix string) []Type {
suffix = strings.ToLower(suffix)
suffix = t.normalizeSuffix(suffix)
var types []Type
for _, tt := range t {
if tt.hasSuffix(suffix) {
Expand All @@ -287,7 +291,7 @@ func (t Types) BySuffix(suffix string) []Type {

// GetFirstBySuffix will return the first type matching the given suffix.
func (t Types) GetFirstBySuffix(suffix string) (Type, SuffixInfo, bool) {
suffix = strings.ToLower(suffix)
suffix = t.normalizeSuffix(suffix)
for _, tt := range t {
if tt.hasSuffix(suffix) {
return tt, SuffixInfo{
Expand All @@ -304,7 +308,7 @@ func (t Types) GetFirstBySuffix(suffix string) (Type, SuffixInfo, bool) {
// is ambiguous.
// The lookup is case insensitive.
func (t Types) GetBySuffix(suffix string) (tp Type, si SuffixInfo, found bool) {
suffix = strings.ToLower(suffix)
suffix = t.normalizeSuffix(suffix)
for _, tt := range t {
if tt.hasSuffix(suffix) {
if found {
Expand All @@ -324,7 +328,7 @@ func (t Types) GetBySuffix(suffix string) (tp Type, si SuffixInfo, found bool) {
}

func (t Types) IsTextSuffix(suffix string) bool {
suffix = strings.ToLower(suffix)
suffix = t.normalizeSuffix(suffix)
for _, tt := range t {
if tt.hasSuffix(suffix) {
return tt.IsText()
Expand Down
3 changes: 1 addition & 2 deletions resources/page/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ type ChildCareProvider interface {
// section.
RegularPagesRecursive() Pages

// Resources returns a list of all resources.
Resources() resource.Resources
resource.ResourcesProvider
}

type MarkupProvider interface {
Expand Down
6 changes: 6 additions & 0 deletions resources/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ var (
_ resource.Cloner = (*genericResource)(nil)
_ resource.ResourcesLanguageMerger = (*resource.Resources)(nil)
_ resource.Identifier = (*genericResource)(nil)
_ resource.PathProvider = (*genericResource)(nil)
_ identity.IdentityGroupProvider = (*genericResource)(nil)
_ identity.DependencyManagerProvider = (*genericResource)(nil)
_ identity.Identity = (*genericResource)(nil)
Expand Down Expand Up @@ -463,6 +464,11 @@ func (l *genericResource) Key() string {
return key
}

// TODO1 test and document this. Consider adding it to the Resource interface.
func (l *genericResource) Path() string {
return l.paths.TargetPath()
}

func (l *genericResource) MediaType() media.Type {
return l.sd.MediaType
}
Expand Down
133 changes: 126 additions & 7 deletions resources/resource/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package resource

import (
"fmt"
"path"
"strings"

"github.com/gohugoio/hugo/common/paths"
Expand All @@ -29,6 +30,53 @@ var _ ResourceFinder = (*Resources)(nil)
// I.e. both pages and images etc.
type Resources []Resource

// TODO1
// TODO1 move to a func + template func. Maybe.
func (r Resources) Mount(base, target string) ResourceGetter {
return resourceGetterFunc(func(namev any) Resource {
name1, err := cast.ToStringE(namev)
if err != nil {
panic(err)
}

isTargetAbs := strings.HasPrefix(target, "/")

if target != "" {
name1 = strings.TrimPrefix(name1, target)
if !isTargetAbs {
name1 = paths.TrimLeading(name1)
}
}

if base != "" && isTargetAbs {
name1 = path.Join(base, name1)
}

for _, res := range r {
name2 := res.Name()

if base != "" && !isTargetAbs {
name2 = paths.TrimLeading(strings.TrimPrefix(name2, base))
}

// TODO1 remove.
// fmt.Println("name1", name1, "name2", name2, "base", base)

if strings.EqualFold(name1, name2) {
return res
}

}

return nil
})
}

type ResourcesProvider interface {
// Resources returns a list of all resources.
Resources() Resources
}

// var _ resource.ResourceFinder = (*Namespace)(nil)
// ResourcesConverter converts a given slice of Resource objects to Resources.
type ResourcesConverter interface {
Expand Down Expand Up @@ -63,21 +111,33 @@ func (r Resources) Get(name any) Resource {
panic(err)
}

namestr = paths.AddLeadingSlash(namestr)
isDotCurrent := strings.HasPrefix(namestr, "./")
if isDotCurrent {
namestr = strings.TrimPrefix(namestr, "./")
} else {
namestr = paths.AddLeadingSlash(namestr)
}

check := func(name string) bool {
if !isDotCurrent {
name = paths.AddLeadingSlash(name)
}
return strings.EqualFold(namestr, name)
}

// First check the Name.
// Note that this can be modified by the user in the front matter,
// also, it does not contain any language code.
for _, resource := range r {
if strings.EqualFold(namestr, paths.AddLeadingSlash(resource.Name())) {
if check(resource.Name()) {
return resource
}
}

// Finally, check the normalized name.
for _, resource := range r {
if nop, ok := resource.(NameNormalizedProvider); ok {
if strings.EqualFold(namestr, paths.AddLeadingSlash(nop.NameNormalized())) {
if check(nop.NameNormalized()) {
return resource
}
}
Expand Down Expand Up @@ -197,14 +257,24 @@ type Source interface {
Publish() error
}

// ResourceFinder provides methods to find Resources.
// Note that GetRemote (as found in resources.GetRemote) is
// not covered by this interface, as this is only available as a global template function.
type ResourceFinder interface {
type ResourceGetter interface {
// Get locates the Resource with the given name in the current context (e.g. in .Page.Resources).
//
// It returns nil if no Resource could found, panics if name is invalid.
Get(name any) Resource
}

type resourceGetterFunc func(name any) Resource

func (f resourceGetterFunc) Get(name any) Resource {
return f(name)
}

// ResourceFinder provides methods to find Resources.
// Note that GetRemote (as found in resources.GetRemote) is
// not covered by this interface, as this is only available as a global template function.
type ResourceFinder interface {
ResourceGetter

// GetMatch finds the first Resource matching the given pattern, or nil if none found.
//
Expand Down Expand Up @@ -235,3 +305,52 @@ type ResourceFinder interface {
// It returns nil if no Resources could found, panics if typ is invalid.
ByType(typ any) Resources
}

// NewResourceGetter creates a new ResourceGetter from the given objects.
// If multiple objects are provided, they are merged into one where
// the first match wins.
func NewResourceGetter(os ...any) ResourceGetter {
var getters multiResourceGetter
for _, o := range os {
if g, ok := unwrapResourceGetter(o); ok {
getters = append(getters, g)
}
}
// TODO1 slice of any etc. + tests.
return getters
}

type multiResourceGetter []ResourceGetter

func (m multiResourceGetter) Get(name any) Resource {
for _, g := range m {
if res := g.Get(name); res != nil {
return res
}
}
return nil
}

func unwrapResourceGetter(v any) (ResourceGetter, bool) {
if v == nil {
return nil, false
}
switch vv := v.(type) {
case ResourceGetter:
return vv, true
case ResourcesProvider:
return vv.Resources(), true
case func(name any) Resource:
return resourceGetterFunc(vv), true
case []any:
var getters multiResourceGetter
for _, vv := range vv {
if g, ok := unwrapResourceGetter(vv); ok {
getters = append(getters, g)
}
}
return getters, len(getters) > 0
}

return nil, false
}
Loading

0 comments on commit c91b32b

Please sign in to comment.