aboutsummaryrefslogtreecommitdiffhomepage
path: root/commands
diff options
context:
space:
mode:
Diffstat (limited to 'commands')
-rw-r--r--commands/commandeer.go14
-rw-r--r--commands/commands.go3
-rw-r--r--commands/config.go4
-rw-r--r--commands/convert.go6
-rw-r--r--commands/deploy.go5
-rw-r--r--commands/deploy_off.go4
-rw-r--r--commands/env.go2
-rw-r--r--commands/gen.go8
-rw-r--r--commands/helpers.go5
-rw-r--r--commands/hugo_windows.go2
-rw-r--r--commands/hugobuilder.go141
-rw-r--r--commands/import.go15
-rw-r--r--commands/list.go15
-rw-r--r--commands/mod.go4
-rw-r--r--commands/new.go4
-rw-r--r--commands/release.go3
-rw-r--r--commands/server.go75
17 files changed, 119 insertions, 191 deletions
diff --git a/commands/commandeer.go b/commands/commandeer.go
index 5d414b04a..1aac08c42 100644
--- a/commands/commandeer.go
+++ b/commands/commandeer.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -259,7 +259,7 @@ func (r *rootCommand) ConfigFromProvider(key int32, cfg config.Provider) (*commo
publishDirStatic := cfg.GetString("publishDirStatic")
workingDir := cfg.GetString("workingDir")
absPublishDirStatic := paths.AbsPathify(workingDir, publishDirStatic)
- staticFs := afero.NewBasePathFs(afero.NewOsFs(), absPublishDirStatic)
+ staticFs := hugofs.NewBasePathFs(afero.NewOsFs(), absPublishDirStatic)
// Serve from both the static and dynamic fs,
// the first will take priority.
@@ -405,8 +405,14 @@ func (r *rootCommand) PreRun(cd, runner *simplecobra.Commandeer) error {
return err
}
- r.commonConfigs = lazycache.New[int32, *commonConfig](lazycache.Options{MaxEntries: 5})
- r.hugoSites = lazycache.New[int32, *hugolib.HugoSites](lazycache.Options{MaxEntries: 5})
+ r.commonConfigs = lazycache.New(lazycache.Options[int32, *commonConfig]{MaxEntries: 5})
+ // We don't want to keep stale HugoSites in memory longer than needed.
+ r.hugoSites = lazycache.New(lazycache.Options[int32, *hugolib.HugoSites]{
+ MaxEntries: 1,
+ OnEvict: func(key int32, value *hugolib.HugoSites) {
+ value.Close()
+ },
+ })
return nil
}
diff --git a/commands/commands.go b/commands/commands.go
index 9d707b841..e21d743ab 100644
--- a/commands/commands.go
+++ b/commands/commands.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -37,5 +37,4 @@ func newExec() (*simplecobra.Exec, error) {
}
return simplecobra.New(rootCmd)
-
}
diff --git a/commands/config.go b/commands/config.go
index 63ee4f7c8..dfe54cba2 100644
--- a/commands/config.go
+++ b/commands/config.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -37,7 +37,6 @@ func newConfigCommand() *configCommand {
&configMountsCommand{},
},
}
-
}
type configCommand struct {
@@ -190,7 +189,6 @@ func (m *configModMounts) MarshalJSON() ([]byte, error) {
Dir: m.m.Dir(),
Mounts: mounts,
})
-
}
type configMountsCommand struct {
diff --git a/commands/convert.go b/commands/convert.go
index 702c9227f..c81ec792a 100644
--- a/commands/convert.go
+++ b/commands/convert.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -134,7 +134,7 @@ func (c *convertCommand) convertAndSavePage(p page.Page, site *hugolib.Site, tar
}
}
- if p.File().IsZero() {
+ if p.File() == nil {
// No content file.
return nil
}
@@ -209,7 +209,7 @@ func (c *convertCommand) convertContents(format metadecoders.Format) error {
var pagesBackedByFile page.Pages
for _, p := range site.AllPages() {
- if p.File().IsZero() {
+ if p.File() == nil {
continue
}
pagesBackedByFile = append(pagesBackedByFile, p)
diff --git a/commands/deploy.go b/commands/deploy.go
index ce1af9546..ca6e4d60e 100644
--- a/commands/deploy.go
+++ b/commands/deploy.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
//go:build !nodeploy
// +build !nodeploy
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -38,7 +38,6 @@ import (
)
func newDeployCommand() simplecobra.Commander {
-
return &simpleCommand{
name: "deploy",
short: "Deploy your site to a Cloud provider.",
diff --git a/commands/deploy_off.go b/commands/deploy_off.go
index 3150dba16..8a481bd96 100644
--- a/commands/deploy_off.go
+++ b/commands/deploy_off.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
//go:build nodeploy
// +build nodeploy
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/commands/env.go b/commands/env.go
index 0652deb87..8e4f03c55 100644
--- a/commands/env.go
+++ b/commands/env.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/commands/gen.go b/commands/gen.go
index 534eb0df5..11c32d778 100644
--- a/commands/gen.go
+++ b/commands/gen.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -101,7 +101,7 @@ See https://xyproto.github.io/splash/docs/all.html for a preview of the availabl
}
if found, _ := helpers.Exists(genmandir, hugofs.Os); !found {
r.Println("Directory", genmandir, "does not exist, creating...")
- if err := hugofs.Os.MkdirAll(genmandir, 0777); err != nil {
+ if err := hugofs.Os.MkdirAll(genmandir, 0o777); err != nil {
return err
}
}
@@ -150,7 +150,7 @@ url: %s
}
if found, _ := helpers.Exists(gendocdir, hugofs.Os); !found {
r.Println("Directory", gendocdir, "does not exist, creating...")
- if err := hugofs.Os.MkdirAll(gendocdir, 0777); err != nil {
+ if err := hugofs.Os.MkdirAll(gendocdir, 0o777); err != nil {
return err
}
}
@@ -177,7 +177,6 @@ url: %s
cmd.PersistentFlags().SetAnnotation("dir", cobra.BashCompSubdirsInDir, []string{})
},
}
-
}
var docsHelperTarget string
@@ -241,7 +240,6 @@ url: %s
newDocsHelper(),
},
}
-
}
type genCommand struct {
diff --git a/commands/helpers.go b/commands/helpers.go
index 3b0c50159..a13bdebc2 100644
--- a/commands/helpers.go
+++ b/commands/helpers.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -110,12 +110,11 @@ func flagsToCfgWithAdditionalConfigBase(cd *simplecobra.Commandeer, cfg config.P
})
return cfg
-
}
func mkdir(x ...string) {
p := filepath.Join(x...)
- err := os.MkdirAll(p, 0777) // before umask
+ err := os.MkdirAll(p, 0o777) // before umask
if err != nil {
log.Fatal(err)
}
diff --git a/commands/hugo_windows.go b/commands/hugo_windows.go
index 169c6288f..c354e889d 100644
--- a/commands/hugo_windows.go
+++ b/commands/hugo_windows.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/commands/hugobuilder.go b/commands/hugobuilder.go
index d2b43cc77..41f42ae6d 100644
--- a/commands/hugobuilder.go
+++ b/commands/hugobuilder.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import (
"runtime/trace"
"strings"
"sync"
+ "sync/atomic"
"time"
"github.com/bep/logg"
@@ -34,6 +35,7 @@ import (
"github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/maps"
+ "github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/common/terminal"
"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/config"
@@ -83,7 +85,6 @@ func (c *hugoBuilder) withConf(fn func(conf *commonConfig)) {
c.confmu.Lock()
defer c.confmu.Unlock()
fn(c.conf)
-
}
type hugoBuilderErrState struct {
@@ -135,46 +136,12 @@ func (c *hugoBuilder) errCount() int {
// getDirList provides NewWatcher() with a list of directories to watch for changes.
func (c *hugoBuilder) getDirList() ([]string, error) {
- var filenames []string
-
- walkFn := func(path string, fi hugofs.FileMetaInfo, err error) error {
- if err != nil {
- c.r.logger.Errorln("walker: ", err)
- return nil
- }
-
- if fi.IsDir() {
- if fi.Name() == ".git" ||
- fi.Name() == "node_modules" || fi.Name() == "bower_components" {
- return filepath.SkipDir
- }
-
- filenames = append(filenames, fi.Meta().Filename)
- }
-
- return nil
- }
-
h, err := c.hugo()
if err != nil {
return nil, err
}
- watchFiles := h.PathSpec.BaseFs.WatchDirs()
- for _, fi := range watchFiles {
- if !fi.IsDir() {
- filenames = append(filenames, fi.Meta().Filename)
- continue
- }
-
- w := hugofs.NewWalkway(hugofs.WalkwayConfig{Logger: c.r.logger, Info: fi, WalkFn: walkFn})
- if err := w.Walk(); err != nil {
- c.r.logger.Errorln("walker: ", err)
- }
- }
- filenames = helpers.UniqueStringsSorted(filenames)
-
- return filenames, nil
+ return helpers.UniqueStringsSorted(h.PathSpec.BaseFs.WatchFilenames()), nil
}
func (c *hugoBuilder) initCPUProfile() (func(), error) {
@@ -441,7 +408,7 @@ func (c *hugoBuilder) copyStatic() (map[string]uint64, error) {
}
func (c *hugoBuilder) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint64, error) {
- infol := c.r.logger.InfoCommand("copy static")
+ infol := c.r.logger.InfoCommand("static")
publishDir := helpers.FilePathSeparator
if sourceFs.PublishFolder != "" {
@@ -467,11 +434,11 @@ func (c *hugoBuilder) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint
if syncer.Delete {
infol.Logf("removing all files from destination that don't exist in static dirs")
- syncer.DeleteFilter = func(f os.FileInfo) bool {
+ syncer.DeleteFilter = func(f fsync.FileInfo) bool {
return f.IsDir() && strings.HasPrefix(f.Name(), ".")
}
}
- infol.Logf("syncing static files to %s", publishDir)
+ start := time.Now()
// because we are using a baseFs (to get the union right).
// set sync src to root
@@ -479,9 +446,10 @@ func (c *hugoBuilder) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint
if err != nil {
return 0, err
}
+ loggers.TimeTrackf(infol, start, nil, "syncing static files to %s", publishDir)
- // Sync runs Stat 3 times for every source file (which sounds much)
- numFiles := fs.statCounter / 3
+ // Sync runs Stat 2 times for every source file.
+ numFiles := fs.statCounter / 2
return numFiles, err
}
@@ -652,13 +620,31 @@ func (c *hugoBuilder) handleBuildErr(err error, msg string) {
func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
staticSyncer *staticSyncer,
evs []fsnotify.Event,
- configSet map[string]bool) {
+ configSet map[string]bool,
+) {
defer func() {
c.errState.setWasErr(false)
}()
var isHandled bool
+ // Filter out ghost events (from deleted, renamed directories).
+ // This seems to be a bug in fsnotify, or possibly MacOS.
+ var n int
+ for _, ev := range evs {
+ keep := true
+ if ev.Has(fsnotify.Create) || ev.Has(fsnotify.Write) {
+ if _, err := os.Stat(ev.Name); err != nil {
+ keep = false
+ }
+ }
+ if keep {
+ evs[n] = ev
+ n++
+ }
+ }
+ evs = evs[:n]
+
for _, ev := range evs {
isConfig := configSet[ev.Name]
configChangeType := configChangeConfig
@@ -726,48 +712,25 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
return
}
- c.r.logger.Infoln("Received System Events:", evs)
+ c.r.logger.Debugln("Received System Events:", evs)
staticEvents := []fsnotify.Event{}
dynamicEvents := []fsnotify.Event{}
- filtered := []fsnotify.Event{}
h, err := c.hugo()
if err != nil {
c.r.logger.Errorln("Error getting the Hugo object:", err)
return
}
+ n = 0
for _, ev := range evs {
if h.ShouldSkipFileChangeEvent(ev) {
continue
}
- // Check the most specific first, i.e. files.
- contentMapped := h.ContentChanges.GetSymbolicLinkMappings(ev.Name)
- if len(contentMapped) > 0 {
- for _, mapped := range contentMapped {
- filtered = append(filtered, fsnotify.Event{Name: mapped, Op: ev.Op})
- }
- continue
- }
-
- // Check for any symbolic directory mapping.
-
- dir, name := filepath.Split(ev.Name)
-
- contentMapped = h.ContentChanges.GetSymbolicLinkMappings(dir)
-
- if len(contentMapped) == 0 {
- filtered = append(filtered, ev)
- continue
- }
-
- for _, mapped := range contentMapped {
- mappedFilename := filepath.Join(mapped, name)
- filtered = append(filtered, fsnotify.Event{Name: mappedFilename, Op: ev.Op})
- }
+ evs[n] = ev
+ n++
}
-
- evs = filtered
+ evs = evs[:n]
for _, ev := range evs {
ext := filepath.Ext(ev.Name)
@@ -788,6 +751,7 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
if istemp {
continue
}
+
if h.Deps.SourceSpec.IgnoreFile(ev.Name) {
continue
}
@@ -811,7 +775,7 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
continue
}
- walkAdder := func(path string, f hugofs.FileMetaInfo, err error) error {
+ walkAdder := func(path string, f hugofs.FileMetaInfo) error {
if f.IsDir() {
c.r.logger.Println("adding created directory to watchlist", path)
if err := watcher.Add(path); err != nil {
@@ -827,11 +791,10 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
}
// recursively add new directories to watch list
- // When mkdir -p is used, only the top directory triggers an event (at least on OSX)
- if ev.Op&fsnotify.Create == fsnotify.Create {
+ if ev.Has(fsnotify.Create) || ev.Has(fsnotify.Rename) {
c.withConf(func(conf *commonConfig) {
if s, err := conf.fs.Source.Stat(ev.Name); err == nil && s.Mode().IsDir() {
- _ = helpers.SymbolicWalk(conf.fs.Source, ev.Name, walkAdder)
+ _ = helpers.Walk(conf.fs.Source, ev.Name, walkAdder)
}
})
}
@@ -872,7 +835,7 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
return
}
path := h.BaseFs.SourceFilesystems.MakeStaticPathRelative(ev.Name)
- path = h.RelURL(helpers.ToSlashTrimLeading(path), false)
+ path = h.RelURL(paths.ToSlashTrimLeading(path), false)
livereload.RefreshPath(path)
} else {
@@ -909,7 +872,7 @@ func (c *hugoBuilder) handleEvents(watcher *watcher.Batcher,
// Nothing has changed.
return
} else if len(changed) == 1 {
- pathToRefresh := h.PathSpec.RelURL(helpers.ToSlashTrimLeading(changed[0]), false)
+ pathToRefresh := h.PathSpec.RelURL(paths.ToSlashTrimLeading(changed[0]), false)
livereload.RefreshPath(pathToRefresh)
} else {
livereload.ForceRefresh()
@@ -944,7 +907,6 @@ func (c *hugoBuilder) hugo() (*hugolib.HugoSites, error) {
var err error
h, err = c.r.HugFromConfig(conf)
return err
-
}); err != nil {
return nil, err
}
@@ -1000,6 +962,7 @@ func (c *hugoBuilder) loadConfig(cd *simplecobra.Commandeer, running bool) error
}
if len(conf.configs.LoadingInfo.ConfigFiles) == 0 {
+ //lint:ignore ST1005 end user message.
return errors.New("Unable to locate config file or config directory. Perhaps you need to create a new site.\nRun `hugo help new` for details.")
}
@@ -1011,15 +974,16 @@ func (c *hugoBuilder) loadConfig(cd *simplecobra.Commandeer, running bool) error
}
return nil
-
}
+var rebuildCounter atomic.Uint64
+
func (c *hugoBuilder) printChangeDetected(typ string) {
msg := "\nChange"
if typ != "" {
msg += " of " + typ
}
- msg += " detected, rebuilding site."
+ msg += fmt.Sprintf(" detected, rebuilding site (#%d).", rebuildCounter.Add(1))
c.r.logger.Println(msg)
const layout = "2006-01-02 15:04:05.000 -0700"
@@ -1034,25 +998,12 @@ func (c *hugoBuilder) rebuildSites(events []fsnotify.Event) error {
}
}
c.errState.setBuildErr(nil)
- visited := c.visitedURLs.PeekAllSet()
h, err := c.hugo()
if err != nil {
return err
}
- if c.fastRenderMode {
- c.withConf(func(conf *commonConfig) {
- // Make sure we always render the home pages
- for _, l := range conf.configs.ConfigLangs() {
- langPath := l.LanguagePrefix()
- if langPath != "" {
- langPath = langPath + "/"
- }
- home := h.PrependBasePath("/"+langPath, false)
- visited[home] = true
- }
- })
- }
- return h.Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyVisited: visited, ErrRecovery: c.errState.wasErr()}, events...)
+
+ return h.Build(hugolib.BuildCfg{NoBuildLock: true, RecentlyVisited: c.visitedURLs, ErrRecovery: c.errState.wasErr()}, events...)
}
func (c *hugoBuilder) reloadConfig() error {
diff --git a/commands/import.go b/commands/import.go
index 18ed7b328..947b6d11f 100644
--- a/commands/import.go
+++ b/commands/import.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -23,7 +23,6 @@ import (
"os"
"path/filepath"
"regexp"
-
"strconv"
"strings"
"time"
@@ -66,7 +65,6 @@ Import from Jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root
}
return c
-
}
type importCommand struct {
@@ -312,7 +310,7 @@ func (c *importCommand) convertJekyllPost(path, relPath, targetDir string, draft
targetFile := filepath.Join(targetDir, relPath)
targetParentDir := filepath.Dir(targetFile)
- os.MkdirAll(targetParentDir, 0777)
+ os.MkdirAll(targetParentDir, 0o777)
contentBytes, err := os.ReadFile(path)
if err != nil {
@@ -398,7 +396,6 @@ func (c *importCommand) copyJekyllFilesAndFolders(jekyllRoot, dest string, jekyl
}
func (c *importCommand) importFromJekyll(args []string) error {
-
jekyllRoot, err := filepath.Abs(filepath.Clean(args[0]))
if err != nil {
return newUserError("path error:", args[0])
@@ -429,11 +426,7 @@ func (c *importCommand) importFromJekyll(args []string) error {
c.r.Println("Importing...")
fileCount := 0
- callback := func(path string, fi hugofs.FileMetaInfo, err error) error {
- if err != nil {
- return err
- }
-
+ callback := func(path string, fi hugofs.FileMetaInfo) error {
if fi.IsDir() {
return nil
}
@@ -462,7 +455,7 @@ func (c *importCommand) importFromJekyll(args []string) error {
for jekyllPostDir, hasAnyPostInDir := range jekyllPostDirs {
if hasAnyPostInDir {
- if err = helpers.SymbolicWalk(hugofs.Os, filepath.Join(jekyllRoot, jekyllPostDir), callback); err != nil {
+ if err = helpers.Walk(hugofs.Os, filepath.Join(jekyllRoot, jekyllPostDir), callback); err != nil {
return err
}
}
diff --git a/commands/list.go b/commands/list.go
index 6690ea9ee..41a45e402 100644
--- a/commands/list.go
+++ b/commands/list.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -31,7 +31,6 @@ import (
// newListCommand creates a new list command and its subcommands.
func newListCommand() *listCommand {
-
createRecord := func(workingDir string, p page.Page) []string {
return []string{
filepath.ToSlash(strings.TrimPrefix(p.File().Filename(), workingDir+string(os.PathSeparator))),
@@ -83,7 +82,6 @@ func newListCommand() *listCommand {
}
return nil
-
}
return &listCommand{
@@ -94,11 +92,10 @@ func newListCommand() *listCommand {
long: `List all of the drafts in your content directory.`,
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
shouldInclude := func(p page.Page) bool {
- if !p.Draft() || p.File().IsZero() {
+ if !p.Draft() || p.File() == nil {
return false
}
return true
-
}
return list(cd, r, shouldInclude,
"buildDrafts", true,
@@ -113,11 +110,10 @@ func newListCommand() *listCommand {
long: `List all of the posts in your content directory which will be posted in the future.`,
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
shouldInclude := func(p page.Page) bool {
- if !resource.IsFuture(p) || p.File().IsZero() {
+ if !resource.IsFuture(p) || p.File() == nil {
return false
}
return true
-
}
return list(cd, r, shouldInclude,
"buildFuture", true,
@@ -131,7 +127,7 @@ func newListCommand() *listCommand {
long: `List all of the posts in your content directory which has already expired.`,
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
shouldInclude := func(p page.Page) bool {
- if !resource.IsExpired(p) || p.File().IsZero() {
+ if !resource.IsExpired(p) || p.File() == nil {
return false
}
return true
@@ -148,14 +144,13 @@ func newListCommand() *listCommand {
long: `List all of the posts in your content directory, include drafts, future and expired pages.`,
run: func(ctx context.Context, cd *simplecobra.Commandeer, r *rootCommand, args []string) error {
shouldInclude := func(p page.Page) bool {
- return !p.File().IsZero()
+ return p.File() != nil
}
return list(cd, r, shouldInclude, "buildDrafts", true, "buildFuture", true, "buildExpired", true)
},
},
},
}
-
}
type listCommand struct {
diff --git a/commands/mod.go b/commands/mod.go
index 20b9d3960..d64d2a983 100644
--- a/commands/mod.go
+++ b/commands/mod.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -69,7 +69,7 @@ so this may/will change in future versions of Hugo.
if err != nil {
return err
}
- return npm.Pack(h.BaseFs.SourceFs, h.BaseFs.Assets.Dirs)
+ return npm.Pack(h.BaseFs.ProjectSourceFs, h.BaseFs.AssetsWithDuplicatesPreserved.Fs)
},
},
},
diff --git a/commands/new.go b/commands/new.go
index 8e348366d..79d2c9e7e 100644
--- a/commands/new.go
+++ b/commands/new.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -64,7 +64,6 @@ Ensure you run this within the root directory of your site.`,
cmd.Flags().String("editor", "", "edit new content with this editor, if provided")
cmd.Flags().BoolVarP(&force, "force", "f", false, "overwrite file if it already exists")
applyLocalFlagsBuildConfig(cmd, r)
-
},
},
&simpleCommand{
@@ -143,7 +142,6 @@ according to your needs.`,
}
return c
-
}
type newCommand struct {
diff --git a/commands/release.go b/commands/release.go
index 54cf936e8..1d1aaad53 100644
--- a/commands/release.go
+++ b/commands/release.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -24,7 +24,6 @@ import (
// Note: This is a command only meant for internal use and must be run
// via "go run -tags release main.go release" on the actual code base that is in the release.
func newReleaseCommand() simplecobra.Commander {
-
var (
step int
skipPush bool
diff --git a/commands/server.go b/commands/server.go
index 63c09fccd..97cf405b7 100644
--- a/commands/server.go
+++ b/commands/server.go
@@ -1,4 +1,4 @@
-// Copyright 2023 The Hugo Authors. All rights reserved.
+// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -27,20 +27,19 @@ import (
"net/http"
"net/url"
"os"
- "sync"
- "sync/atomic"
-
- "github.com/bep/mclib"
-
"os/signal"
"path"
"path/filepath"
"regexp"
"strconv"
"strings"
+ "sync"
+ "sync/atomic"
"syscall"
"time"
+ "github.com/bep/mclib"
+
"github.com/bep/debounce"
"github.com/bep/simplecobra"
"github.com/fsnotify/fsnotify"
@@ -83,10 +82,14 @@ const (
)
func newHugoBuilder(r *rootCommand, s *serverCommand, onConfigLoaded ...func(reloaded bool) error) *hugoBuilder {
+ var visitedURLs *types.EvictingStringQueue
+ if s != nil && !s.disableFastRender {
+ visitedURLs = types.NewEvictingStringQueue(20)
+ }
return &hugoBuilder{
r: r,
s: s,
- visitedURLs: types.NewEvictingStringQueue(100),
+ visitedURLs: visitedURLs,
fullRebuildSem: semaphore.NewWeighted(1),
debounce: debounce.New(4 * time.Second),
onConfigLoaded: func(reloaded bool) error {
@@ -120,7 +123,6 @@ func newServerCommand() *serverCommand {
},
withc: func(cmd *cobra.Command, r *rootCommand) {
cmd.Flags().BoolVar(&uninstall, "uninstall", false, "Uninstall the local CA (but do not delete it).")
-
},
},
},
@@ -219,7 +221,7 @@ func (f *fileChangeDetector) filterIrrelevant(in []string) []string {
}
type fileServer struct {
- baseURLs []string
+ baseURLs []urls.BaseURL
roots []string
errorTemplate func(err any) (io.Reader, error)
c *serverCommand
@@ -255,12 +257,6 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
r.Println("Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender")
}
- // We're only interested in the path
- u, err := url.Parse(baseURL)
- if err != nil {
- return nil, nil, "", "", fmt.Errorf("invalid baseURL: %w", err)
- }
-
decorate := func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if f.c.showErrorInBrowser {
@@ -280,7 +276,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
port = lrport
}
})
- lr := *u
+ lr := baseURL.URL()
lr.Host = fmt.Sprintf("%s:%d", lr.Hostname(), port)
fmt.Fprint(w, injectLiveReloadScript(r, lr))
@@ -311,7 +307,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
// This matches Netlify's behaviour and is needed for SPA behaviour.
// See https://docs.netlify.com/routing/redirects/rewrites-proxies/
if !redirect.Force {
- path := filepath.Clean(strings.TrimPrefix(requestURI, u.Path))
+ path := filepath.Clean(strings.TrimPrefix(requestURI, baseURL.Path()))
if root != "" {
path = filepath.Join(root, path)
}
@@ -338,7 +334,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
switch redirect.Status {
case 404:
w.WriteHeader(404)
- file, err := fs.Open(strings.TrimPrefix(redirect.To, u.Path))
+ file, err := fs.Open(strings.TrimPrefix(redirect.To, baseURL.Path()))
if err == nil {
defer file.Close()
io.Copy(w, file)
@@ -347,7 +343,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
}
return
case 200:
- if r2 := f.rewriteRequest(r, strings.TrimPrefix(redirect.To, u.Path)); r2 != nil {
+ if r2 := f.rewriteRequest(r, strings.TrimPrefix(redirect.To, baseURL.Path())); r2 != nil {
requestURI = redirect.To
r = r2
}
@@ -385,10 +381,10 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
fileserver := decorate(http.FileServer(fs))
mu := http.NewServeMux()
- if u.Path == "" || u.Path == "/" {
+ if baseURL.Path() == "" || baseURL.Path() == "/" {
mu.Handle("/", fileserver)
} else {
- mu.Handle(u.Path, http.StripPrefix(u.Path, fileserver))
+ mu.Handle(baseURL.Path(), http.StripPrefix(baseURL.Path(), fileserver))
}
if r.IsTestRun() {
var shutDownOnce sync.Once
@@ -401,7 +397,7 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
endpoint := net.JoinHostPort(f.c.serverInterface, strconv.Itoa(port))
- return mu, listener, u.String(), endpoint, nil
+ return mu, listener, baseURL.String(), endpoint, nil
}
func (f *fileServer) rewriteRequest(r *http.Request, toPath string) *http.Request {
@@ -469,7 +465,6 @@ func (c *serverCommand) Name() string {
}
func (c *serverCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error {
-
// Watch runs its own server as part of the routine
if c.serverWatch {
@@ -676,7 +671,7 @@ func (c *serverCommand) createCertificates(conf *commonConfig) error {
// Create the directory if it doesn't exist.
if _, err := os.Stat(keyDir); os.IsNotExist(err) {
- if err := os.MkdirAll(keyDir, 0777); err != nil {
+ if err := os.MkdirAll(keyDir, 0o777); err != nil {
return err
}
}
@@ -701,7 +696,6 @@ func (c *serverCommand) createCertificates(conf *commonConfig) error {
// Yes, this is unfortunate, but it's currently the only way to use Mkcert as a library.
os.Args = []string{"-cert-file", c.tlsCertFile, "-key-file", c.tlsKeyFile, hostname}
return mclib.RunMain()
-
}
func (c *serverCommand) verifyCert(rootPEM, certPEM []byte, name string) error {
@@ -831,9 +825,9 @@ func (c *serverCommand) partialReRender(urls ...string) error {
c.errState.setWasErr(false)
}()
c.errState.setBuildErr(nil)
- visited := make(map[string]bool)
+ visited := types.NewEvictingStringQueue(len(urls))
for _, url := range urls {
- visited[url] = true
+ visited.Add(url)
}
h, err := c.hugo()
@@ -846,7 +840,7 @@ func (c *serverCommand) partialReRender(urls ...string) error {
func (c *serverCommand) serve() error {
var (
- baseURLs []string
+ baseURLs []urls.BaseURL
roots []string
h *hugolib.HugoSites
)
@@ -863,18 +857,17 @@ func (c *serverCommand) serve() error {
if isMultiHost {
for _, l := range conf.configs.ConfigLangs() {
- baseURLs = append(baseURLs, l.BaseURL().String())
+ baseURLs = append(baseURLs, l.BaseURL())
roots = append(roots, l.Language().Lang)
}
} else {
l := conf.configs.GetFirstLanguageConfig()
- baseURLs = []string{l.BaseURL().String()}
+ baseURLs = []urls.BaseURL{l.BaseURL()}
roots = []string{""}
}
return nil
})
-
if err != nil {
return err
}
@@ -946,13 +939,9 @@ func (c *serverCommand) serve() error {
servers = append(servers, srv)
if doLiveReload {
- u, err := url.Parse(helpers.SanitizeURL(baseURLs[i]))
- if err != nil {
- return err
- }
-
- mu.HandleFunc(u.Path+"/livereload.js", livereload.ServeJS)
- mu.HandleFunc(u.Path+"/livereload", livereload.Handler)
+ baseURL := baseURLs[i]
+ mu.HandleFunc(baseURL.Path()+"livereload.js", livereload.ServeJS)
+ mu.HandleFunc(baseURL.Path()+"livereload", livereload.Handler)
}
c.r.Printf("Web Server is available at %s (bind address %s) %s\n", serverURL, c.serverInterface, roots[i])
wg1.Go(func() error {
@@ -971,8 +960,12 @@ func (c *serverCommand) serve() error {
if c.r.IsTestRun() {
// Write a .ready file to disk to signal ready status.
// This is where the test is run from.
+ var baseURLs []string
+ for _, baseURL := range srv.baseURLs {
+ baseURLs = append(baseURLs, baseURL.String())
+ }
testInfo := map[string]any{
- "baseURLs": srv.baseURLs,
+ "baseURLs": baseURLs,
}
dir := os.Getenv("WORK")
@@ -983,7 +976,7 @@ func (c *serverCommand) serve() error {
if err != nil {
return err
}
- err = os.WriteFile(readyFile, b, 0777)
+ err = os.WriteFile(readyFile, b, 0o777)
if err != nil {
return err
}
@@ -1167,7 +1160,7 @@ func cleanErrorLog(content string) string {
return strings.Join(keep, ": ")
}
-func injectLiveReloadScript(src io.Reader, baseURL url.URL) string {
+func injectLiveReloadScript(src io.Reader, baseURL *url.URL) string {
var b bytes.Buffer
chain := transform.Chain{livereloadinject.New(baseURL)}
chain.Apply(&b, src)