Skip to content

Commit b39e799

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 b39e799

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
-978
lines changed

‎commands/hugobuilder.go‎

Lines changed: 5 additions & 1 deletion
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‎

Lines changed: 4 additions & 2 deletions
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‎

Lines changed: 15 additions & 0 deletions
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‎

Lines changed: 1 addition & 1 deletion
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‎

Lines changed: 21 additions & 0 deletions
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‎

Lines changed: 13 additions & 0 deletions
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‎

Lines changed: 5 additions & 2 deletions
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‎

Lines changed: 93 additions & 15 deletions
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,9 +160,77 @@ 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
@@ -171,25 +240,34 @@ func UrlToFilename(s string) (string, bool) {
171240

172241
if p == "" {
173242
p, _ = url.QueryUnescape(u.Opaque)
174-
return filepath.FromSlash(p), true
243+
return filepath.FromSlash(p), false
175244
}
176245

177-
p = filepath.FromSlash(p)
246+
isWindows := runtime.GOOS == "windows"
178247

179-
if u.Host != "" {
180-
// C:\data\file.txt
181-
p = strings.ToUpper(u.Host) + ":" + p
248+
if isWindows {
249+
if len(p) == 0 || p[0] != '/' {
250+
return filepath.FromSlash(p), false
251+
}
252+
p = filepath.FromSlash(p)
253+
if len(u.Host) == 1 {
254+
// file://c/Users/...
255+
return strings.ToUpper(u.Host) + ":" + p, true
256+
}
257+
if u.Host != "" && u.Host != "localhost" {
258+
if filepath.VolumeName(u.Host) != "" {
259+
return "", false
260+
}
261+
return `\\` + u.Host + p, true
262+
}
263+
if vol := filepath.VolumeName(p[1:]); vol == "" || strings.HasPrefix(vol, `\\`) {
264+
return "", false
265+
}
266+
return p[1:], true
182267
}
183268

184269
return p, true
185270
}
186271

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)
193-
}
194-
return u.String()
272+
func convertFileURLPathWindows(host, path string) (string, bool) {
195273
}

‎common/types/closer.go‎

Lines changed: 7 additions & 0 deletions
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‎

Lines changed: 2 additions & 2 deletions
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 {

0 commit comments

Comments
 (0)