Skip to content

Commit 329b370

Browse files
committed
Add js.Batch
Fixes gohugoio#12626 Closes gohugoio#7499 Closes gohugoio#9978 Closes gohugoio#12879 Closes gohugoio#13113 Fixes gohugoio#13116
1 parent 989b299 commit 329b370

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+4495
-981
lines changed

commands/hugobuilder.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,11 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
920920

921921
changed := c.changeDetector.changed()
922922
if c.changeDetector != nil {
923-
lrl.Logf("build changed %d files", len(changed))
923+
if len(changed) >= 10 {
924+
lrl.Logf("build changed %d files", len(changed))
925+
} else {
926+
lrl.Logf("build changed %d files: %q", len(changed), changed)
927+
}
924928
if len(changed) == 0 {
925929
// Nothing has changed.
926930
return

commands/server.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"path"
3333
"path/filepath"
3434
"regexp"
35+
"sort"
3536
"strconv"
3637
"strings"
3738
"sync"
@@ -210,16 +211,17 @@ func (f *fileChangeDetector) changed() []string {
210211
}
211212
}
212213

213-
return f.filterIrrelevant(c)
214+
return f.filterIrrelevantAndSort(c)
214215
}
215216

216-
func (f *fileChangeDetector) filterIrrelevant(in []string) []string {
217+
func (f *fileChangeDetector) filterIrrelevantAndSort(in []string) []string {
217218
var filtered []string
218219
for _, v := range in {
219220
if !f.irrelevantRe.MatchString(v) {
220221
filtered = append(filtered, v)
221222
}
222223
}
224+
sort.Strings(filtered)
223225
return filtered
224226
}
225227

common/herrors/errors.go

+15
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,21 @@ func IsNotExist(err error) bool {
133133
return false
134134
}
135135

136+
// IsExist returns true if the error is a file exists error.
137+
// Unlike os.IsExist, this also considers wrapped errors.
138+
func IsExist(err error) bool {
139+
if os.IsExist(err) {
140+
return true
141+
}
142+
143+
// os.IsExist does not consider wrapped errors.
144+
if os.IsExist(errors.Unwrap(err)) {
145+
return true
146+
}
147+
148+
return false
149+
}
150+
136151
var nilPointerErrRe = regexp.MustCompile(`at <(.*)>: error calling (.*?): runtime error: invalid memory address or nil pointer dereference`)
137152

138153
const deferredPrefix = "__hdeferred/"

common/herrors/file_error.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ func extractPosition(e error) (pos text.Position) {
384384
case godartsass.SassError:
385385
span := v.Span
386386
start := span.Start
387-
filename, _ := paths.UrlToFilename(span.Url)
387+
filename, _ := paths.UrlStringToFilename(span.Url)
388388
pos.Filename = filename
389389
pos.Offset = start.Offset
390390
pos.ColumnNumber = start.Column

common/hreflect/helpers.go

+21
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,27 @@ func AsTime(v reflect.Value, loc *time.Location) (time.Time, bool) {
223223
return time.Time{}, false
224224
}
225225

226+
// ToSliceAny converts the given value to a slice of any if possible.
227+
func ToSliceAny(v any) ([]any, bool) {
228+
if v == nil {
229+
return nil, false
230+
}
231+
switch vv := v.(type) {
232+
case []any:
233+
return vv, true
234+
default:
235+
vvv := reflect.ValueOf(v)
236+
if vvv.Kind() == reflect.Slice {
237+
out := make([]any, vvv.Len())
238+
for i := 0; i < vvv.Len(); i++ {
239+
out[i] = vvv.Index(i).Interface()
240+
}
241+
return out, true
242+
}
243+
}
244+
return nil, false
245+
}
246+
226247
func CallMethodByName(cxt context.Context, name string, v reflect.Value) []reflect.Value {
227248
fn := v.MethodByName(name)
228249
var args []reflect.Value

common/hreflect/helpers_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,19 @@ func TestIsContextType(t *testing.T) {
5050
c.Assert(IsContextType(reflect.TypeOf(valueCtx)), qt.IsTrue)
5151
}
5252

53+
func TestToSliceAny(t *testing.T) {
54+
c := qt.New(t)
55+
56+
checkOK := func(in any, expected []any) {
57+
out, ok := ToSliceAny(in)
58+
c.Assert(ok, qt.Equals, true)
59+
c.Assert(out, qt.DeepEquals, expected)
60+
}
61+
62+
checkOK([]any{1, 2, 3}, []any{1, 2, 3})
63+
checkOK([]int{1, 2, 3}, []any{1, 2, 3})
64+
}
65+
5366
func BenchmarkIsContextType(b *testing.B) {
5467
type k string
5568
b.Run("value", func(b *testing.B) {

common/maps/cache.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,14 @@ func (c *Cache[K, T]) set(key K, value T) {
113113
}
114114

115115
// ForEeach calls the given function for each key/value pair in the cache.
116-
func (c *Cache[K, T]) ForEeach(f func(K, T)) {
116+
// If the function returns false, the iteration stops.
117+
func (c *Cache[K, T]) ForEeach(f func(K, T) bool) {
117118
c.RLock()
118119
defer c.RUnlock()
119120
for k, v := range c.m {
120-
f(k, v)
121+
if !f(k, v) {
122+
return
123+
}
121124
}
122125
}
123126

common/paths/url.go

+93-18
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2021 The Hugo Authors. All rights reserved.
1+
// Copyright 2024 The Hugo Authors. All rights reserved.
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ import (
1818
"net/url"
1919
"path"
2020
"path/filepath"
21+
"runtime"
2122
"strings"
2223
)
2324

@@ -159,37 +160,111 @@ func Uglify(in string) string {
159160
return path.Clean(in)
160161
}
161162

163+
// URLEscape escapes unicode letters.
164+
func URLEscape(uri string) string {
165+
// escape unicode letters
166+
u, err := url.Parse(uri)
167+
if err != nil {
168+
panic(err)
169+
}
170+
return u.String()
171+
}
172+
173+
// TrimExt trims the extension from a path..
174+
func TrimExt(in string) string {
175+
return strings.TrimSuffix(in, path.Ext(in))
176+
}
177+
178+
// From https://github.com/golang/go/blob/e0c76d95abfc1621259864adb3d101cf6f1f90fc/src/cmd/go/internal/web/url.go#L45
179+
func UrlFromFilename(filename string) (*url.URL, error) {
180+
if !filepath.IsAbs(filename) {
181+
return nil, fmt.Errorf("filepath must be absolute")
182+
}
183+
184+
// If filename has a Windows volume name, convert the volume to a host and prefix
185+
// per https://blogs.msdn.microsoft.com/ie/2006/12/06/file-uris-in-windows/.
186+
if vol := filepath.VolumeName(filename); vol != "" {
187+
if strings.HasPrefix(vol, `\\`) {
188+
filename = filepath.ToSlash(filename[2:])
189+
i := strings.IndexByte(filename, '/')
190+
191+
if i < 0 {
192+
// A degenerate case.
193+
// \\host.example.com (without a share name)
194+
// becomes
195+
// file://host.example.com/
196+
return &url.URL{
197+
Scheme: "file",
198+
Host: filename,
199+
Path: "/",
200+
}, nil
201+
}
202+
203+
// \\host.example.com\Share\path\to\file
204+
// becomes
205+
// file://host.example.com/Share/path/to/file
206+
return &url.URL{
207+
Scheme: "file",
208+
Host: filename[:i],
209+
Path: filepath.ToSlash(filename[i:]),
210+
}, nil
211+
}
212+
213+
// C:\path\to\file
214+
// becomes
215+
// file:///C:/path/to/file
216+
return &url.URL{
217+
Scheme: "file",
218+
Path: "/" + filepath.ToSlash(filename),
219+
}, nil
220+
}
221+
222+
// /path/to/file
223+
// becomes
224+
// file:///path/to/file
225+
return &url.URL{
226+
Scheme: "file",
227+
Path: filepath.ToSlash(filename),
228+
}, nil
229+
}
230+
162231
// UrlToFilename converts the URL s to a filename.
163232
// If ParseRequestURI fails, the input is just converted to OS specific slashes and returned.
164-
func UrlToFilename(s string) (string, bool) {
233+
func UrlStringToFilename(s string) (string, bool) {
165234
u, err := url.ParseRequestURI(s)
166235
if err != nil {
167236
return filepath.FromSlash(s), false
168237
}
169238

170239
p := u.Path
171240

172-
if p == "" {
173-
p, _ = url.QueryUnescape(u.Opaque)
174-
return filepath.FromSlash(p), true
175-
}
176-
177-
p = filepath.FromSlash(p)
241+
isWindows := runtime.GOOS == "windows"
178242

179-
if u.Host != "" {
180-
// C:\data\file.txt
181-
p = strings.ToUpper(u.Host) + ":" + p
243+
if isWindows {
244+
if len(p) == 0 || p[0] != '/' {
245+
return "", false
246+
}
247+
p = filepath.FromSlash(p)
248+
if len(u.Host) == 1 {
249+
// file://c/Users/...
250+
return strings.ToUpper(u.Host) + ":" + p, true
251+
}
252+
return convertFileURLPathWindows(u.Host, p)
182253
}
183254

184255
return p, true
185256
}
186257

187-
// URLEscape escapes unicode letters.
188-
func URLEscape(uri string) string {
189-
// escape unicode letters
190-
u, err := url.Parse(uri)
191-
if err != nil {
192-
panic(err)
258+
func convertFileURLPathWindows(host, path string) (string, bool) {
259+
if host != "" && host != "localhost" {
260+
if filepath.VolumeName(host) != "" {
261+
return "", false
262+
}
263+
return `\\` + host + path, true
193264
}
194-
return u.String()
265+
266+
if vol := filepath.VolumeName(path[1:]); vol == "" || strings.HasPrefix(vol, `\\`) {
267+
return "", false
268+
}
269+
return path[1:], true
195270
}

common/types/closer.go

+7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ type Closer interface {
1919
Close() error
2020
}
2121

22+
// CloserFunc is a convenience type to create a Closer from a function.
23+
type CloserFunc func() error
24+
25+
func (f CloserFunc) Close() error {
26+
return f()
27+
}
28+
2229
type CloseAdder interface {
2330
Add(Closer)
2431
}

config/allconfig/configlanguage.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,11 @@ func (c ConfigLanguage) Watching() bool {
137137
return c.m.Base.Internal.Watch
138138
}
139139

140-
func (c ConfigLanguage) NewIdentityManager(name string) identity.Manager {
140+
func (c ConfigLanguage) NewIdentityManager(name string, opts ...identity.ManagerOption) identity.Manager {
141141
if !c.Watching() {
142142
return identity.NopManager
143143
}
144-
return identity.NewManager(name)
144+
return identity.NewManager(name, opts...)
145145
}
146146

147147
func (c ConfigLanguage) ContentTypes() config.ContentTypesProvider {

config/configProvider.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ type AllProvider interface {
5858
BuildDrafts() bool
5959
Running() bool
6060
Watching() bool
61-
NewIdentityManager(name string) identity.Manager
61+
NewIdentityManager(name string, opts ...identity.ManagerOption) identity.Manager
6262
FastRenderMode() bool
6363
PrintUnusedTemplates() bool
6464
EnableMissingTranslationPlaceholders() bool

0 commit comments

Comments
 (0)